Загрузка урока...
PyTorch — один из самых популярных фреймворков обучения нейронных сетей на сегодняшний день. Он основан на библиотеке Torch, которая, в свою очередь, предоставляет большое количество алгоритмов для глубинного обучения и научных расчётов. Одна из особенностей PyTorch — следование стилю Python и философии программирования на этом языке. Возможно, это самый «питоновский» из всех фреймворков для машинного обучения!
У PyTorch открытый исходный код и вокруг него выстроена экосистема, состоящая из различных библиотек, разрабатываемых сторонними командами. В результате получается комплексный и мощный инструмент для решения задач Deep Learning.
Выделают следующие важные особенности PyTorch:
Динамический граф вычислений
CUDA
Автоматическое дифференцирование
Давайте рассмотрим их подробнее!
Перед любым фреймворком глубинного обучения стоит три основных задачи:
Задавать граф вычислений
Дифференцировать его
И, конечно, вычислять!
Существуют следующие варианты для определения графа вычислений:
Через фиксированные модули
При таком подходе пользователь фреймворка комбинирует заранее подготовленные блоки в граф вычислений и запускает его. Прямой и обратный проходы уже прописаны в каждом блоке и определение новых блоков требует от пользователя наличия специфических навыков.
Если решаемая задача укладывается в заранее определенные модули, то тогда скорость разработки и время выполнения становится максимальными! Но как показала практика применения, много задач с которыми сталкиваются инженеры машинного обучения, имеют свои особенности, не предусмотренные разработчиками фреймворков. И в этом случае, затруднения с расширяемостью стандартных блоков становятся критическим фактором для применения такого подхода.
К фреймворкам с данным типом определения графа вычислений относятся: Caffe, Caffe2, CNTK, Kaldi, DL4J, Keras
Через статический граф вычислений
В данном случае пользователь имеет возможность создать граф вычислений произвольной структуры и размера. После того как все сконструированно происходит процесс компиляции и внести какие-либо изменения в финальную сборку не представляется возможным (доступны только два действия: запустить граф в прямом или обратном направлениях).
Фреймворки, декларирующие такой подход, с одной стороны дают скорость выполнения (за счет компиляции) и гибкость на этапе разработки, с другой стороны — отладка становится затруднительной.
К таким фреймворкам относятся: Theano, TensorFlow, MXNet
Через динамический граф вычислений
Практика показала, что для процесса разработки удобнее пересчитывать граф вычислений на лету. При таком подходе граф не существует как фиксированная структура, а динамически выстраивается непосредственно при прямом проходе для того, чтобы затем иметь возможность сделать проход обратный.
При каждом проходе граф перестраивается по новым условиям. В результате у разработчика меньше ограничений, и он может реализовать более сложные и нестандартные решения.
Torch и PyTorch как раз являются фреймворками реализующими данную парадигму.

