从基础概念到实现:入门PyTorch深度神经网络框架

PyTorch的构建者表明,PyTorch的哲学是解决当务之急,也就是说即时构建和运行我们的计算图。这恰好适合Python的编程方法,因为我们不需要等待整个代码都被写入才能知道是否起作用。我们很容易运行部分代码,并实时检查它。

PyTorch是一个基于Python的库,旨在为深度学习提供一个灵活的开发平台.PyTorch的工作流程非常接近于Python的科学计算库NumPy。那么为什么我们需要使用PyTorch构建深度学习模型?以下作者根据实际经验提供了三个理由:

  • 便于使用的API:它的使用如同Python那样简单。

  • 支持Python:正如上文所述,PyTorch可以平滑地与Python数据科学栈相结合。它与NumPy一样简单,甚至我们都感觉不出它们的区别。

  • 动态计算图:PyTorch不再使用特定的函数预定义计算图,而是提供构造动态计算图的框架,甚至我们可以在运行时修正它们。这种动态框架在我们不知道所构建的神经网络需要多少内存时非常有用。

其它一些使用PyTorch的优点还有多GPU支持,自定义数据加载器和极简的预处理过程等。自从它在2016年1月份发布以来,许多研究者将其采用为标准的实现库,因为它构造新颖的,极其复杂的计算图同样非常简单。即使这样,PyTorch被主流数据科学家和研究员接收还是花了很长时间,因为它目前仍然是新的项目,且还有很多地方需要构建与完善。

PyTorch基础

在讨论PyTorch的各个组件前,我们需要了解它的工作流.PyTorch使用一种称之为imperative / eager的范式,即每一行代码都要求构建一个图以定义完整计算图的一个部分。即使完整的计算图还没有完成构建,我们也可以独立地执行这些作为组件的小计算图,这种动态计算图被称为「限定逐运行」方法。

从基础概念到实现:入门PyTorch深度神经网络框架

更多介绍请查看:HTTP://pytorch.org/about/

安装PyTorch非常简单,我们可以按照自己的系统跟随官方文档的步骤轻松完成。例如以下选择在Linux,Python 3.5和CUDA 9.1的环境下安装PyTorch:

从基础概念到实现:入门PyTorch深度神经网络框架

conda安装pytorch torchvision cuda91 -c pytorch

我们在基础部分主要需要了解的PyTorch元量有PyTorch张量,数学运算,自动求导模块,最优化模块和神经网络模块。下面本文会依次对这些模块进行简要的介绍:

PyTorch张量

张量其实就是多维数组,PyTorch中的张量非常类似于NumPy中的Ndarry,只不过张量可以用于GPU.PyTorch支持多种类型的张量,我们可以如下简单地定义一个一维矩阵:

# import pytorchimport torch# define a tensortorch.FloatTensor([2])2[torch.FloatTensor of size 1]

数学运算

如NumPy一样,高效地实现数学函数对于科学计算库至关重要.PyTorch提供了一个简单的接口,并支持200多种数学运算,以下是PyTorch实现简单加运算的过程:

a = torch.FloatTensor([2])b = torch.FloatTensor([3])a + b5
[torch.FloatTensor of size 1]

这种运算与Python非常像,我们可以在定义的PyTorch张量上执行多种矩阵运算。例如我们可以转置二维张量:

matrix = torch.randn(3, 3)matrix-1.3531 -0.5394 0.89341.7457 -0.6291 -0.0484-1.3502 -0.6439 -1.5652[torch.FloatTensor of size 3x3]matrix.t()-2.1139 1.8278 0.19760.6236 0.3525 0.2660-1.4604 0.8982 0.0428[torch.FloatTensor of size 3x3]

AutoGrad模块

PyTorch使用的技术为自动微分(自动微分)。在这种机制下,系统会有一个Recorder来记录我们执行的运算,然后再反向计算对应的梯度。这种技术在构建神经网络的过程中十分强大,因为我们可以通过计算前向传播过程中参数的微分来节省时间。

from torch.autograd import Variablex = Variable(train_x)y = Variable(train_y, requires_grad=False)

从基础概念到实现:入门PyTorch深度神经网络框架

最优化模块

torch.optim是实现神经网络中多种优化算法的模块,它目前已经支持大多数一般的方法,所以我们不需要从头构建优化算法。以下展示了使用Adam优化器的基本代码:

optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

神经网络模块

PyTorch AutoGrad使得计算图的定义和梯度的计算十分简单,但原版的AutoGrad可能对定义复杂的神经网络显得太底层,因此我们需要神经网络模块帮助简化工作。该nn包定义了一组函数,我们可以将其视为有一些可训练权重的神经网络层级。我们也可以将此神经网络模块视为类似于Keras的PyTorch组件。

import torch# define modelmodel = 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的基本组件,我们可以使用它们快速构建神经网络。当然以上只是简单的概念介绍,每一个模块都有非常多的函数与方法,读者可以详细查阅PyTorch文档了解更多。

构建神经网络(NumPy与PyTorch)

在这一部分中,我们分别使用NumPy和PyTorch构造简单的神经网络以实现二元分类问题,本文的后面会对这一部分的代码进行解释。

