共计 10342 个字符,预计需要花费 26 分钟才能阅读完成。
简介
每过一段时间,就会有一个深度学习库被开发,这些深度学习库往往可以改变深度学习领域的景观。Pytorch 就是这样一个库。
在过去的一段时间里,我研究了 Pytorch,我惊叹于它的操作简易。Pytorch 是我迄今为止所使用的深度学习库中最灵活的,最轻松的。
在本文中,我们将以实践的方式来探索 Pytorch,包括基础知识与案例研究。我们会使用 numpy 和 Pytorch 分别从头开始构建神经网络,看看他们的相似之处。
提示:本文假设你已经对深度学习有一定的了解。如果你想深入学习深度学习,请先阅读本文。
内容目录
- Pytorch 概述
- 深层技术
- 在 Numpy 和 Pytorch 中构建神经网络
- 与其他深度学习库的比较
- 案例研究——使用 Pytorch 解决图像识别问题
Pytorch 概述
Pytorch 的创作者说他们有一种理念——他们希望工具能够即时运作,这也就以为这我们必须及时进行运算。这也非常适用于 python 的编程方式,因为我们不必去等到程序都编完而确定整个代码是否有效。我们可以轻松得运行部分代码并且可以实时检查。这个神经网络的软件,对我来说是非常好用的。
PyTorch 是一个基于 python 的库,旨在提供灵活性作为深度学习开发平台。PyTorch 的工作流程也尽可能接近 python 的科学计算库——numpy。
现在你可能会问,为什么我们会使用 PyTorch 来构建深度学习模型?我可以列出三个可能有助于回答这个问题的事情:
- 易于使用 API——它就像 python 一样简单。
- 支持 Python——就像上面所介绍的,Pytorch 与 python 数据科学堆栈平滑集成。它跟 numpy 非常相似,你可能都没有注意到它们的区别。
- 动态计算图形——Pytorch 不是使用特定功能的预定义图形,而是为我们提供了构建计算图形的框架,甚至可以在运行的时候更改它们。这对于我们不知道创建神经网络需要多少内存的情况非常有用。
使用 Pytorch 还有一些其他的优点,例如它的 multiGPU 支持,自定义数据加载器和简化的预处理器。
自 2016 年 1 月初发布以来,许多研究人员将其作为首选库,因为它易于构建新颖甚至极其复杂的图形。话虽如此,因为它比较新并且正在发展中,PyTorch 仍然需要一段时间才能被大多数数据科学从业者采用。
深层技术
在深入了解工作细节之前,让我们来看看 Pytorch 的工作流程。
Pytorch 构建图形所需的每行代码都定义了该图形的一个组件。即使在完全构建图形之前,我们也可以独立地对这些组件进行计算。这叫做“按运行定义”的方法。
安装 PyTorch 非常简单。您可以按照官方文档中提到的步骤操作,并根据您的系统规范运行命令。例如,这是我根据我选择的选项使用的命令:
conda install pytorch torchvision cuda91 -c pytorch
开始使用 Pytorch 时我们应该知道的主要元素是:
- Pytorch 张量
- 数学运算
- Autograd 模块
- Optim 模块
- nn 模块
下面,我们具体看看每块元素是什么情况。
Pytorch 张量
张量只不多是多维数组。Pytorch 中的张量类似于 numpy 的 ndarray,另外张量也可以在 GPU 上使用。Pytorch 支持各种类型的张量。
你可以定义一个简单的一位矩阵,如下所示:
import pytorch import torch # define a tensor torch.FloatTensor([2])
2 [torch.FloatTensor of size 1]
数学运算
与 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 3×3] matrix.t() 0.7162 -0.3503 -0.1093 1.0152 -0.9452 -0.0927 1.1525 -1.0861 -0.0476 [torch.FloatTensor of size 3×3]
Autograd 模块
Pytorch 使用了一种自动微分的技术。也就是说,我们有一个记录器记录我们执行过的操作,然后它会将操作往后执行以计算梯度。这种技术在构建神经网络时非常有用,因为我们通过计算前向传播本身的参数差异来节省一个周期的时间。
from torch.autograd import Variable x = Variable(train_x) y = Variable(train_y, requires_grad=False)
Optim 模块
torch.optim 是一个实现用于构建神经网络的各种优化算法的模块。已经支持了大多数的常用算法,所以我们可以免去从头开始构建它们的麻烦。
下面是使用 Adam optimizer 的代码:
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
nn 模块
Pytorch 中的 autograd 可以很容易定义图形并计算梯度,但原始的 autograd 对于定义复杂的神经网络来说可能有点过于低级了。这就是 nn 模块可以提供帮助的地方。
nn 扩展包定义了一组模块,我们可以把它看做成一个能从输入产生输出并且包含一些可训练的权重的神经网络层。
你其实可以把 nn 模块视作 Pytorch 的 keras!
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 与 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)
现在,让我们试试来寻找我们的简单案例在两种库中的不同(不同之处已注释)。
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)方面由于所有其他主要的深度学习库,在每个 epoch 下都具有最低的中值时间。
Pytorch 中用于数据加载的 API 设计得很好。数据集,采样器和数据加载器的接口都是特定的。
在比较 TensorFlow 中的数据加载工具(读取器,队列等)时,我发现 Pytorch 的数据加载模块非常易于使用。此外,我们在构建神经网络时,Pytorch 毫无缺陷的,我们并不需要依赖像 keras 这样的第三方高级库。
另一方面,我不太建议使用 Pytorch 进行部署,因为 Pytorch 尚未发展到这一步。正如 Pytorch 的开发人员所说:“我们看到的是用户首先创建一个 Pytorch 模型,当他们准备将他们的模型部署到生产中时,他们只需要将其转换成 Caffe2 模型,然后将其运送到其他平台。”
案例研究——在 Pytorch 中解决一个图像识别问题
我们下面来解决 Analytics Vidhya 社区里的深度学习问题——手写数字识别。我们先来看看问题是什么样子的:
我们的任务是识别图像,图像中是给定的 28*28 图像的阿拉伯数字。我们有一组用于训练的图像,其他的图像用于测试我们的模型。
首先,我们下载训练集与测试集。数据集包含所有图像的压缩文件,训练集和测试集对应的名字是 train.csv 和 test.csv。数据集中没有其他内容,仅仅是“.png”格式的原始图像。
下面让我们开始吧:
第一步:准备
a)导入所有会用到的库
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
b)让我们设置一个种子值,那么我们就可以控制我们的模型随机数了。
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:数据加载与处理
a)现在让我们来看我们的数据集,它是.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()
b)我们来看看数据集是什么样的,我们读取数据集中的图片并将它显示出来。
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()
d)为了便于数据操作,我们将所有图像存储为 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
e)由于这是一个典型的机器学习问题,我们创建了一个测试集来测试我们的模型是否能够正常运行。我们采用 7:3 的分割比例来设置训练集与测试集。
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:]
步骤二:建立模型
a)接下来就是主体部分了,让我们先来定义神经网络的架构。我们定义了一个具有输入层,隐藏层和输出层的 3 层神经网络。输入与输出中的神经元的数量是固定的,因为输入的是我们训练集中 28*28 的图像,输出的是十个类别。在隐藏层中我们设置了 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.001
b)下面该训练我们的模型了。
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
这是一个比较完美的结果了,尤其是当我们仅仅用了一个非常简单的神经网络模型并且只训练了 5 个周期。
结语
我希望这篇文章能够帮助你从如何构建神经网络模型的角度去了解 Pytorch。但是,文字有限,这里我们仅仅展示了很小的一方面。如果想深入研究,你可以阅读官方的 Pytorch 网站上的文档与教程。