莫煩pytorch學習記錄
感謝莫煩大神Pytorch B站視訊:https://www.bilibili.com/video/av15997678?p=11
PyTorch是什麼?
它是一個基於Python的科學計算包,其主要是為了解決兩類場景:
1、一種是可以替代Numpy進行科學計算,同時還可以使用張量在GPU上進行加速運算。
2、一個深度學習的研究平臺,提供最大的靈活性和速度。
Numpy與Torch之間的轉換
import torch import numpy as np from torch.autograd import Variable # torch 中 Variable 模組 ### Torch 自稱為神經網路界的 Numpy,# 因為他能將 torch 產生的 tensor 放在 GPU 中加速運算 np_data = np.arange(6).reshape((2,3)) torch_data = torch.from_numpy(np_data) #torch形式 tensor2array = torch_data.numpy() # numpy形式 # torch 做的和 numpy 能很好的相容. # 比如這樣就能自由地轉換 numpy array 和 torch tensor 了 print( '\nnumpy array:', np_data, # [[0 1 2], [3 4 5]] '\ntorch tensor:', torch_data, # 0 1 2 \n 3 4 5 [torch.LongTensor of size 2x3] '\ntensor to array:', tensor2array, # [[0 1 2], [3 4 5]] )
numpy array: [[0 1 2] [3 4 5]] torch tensor: tensor([[0, 1, 2], [3, 4, 5]], dtype=torch.int32) tensor to array: [[01 2] [3 4 5]]
Torch中的數學運算與numpy的對比
API手冊
常用計算: 注意!!!!所有在pytorch
裡的計算,都要先轉換為tensor
的形式,不然就報錯,切記!!!
#abs絕對值運算 data = [-1, -2, 1, 2] tensor = torch.FloatTensor(data) # 轉換成32位浮點tensor print( '\nabs', '\nnumpy: ', np.abs(data), # [1 2 1 2] '\ntorch: ', torch.abs(tensor) # [1 2 1 2] ) #sin 三角函式 sin print( '\nsin', '\nnumpy: ', np.sin(data), # [-0.84147098 -0.90929743 0.84147098 0.90929743] '\ntorch: ', torch.sin(tensor) # [-0.8415 -0.9093 0.8415 0.9093] ) #mean 均值 print( '\nmean', '\nnumpy: ', np.mean(data), # 0.0 '\ntorch: ', torch.mean(tensor) # 0.0 )
矩陣計算:注意,有些numpy的封裝的函式跟pytorch的不一樣,這一點一定要區分清楚,也是很容易出問題的一個地方。
# matrix multiplication 矩陣點乘 data = [[1,2], [3,4]] tensor = torch.FloatTensor(data) # 轉換成32位浮點 tensor # correct method print( '\nmatrix multiplication (matmul)', '\nnumpy: ', np.matmul(data, data), # [[7, 10], [15, 22]] '\ntorch: ', torch.mm(tensor, tensor) # [[7, 10], [15, 22]] ) # !!!! 下面是錯誤的方法 !!!! data = np.array(data) print( '\nmatrix multiplication (dot)', '\nnumpy: ', data.dot(data), # [[7, 10], [15, 22]] 在numpy 中可行 # 關於 tensor.dot() 有了新的改變, 它只能針對於一維的陣列. 所以上面的有所改變. # '\ntorch: ', tensor.dot(tensor) # torch 會轉換成 [1,2,3,4].dot([1,2,3,4) = 30.0 # 變為 # '\ntorch: ', torch.dot(tensor.dot(tensor))# )
Variable
tensor = torch.FloatTensor([[1,2],[3,4]]) # 裡面的值會不停的變化. 就像一個裝雞蛋的籃子, 雞蛋數會不停變動. # 那誰是裡面的雞蛋呢, 自然就是 Torch 的 Tensor 咯. # 如果用一個 Variable 進行計算, 那返回的也是一個同類型的 Variable. # 把雞蛋放到籃子裡, variable = Variable(tensor, requires_grad=True)#requires_grad是參不參與誤差反向傳播, 要不要計算梯度 print('\n',tensor) """ 1 2 3 4 [torch.FloatTensor of size 2x2] """ print(variable) """ Variable containing: 1 2 3 4 [torch.FloatTensor of size 2x2] """
variable的計算
模仿一個計算梯度的情況
比較tensor的計算和variable的計算,在正向傳播它們是看不出有什麼不同的,而且variable
和tensor
有個很大的區別,variable
是儲存變數的,是會改變的,而tensor
是不會改變的,是我們輸入時就設定好的引數,variable
會在反向傳播後修正自己的數值。 這是我覺得他們最大的不同。
t_out = torch.mean(tensor*tensor) # x^2 v_out = torch.mean(variable*variable) # x^2 print('\n',t_out) print('\n',v_out) # 7.5
假設mean的均值做為結果的誤差,對誤差反向傳播得到各項梯度。利用這個例子去看,在反向傳播中它們之間的不同。
v_out = torch.mean(variable*variable)
就是給各個variable
搭建一個運算的步驟,搭建的網路也是其中一種運算的步驟。
v_out.backward() # 模擬 v_out 的誤差反向傳遞,在背景計算圖中加速運算 # 下面兩步看不懂沒關係, 只要知道 Variable 是計算圖的一部分, 可以用來傳遞誤差就好. # v_out = 1/4 * sum(variable*variable) 這是計算圖中的 v_out 計算步驟 # 針對於 v_out 的梯度就是, d(v_out)/d(variable) = 1/4*2*variable = variable/2 print('\n',variable.grad) # 初始 Variable 的梯度 ''' 0.5000 1.0000 1.5000 2.0000 '''
可以看到,在backward中已經計算好梯度了,利用*.grad將背景中計算好的variable的各項梯度print出來。
這樣如果是個網路的運算步驟也可以在backward中將各個梯度計算好。
獲取Variable裡面的資料
直接print(variable)
只會輸出Variable
形式的資料, 在很多時候是用不了的(比如想要用plt
畫圖), 需要轉換成tensor
形式.
## 獲取 Variable 裡面的資料 print(variable) # Variable 形式 """ Variable containing: 1 2 3 4 [torch.FloatTensor of size 2x2] """ print(variable.data) # tensor 形式 """ 1 2 3 4 [torch.FloatTensor of size 2x2] """ print(variable.data.numpy()) # numpy 形式 """ [[ 1. 2.] [ 3. 4.]] """
常用幾種激勵函式及影象
常用幾種激勵函式:relu
,sigmoid
,tanh
,softplus
# 做一些假資料來觀看影象 x = torch.linspace(-5, 5, 200) # x data (tensor), shape=(100, 1) x = Variable(x) # Torch 中的激勵函式有很多, 不過我們平時要用到的就這幾個. # relu, sigmoid, tanh, softplus. 那我們就看看他們各自長什麼樣啦. x_np = x.data.numpy() # 換成 numpy array, 出圖時用
莫煩大神那時的版本用的是torch.nn.relu
,但後來版本改了,直接用torch.relu
就可以,其他激勵函式也一樣。
# 幾種常用的 激勵函式 y_relu = F.relu(x).data.numpy() y_sigmoid = torch.sigmoid(x).data.numpy() y_tanh = torch.tanh(x).data.numpy() y_softplus = F.softplus(x).data.numpy() # y_softmax = F.softmax(x) softmax 比較特殊, 不能直接顯示, 不過他是關於概率的, 用於分類
#用pit畫 plt.figure(1,figsize=(8,6)) plt.subplot(221) plt.plot(x_np,y_relu, c='red', label='relu') plt.ylim(-1,5) plt.legend(loc='best') plt.subplot(222) plt.plot(x_np,y_sigmoid, c='red',label='sigmoid') plt.ylim(-0.2,1.2) plt.legend(loc='best') plt.subplot(223) plt.plot(x_np,y_tanh, c='red',label='tanh') plt.ylim(-1.2,1.2) plt.legend(loc='best') plt.subplot(224) plt.plot(x_np,y_softplus, c='red',label='softplus') plt.ylim(-0.2,6) plt.legend(loc='best') #用ax畫 fig, ax = plt.subplots(2,2,figsize=(8,6)) ax[0,0].plot(x_np,y_relu,c='red', label='relu') ax[0,0].set_title('relu',fontsize=18) ax[0,0].set_ylim(-1,5) ax[0,0].legend() ax[0,1].plot(x_np,y_sigmoid) ax[1,0].plot(x_np,y_tanh) ax[1,1].plot(x_np,y_softplus) plt.show()
線性擬合迴歸
import torch import matplotlib.pyplot as plt import torch.nn.functional as F #######捏造資料####### x = torch.unsqueeze(torch.linspace(-1,1,500),dim=1)#x的資料,shape=(500,1) y = x.pow(2) + 0.2*torch.rand(x.size()) # 畫圖看看捏的資料咋樣 fig,ax = plt.subplots(2,1) ax[0].scatter(x.data.numpy(),y.data.numpy()) ax[0].set_title("Pinched data",fontsize=18)
搭建網路
# 建立一個神經網路我們可以直接運用 torch 中的體系. 先定義所有的層屬性(__init__()), # 然後再一層層搭建(forward(x))層於層的關係連結. 建立關係的時候, 我們會用到激勵函式. class Net(torch.nn.Module): #繼承torch中的Module def __init__(self,n_feature,n_hidden,n_output): super(Net, self).__init__() #繼承__init__功能 # 定義每層用什麼樣的形式 self.hidden = torch.nn.Linear(n_feature,n_hidden) # 隱藏層線性輸出 self.predict = torch.nn.Linear(n_hidden,n_output) # 輸出層線性輸出 def forward(self,x): # 這同時也是Module中的forward功能 # 正向傳播輸入值,神經網路分析出輸出值 x = F.relu(self.hidden(x)) # 激勵函式(隱藏層的線性值) x = self.predict(x) # 輸出值 return x net = Net(n_feature=1, n_hidden=10, n_output=1) print(net) """ Net ( (hidden): Linear (1 -> 10) (predict): Linear (10 -> 1) ) """
開始訓練
#optimizer訓練工具 optimizer = torch.optim.SGD(net.parameters(), lr=0.2) # 傳入net的所有引數,學習率 loss_func = torch.nn.MSELoss() # 預測值和真實值的誤差計算公式(均方差) plt.ion() for t in range(10000): prediction = net(x) # 餵給net訓練資料x,輸出預測值 loss = loss_func(prediction, y) # 計算兩者的誤差,#要預測值在前,label在後 optimizer.zero_grad() # 清空上一步的殘餘更新引數值,#net.parameters()所有引數梯度變為0 loss.backward() # 誤差反向傳播,計算引數更新 optimizer.step() # 將引數更新施加到net的parameters上,#optimizr優化parameters
視覺化訓練過程
if t % 5 == 0: # plot and show learning process ax[1].cla() ax[1].scatter(x.data.numpy(), y.data.numpy()) ax[1].plot(x.data.numpy(), prediction.data.numpy(), 'r-', lw=5) ax[1].text(0.5, 0, 'Loss=%.4f' % loss.data.numpy(), fontdict={'size': 20, 'color': 'red'}) plt.pause(0.01) #如果在指令碼中使用ion()命令開啟了互動模式,沒有使用ioff()關閉的話, # 則影象會一閃而過,並不會常留。要想防止這種情況,需要在plt.show()之前加上ioff()命令。 plt.ioff() plt.show()
區分型別 (分類)
捏個數據
import torch import matplotlib.pyplot as plt # 假資料 n_data = torch.ones(100, 2) # 資料的基本形態 x0 = torch.normal(2*n_data, 1) # 型別0 x data (tensor), shape=(100, 2) y0 = torch.zeros(100) # 型別0 y data (tensor), shape=(100, ) x1 = torch.normal(-2*n_data, 1) # 型別1 x data (tensor), shape=(100, 1) y1 = torch.ones(100) # 型別1 y data (tensor), shape=(100, ) # 注意 x, y 資料的資料形式是一定要像下面一樣 (torch.cat 是在合併資料) x = torch.cat((x0, x1), 0).type(torch.FloatTensor) # FloatTensor = 32-bit floating y = torch.cat((y0, y1), ).type(torch.LongTensor) # LongTensor = 64-bit integer # 畫圖 會出錯:會報錯,因為畫圖x和y的數量不相同,x矩陣的形狀是(200,2)的,而y矩陣的形狀是(200), # 所以需要把x分成兩部分來畫圖才可以的。 # plt.scatter(x.data.numpy(), y.data.numpy()) # plt.show() # 畫圖 plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=y.data.numpy(), s=100, lw=0, cmap='RdYlGn') plt.show()
搭個網路
class Net(torch.nn.Module): def __init__(self,n_feature,n_hidden,n_outpot): super(Net,self).__init__() #繼承__init__的功能 self.hidden = torch.nn.Linear(n_feature,n_hidden) self.out = torch.nn.Linear(n_hidden,n_outpot) def forward(self,x): x = torch.relu(self.hidden(x)) x = self.out(x) return x net = Net(n_feature=2,n_hidden=10,n_outpot=2) print(net)
訓練網路
optimizer = torch.optim.SGD(net.parameters(), lr=0.02) # 傳入 net 的所有引數, 學習率 # 算誤差的時候, 注意真實值!不是! one-hot 形式的, 而是1D Tensor, (batch,) # 但是預測值是2D tensor (batch, n_classes) loss_func = torch.nn.CrossEntropyLoss() for t in range(100): out = net(x) # 餵給 net 訓練資料 x, 輸出分析值 loss = loss_func(out, y) # 計算兩者的誤差 optimizer.zero_grad() # 清空上一步的殘餘更新引數值 loss.backward() # 誤差反向傳播, 計算引數更新值 optimizer.step() # 將引數更新值施加到 net 的 parameters 上
視覺化
plt.ion() for t in range(100): out = net(x) # 餵給 net 訓練資料 x, 輸出分析值 loss = loss_func(out, y) # 計算兩者的誤差 optimizer.zero_grad() # 清空上一步的殘餘更新引數值 loss.backward() # 誤差反向傳播, 計算引數更新值 optimizer.step() # 將引數更新值施加到 net 的 parameters 上 # 接著上面來 if t % 2 == 0: plt.cla() # 過了一道 softmax 的激勵函式後的最大概率才是預測值 prediction = torch.max(F.softmax(out), 1)[1] #prediction=torch.max(F.softmax(out), 1) 中的1,表示【0,0,1】預測結果中,結果為1的結果的位置。 pred_y = prediction.data.numpy().squeeze()#利用squeeze()函式將表示向量的陣列轉換為秩為1的陣列,這樣利用matplotlib庫函式畫圖時,就可以正常的顯示結果了 target_y = y.data.numpy() plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=pred_y, s=100, lw=0, cmap='RdYlGn') accuracy = sum(pred_y == target_y) / 200. # 預測中有多少和真實值一樣 plt.text(1.5, -4, 'Accuracy=%.2f' % accuracy, fontdict={'size': 20, 'color': 'red'}) plt.pause(0.1) plt.ioff() plt.show()
快速搭建法
Torch 中提供了很多方便的途徑, 同樣是神經網路, 能快則快, 我們看看如何用更簡單的方式搭建同樣的迴歸神經網路.
上一節用的方法更加底層,其實有更加快速的方法,對比一下就很清楚了:
Method 1
我們用 class 繼承了一個 torch 中的神經網路結構, 然後對其進行了修改
class Net(torch.nn.Module): #我們用 class 繼承了一個 torch 中的神經網路結構, 然後對其進行了修改 def __init__(self,n_feature,n_hidden,n_outpot): super(Net,self).__init__() #繼承__init__的功能, self.hidden = torch.nn.Linear(n_feature,n_hidden) self.out = torch.nn.Linear(n_hidden,n_outpot) def forward(self,x): x = torch.relu(self.hidden(x)) x = self.out(x) return x net1 = Net(n_feature=2,n_hidden=10,n_outpot=2)
Method 2
用nn
庫裡一個函式就能快速搭建了,注意ReLU
也算做一層加入到網路序列中
net2 = torch.nn.Sequential( torch.nn.Linear(2,10), torch.nn.ReLU(), torch.nn.Linear(10,2) ) print(net1) print(net2)
結果是類似的:就是net2中將ReLU也做為一個神經層
print(net1) """ Net( (hidden): Linear(in_features=2, out_features=10, bias=True) (out): Linear(in_features=10, out_features=2, bias=True) ) """ print(net2) """ Sequential( (0): Linear(in_features=2, out_features=10, bias=True) (1): ReLU() (2): Linear(in_features=10, out_features=2, bias=True) ) """
儲存與提取網路
儲存
#######捏造資料####### torch.manual_seed(1) # reproducible 使得每次隨機初始化的隨機數是一致的 x = torch.unsqueeze(torch.linspace(-1,1,500),dim=1)#x的資料,shape=(500,1) y = x.pow(2) + 0.2*torch.rand(x.size()) def save(): # 搭建網路 net1 = torch.nn.Sequential( torch.nn.Linear(1, 10), torch.nn.ReLU(), torch.nn.Linear(10, 1), ) optimizer = torch.optim.SGD(net1.parameters(), lr=0.5) loss_func = torch.nn.MSELoss() # 訓練 for t in range(100): prediction = net1(x) loss = loss_func(prediction, y) optimizer.zero_grad() loss.backward() optimizer.step() torch.save(net1, 'net.pkl') # 儲存整個網路 torch.save(net1.state_dict(), 'net_params.pkl') # 只儲存網路中的引數 (速度快, 佔記憶體少)
提取網路
# 這種方式將會提取整個神經網路, 網路大的時候可能會比較慢. def restore_net(): # restore entire net1 to net2 net2 = torch.load('net.pkl') prediction = net2(x)
只提取網路引數
# 這種方式將會提取所有的引數, 然後再放到你的新建網路中. def restore_params(): # 新建 net3 net3 = torch.nn.Sequential( torch.nn.Linear(1, 10), torch.nn.ReLU(), torch.nn.Linear(10, 1) ) # 將儲存的引數複製到 net3 net3.load_state_dict(torch.load('net_params.pkl')) prediction = net3(x)
完整程式碼並檢視三個網路模型的結果
import torch import matplotlib.pyplot as plt import torch.nn.functional as F from matplotlib import animation #######捏造資料####### torch.manual_seed(1) # reproducible 使得每次隨機初始化的隨機數是一致的 x = torch.unsqueeze(torch.linspace(-1,1,100),dim=1)#x的資料,shape=(500,1) y = x.pow(2) + 0.2*torch.rand(x.size()) def save(): # 搭建網路 net1 = torch.nn.Sequential( torch.nn.Linear(1, 100), torch.nn.ReLU(), torch.nn.Linear(100, 1), ) optimizer = torch.optim.SGD(net1.parameters(), lr=0.3) loss_func = torch.nn.MSELoss() # 訓練 for t in range(1000): prediction = net1(x) loss = loss_func(prediction, y) optimizer.zero_grad() loss.backward() optimizer.step() torch.save(net1, 'net.pkl') # 儲存整個網路 torch.save(net1.state_dict(), 'net_params.pkl') # 只儲存網路中的引數 (速度快, 佔記憶體少) # plot result plt.figure(1, figsize=(10,3)) plt.subplot(131) plt.title('Net1') plt.scatter(x.data.numpy(), y.data.numpy()) plt.plot(x.data.numpy(), prediction.data.numpy(), 'r-', lw=5) # 這種方式將會提取整個神經網路, 網路大的時候可能會比較慢. def restore_net(): # restore entire net1 to net2 net2 = torch.load('net.pkl') prediction = net2(x) # plot result plt.figure(1, figsize=(10,3)) plt.subplot(132) plt.title('Net2') plt.scatter(x.data.numpy(), y.data.numpy()) plt.plot(x.data.numpy(), prediction.data.numpy(), 'r-', lw=5) # 這種方式將會提取所有的引數, 然後再放到你的新建網路中. def restore_params(): # 新建 net3 net3 = torch.nn.Sequential( torch.nn.Linear(1, 100), torch.nn.ReLU(), torch.nn.Linear(100, 1) ) # 將儲存的引數複製到 net3 net3.load_state_dict(torch.load('net_params.pkl')) prediction = net3(x) # plot result plt.figure(1, figsize=(10,3)) plt.subplot(133) plt.title('Net3') plt.scatter(x.data.numpy(), y.data.numpy()) plt.plot(x.data.numpy(), prediction.data.numpy(), 'r-', lw=5) # 儲存 net1 (1. 整個網路, 2. 只有引數) save() # 提取整個網路 restore_net() # 提取網路引數, 複製到新網路 restore_params() plt.show()