pytorch(一)

Tensor

tensor 是Pytorch重要的數據結構 ,可以認爲是一個高緯的數組,它是一個(標量),一維數組(向量),二維矩陣(矩陣)以及更高維度的數組, Tesnor 跟numpy 的 ndarrays類似。

from __feature__ import  print_function
import torch as t


#構建一個(5,3) 的矩陣,只是分配了空間,未初始化
x = Tensor(5,3)
print(x)
#使用0-1均勻分佈隨機初始化二維數組
x = t.rand(5,3)
print(x)

out:tensor([[8.4490e-39, 9.6429e-39, 9.2755e-39],
[1.0286e-38, 9.0919e-39, 8.9082e-39],
[9.2755e-39, 8.4490e-39, 1.0194e-38],
[9.0919e-39, 8.4490e-39, 9.6429e-39],
[1.0653e-38, 9.6429e-39, 1.0745e-38]])
tensor([[ 0.0292, 0.5167, 0.9883],
[ 0.2294, 0.1510, 0.5729],
[ 0.6362, 0.2311, 0.9526],
[ 0.6667, 0.8822, 0.3827],
[ 0.3663, 0.3416, 0.4670]])

print(x.size())  #查看x 的形狀
x.size()[1]  #查看列數 與下面的方法等價
x.size(1) #兩者是相等的 torch.Size 是tuple對象的子類,它支持x.size()[1]

Tensor 的加法

y = t.rand(5,3)
#加法的一種寫法
x+y
#第二種加法的寫法
t.add(x,y)
#第三種加法的寫法
result = t.Tensor(5,3) 先定義
t.add(x,y,out = result)
result

普通的加法並不會改變y的值

print("開始的y")
print(y)
y.add(x)
print("相加後的y")
print(y)

#第二種加法y的結果
y.add_(x)
print(yu)
y的結果變了

** 注意,函數名後面帶下劃線_ 的函數會修改Tensor本身。例如,x.add_(y)和x.t_()會改變 x,但x.add(y)和x.t()返回一個新的Tensor, 而x不變。

Tensor的選取過程跟numpy 類似

x[:,1] #所有行的1列
Tensor和Numpy的數組之間的互操作非常容易且快速。對於Tensor不支持的操作,可以先轉爲Numpy數組處理,之後再轉回Tensor。
import numpy
import tensor as t
a = t.one(5) #創建一個全系的tensor
print(a)
b = a.numpy()  #Tensor --> Numpy
print(b)

c = t.from_numpy(a)  # Numpy -- > Tensor
Numpy與Tensor 共享內存,意味着一個發生改變另一個也會發生改變
b.add_(1)
print(a)  #out:[2. 2. 2. 2. 2.]
print(b) #out:tensor([2., 2., 2., 2., 2.], dtype=torch.float64)

Tensor 可以通過.cuda方法轉化成GPU的Tensor 從而享受Tensor的加速運算

if t.cuda.is_available():
	x = x.cuda()
	y = y.,cuda()
	x  + y

CUDA 帶來的速度提升 需要有大量的數據才能體現出來

Autograd 自動微分

深度學習的算法本質上是通過反向傳播求導數,而PyTorch的Autograd模塊則實現了此功能。在Tensor上的所有操作,Autograd都能爲它們自動提供微分,避免了手動計算導數的複雜過程。

autograd.Variable是Autograd中的核心類,它簡單封裝了Tensor,並支持幾乎所有Tensor有的操作。Tensor在被封裝爲Variable之後,可以調用它的.backward實現反向傳播,自動計算所有梯度

Variable主要包含三個屬性。

data:保存Variable所包含的Tensor
grad:保存data對應的梯度,grad也是個Variable,而不是Tensor,它和data的形狀一樣。
grad_fn:指向一個Function對象,這個Function用來反向傳播計算輸入的梯度

使用Tensor 創建一個Variable

