從頭開始瞭解PyTorch的簡單實現

選自GitHub上,機器之心編譯。
本教程展現瞭如何從瞭解張量開始到使用PyTorch訓練簡單的神經網絡,是很是基礎的PyTorch入門資源.PyTorch創建在Python和火炬庫之上,並提供了一種相似Numpy的抽象方法來表徵量(或多維數組),它還能利用GPU來提高性能。本教程的代碼並不完整,詳情請查看原Jupyter Notebook文檔。
PyTorch使入門深度學習變得簡單,即便你這方面的背景知識不太充足。至少,知道多層神經網絡模型可視爲由權重鏈接的節點圖就是有幫助的,你能夠基於前向和反向傳播,利用優化過程(如梯度計算)從數據中估計權重。
  • 必備知識:該教程假設讀者熟悉Python和NumPy。
  • 必備軟件:在運行原Jupyter Notebook以前你須要安裝PyTorch。原筆記本有代碼單元格能夠驗證你是否作好準備。
  • 必需硬件:你須要安裝NVIDIA GPU和CUDA SDK。據報告此舉可能實現10-100的加速。固然,若是你沒有進行此設置,那麼你仍然能夠在僅使用CPU的狀況下運行PyTorch。可是,記住,在訓練神經網絡模型時,生命苦短!因此仍是儘量使用GPU吧!

1.必要的PyTorch背景

  • PyTorch是一個創建在Torch庫之上的Python包,旨在加速深度學習應用。
  • PyTorch提供一種相似NumPy的抽象方法來表徵張量(或多維數組),它能夠利用GPU來加速訓練。

1.1 PyTorch張量

PyTorch的關鍵數據結構是張量,即多維數組。其功能與NumPy的ndarray對象相似,以下咱們可使用torch.Tensor()建立張量。
#生成2-d pytorch張量(即,基質)
 pytorch_tensor = torch.Tensor(10,20)
的打印(「類型:」 ,類型(pytorch_tensor), 「 和尺寸:」,pytorch_tensor.shape)

複製代碼
若是你須要一個兼容NumPy的表徵,或者你想從現有的NumPy對象中建立一個PyTorch張量,那麼就很簡單了。
#將pytorch張量轉換爲numpy數組:
 numpy_tensor = pytorch_tensor.numpy()
print(「type:」,type(numpy_tensor),「and size:」,numpy_tensor.shape)

#將numpy數組轉換爲Pytorch Tensor:
 print(「type:」,type(torch.Tensor(numpy_tensor)),「and size:」,torch.Tensor(numpy_tensor).shape)

複製代碼

1.2 PyTorch與NumPy

PyTorch並非NumPy的簡單替代品,但它實現了不少NumPy功能。其中有一個不便之處是其命名規則,有時它和和NumPy的命名方法至關不一樣。咱們來舉幾個例子說明其中的區別:
1張量建立
T = torch.rand(2,4,3,5)
一個= np.random.rand(2,4,3,5)

複製代碼
2張量分割
T = torch.rand(2,4,3,5)
一個= t.numpy()
pytorch_slice = T [ 0,1:3,:,4 ] 
numpy_slice = A [ 0,1:3,:,4 ] 
打印('張量[0,1:3,:1,4]:\ N',pytorch_slice)
打印('NdArray [0,1:3,:1,4]:\ N',numpy_slice)

------- -------------------------------------------------- ---------------- 
張量[ 0,1:3,:,4 ]:

 0.2032   0.1594   0.3114 
 0.9073   0.6497   0.2826
 [torch.FloatTensor大小的2 ×3] 

NdArray [ 0,1:3,:,4 ]:
[[ 0.20322084   0.15935552   0.31143939 ] 
[ 0.90726137   0.64966112   0.28259504 ]]

複製代碼
3張量掩蔽
t = t  - 0.5
 a = t.numpy()
pytorch_masked = t [t> 0 ] 
numpy_masked = a [a> 0 ]

複製代碼
4張量重塑
pytorch_reshape = t.view([ 6,5,4 ])
numpy_reshape = a.reshape([ 6,5,4 ])

複製代碼

1.3 PyTorch變量

  • PyTorch張量的簡單封裝
  • 幫助創建計算圖
  • Autograd(自動微分庫)的必要部分
  • 將關於這些變量的梯度保存在.grad中
