Главная » Хабрахабр » Dive into pyTorch

Dive into pyTorch

Меня зовут Артур Кадурин, я руковожу исследованиями в области глубокого обучения для разработки новых лекарственных препаратов в компании Insilico Medicine. Всем привет. В Insilico мы используем самые современные методы машинного обучения, а также сами разрабатываем и публикуем множество статей для того чтобы вылечить такие заболевания как рак или болезнь Альцгеймера, а возможно и старение как таковое.

Эта серия статей не будет очередным обзором GANов(Generative Adversarial Networks), но позволит глубже заглянуть под капот нейронных сетей и охватит более широкий спектр архитектур. В рамках подготовки своего курса по глубокому обучению я собираюсь опубликовать серию статей на тему Состязательных(Adversarial) сетей с разбором того что же это такое и как этим пользоваться. Хотя GANы мы конечно тоже разберем.

Хочу сразу заметить, что это не введение в нейронные сети, поэтому я исхожу из того, что вы уже знаете такие слова как "слой", "батч", "бэкпроп" и т.д. Для того чтобы дальше беспрепятственно обсуждать состязательные сети я решил сначала сделать небольшое введение в pyTorch. Помимо базовых знаний о нейросетях, вам, конечно, понадобится понимание языка python.

Если вы захотите запускать обучение на видеокарте, то для видеокарт от NVIDIA вам потребуется nvidia-docker, думаю с этой частью у большинства из вас проблем не будет, поэтому остальное я оставляю вам. Для того чтобы было удобно пользоваться pyTorch я подготовил докер-контейнер с jupyter'ом и кодом в ноутбуках.

Все необходимое для этого поста доступно в моем репозитории spoilt333/adversarial с тегом intro на Docker Hub, или в моем репозитории на GitHub.

После установки докера запустить контейнер можно например с помощью такой команды:

docker run -id --name intro -p 8765:8765 spoilt333/adversarial:intro

0. В контейнере автоматически запустится сервер jupyter'а, который будет доступен по http://127. 1:8765 с паролем "password"(без кавычек). 0. Если вы не хотите запускать чужой контейнер у себя на машине(правильно!), то собрать свой такой же, предварительно проверив что там все ок, можно из докерфайла который есть в репозитории на GitHub.

pyTorch — это большой фреймворк позволяющий создавать динамические графы вычислений и автоматически вычислять градиенты по этим графам. Если у вас все запустилось и вы смогли подключиться к jupyter, то давайте перейдем к тому что же из себя представляет pyTorch. Но, помимо самой возможности обучать модели, pyTorch это еще и огромная библиотека включающая датасеты, готовые модели, современные слои и комьюнити вокруг всего этого. Для машинного обучения это как раз то что нужно.

Датасет MNIST представляет из себя 70. В Deep Learning, довольно продолжительное время, было практически стандартом тестировать все новые модели на задаче распознавания рукописных цифр. Он сразу же разбит на тренировочное и тестовое множества для того чтобы обеспечить одинаковые условия всем кто тестируется на этом датасете. 000 размеченных рукописных цифр примерно поровну распределенных между классами. Несмотря на то что сравнивать между собой state-of-the-art модели на этом датасете уже не имеет большого смысла, для демонстрационных целей он нам подойдет идеально. В pyTorch, естественно, для него есть простые интерфейсы.

Примеры цифр из MNIST

И, как нетрудно заметить, далеко не все цифры легко может "распознать" даже человек. Каждый пример в MNIST представляет из себя изображение размером 28х28 пикселей в оттенках серого. Но давайте перейдем к основному "блюду". В ноутбуке mnist.ipynb вы можете посмотреть на пример загрузки и отображения датасета, а несколько полезных функций вынесены в файл utils.py.

Один из способов сделать нейронную сеть с помощью pyTorch — это наследоваться от класса nn. В ноутбуке mnist-basic.ipynb реализована двухслойная полносвязная нейронная сеть решающая задачу классификации. Module и реализовать свои функции инициализации и forward

def __init__(self): super(Net, self).__init__() self.fc1 = nn.Linear(784, 100) self.fc2 = nn.Linear(100, 10)

В нашем случае это линейные слои nn. Внутри функции __init__ мы объявляем слои будущей нейронной сети. Эти самые веса и будут "обучаться" в процессе тренировки нейронной сети. Linear которые имеют вид W'x+b, где W — матрица весов размером (input, output) и b — вектор смещения размером output.

def forward(self, x): x = x.view(-1, 28*28) x = F.relu(self.fc1(x)) x = self.fc2(x) x = F.softmax(x, dim=1) return x

