每過(guò)一段時(shí)間,總會(huì)有一個(gè)python庫(kù)被開(kāi)發(fā)出來(lái)声诸,改變深度學(xué)習(xí)領(lǐng)域酱讶。而PyTorch就是這樣一個(gè)庫(kù)。
在過(guò)去的幾周里彼乌,我一直在嘗試使用PyTorch泻肯。我發(fā)現(xiàn)它非常好上手。迄今為止慰照,在我所有的各種深度學(xué)習(xí)庫(kù)中灶挟,PyTorch一直是最靈活和容易的。
在本文中毒租,我們將探索PyTorch的實(shí)際應(yīng)用稚铣,其中包括基礎(chǔ)知識(shí)和案例研究。我們還將比較使用numpy和PyTorch構(gòu)建的神經(jīng)網(wǎng)絡(luò)蝌衔,以查看它們?cè)趯?shí)現(xiàn)中的相似之處榛泛。
PyTorch概述
PyTorch的創(chuàng)作者說(shuō)他們信奉的道理是 - 解決當(dāng)務(wù)之急。這意味著我們立即運(yùn)行我們的計(jì)算噩斟。這恰好適合python的編程方法,因?yàn)槲覀儾槐氐却写a都寫(xiě)完才能知道它是否有效孤个。我們可以運(yùn)行部分代碼并實(shí)時(shí)檢查它剃允。對(duì)于我,一個(gè)神經(jīng)網(wǎng)絡(luò)調(diào)試器來(lái)說(shuō)齐鲤,這無(wú)意是一個(gè)福音斥废!
PyTorch是一個(gè)基于python的庫(kù),旨在提供靈活的深度學(xué)習(xí)開(kāi)發(fā)平臺(tái)给郊。PyTorch的工作流程很可能接近python的科學(xué)計(jì)算庫(kù) - numpy牡肉。
那么,為什么我們要使用PyTorch來(lái)構(gòu)建深度學(xué)習(xí)模型淆九?:
易于使用的API?- 如Python一樣簡(jiǎn)單统锤。
Python支持?- 如上所述,PyTorch平滑地與Python數(shù)據(jù)科學(xué)堆棧結(jié)合炭庙。它與numpy非常相似饲窿,你甚至很難注意到他們的差異。
動(dòng)態(tài)計(jì)算圖?- PyTorch并沒(méi)有為特定功能預(yù)定義圖形焕蹄,而是為我們提供了一個(gè)框架逾雄,可以隨時(shí)隨地構(gòu)建計(jì)算圖,甚至在運(yùn)行時(shí)更改計(jì)算圖。我們不知道創(chuàng)建一個(gè)神經(jīng)網(wǎng)絡(luò)需要多少內(nèi)存時(shí)鸦泳,這非常有用银锻。
使用PyTorch的還有其他的優(yōu)點(diǎn),它能夠支持multiGPU做鹰,自定義數(shù)據(jù)加載和簡(jiǎn)化的預(yù)處理程序击纬。
自2016年1月初發(fā)布以來(lái),許多研究人員已將其作為一種實(shí)現(xiàn)庫(kù)誊垢,因?yàn)樗子跇?gòu)建新穎甚至非常復(fù)雜的計(jì)算圖掉弛。話雖如此,PyTorch仍然需要一段時(shí)間才能被大多數(shù)數(shù)據(jù)科學(xué)從業(yè)者所采用喂走,因?yàn)樗切碌牟⑶艺诮ㄔO(shè)中殃饿。
在深入細(xì)節(jié)之前,讓我們了解PyTorch的工作流程芋肠。
PyTorch使用命令式/熱切式范式乎芳。也就是說(shuō),構(gòu)建圖形的每行代碼都定義了該圖的一個(gè)組件帖池。即使在圖形完全構(gòu)建之前奈惑,我們也可以獨(dú)立地對(duì)這些組件進(jìn)行計(jì)算。這被稱為運(yùn)行時(shí)定義(define-by-run)法睡汹。
安裝PyTorch非常簡(jiǎn)單肴甸。您可以按照它的官方文檔操作,并根據(jù)自己的系統(tǒng)規(guī)格運(yùn)行命令囚巴。例如原在,下面是我根據(jù)我的情況使用的命令:
conda install pytorch torchvision cuda91 -c pytorch
我們?cè)陂_(kāi)始使用PyTorch時(shí)應(yīng)該了解的主要元素是:
PyTorch張量
數(shù)學(xué)運(yùn)算
Autograd模塊
Optim模塊
nn模塊
PyTorch張量
張量不過(guò)是多維數(shù)組。PyTorch中的張量與numpy的ndarray相似彤叉,張量也可以在GPU上使用庶柿。PyTorch支持很多類(lèi)型的張量。
你可以定義一個(gè)簡(jiǎn)單的一維矩陣如下:
# import pytorch
import torch
# define a tensor
torch.FloatTensor([2])
2
[torch.FloatTensor of size 1]
數(shù)學(xué)運(yùn)算
與numpy一樣秽浇,科學(xué)計(jì)算庫(kù)需要高效的實(shí)現(xiàn)數(shù)學(xué)函數(shù)浮庐。PyTorch提供了一個(gè)相似的接口,可以使用超過(guò)200多種數(shù)學(xué)運(yùn)算柬焕。
以下是PyTorch中一個(gè)簡(jiǎn)單加法操作的例子:
a = torch.FloatTensor([2])
b = torch.FloatTensor([3])
a + b
5
[torch.FloatTensor of size 1]
這看起來(lái)不像是一種quinessential python方法嗎审残?我們也可以對(duì)我們定義的PyTorch張量執(zhí)行各種矩陣運(yùn)算。例如击喂,我們將轉(zhuǎn)置一個(gè)二維矩陣:
matrix = torch.randn(3, 3)
matrix
-1.3531 -0.5394? 0.8934
1.7457 -0.6291 -0.0484
-1.3502 -0.6439 -1.5652
[torch.FloatTensor of size 3x3]
matrix.t()
-2.1139? 1.8278? 0.1976
0.6236? 0.3525? 0.2660
-1.4604? 0.8982? 0.0428
[torch.FloatTensor of size 3x3]
Autograd模塊
PyTorch使用一種稱為自動(dòng)微分(automatic differentiation)的技術(shù)维苔。也就是說(shuō),有一個(gè)記錄我們所執(zhí)行的操作的記錄器懂昂,然后它會(huì)反向回放以計(jì)算我們的梯度介时。這種技術(shù)在建立神經(jīng)網(wǎng)絡(luò)時(shí)尤為有效,因?yàn)槲覀兛梢酝ㄟ^(guò)計(jì)算正向傳遞過(guò)程中參數(shù)的微分來(lái)節(jié)省一個(gè)周期的時(shí)間。
from torch.autograd import Variable
x = Variable(train_x)
y = Variable(train_y, requires_grad=False)
Optim模塊
torch.optim 是一個(gè)實(shí)現(xiàn)構(gòu)建神經(jīng)網(wǎng)絡(luò)的各種優(yōu)化算法的模塊沸柔。大多數(shù)常用的方法已經(jīng)被支持循衰,因此我們不必從頭開(kāi)始構(gòu)建它們(除非你樂(lè)意這么做)。
以下是使用Adam優(yōu)化的代碼:
optimizer = torch.optim.Adam(model.parameters()褐澎,lr = learning_rate)
nn模塊
PyTorch的autograd模塊可以很容易地定義計(jì)算圖和梯度会钝,但是默認(rèn)的autograd對(duì)于定義復(fù)雜的神經(jīng)網(wǎng)絡(luò)可能有些低級(jí)。這時(shí)就要用到nn模塊工三。
nn包定義了一組模塊迁酸,我們可以將其視為一個(gè)神經(jīng)網(wǎng)絡(luò)層,它可以從輸入生成輸出俭正,并且具有一些可訓(xùn)練的權(quán)重奸鬓。
您可以將一個(gè)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()
在Numpy與PyTorch建立一個(gè)神經(jīng)網(wǎng)絡(luò)的對(duì)比
我之前提到PyTorch和Numpy非常相似掸读。那么串远,我們來(lái)看看為什么,一個(gè)簡(jiǎn)單的神經(jīng)網(wǎng)絡(luò)的實(shí)現(xiàn)來(lái)解決二元分類(lèi)問(wèn)題儿惫。使用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 pytorchimport 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() *lr print('actual :\n', y, '\n')print('predicted :\n', output)
與其他深度學(xué)習(xí)庫(kù)比較
在一個(gè)基準(zhǔn)腳本中澡罚,PyTorch 在訓(xùn)練LSTM方面表現(xiàn)優(yōu)于所有其他主要的深度學(xué)習(xí)庫(kù),每個(gè)周期的中位時(shí)間最低(參見(jiàn)下圖)肾请。
在PyTorch中用于數(shù)據(jù)加載的API設(shè)計(jì)良好留搔。接口在數(shù)據(jù)集,采樣器和數(shù)據(jù)加載器中指定铛铁。
在比較TensorFlow中的數(shù)據(jù)加載工具(readers催式,queues,等)時(shí),我發(fā)現(xiàn)PyTorch的數(shù)據(jù)加載模塊非常好用避归。另外,PyTorch可以無(wú)縫的構(gòu)建神經(jīng)網(wǎng)絡(luò)管呵,我們不必依賴像keras這樣的第三方高級(jí)庫(kù)梳毙。
另一方面,我不會(huì)推薦使用PyTorch進(jìn)行部署捐下。PyTorch還在發(fā)展中账锹。正如PyTorch開(kāi)發(fā)人員所說(shuō):“我們看到的是,用戶首先創(chuàng)建了一個(gè)PyTorch模型坷襟。當(dāng)他們準(zhǔn)備將他們的模型部署到生產(chǎn)環(huán)境中時(shí)奸柬,他們只需將其轉(zhuǎn)換為Caffe 2模型,然后將其發(fā)布到移動(dòng)平臺(tái)或其他平臺(tái)中婴程±龋“
案例研究 - 解決PyTorch中的圖像識(shí)別問(wèn)題
為了熟悉PyTorch,我們將解決分析方面的深度學(xué)習(xí)實(shí)踐問(wèn)題 -?識(shí)別數(shù)字。
我們的要做的是一個(gè)圖像識(shí)別問(wèn)題桌粉,從一個(gè)給定的28×28像素的圖像中識(shí)別數(shù)字蒸绩。我們有一部分圖像用于訓(xùn)練,其余部分用于測(cè)試我們的模型铃肯。
首先患亿,下載訓(xùn)練集與測(cè)試集。數(shù)據(jù)集包含所有圖像的壓縮文件押逼,并且train.csv和test.csv都具有相應(yīng)訓(xùn)練和測(cè)試圖像的名稱步藕。數(shù)據(jù)集中不提供任何額外特征,圖片為.png格式挑格。
第0步:準(zhǔn)備工作
a)導(dǎo)入所有需要用到的庫(kù)
# 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)讓我們?cè)O(shè)置seed值咙冗,以便我們可以控制模型的隨機(jī)性
# To stop potential randomness
seed = 128
rng = np.random.RandomState(seed)
c)第一步是設(shè)置目錄路徑,以便妥善保存恕齐!
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步:數(shù)據(jù)加載和預(yù)處理
a)現(xiàn)在讓我們讀取數(shù)據(jù)集乞娄。
# 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)讓我們看看我們的數(shù)據(jù)是什么樣的。我們閱讀我們的圖像并顯示它显歧。
# 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)為了便于數(shù)據(jù)處理仪或,讓我們將所有圖像存儲(chǔ)為numpy數(shù)組
# 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)這是一個(gè)典型的機(jī)器學(xué)習(xí)問(wèn)題,為了測(cè)試我們模型的是否正常運(yùn)行士骤,我們創(chuàng)建一個(gè)驗(yàn)證集范删。我們分離他們的比例為70:30(驗(yàn)證集為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步:建立模型
a)下面拷肌,我們定義神經(jīng)網(wǎng)絡(luò)架構(gòu)到旦。我們定義了一個(gè)3個(gè)層(輸入,隱藏和輸出)的神經(jīng)網(wǎng)絡(luò)巨缘。輸入和輸出中神經(jīng)元的數(shù)量是固定的添忘,因?yàn)檩斎胧俏覀儓D像28×28,并且輸出是代表類(lèi)的10×1向量(即每個(gè)像素對(duì)應(yīng)一個(gè)輸入神經(jīng)元若锁,每個(gè)類(lèi)為一個(gè)輸出)搁骑。我們?cè)陔[藏層中采用50個(gè)神經(jīng)元。在這里又固,我們使用Adam作為我們的優(yōu)化算法仲器,它是梯度下降算法的一個(gè)不錯(cuò)的變種。
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)訓(xùn)練模型
# 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)
訓(xùn)練分?jǐn)?shù)是:
0.8779008746355685
驗(yàn)證分?jǐn)?shù)是:
0.867482993197279
這個(gè)分?jǐn)?shù)相當(dāng)?shù)母哐龉冢覀儾庞?xùn)練這個(gè)簡(jiǎn)單的神經(jīng)網(wǎng)絡(luò)五個(gè)周期而已乏冀。
原文網(wǎng)址:?http://www.atyun.com/17092_一個(gè)簡(jiǎn)單而強(qiáng)大的深度學(xué)習(xí)庫(kù)-pytorch&=3.html
關(guān)于人工智能,如果想了解更多洋只,可關(guān)注微信公眾號(hào):atyun_com或者網(wǎng)站AiTechYun(http://www.atyun.com/)辆沦,最快獲得第一手信息昼捍。