## Neural network in numpyimport numpy as np#Input arrayX=np.array([[1,0,1,0],[1,0,1,1],[0,1,0,1]])#Outputy=np.array([[1],[1],[0]])#Sigmoid Functiondef sigmoid (x):return 1/(1 + np.exp(-x))#Derivative of Sigmoid Functiondef derivatives_sigmoid(x):return x * (1 - x)#Variable initializationepoch=5000 #Setting training iterationslr=0.1 #Setting learning rateinputlayer_neurons = X.shape[1] #number of features in data sethiddenlayer_neurons = 3 #number of hidden layers neuronsoutput_neurons = 1 #number of neurons at output layer#weight and bias initializationwh=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) *lrprint('actual :
', y, '
')print('predicted :
', output)

现在,我们会发现使用PyTorch实现相同的网络会非常简单。以下的代码同样也用粗体表示出它与NumPy的不同之处:

## neural network in pytorch*import torch*#Input arrayX = *torch.Tensor*([[1,0,1,0],[1,0,1,1],[0,1,0,1]])#Outputy = *torch.Tensor*([[1],[1],[0]])#Sigmoid Functiondef sigmoid (x): return 1/(1 + *torch.exp*(-x))#Derivative of Sigmoid Functiondef derivatives_sigmoid(x): return x * (1 - x)#Variable initializationepoch=5000 #Setting training iterationslr=0.1 #Setting learning rateinputlayer_neurons = X.shape[1] #number of features in data sethiddenlayer_neurons = 3 #number of hidden layers neuronsoutput_neurons = 1 #number of neurons at output layer#weight and bias initializationwh=*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() *lrprint('actual :
', y, '
')print('predicted :
', output)

对比其它深度学习库

在一份基准脚本中,它显示出PyTorch在训练长短期记忆(LSTM)网络上比其它主要框架的表现都要好,因为它运行一个Epoch有最少的中位数时间。

PyTorch中的数据加载API经过了优良的设计,接口是针对特定数据集,采样器和数据加载器而构造的。对比于TensorFlow的数据加载工具(读者,队列等),我发现PyTorch的数据加载模块更易于使用。同时它们还能无缝对接神经网络构建模块,所以我们不需要第三方高级库。

然而,我并不推荐使用PyTorch部署模型,因为PyTorch仍然不是那么成熟。正如PyTorch开发者所说:「我们经常看到用户首先创建一个PyTorch模型来测试是否可行,然后当需要部署模型到生产中时,他们会转化为Caffe 2等其他框架,并将其部署到移动端或其它平台。」

从基础概念到实现:入门PyTorch深度神经网络框架

案例研究:用PyTorch解决图像识别问题

为了进一步熟悉PyTorch,我们将使用它解决Analytics Vidhya的深度学习实践问题:识别手写数字。我们的问题是给定一张28 x 28的图像,利用模型识别其所有代表的手写数字。

所以首先我们需要下载训练集与测试集,数据集包含了一个压缩文件以储存所有的图像。其中train.csv和test.csv分别储存了训练和测试图像,且图像的格式为png。下面我们将一步步构建简单的神经网络以实现手写数字识别功能。

第0步:准备工作

一)导入必要的函数库

# import modules%pylab inlineimport osimport numpy as npimport pandas as pdfrom scipy.misc import imreadfrom sklearn.metrics import accuracy_score

b)设置随机的种子,因此我们能控制模型产生的随机数基本不变(伪随机数)。

# To stop potential randomnessseed = 128rng = np.random.RandomState(seed)

c)中设置工作目录的路径。

root_dir = os.path.abspath('.')data_dir = os.path.join(root_dir, 'data')# check for existenceos.path.exists(root_dir), os.path.exists(data_dir)

第1步:加载与预处理数据

a)现在读取CSV格式的数据集,并获取文件名与对应的标注。

# load datasettrain = 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()

从基础概念到实现:入门PyTorch深度神经网络框架

二)接下来可以打印准备好的图片。

# print an imageimg_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()

c)对于更简单的数据操作,我们可以储存所有的图像作为NumPy数组。

# load images to create train and test settemp = []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.0train_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.0test_x = test_x.reshape(-1, 784).astype('float32')train_y = train.label.values

d)因为这个是一个典型的机器学习问题,所以我们可以创建验证集以监控模型的运行情况。下面我们以7:3的比例分割训练集与验证集。

# create validation setsplit_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步:构建模型

a)下面是模型的主体,我们定义的神经网络共有三层,即输入层,隐藏层和输出层。输入层和输出层的神经元数量是固定的,即28 x 28和10 x 1,它们分别代表了输入图像的像素和类别。我们在隐藏层采用了50个神经元,并采用Adam作为最优化算法。

import torchfrom torch.autograd import Variable# number of neurons in each layerinput_num_units = 28*28hidden_num_units = 500output_num_units = 10# set remaining variablesepochs = 5batch_size = 128learning_rate = 0.001

B)以下将开始训练模型。

# define modelmodel = 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 algorithmoptimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)## helper functions# preprocess a batch of datasetdef preproc(unclean_batch_x): """Convert values to range 0-1""" temp_batch = unclean_batch_x / unclean_batch_x.max() return temp_batch# create a batchdef 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 networktotal_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 accuracyx, 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 accuracyx, 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个Epoch。以上,本文介绍了简单的PyTorch入门概念,并利用简单的案例熟悉PyTorch的使用。读者可以继续阅读PyTorch的文档以了解更多信息。

相关推荐