結構圖:
計算圖和變量:在PyTorch中,神經網絡會使用相互鏈接的變量做爲計算圖來表示.PyTorch容許經過代碼構造計算圖來構建網絡模型;以後PyTorch會簡化估計模型權重的流程,例如經過自動計算梯度的方式。
舉例來講,假設咱們想構建兩層模型,那麼首先要輸入和輸出建立張量變量。咱們能夠將PyTorch Tensor包裝進變量對象中:
從 torch.autograd 進口可變
進口 torch.nn.functional 做爲 ˚F 

X =變數(torch.randn(4,1),requires_grad = 假)
Y =變量(torch.randn(3,1),requires_grad = 假)

複製代碼
咱們把requires_grad設置爲True,代表咱們想要自動計算梯度,這將用於反向傳播中以優化權重。
如今咱們來定義權重:
W1 =變數(torch.randn(5,4),requires_grad = 真)
W2 =變量(torch.randn(3,5),requires_grad = 真)

複製代碼
訓練模型:
def  model_forward (x):
    return F.sigmoid(w2 @ F.sigmoid(w1 @ x))

 print(w1)
 print(w1.data.shape)
 print(w1.grad)#最初,不存在

 ---- -------------------------------------------------- -------------------
可變含:
  1.6068  -1.3304  -0.6717  -0.6097 
-0.3414  -0.5062  -0.2533   1.0260 
-0.0341  -1.2144  -1.5983  -0.1392 
-0.5473   0.0084   0.4054   0.0970 
 0.3596   0.5987  -0.0324   0.6116
 [torch.Float傳感器的大小5 X4的] 

torch.Size([ 5,4 ])
無

複製代碼

1.4 PyTorch反向傳播

這樣咱們有了輸入和目標,模型權重,那麼是時候訓練模型了咱們須要三個組件:
損失函數:描述咱們模型的預測距離目標還有多遠;
導入 torch.nn 爲 nn 
條件= nn.MSELoss()

複製代碼
優化算法:用於更新權重;
導入 torch.optim 做爲 optim 
optimizer = optim.SGD([w1,w2],lr = 0.001)

複製代碼
反向傳播步驟:
對於曆元在範圍(10):
    損耗=標準(model_forward(x)中,y)的
    optimizer.zero_grad() #零出之前梯度
     loss.backward()#計算新梯度
     optimizer.step() #應用這些梯度


打印( w1)

------------------------------------------------ ------------------------- 
變量包含:
 1.6067  -1.3303  -0.6717  -0.6095 
-0.3414  -0.5062  -0.2533   1.0259 
-0.0340  -1.2145  -1.5983  -0.1396 
-0.5476   0.0085   0.4055   0.0976
 0.3597   0.5986  -0.0324   0.6113
 [火炬。尺寸爲5 x4的飛濺傳感器 ]

複製代碼

1.5 PyTorch CUDA接口

PyTorch的優點之一是爲張量和autograd庫提供CUDA接口。使用CUDA GPU,你不只能夠加速神經網絡訓練和推斷,還能夠加速任何映射至PyTorch張量的工做負載。
你能夠調用torch.cuda.is_available()函數,檢查PyTorch中是否有可用CUDA。
cuda_gpu = torch.cuda.is_available()
if(cuda_gpu):
    print(「Great,you have a GPU!」)
elseprint(「Life is short  -  consider a GPU!」)

複製代碼
很好,如今你有GPU了。
.cuda()
以後,使用cuda加速代碼就和調用同樣簡單。若是你在張量上調用.cuda(),則它將執行從CPU到CUDA GPU的數據遷移。若是你在模型上調用.cuda(),則它不只將全部內部儲存移到GPU,還將整個計算圖映射至GPU。
要想將張量或模型複製回CPU,好比想和NumPy交互,你能夠調用.cpu()。
若是 cuda_gpu:
    x = x.cuda()
    printtype(x.data))

x = x.cpu()
printtype(x.data))

--------------- -------------------------------------------------- -------- 
< class ' 火炬。cuda。FloatTensor '> 
< class ' 火炬。FloatTensor '>

