Введение
Время от времени разрабатывается библиотека Python, которая может изменить ситуацию в области глубокого обучения. PyTorch - одна из таких библиотек.
В последние несколько недель я немного увлекся PyTorch. Я был потрясен тем, насколько легко ее понять. Среди различных библиотек глубокого обучения, которые я использовал до сих пор, PyTorch была самой гибкой и простой из всех.
В этой статье мы рассмотрим PyTorch с более практическим подходом, охватывая основы вместе с примерами. Мы также сравним нейронную сеть, созданную с нуля в numpy и в PyTorch, чтобы увидеть их сходство в реализации.
Примечание. В этой статье предполагается, что у вас есть базовые представления о глубоком обучении. Если вы хотите быстро освоить глубокое обучение, сначала прочтите эту статью.
Обзор PyTorch
Создатели PyTorch говорят, что у них есть философия - они хотят быть императивными. Это означает, что мы немедленно запускаем наши вычисления. Это вписывается в методологию программирования на Python, поскольку нам не нужно ждать, пока будет написан весь код, прежде чем мы узнаем, работает он или нет. Мы можем легко запустить часть кода и проверить ее в реальном времени. Для меня, отладчика нейронной сети, это благо!
PyTorch - это библиотека Python, созданная для обеспечения гибкости в качестве платформы разработки глубокого обучения. Рабочий процесс PyTorch максимально приближен к библиотеке научных вычислений Python - numpy.
Теперь вы можете спросить, почему нужно использовать именно PyTorch для построения моделей глубокого обучения? Я могу перечислить три вещи, которые могут помочь ответить на этот вопрос:
- Простой в использовании API - он настолько прост, насколько может быть прост Python.
- Поддержка Python - как упоминалось выше, PyTorch плавно интегрируется со стеком науки о данных Python. Он настолько похож на numpy, что вы можете даже не заметить разницы.
- Динамические графики вычислений - вместо предопределенных графиков с определенными функциями PyTorch предоставляет нам основу для построения графиков вычислений по ходу работы и даже изменения их во время выполнения. Это полезно в ситуациях, когда мы не знаем, сколько памяти потребуется для создания нейронной сети.
Еще несколько преимуществ использования PyTorch - это поддержка нескольких графических процессоров, пользовательские загрузчики данных и упрощенные препроцессоры.
С момента ее релиза в начале января 2016 года многие исследователи приняли ее в качестве полезной библиотеки из-за простоты построения новых и чрезвычайно сложных графиков. Тем не менее, пройдет еще некоторое время, прежде чем PyTorch будет принята большинством практиков в области науки о данных из-за того, что она новая и находится в стадии разработки.
Погружение в технические детали
Прежде чем углубляться в детали, давайте рассмотрим рабочий процесс PyTorch.
PyTorch использует парадигму императива. То есть каждая строка кода, необходимая для построения графика, определяет компонент этого графика. Мы можем независимо выполнять вычисления над этими компонентами, даже до того, как ваш график будет полностью построен. Это называется методологией «определения по запуску».
Source: http://pytorch.org/about/
Установить PyTorch довольно просто. Вы можете выполнить шаги, указанные в официальных документах, и запустить команду в соответствии со спецификациями вашей системы. Например, команда, которую я использовал на основе выбранных мной параметров:
conda install pytorch torchvision cuda91 -c pytorch
Основные элементы, которые мы должны знать, начиная работать с PyTorch:
- PyTorch Tensors
- Mathematical Operations
- Autograd module
- Optim module и
- nn module
Ниже мы подробно рассмотрим каждый из них.
PyTorch Tensors
Tensors - это ни что иное, как многомерные массивы. Tensors в PyTorch похожи на ndarrays numpy, с той лишь разницей, что тензоры можно использовать и на графическом процессоре. PyTorch поддерживает различные типы тензоров.
# import pytorch import torch # define a tensor torch.FloatTensor([2])
2 [torch.FloatTensor of size 1]
Mathematical Operations
Как и в случае с numpy, очень важно, чтобы библиотека научных вычислений имела эффективные реализации математических функций. PyTorch предоставляет вам аналогичный интерфейс с более чем 200 математическими операциями, которые вы можете использовать.
Ниже приведен пример простой операции сложения в PyTorch:
a = torch.FloatTensor([2]) b = torch.FloatTensor([3]) a + b
5 [torch.FloatTensor of size 1]
Разве это не похоже на типичный подход python? Мы также можем выполнять различные матричные операции с определенными тензорами PyTorch. Например, мы транспонируем двумерную матрицу:
matrix = torch.randn(3, 3) matrix 0.7162 1.0152 1.1525 -0.3503 -0.9452 -1.0861 -0.1093 -0.0927 -0.0476 [torch.FloatTensor of size 3x3] matrix.t() 0.7162 -0.3503 -0.1093 1.0152 -0.9452 -0.0927 1.1525 -1.0861 -0.0476 [torch.FloatTensor of size 3x3]
Autograd module
PyTorch использует метод, называемый автоматическим дифференцированием. То есть у нас есть регистратор, который записывает, какие операции мы выполнили, а затем воспроизводит их в обратном порядке для вычисления наших градиентов. Этот метод особенно эффективен при построении нейронных сетей, поскольку мы экономим время на одной эпохе, вычисляя дифференциацию параметров на самом прямом проходе.
Source: http://pytorch.org/about/
from torch.autograd import Variable x = Variable(train_x) y = Variable(train_y, requires_grad=False)
Optim module
torch.optim - модуль, реализующий различные алгоритмы оптимизации, используемые для построения нейронных сетей. Большинство часто используемых методов уже поддерживаются, поэтому нам не нужно создавать их с нуля (если вы этого не хотите!).
Ниже приведен код для использования оптимизатора Adam:
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
nn module
PyTorch autograd позволяет легко определять вычислительные графы и принимать градиенты, но исходный autograd может быть слишком низкоуровневым для определения сложных нейронных сетей. Здесь может помочь модуль nn.
Пакет nn определяет набор модулей, которые мы можем рассматривать как слой нейронной сети, который производит вывод из ввода и может иметь некоторые обучаемые веса.
Вы можете рассматривать модуль nn как основу PyTorch!
import torch # define model model = torch.nn.Sequential( torch.nn.Linear(input_num_units, hidden_num_units), torch.nn.ReLU(), torch.nn.Linear(hidden_num_units, output_num_units), ) loss_fn = torch.nn.CrossEntropyLoss()
Теперь, когда вы знаете основные компоненты PyTorch, вы можете легко создать свою собственную нейронную сеть с нуля. Следуйте за ним, если хотите знать, как это сделать!
Создание нейронной сети в Numpy vs. PyTorch
Я уже упоминал ранее, что PyTorch и Numpy очень похожи. Посмотрим, почему. В этом разделе мы увидим реализацию простой нейронной сети для решения задачи двоичной классификации (вы можете просмотреть эту статью, чтобы получить подробное объяснение).
## Neural network in numpy import numpy as np #Input array X=np.array([[1,0,1,0],[1,0,1,1],[0,1,0,1]]) #Output y=np.array([[1],[1],[0]]) #Sigmoid Function def sigmoid (x): return 1/(1 + np.exp(-x)) #Derivative of Sigmoid Function def derivatives_sigmoid(x): return x * (1 - x) #Variable initialization epoch=5000 #Setting training iterations lr=0.1 #Setting learning rate inputlayer_neurons = X.shape[1] #number of features in data set hiddenlayer_neurons = 3 #number of hidden layers neurons output_neurons = 1 #number of neurons at output layer #weight and bias initialization wh=np.random.uniform(size=(inputlayer_neurons,hiddenlayer_neurons)) bh=np.random.uniform(size=(1,hiddenlayer_neurons)) wout=np.random.uniform(size=(hiddenlayer_neurons,output_neurons)) bout=np.random.uniform(size=(1,output_neurons)) for i in range(epoch): #Forward Propogation hidden_layer_input1=np.dot(X,wh) hidden_layer_input=hidden_layer_input1 + bh hiddenlayer_activations = sigmoid(hidden_layer_input) output_layer_input1=np.dot(hiddenlayer_activations,wout) output_layer_input= output_layer_input1+ bout output = sigmoid(output_layer_input) #Backpropagation E = y-output slope_output_layer = derivatives_sigmoid(output) slope_hidden_layer = derivatives_sigmoid(hiddenlayer_activations) d_output = E * slope_output_layer Error_at_hidden_layer = d_output.dot(wout.T) d_hiddenlayer = Error_at_hidden_layer * slope_hidden_layer wout += hiddenlayer_activations.T.dot(d_output) *lr bout += np.sum(d_output, axis=0,keepdims=True) *lr wh += X.T.dot(d_hiddenlayer) *lr bh += np.sum(d_hiddenlayer, axis=0,keepdims=True) *lr print('actual :\n', y, '\n') print('predicted :\n', output)
Теперь попытайтесь найти разницу в супер простой реализации того же самого в PyTorch (различия выделены жирным шрифтом в приведенном ниже коде).
## neural network in pytorch import torch #Input array X = torch.Tensor([[1,0,1,0],[1,0,1,1],[0,1,0,1]]) #Output y = torch.Tensor([[1],[1],[0]]) #Sigmoid Function def sigmoid (x): return 1/(1 + torch.exp(-x)) #Derivative of Sigmoid Function def derivatives_sigmoid(x): return x * (1 - x) #Variable initialization epoch=5000 #Setting training iterations lr=0.1 #Setting learning rate inputlayer_neurons = X.shape[1] #number of features in data set hiddenlayer_neurons = 3 #number of hidden layers neurons output_neurons = 1 #number of neurons at output layer #weight and bias initialization wh=torch.randn(inputlayer_neurons, hiddenlayer_neurons).type(torch.FloatTensor) bh=torch.randn(1, hiddenlayer_neurons).type(torch.FloatTensor) wout=torch.randn(hiddenlayer_neurons, output_neurons) bout=torch.randn(1, output_neurons) for i in range(epoch): #Forward Propogation hidden_layer_input1 = torch.mm(X, wh) hidden_layer_input = hidden_layer_input1 + bh hidden_layer_activations = sigmoid(hidden_layer_input) output_layer_input1 = torch.mm(hidden_layer_activations, wout) output_layer_input = output_layer_input1 + bout output = sigmoid(output_layer_input1) #Backpropagation E = y-output slope_output_layer = derivatives_sigmoid(output) slope_hidden_layer = derivatives_sigmoid(hidden_layer_activations) d_output = E * slope_output_layer Error_at_hidden_layer = torch.mm(d_output, wout.t()) d_hiddenlayer = Error_at_hidden_layer * slope_hidden_layer wout += torch.mm(hidden_layer_activations.t(), d_output) *lr bout += d_output.sum() *lr wh += torch.mm(X.t(), d_hiddenlayer) *lr bh += d_output.sum() *lr print('actual :\n', y, '\n') print('predicted :\n', output)
Сравнение с другими библиотеками глубокого обучения
В одном сценарии тестирования успешно показано, что PyTorch превосходит все другие основные библиотеки глубокого обучения в обучении сети с долгой краткосрочной памятью (LSTM), имея самое низкое среднее время на эпоху.
API-интерфейсы для загрузки данных в PyTorch хорошо разработаны. Интерфейсы указываются в наборе данных, сэмплере и загрузчике данных.
Сравнивая инструменты для загрузки данных в TensorFlow (считыватели, очереди и т. д.), я обнаружил, что модули загрузки данных PyTorch довольно просты в использовании. Кроме того, PyTorch работает без проблем, когда мы пытаемся построить нейронную сеть, поэтому нам не нужно полагаться на сторонние высокоуровневые библиотеки, такие как keras.
С другой стороны, я бы пока не рекомендовал использовать PyTorch для развертывания. PyTorch еще предстоит развиваться. Как сказали разработчики PyTorch: «Мы видим, что пользователи сначала создают модель PyTorch. Когда они будут готовы запустить свою модель в производство, они просто преобразуют ее в модель Caffe 2, а затем отправляют либо на мобильную, либо на другую платформу».
Пример - решение проблемы распознавания изображений в PyTorch
Чтобы познакомиться с PyTorch, мы решим практическую задачу глубокого обучения в Analytics Vidhya - определение цифр. Давайте посмотрим на нашу постановку задачи:
Наша проблема - проблема распознавания изображений, чтобы идентифицировать цифры из изображений 28 x 28. У нас есть подмножество изображений для обучения, а остальные - для тестирования нашей модели.
Итак, сначала скачайте обучающий и тестовые файлы. Набор данных содержит заархивированный файл со всеми изображениями, и файлы train.csv и test.csv содержат имена соответствующих обучающих и тестовых изображений. Никаких дополнительных функций в наборах данных не предусмотрено, только необработанные изображения в формате «.png».
Давайте начнем:
ШАГ 0: Готовимся
а) Импортируйте все необходимые библиотеки
# import modules %pylab inline import os import numpy as np import pandas as pd from scipy.misc import imread from sklearn.metrics import accuracy_score
б) Давайте зададим начальное значение, чтобы мы могли контролировать случайность наших моделей.
# To stop potential randomness seed = 128 rng = np.random.RandomState(seed)
c) Первый шаг - задать пути к каталогам для сохранности!
root_dir = os.path.abspath('.') data_dir = os.path.join(root_dir, 'data') # check for existence os.path.exists(root_dir), os.path.exists(data_dir)
ШАГ 1: Загрузка и предварительная обработка данных
а) Теперь давайте прочитаем наши наборы данных. Они представлены в формате .csv и имеют имя файла с соответствующими метками.
# load dataset train = pd.read_csv(os.path.join(data_dir, 'Train', 'train.csv')) test = pd.read_csv(os.path.join(data_dir, 'Test.csv')) sample_submission = pd.read_csv(os.path.join(data_dir, 'Sample_Submission.csv')) train.head()
б) Давайте посмотрим, как выглядят наши данные! Читаем наше изображение и показываем его.
# print an image img_name = rng.choice(train.filename) filepath = os.path.join(data_dir, 'Train', 'Images', 'train', img_name) img = imread(filepath, flatten=True) pylab.imshow(img, cmap='gray') pylab.axis('off') pylab.show()
г) Для упрощения работы с данными давайте сохраним все изображения в виде массивов numpy.
# load images to create train and test set temp = [] for img_name in train.filename: image_path = os.path.join(data_dir, 'Train', 'Images', 'train', img_name) img = imread(image_path, flatten=True) img = img.astype('float32') temp.append(img) train_x = np.stack(temp) train_x /= 255.0 train_x = train_x.reshape(-1, 784).astype('float32') temp = [] for img_name in test.filename: image_path = os.path.join(data_dir, 'Train', 'Images', 'test', img_name) img = imread(image_path, flatten=True) img = img.astype('float32') temp.append(img) test_x = np.stack(temp) test_x /= 255.0 test_x = test_x.reshape(-1, 784).astype('float32') train_y = train.label.values
д) Поскольку это типичная проблема машинного обучения, для проверки правильности работы нашей модели мы создаем набор для проверки. Давайте возьмем размер разделения 70:30 для набора обучающих и тестовых данных.
# create validation set split_size = int(train_x.shape[0]*0.7) train_x, val_x = train_x[:split_size], train_x[split_size:] train_y, val_y = train_y[:split_size], train_y[split_size:]
ШАГ 2: Построение модели
а) Теперь самое главное! Давайте определим архитектуру нашей нейронной сети. Мы определяем нейронную сеть с 3 уровнями: входной, скрытый и выходной. Количество нейронов на входе и выходе фиксировано, так как на входе используется изображение 28 x 28, а на выходе - вектор 10 x 1, представляющий класс. Задаем 50 нейронов в скрытом слое. Здесь мы используем в качестве алгоритма оптимизации Adam, который является эффективным вариантом алгоритма градиентного спуска.
import torch from torch.autograd import Variable
# number of neurons in each layer input_num_units = 28*28 hidden_num_units = 500 output_num_units = 10 # set remaining variables epochs = 5 batch_size = 128 learning_rate = 0.00
б) Время обучить нашу модель
# define model model = torch.nn.Sequential( torch.nn.Linear(input_num_units, hidden_num_units), torch.nn.ReLU(), torch.nn.Linear(hidden_num_units, output_num_units), ) loss_fn = torch.nn.CrossEntropyLoss() # define optimization algorithm optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
## helper functions # preprocess a batch of dataset def preproc(unclean_batch_x): """Convert values to range 0-1""" temp_batch = unclean_batch_x / unclean_batch_x.max() return temp_batch # create a batch def batch_creator(batch_size): dataset_name = 'train' dataset_length = train_x.shape[0] batch_mask = rng.choice(dataset_length, batch_size) batch_x = eval(dataset_name + '_x')[batch_mask] batch_x = preproc(batch_x) if dataset_name == 'train': batch_y = eval(dataset_name).ix[batch_mask, 'label'].values return batch_x, batch_y
# train network total_batch = int(train.shape[0]/batch_size) for epoch in range(epochs): avg_cost = 0 for i in range(total_batch): # create batch batch_x, batch_y = batch_creator(batch_size) # pass that batch for training x, y = Variable(torch.from_numpy(batch_x)), Variable(torch.from_numpy(batch_y), requires_grad=False) pred = model(x) # get loss loss = loss_fn(pred, y) # perform backpropagation loss.backward() optimizer.step() avg_cost += loss.data[0]/total_batch print(epoch, avg_cost)
# get training accuracy x, y = Variable(torch.from_numpy(preproc(train_x))), Variable(torch.from_numpy(train_y), requires_grad=False) pred = model(x) final_pred = np.argmax(pred.data.numpy(), axis=1) accuracy_score(train_y, final_pred)
# get validation accuracy x, y = Variable(torch.from_numpy(preproc(val_x))), Variable(torch.from_numpy(val_y), requires_grad=False) pred = model(x) final_pred = np.argmax(pred.data.numpy(), axis=1) accuracy_score(val_y, final_pred)
Результат обучения:
0.8779008746355685
тогда как оценка валидации:
0.867482993197279
Это довольно впечатляющий результат, особенно если учесть, что мы обучили очень простую нейронную сеть всего за пять эпох!
Заключение
Я надеюсь, что эта статья дала вам представление о том, как PyTorch может изменить перспективу построения моделей глубокого обучения. В этой статье мы коснулись лишь поверхности. Чтобы погрузиться глубже, вы можете почитать документацию и руководства на самой официальной странице PyTorch.
Комментариев нет:
Отправить комментарий