from torch.autograd import Variable
x = Variable(t.ones(2,2),requires_grad = True

out:tensor([[1., 1.],
[1., 1.]], requires_grad=True)

y = x.sum()
print(y)

out:tensor(4., grad_fn=)

y.grad_fn

<SumBackward0 at 0x7fc14824b860>

y.backward() 反向傳播,計算梯度
# y = sum(x) = x[0][0] + x[0][1] + x[1][0] + x[1][0]+x[1][1]
x.grad
#tensor([[1., 1.],
        #[1., 1.]])
注意:grad在反向傳播過程中是累加的(accumulated),這意味着每一次運行反向傳播,梯度都會累加之前的梯度,所以反向傳播之前需把梯度清零。
y.backgrad()
x.grad()

y.backward()
x.grad

out: tensor([[2., 2.],
[2., 2.]])

Variable containing:
3 3
3 3
[torch.FloatTensor of size 2x2]

#以下劃線結束的就是 inplace操作
x.grad.data.zero_()
y.backward()
x.grad

out :Variable containing:
1 1
1 1
[torch.FloatTensor of size 2x2]

定義一個網絡

import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__ (self):
        super(Net,self).__init__()
        #nn.Module子類 必須在構造函數中執行父類函數
        
        #等價於 nn.Module.__init__()
        # 卷積層'1'表示通道數量,6表示輸出的通道數,5表示5個卷積核
        self.conv1 = nn.Conv2d(1,6,5)
        self.conv2 = nn.Conv2d(6,16,5)
        #全連接層 y = wx + b
        self.fc1 =  nn.Linear(15*5*5, 120)
        self.fc2 = nn.Linear(120,84)
        self.fc3 =  nn.Linear(84,10)
    def forward(self,x):
        #卷積 -- > 激活 -->池化
        x = F.max_pool2d(F.relu(self.conv1(x)),(2,2))
        x = F.max_pool2d(F.relu(self.conv2(x)),2)
		#reshape  “-1”表示 自適應
        x = x.view(x.size()[0],-1)
        x = F.relu(self.fc1(x))
        x = F.relu(slef.fc2(x))
        x = self.fc3(x)
        return  x
net = Net()
print(net)
		

out: Net(
(conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
(conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
(fc1): Linear(in_features=375, out_features=120, bias=True)
(fc2): Linear(in_features=120, out_features=84, bias=True)
(fc3): Linear(in_features=84, out_features=10, bias=True)
)

只要在nn.Module的子類中定義了forward函數,backward函數就會自動被實現(利用Autograd)。在forward 函數中可使用任何Variable支持的函數,還可以使用if、for循環、print、log等Python語法,寫法和標準的Python寫法一致。網絡的可學習參數通過net.parameters()返回,net.named_parameters可同時返回可學習的參數及名稱。

params = list(net.parameters())
print(len(params))

out:10

for name,parameters in net.named_parameters():
    print(name,':',parameters.size())

out:
conv1.weight : torch.Size([6, 1, 5, 5])
conv1.bias : torch.Size([6])
conv2.weight : torch.Size([16, 6, 5, 5])
conv2.bias : torch.Size([16])
fc1.weight : torch.Size([120, 400])
fc1.bias : torch.Size([120])
fc2.weight : torch.Size([84, 120])
fc2.bias : torch.Size([84])
fc3.weight : torch.Size([10, 84])
fc3.bias : torch.Size([10])

input = Variable(t.randn(1, 1, 32, 32))
out = net(input)
out.size()
net.zero_grad() # 所有參數的梯度清零
out.backward(Variable(t.ones(1,10))) # 反向傳播  需要放入一個維度相同的
torch.Size([1, 10])

損失函數

nn實現了神經網絡中大多數的損失函數,例如nn.MSELoss用來計算均方誤差,nn.CrossEntropyLoss用來計算交叉熵損失。

output = net(input)
target = Variable(t.arange(0,10))  
criterion = nn.MSELoss()
loss = criterion(output, target)
loss

Variable containing:
28.5536
[torch.FloatTensor of size 1]

loss的計算圖:

input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d  
      -> view -> linear -> relu -> linear -> relu -> linear 
      -> MSELoss
      -> loss
 當調用loss.backward()時,該圖會動態生成並自動微分,也即會自動計算圖中參數(Parameter)的導數。
# 運行.backward,觀察調用之前和調用之後的grad
net.zero_grad() # 把net中所有可學習參數的梯度清零
print('反向傳播之前 conv1.bias的梯度')
print(net.conv1.bias.grad)
loss.backward()
print('反向傳播之後 conv1.bias的梯度')
print(net.conv1.bias.grad)

反向傳播之前 conv1.bias的梯度
Variable containing:
0
0
0
0
0
0
[torch.FloatTensor of size 6]

反向傳播之後 conv1.bias的梯度
Variable containing:
1.00000e-02 *
-4.2109
-2.7638
-5.8431
1.3761
-2.4141
-1.2015
[torch.FloatTensor of size 6]

在反向傳播計算完所有參數的梯度後,還需要使用優化方法來更新網絡的權重和參數,例如隨機梯度下降法(SGD)的更新策略如下:

weight = weight - learning_rate * gradient

learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)# inplace 減法
import torch.optim as optim
#新建一個優化器,指定要調整的參數和學習率
optimizer = optim.SGD(net.parameters(), lr = 0.01)

# 在訓練過程中
# 先梯度清零(與net.zero_grad()效果一樣)
optimizer.zero_grad() 

# 計算損失
output = net(input)
loss = criterion(output, target)

#反向傳播
loss.backward()

#更新參數
optimizer.step()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章