複製代碼
咱們來定義兩個函數(訓練函數和測試函數)來使用咱們的模型執行訓練和推斷任務。該代碼一樣來自PyTorch官方教程,咱們摘選了全部訓練/推斷的必要步驟。
對於訓練和測試網絡,咱們須要執行一系列動做,這些動做可直接映射至PyTorch代碼:
1.咱們將模型轉換到訓練/推斷模式;
2.咱們經過在數據集上成批獲取圖像,以迭代訓練模型;
3.對於每個批量的圖像,咱們都要加載數據和標註,運行網絡的前向步驟來獲取模型輸出;
4.咱們定義損失函數,計算每一個批量的模型輸出和目標之間的損失;
5.訓練時,咱們初始化梯度爲零,使用上一步定義的優化器和反向傳播,來計算全部與損失有關的層級梯度;
6.訓練時,咱們執行權重更新步驟。
def  train (model,epoch,criterion,optimizer,data_loader):
     model.train()
     for batch_idx,(data,target) in enumerate(data_loader):
         if cuda_gpu:
            data,target = data.cuda(),target.cuda( )
            model.cuda()
        數據,目標=變量(數據),變量(目標)
        輸出=模型(數據)

        optimizer.zero_grad()
        損失=標準(輸出,目標)
        loss.backward()
        optimizer.step()
         if( batch_idx + 1)% 400 == 0:
            print'Train Epoch:{} [{} / {}({:.0f}%)] \ tLoss:{:.6f}'. format(
                epoch,(batch_idx + 1)* len(data),len(data_loader.dataset ),
                100 *(batch_idx + 1)/ LEN(data_loader),loss.data [ 0 ]))


DEF  試驗(模型,曆元,標準,data_loader) :
     model.eval()
    test_loss = 0
     正確= 0 
    爲數據,目標在 data_loader中:
        if cuda_gpu:
            data,target = data.cuda(),target.cuda()
            model.cuda()
        數據,target =變量(數據),變量(目標)
        output = model(data)
        test_loss + = criterion(output,target).data [ 0 ] 
        pred = output.data.max(1)[ 1 ] #獲取最大對數機率索引
         + = pred.eq(目標數據).cpu()。sum()

    test_loss / = len(data_loader)#失敗函數已經在批量大小上取平均值
     acc = correct / len(data_loader.dataset)
    print'\ nTest set:Average loss:{:。 4f},準確度:{} / {}({:.0f }%)\ n'.format(
        test_loss,correct,len(data_loader.dataset),100. * acc))
    return(acc,test_loss)

複製代碼
如今介紹完畢,讓咱們開始此次數據科學之旅吧!

2.使用PyTorch進行數據分析

  • 使用torch.nn庫構建模型
  • 使用torch.autograd庫訓練模型
  • 將數據封裝進torch.utils.data.Dataset庫
  • 使用NumPy接口鏈接你的模型,數據和你最喜歡的工具
在查看複雜模型以前,咱們先來看個簡單的:簡單合成數據集上的線性迴歸,咱們可使用sklearn工具生成這樣的合成數據集。
從 sklearn.datasets 導入 make_regression 
進口 seaborn 做爲 SNS 
進口熊貓做爲 PD 
進口 matplotlib.pyplot 做爲 PLT 

sns.set()

x_train,y_train,W_target = make_regression(N_SAMPLES次= 100,n_features = 1,噪聲= 10,COEF = 真)

DF = pd.DataFrame(data = { 'X':x_train.ravel(),'Y':y_train.ravel()})

sns.lmplot(x = 'X',y = 'Y',data = df,fit_reg = True)
plt.show()

1)x_torch = torch.FloatTensor(x_train)
y_torch = torch.FloatTensor(y_train)
y_torch = y_torch.view(y_torch.size()[ 0 ],1)

複製代碼
PyTorch的nn庫中有大量有用的模塊,其中一個就是線性模塊。如名字所示,它對輸入執行線性變換,即線性迴歸。
class  LinearRegression (torch.nn.Module):
    def  __init__ (self,input_size,output_size):
         super(LinearRegression,self).__ init __()
        self.linear = torch.nn.Linear(input_size,output_size)  

     def  forward (self,x ):
        返回 self.linear(x)的

模型=線性迴歸( 1, 1)

複製代碼
要訓練線性迴歸,咱們須要從nn庫中添加合適的損失函數。對於線性迴歸,咱們將使用MSELoss() - 均方差損失函數。

咱們還須要使用優化函數(SGD),並運行與以前示例相似的反向傳播。本質上,咱們重複上文定義的train()函數中的步驟。不能直接使用該函數的緣由是咱們實現它的目的是分類而不是迴歸,以及咱們使用交叉熵損失和最大元素的索引做爲模型預測。而對於線性迴歸,咱們使用線性層的輸出做爲預測。git

準則= torch.nn.MSELoss()
優化= torch.optim.SGD(model.parameters(),LR = 0.1)   


對於曆元在範圍(50):
    數據,目標=變量(x_torch),變量(y_torch)
    輸出= model(data)

    optimizer.zero_grad()
    loss = criterion(output,target)
    loss.backward()
    optimizer.step()

predict = model(Variable(x_torch))。data.numpy()