Особенностью глубинного обучения является серьезная ресурсоемкость. Поэтому для обучения моделей начали использовать графические процессоры видеокарт (GPU) — оказалось, что их мощности и архитектура, оптимизированная для матричных вычислений, прекрасно подходят для данной задачи!
CUDA — это технология, разработанная NVIDIA, позволяющая запускать вычисления на видеокарте. PyTorch поддерживает CUDA из коробки и предоставляет одинаковый API для вычислений на CPU и на GPU.
Для выполнения операция на GPU достаточно указать следующее:
import torch
device = torch.device("cuda:0")Чтобы в дальнейшем использовать именно это устройство для вычислений, достаточно указать его в качестве аргумента при инициализации тензоров (подробнее про них расскажем в следующем уроке):
torch.randn(5, 5, device=device)Важно отметить следующее ограничение — CUDA работает только с видеокартами NVIDIA, поэтому не каждая конфигурация компьютера подойдет для ее использования.
Тензоры — это многомерные массивы, по своей структуре, больше всего схожие с матрицами. С их помощью строятся модели глубинного обучения, хранится и обрабатывается информация. Как раз графические процессоры (GPU) показывают свою эффективность при вычислении операций на тензорах!
Тензоры в PyTorch похожи на массивы в NumPy. Их можно создавать из Python-массивов или генерировать с помощью специальной функции. Значения внутри тензоров могут быть целочисленными, с плавающей точкой, знаковыми и беззнаковыми. Важно отметить, что в отличии от NumPy автоматического приведения типов в PyTorch нет, поэтому можно не опасаться, что без нашего ведома будет присвоен тип, занимающий слишком много памяти.
В PyTorch тензоры могут определяться несколькими способами. Например:
import torch
x = torch.Tensor(3, 4)Этот код создает тензор размера (3, 4), заполненный нулями:
0 0 0 0
0 0 0 0
0 0 0 0
[torch.FloatTensor of size 3x4]Выделяют следующие особенности тензоров в PyTorch:
они оптимизированы для автоматического дифференцирования (то есть поиска производной на каждом шаге)
их можно запускать и на основных процессорных мощностях, и на видеокарте
(а также перемещать с CPU на GPU и обратно!)
PyTorch использует автоматическое дифференцирование для вычислений градиентов, которые используются в алгоритме обратного распространения ошибки (backpropagation). Достоинством такого подхода является скорость вычисления. Автоматическое дифференцирование дополняет динамические вычисления. Получается комфортная схема для разработчика, в которой PyTorch самостоятельно построит граф и вычислит веса на лету.
Чтобы активировать автоматическое дифференцирование, следует в аргументе тензора указать requires_grad=True, либо вызвать метод requires_grad_(True). Рассмотрим на примере!
Дана функция f=(x+y)⋅zf=(x+y)⋅z. Подадим ей на вход x=−2,y=5,z=−4x=−2,y=5,z=−4.
Тогда наша функция будет представлена следующим графом ⬇️

Давайте посмотрим как в PyTorch можно посчитать ∂f∂x,∂f∂y,∂f∂z∂x∂f,∂y∂f,∂z∂f
Определим функцию как последовательность операций
x = torch.tensor(-2., requires_grad=True)
y = torch.tensor(5., requires_grad=True)
z = torch.tensor(-4., requires_grad=True)
q = x + y
f = q * zПо описанной последовательности операций на лету строится граф вычислений, и обратный проход выполняется по нему.
f.backward()
print('df/dz =', z.grad)
print('df/dx =', x.grad)
print('df/dy =', y.grad)Вызов метода backward() вычисляет градиенты для всех тензоров, у которых requires_grad == True.

Прежде чем переходить непосредственно к работе с тензорами, необходимо корректно установить саму библиотеку PyTorch. Для этого нужно перейти на сайт проекта и выбрать конфигурацию подходящую для вашей системы. Например:
Если вы работаете в Google Colab, то никаких предварительных действий не потребуется, фреймворк уже установлен заранее.
Как и всегда, начинаем c импорта библиотеки очевидной командой import torch.
Попробуем создать несколько тензоров. Тензоры с вещественными компонентами типа float32 создаются следующим образом:
v = torch.empty(5) # вектор из 5 элементов (заполнен "мусором")
m = torch.empty(3, 3) # матрица формы (3,3) (заполнена "мусором")
x = torch.empty_like(m) # матрица такой же формы как m (заполнена "мусором")Тензор можно также создать из списка Python или numpy-массива:
x = torch.tensor( [ [0, 1, 2], [3, 4, 5] ] ) # матрица размера (2, 3) типа int64
s = torch.tensor(123.) # скаляр (dim=0) типа float32
ss = torch.tensor(123, dtype=torch.double) # тоже скаляр, но типа float64
e = torch.from_numpy(np.eye(3)) # единичная матрица размера (3, 3)Основные свойства тензора можно получить следующим образом:
print( x.dim(), x.type(), x.numel() ) # размерность, тип элементов, их количество
print( x.size(), x.shape ) # torch.Size([2, 3])
print( tuple(x.shape), *x.shape ) # (2, 3)
print( s.item(), s.dim() ) # 123 0В этом материале нет файлов для просмотра.