оригинальное название| Ускорьте свои алгоритмы, часть 1 — PyTorch
автор | Puneet Grover
переводчик| kbsc13 (автор публичного аккаунта «The Growth of Algorithm Apes»)
оригинальный | к data science.com/speed-up-yo…
утверждение| Перевод предназначен для общения и обучения, добро пожаловать на перепечатку, но, пожалуйста, сохраните эту статью для коммерческих или незаконных целей.
предисловие
Эта статья в основном расскажет, как принятьcuda
иpycuda
Проверяйте, инициализируйте устройства GPU и ускоряйте работу своих алгоритмов.
PyTorch
даtorch
Версия на Python, которая представляет собой фреймворк для глубокого обучения, разработанный и открытый исследовательской группой искусственного интеллекта Facebook, также является очень популярным фреймворком, особенно среди исследователей, которые догнали его всего за несколько лет.Tensorflow
тенденция. Это в основном связано с преимуществами его простого динамического вычислительного графа.
pycuda
Являетсяpython
Сторонняя библиотека для работы с API параллельных вычислений Nvidia CUDA.
Каталог этой статьи выглядит следующим образом:
- как проверить
cuda
он годен или нет? - Как получить больше
cuda
Информация об устройстве? - Способы хранения тензоров и запуска моделей на GPU
- Когда есть несколько графических процессоров, как их выбрать и использовать
- параллелизм данных
- Параллельное сравнение данных
- torch.multiprocessing
Код в этой статье использует блокнот Jupyter, а адрес Github:
NB viewer.Fear Intel.org/GitHub/PU...
1. Как проверить, доступна ли cuda?
экзаменcuda
Код того, доступен ли он, очень прост и выглядит так:
import torch
torch.cuda.is_available()
# True
2. Как получить больше информации об устройствах cuda?
Чтобы получить основную информацию об устройстве, используйтеtorch.cuda
Да, но если вы хотите получить более подробную информацию, вам нужно использоватьpycuda
.
Реализованный код выглядит так:
import torch
import pycuda.driver as cuda
cuda.init()
## Get Id of default device
torch.cuda.current_device()
# 0
cuda.Device(0).name() # '0' is the id of your GPU
# Tesla K80
Или вот так:
torch.cuda.get_device_name(0) # Get name device with ID '0'
# 'Tesla K80'
Вот простой класс, написанный для полученияcuda
Информация:
# A simple class to know about your cuda devices
import pycuda.driver as cuda
import pycuda.autoinit # Necessary for using its functions
cuda.init() # Necesarry for using its functions
class aboutCudaDevices():
def __init__(self):
pass
def num_devices(self):
"""返回 cuda 设备的数量"""
return cuda.Device.count()
def devices(self):
"""获取所有可用的设备的名称"""
num = cuda.Device.count()
print("%d device(s) found:"%num)
for i in range(num):
print(cuda.Device(i).name(), "(Id: %d)"%i)
def mem_info(self):
"""获取所有设备的总内存和可用内存"""
available, total = cuda.mem_get_info()
print("Available: %.2f GB\nTotal: %.2f GB"%(available/1e9, total/1e9))
def attributes(self, device_id=0):
"""返回指定 id 的设备的属性信息"""
return cuda.Device(device_id).get_attributes()
def __repr__(self):
"""输出设备的数量和其id、内存信息"""
num = cuda.Device.count()
string = ""
string += ("%d device(s) found:\n"%num)
for i in range(num):
string += ( " %d) %s (Id: %d)\n"%((i+1),cuda.Device(i).name(),i))
string += (" Memory: %.2f GB\n"%(cuda.Device(i).total_memory()/1e9))
return string
# You can print output just by typing its name (__repr__):
aboutCudaDevices()
# 1 device(s) found:
# 1) Tesla K80 (Id: 0)
# Memory: 12.00 GB
Если вы хотите узнать текущее использование памяти, код запроса выглядит следующим образом:
import torch
# Returns the current GPU memory usage by
# tensors in bytes for a given device
# 返回当前使用的 GPU 内存,单位是字节
torch.cuda.memory_allocated()
# Returns the current GPU memory managed by the
# caching allocator in bytes for a given device
# 返回当前缓存分配器中的 GPU 内存
torch.cuda.memory_cached()
пустойcuda
Кэшированный код выглядит так:
# Releases all unoccupied cached memory currently held by
# the caching allocator so that those can be used in other
# GPU application and visible in nvidia-smi
# 释放所有非占用的内存
torch.cuda.empty_cache()
Но следует отметить, что вышеуказанная функция не освобождаетtensors
занятой памяти графического процессора и, следовательно, не увеличивает доступную в данный момент память графического процессора.
3. Способы хранения тензоров и запуска моделей на GPU
Если вы хотите сохранить переменную в процессоре, вы можете записать ее, как показано в следующем коде:
a = torch.DoubleTensor([1., 2.])
Переменнаяa
Будет оставаться на процессоре и выполнять различные операции на процессоре, если вы хотите преобразовать его в графический процессор, вам нужно использовать.cuda
, есть два способа достижения
# 方法1
a = torch.FloatTensor([1., 2.]).cuda()
# 方法2
a = torch.cuda.FloatTensor([1., 2.])
Этот подход выберет первый графический процессор по умолчанию, который можно просмотреть следующими двумя способами:
# 方法1
torch.cuda.current_device()
# 0
# 方法2
a.get_device()
# 0
Кроме того, вы также можете запустить модель на графическом процессоре, пример выглядит следующим образом, простое использованиеnn.Sequential
Определите модель:
sq = nn.Sequential(
nn.Linear(20, 20),
nn.ReLU(),
nn.Linear(20, 4),
nn.Softmax()
)
Затем поместите его на GPU и запустите:
model = sq.cuda()
Как определить, выполняется ли модель на графическом процессоре Проверить, выполняются ли параметры модели на графическом процессоре, можно следующими способами:
# From the discussions here: discuss.pytorch.org/t/how-to-check-if-model-is-on-cuda
# 参考 https://discuss.pytorch.org/t/how-to-check-if-model-is-on-cuda/180
next(model.parameters()).is_cuda
# True
4. Когда есть несколько графических процессоров, как их выбрать и использовать
Предполагая, что есть 3 графических процессора, мы можем инициализировать и выделитьtensors
Для любого указанного GPU код выглядит следующим образом, вот распределениеtensors
К указанному GPU есть 3 метода:
- При инициализации тензора укажите параметры
device
.to(cuda_id)
.cuda(cuda_id)
cuda0 = torch.device('cuda:0')
cuda1 = torch.device('cuda:1')
cuda2 = torch.device('cuda:2')
# 如果只是采用 .cuda() 方法,默认是放到 cuda:0 的 GPU 上
# 下面是 3 种实现方法
x = torch.Tensor([1., 2.], device=cuda1)
# Or
x = torch.Tensor([1., 2.]).to(cuda1)
# Or
x = torch.Tensor([1., 2.]).cuda(cuda1)
# 修改默认的设备方法,输入希望设置为默认设备的 id
torch.cuda.set_device(2)
# 调用环境变量 CUDA_VISIBLE_DEVICES,可以设置想采用的 GPU 的数量和哪几个 GPU
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0,2"
При наличии нескольких GPU можно разделить работу приложения, но есть проблема общения друг с другом, но если вам не нужно часто обмениваться информацией, то эту проблему можно проигнорировать.
На самом деле проблема в другом, т.PyTorch
Все операции GPU по умолчанию являются асинхронными операциями. Но копирование данных между CPU и GPU или между двумя GPU необходимо синхронизировать, когда вы передаете функциюtorch.cuda.Stream()
Вы должны знать об этой проблеме синхронизации при создании собственных потоков.
Ниже приводится официальный документ о предыдущемнеправильный пример:
cuda = torch.device('cuda')
# 创建一个流
s = torch.cuda.Stream()
A = torch.empty((100, 100), device=cuda).normal_(0.0, 1.0)
with torch.cuda.stream(s):
# because sum() may start execution before normal_() finishes!
# sum() 操作可能在 normal_() 结束前就可以执行了
B = torch.sum(A)
Если вы хотите полностью использовать несколько графических процессоров, вам следует сделать следующее:
- Используйте все графические процессоры для разных задач или приложений;
- В мультимодели каждый GPU применяет одну модель, и у каждого есть копия данных, которая была завершена операциями предварительной обработки;
- Каждый GPU использует копию нарезанных входных данных и модели, и каждый GPU будет вычислять результаты отдельно и отправлять результаты на тот же GPU для дальнейших операций.
5. Параллелизм данных
Параллельные операции с данными требуют от нас разделения данных на несколько частей и отправки их на несколько графических процессоров для параллельных вычислений.
Параллельные операции с данными в PyTorch могут быть достигнуты с помощьюtorch.nn.DataParallel
.
Ниже приведен простой пример. Для достижения параллелизма данных первый подход заключается в использованииnn.parallel
Несколько функций в , функции, реализованные соответственно, следующие:
- Репликация: копирование модели на несколько графических процессоров;
- Распределение (Scatter): разделите входные данные на несколько копий в соответствии с их первым измерением (обычно размером пакета) и отправьте их на несколько графических процессоров;
- Сбор: данные, отправленные обратно с нескольких графических процессоров, снова соединены вместе;
- Параллельное приложение (parallel_apply): примените распределенные входные данные, полученные на третьем этапе, к нескольким моделям, скопированным на первом этапе.
Код реализации выглядит следующим образом:
# Replicate module to devices in device_ids
replicas = nn.parallel.replicate(module, device_ids)
# Distribute input to devices in device_ids
inputs = nn.parallel.scatter(input, device_ids)
# Apply the models to corresponding inputs
outputs = nn.parallel.parallel_apply(replicas, inputs)
# Gather result from all devices to output_device
result = nn.parallel.gather(outputs, output_device)
На самом деле существует более простой и часто используемый метод реализации, как показано ниже, для которого требуется всего одна строка кода:
model = nn.DataParallel(model, device_ids=device_ids)
result = model(input)
6. Сравнение параллелизма данных
Согласно статьеmedium.com/@ilakarma N…и Гитхаб:GitHub.com/IL karma N/D О…Полученные результаты сравнения скорости вычислений разных фреймворков при использовании одного GPU и 4-х GPU следующие:
Из рисунка видно, что, несмотря на наличие проблемы связи между несколькими графическими процессорами, скорость параллельной работы данных все же очевидна. иPyTorch
Скорость работы уступает толькоChainer
, но его параллельный подход к данным настолько прост, что его можно реализовать с помощью одной строки кода.
7. torch.multiprocessing
torch.multiprocessing
для Pythonmultiprocessing
Инкапсуляция модуля и процентная совместимость с исходным модулем, то есть он может использовать исходный модуль, такой какQueue
,Pipe
,Array
и другие методы. А чтобы ускорить процесс, был добавлен новый метод --share_memory_()
, что позволяет данным находиться в особом состоянии, которое любой процесс может использовать напрямую, не копируя его.
Таким образом можно разделитьTensors
, параметры моделиparameters
, который может быть разделен между процессорами или графическими процессорами.
Ниже показан пример обучения модели с несколькими процессами:
# Training a model using multiple processes:
import torch.multiprocessing as mp
def train(model):
for data, labels in data_loader:
optimizer.zero_grad()
loss_fn(model(data), labels).backward()
optimizer.step() # This will update the shared parameters
model = nn.Sequential(nn.Linear(n_in, n_h1),
nn.ReLU(),
nn.Linear(n_h1, n_out))
model.share_memory() # Required for 'fork' method to work
processes = []
for i in range(4): # No. of processes
p = mp.Process(target=train, args=(model,))
p.start()
processes.append(p)
for p in processes:
p.join()
Дополнительные примеры использования можно найти в официальной документации:
Ссылаться на:
- documen.tician.de/pycuda/
- py torch.org/docs/stable…
- обсудить.py torch.org/he/how-to-eat…
- py torch.org/tutorials/ нет…
- medium.com/@ilakarma N…
Добро пожаловать в мой общедоступный аккаунт WeChat--Рост алгоритма обезьяны, или отсканируйте QR-код ниже, чтобы общаться, учиться и развиваться вместе!