複製代碼
如今咱們能夠打印出原始數據和適合PyTorch的線性迴歸。
plt.plot(x_train,y_train,'o',label = '原始數據')
plt.plot(x_train,predicted,label = 'Fitted line')
plt.legend()
plt.show()

複製代碼
爲了轉向更復雜的模型,咱們下載了MNIST數據集至「數據集」文件夾中,並測試了一些PyTorch中可用的初始預處理.PyTorch具有數據加載器和處理器,可用於不一樣的數據集。下載好後,你能夠隨時使用。你還能夠將數據包裝進PyTorch張量,建立本身的數據加載器類別。
批大小(batch size)是機器學習中的術語,指一次迭代中使用的訓練樣本數量。批大小能夠是如下三種之一:
  • batch模式:批大小等於整個數據集,所以迭代和epoch值一致;
  • mini-batch模式:批大小大於1但小於整個數據集的大小。一般,數量能夠是能被整個數據集整除的值。
  • 隨機模式:批大小等於1.所以梯度和神經網絡參數在每一個樣本以後都要更新。
從 torchvision 導入數據集,變換

batch_num_size = 64

 train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('data',train = True,download = True,transform = transforms.Compose([ 
        transforms.ToTensor()),
        轉換。 Normalize((0.1307,),(0.3081,))
    ])),
    batch_size = batch_num_size,shuffle = True)

test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('data',train = False,transform = transforms。撰寫([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,),(0.3081,))
    ])),
    batch_size = batch_num_size,shuffle = True)

複製代碼

3. PyTorch中的LeNet卷積神經網絡(CNN)

如今咱們從頭開始建立第一個簡單神經網絡。該網絡要執行圖像分類,識別MNIST數據集中的手寫數字。這是一個四層的卷積神經網絡(CNN),一種分析MNIST數據集的常見架構。該代碼來自PyTorch官方教程,你能夠在這裏(http://pytorch.org/tutorials/)找到更多示例。
咱們將使用torch.nn庫中的多個模塊:
1.線性層:使用層的權重對輸入張量執行線性變換;
2. Conv1和Conv2:卷積層,每一個層輸出在卷積核(小尺寸的權重張量)和一樣尺寸輸入區域之間的點積;
3. Relu:修正線性單元函數,使用逐元素的激活函數max(0,x);
池化層:使用max運算執行特定區域的下采樣(一般2x2像素);
5. Dropout2D:隨機將輸入張量的全部通道設爲零。當特徵圖具有強相關時,dropout2D提高特徵圖之間的獨立性;
6. Softmax:將Log(Softmax(x))函數應用到n維輸入張量,以使輸出在0到1之間。
類 LeNet (nn.Module) :
    DEF  __init__ (個體):
         超級(LeNet,自我).__ INIT __()
        self.conv1 = nn.Conv2d( 1, 10,kernel_size = 5)
        self.conv2 = nn.Conv2d( 10, 20,kernel_size = 5)
        self.conv2_drop = nn.Dropout2d()
        self.fc1 = nn.Linear( 320, 50)
        self.fc2 = nn.Linear( 50, 10)

     DEF  向前(個體,X) :
         X = F .relu(F.max_pool2d(self.conv1(x)的2))
        X = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(X)),2))
        X = x.view(-1,320)
        X = F.relu(self.fc1(X ))
        x = F.dropout(x,training = self.training)
        x = self.fc2(x)
        return F.log_softmax(x,dim = 1)

複製代碼
建立LeNet類後,建立對象並移至GPU:
model = LeNet()
if cuda_gpu:
    model.cuda()

print'MNIST_net model:\ n'print(model)

----------------------- -------------------------------------------------- 
MNIST_net模型:

LeNet(
  (CONV1):Conv2d(1,10,kernel_size =(5,5),跨度=(1,1))
  (CONV2):Conv2d(10,20,kernel_size =(5,5),步幅=(1,1))
  (conv2_drop):Dropout2d(p值=0.5)
  (fc1):線性(in_features = 320,out_features = 50,bias = 真)
  (fc2):線性(in_features = 50,out_features = 10,bias = 真)
)

複製代碼
要訓練該模型,咱們須要使用帶動量的SGD,學習率爲0.01,動量爲0.5。
criteria = nn.CrossEntropyLoss()   
優化器= optim.SGD(model.parameters(),lr = 0.005,動量= 0.9)

複製代碼
僅僅須要5個epoch(一個epoch意味着你使用整個訓練數據集來更新訓練模型的權重),咱們就能夠訓練出一個至關準確的LeNet模型。這段代碼檢查能夠肯定文件中是否已有預訓練好。的模型有則加載;無則訓練一個並保存至磁盤。
import os epochs

 = 5 