Для простоты примера мы будем работать с примерами из MNIST не как с изображениями, а как с векторами каждая размерность которых соответствует одному из пикселей. Метод forward используется непосредственно для преобразования входных данных с помощью заданной нейройнной сети в ее выходы. "-1" в качестве первого аргумента функции означает, что количество элементов в первой размерности будет вычислено автоматически. Функция view() это аналог numpy.reshape(), она переиндексирует тензор с данными заданным образом. Если исходный тензор x имеет размерность (N, 28, 28), то после

x = x.view(-1, 28*28)

его размерность станет равна (N, 784).

x = F.relu(self.fc1(x))

Аналогичным образом устроены и функции активации. Применение слоев к данным в pyTorch реализовано максимально просто, вы можете "вызвать" слой передав ему в качестве аргумента батч данных и получить на выходе результат преобразования. В данном случае я использую relu, так как это наиболее популярная функция активации в задачах компьютерного зрения, однако вы легко можете поэкспериментировать с другими реализованными в pyTorch функциями, благо их там достаточно.

x = self.fc2(x)
x = F.softmax(x, dim=1)

В качестве функции активации на выходе сети мы используем softmax. Так как мы решаем задачу классификации на 10 классов, то и выход нашей сети имеет размерность 10. Теперь значения которые возвращает функция forward можно интерпретировать как вероятности того что входной пример принадлежит к соответствующим классам.

model = Net()
optimizer = optim.SGD(model.parameters(), lr=0.01)

Для того чтобы получился симпатичный график обучения я выбрал обыкновенный стохастический градиентный спуск, но в pyTorch, конечно же, реализованы и более продвинутые методы. Теперь мы можем создать экземпляр нашей сети и выбрать функцию оптимизации. Вы можете попробовать например RMSProp или Adam.

def train(epoch): for data, target in train_loader: data, target = Variable(data), Variable(target) optimizer.zero_grad() output = model(data) loss = F.cross_entropy(output, target) loss.backward() optimizer.step()

data — это примеры, а target — соответствующие метки. Функция train содержит основной цикл обучения в котором мы итерируемся по батчам из тренировочного множества. Для того чтобы pyTorch смог корректно считать градиенты мы оборачиваем данные в класс Variable, и в начале каждой итерации обнуляем текущее значение градиентов:

data, target = Variable(data), Variable(target)
optimizer.zero_grad()

За вызовом model(data) скрыт вызов функции forward, поэтому в output попадают выходы сети. Обработка данных всей сетью в pyTorch ничем не отличается от применения отдельного слоя. Теперь остается только посчитать значение функции ошибки и сделать шаг обратного распространения:

loss = F.cross_entropy(output, target)
loss.backward()

Для того чтобы обновить веса мы вызываем optimizer.step(), который опираясь на свои параметры(у нас это learning rate) обновляет веса. На самом деле, при вызове loss.backward() веса сети еще не обновляются, но для всех весов использовавшихся при вычислении ошибки pyTorch считает градиенты используя построенный граф вычислений.

Вот пример из тестового множества с предсказанными ответами После 20 эпох обучения наша сеть угадывает цифры с точностью 91%, что, конечно, далеко от SOTA результатов, однако, весьма неплохо для 5 минут программирования.

[[1 1 5 2 4 6 9 9 9 9] [2 3 5 4 4 1 3 2 4 7] [5 0 3 9 4 5 3 2 3 2] [0 3 8 2 5 5 8 7 8 6] [8 3 6 8 4 8 5 1 3 9]]

В следующих постах я постараюсь рассказать о состязательных сетях в таком же стиле с примерами кода и подготовленными докер-контейнерами, в частности я планирую коснуться таких тем как domain adaptation, style transfer, generative adversarial networks и разобрать несколько наиболее важных статей в этой области.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Справочная: Яндекс.Телефон

Чтобы без прикрас — и про раздражающий вибромотор, и про классное решение с цветовым фильтром, и про глянцевый корпус, собирающий отпечатки, и про милую Я-скрепку, которая неуловимо похожа на автомат Калашникова. Комментарии под постом «Яндекса» про их «Телефон» ясно дали ...

Что если разделение прибыли 30/70 перестанет быть стандартом геймдева?

На середине разработки игры могут поменяться движок, жанр, сюжет и сеттинг, но одно известно точно — когда игра выйдет, магазины заберут 30% прибыли. Геймдев — индустрия полная неопределенностей. С одной стороны его диктуют закрытые платформы, вроде игровых консолей или айфонов, ...