Серия PyTorch | Как ускорить обучение модели?

PyTorch

оригинальное название| Ускорьте свои алгоритмы, часть 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.

Каталог этой статьи выглядит следующим образом:

  1. как проверитьcudaон годен или нет?
  2. Как получить большеcudaИнформация об устройстве?
  3. Способы хранения тензоров и запуска моделей на GPU
  4. Когда есть несколько графических процессоров, как их выбрать и использовать
  5. параллелизм данных
  6. Параллельное сравнение данных
  7. 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)

Если вы хотите полностью использовать несколько графических процессоров, вам следует сделать следующее:

  1. Используйте все графические процессоры для разных задач или приложений;
  2. В мультимодели каждый GPU применяет одну модель, и у каждого есть копия данных, которая была завершена операциями предварительной обработки;
  3. Каждый GPU использует копию нарезанных входных данных и модели, и каждый GPU будет вычислять результаты отдельно и отправлять результаты на тот же GPU для дальнейших операций.

5. Параллелизм данных

Параллельные операции с данными требуют от нас разделения данных на несколько частей и отправки их на несколько графических процессоров для параллельных вычислений.

Параллельные операции с данными в PyTorch могут быть достигнуты с помощьюtorch.nn.DataParallel.

Ниже приведен простой пример. Для достижения параллелизма данных первый подход заключается в использованииnn.parallelНесколько функций в , функции, реализованные соответственно, следующие:

  1. Репликация: копирование модели на несколько графических процессоров;
  2. Распределение (Scatter): разделите входные данные на несколько копий в соответствии с их первым измерением (обычно размером пакета) и отправьте их на несколько графических процессоров;
  3. Сбор: данные, отправленные обратно с нескольких графических процессоров, снова соединены вместе;
  4. Параллельное приложение (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()

Дополнительные примеры использования можно найти в официальной документации:

py torch.org/docs/stable…

Ссылаться на:

  1. documen.tician.de/pycuda/
  2. py torch.org/docs/stable…
  3. обсудить.py torch.org/he/how-to-eat…
  4. py torch.org/tutorials/ нет…
  5. medium.com/@ilakarma N…

Добро пожаловать в мой общедоступный аккаунт WeChat--Рост алгоритма обезьяны, или отсканируйте QR-код ниже, чтобы общаться, учиться и развиваться вместе!