if(os.path.isfile('pretrained / MNIST_net.t7')):
    print'Loading model')
    model.load_state_dict(torch.load('pretrained / MNIST_net.t7',map_location = lambda storage ,LOC:存儲))
    ACC,損耗=試驗(模型,1,標準,test_loader)
不然:
    打印('訓練模式')
    用於曆元在範圍(1,曆元+ 1:)
        系(模型,曆元,標準,優化,train_loader)
        acc,loss = test(model,1,criterion,test_loader)
    torch.save(model.state_dict(),'pretrained / MNIST_net.t7')

------------------ -------------------------------------------------- ----- 
加載模型
試驗組:平均損耗:0.0471,準確度:9859號文件 / 10000(99%)

複製代碼
如今咱們來看下模型。首先,打印出該模型的信息。打印函數顯示全部層(如Dropout被實現爲一個單獨的層)及其名稱和參數。一樣有一個迭代器在模型中全部已命名模塊之間運行。當你具有一個包含多個「內部」模型的複雜時,此有所幫助。在全部已命名模塊之間的迭代容許咱們建立模型解析器,可讀取模型參數,建立與該網絡相似的模塊。
print'Internal models:'for idx,m in enumerate(model.named_modules()):
    print(idx,' - >',m)
    print'-------------- -------------------------------------------------- ---------」) #輸出: 內部模型: 0 - >('',LeNet( (CONV1):Conv2d(1,10,kernel_size =(5,5),跨度=(1,1)) (CONV2):Conv2d(10,20,kernel_size =(5,5),跨度=(1,1)) (conv2_drop):Dropout2d(p值= 0.5) (FC1):線性(in_features = 320,out_features = 50,偏壓= 真) (FC2):線性(in_features = 50,out_features = 10,bias = True) )) ----------------------------------------- -------------------------------- 1 - >('CONV1',Conv2d(1,10,kernel_size =(5,5),跨度=(1,1))) -------------------------------------------------- ----------------------- 2 - >('CONV2',Conv2d(10,20,kernel_size =(5,5),跨度=(1,1))) ---------------------------------------------- --------------------------- 3 - >('conv2_drop',Dropout2d(p = 0.5)) -------- -------------------------------------------------- --------------- 4 - >('fc1',Linear(in_features = 320,out_features = 50,bias = True)) -------------------------------------------------- ----------------------- 5 - >('fc2',Linear(in_features = 50,out_features = 10,bias = True)) ---- -------------------------------------------------- ------------------- 複製代碼
你可使用.cpu()方法將張量移到CPU(或確保它在那裏)。或者,當GPU可用時(torch.cuda。可用),使用.cuda()方法將張量移至GPU。你能夠看到張量是否在GPU上,其類型爲torch.cuda.FloatTensor。若是張量在CPU上,則其類型爲torch.FloatTensor 。
printtype(t.cpu()。data))
if torch.cuda.is_available():
    print(「Cuda is available」)
    printtype(t.cuda()。data))
elseprint(「Cuda is不可用「)

---------------------------------------------- --------------------------- 
< 類 ' 炬。FloatTensor '> 
Cuda的 是 可用的
 < 類 ' 炬。cuda。FloatTensor '>

複製代碼
若是張量在CPU上,咱們能夠將其轉換成NumPy數組,其共享一樣的內存位置,改變其中一個就會改變另外一個。
若是 torch.cuda.is_available():
    嘗試:
        打印(t.data.numpy()),
    除了 RuntimeError 爲 e:
        「你不能將GPU張量轉換爲numpy nd數組,你必須將你的weight tendor複製到cpu而後獲取numpy數組「

 printtype(t.cpu()。data.numpy()))
print(t.cpu()。data.numpy()。shape)
print(t.cpu()。data)。 numpy的())

複製代碼
如今咱們瞭解瞭如何將張量轉換成NumPy數組,咱們能夠利用該知識使用matplotlib進行可視化!咱們來打印出第一卷積層的卷積濾波器。
data = model.conv1.weight.cpu()。data.numpy()
print(data.shape)
print(data [:, 0 ] .shape)

kernel_num = data.shape [ 0 ] 

fig,axes = plt.subplots( NCOLS = kernel_num,figsize =(2 * kernel_num,2))

爲山口在範圍(kernel_num):
    軸[COL] .imshow(數據[COL,0,:,:],CMAP = plt.cm.gray)
PLT。顯示()

複製代碼
以上是簡要的教程資源,還有更多的內容和實驗能夠查看原項目瞭解更多。
相關文章
相關標籤/搜索