# 深度學習與pytorch
##### 中和高中吳振榮
## Torch 和 Numpy
Torch自稱是神經網路的numpy,但是還是有許多人對numpy比較熱愛,所以Torch在和numpy做調配的時候非常方便。
### array to torch:from_numpy()
首先利用torch把numpy的array轉成torch的tensor。
```python=
import torch
import numpy as np
np_data = np.arange(1,7).reshape((2,3))
torch_data = torch.from_numpy(np_data)
print(np_data)
print()
print(torch_data)
```
把array放進from_numpy()即可得到一個tensor(張量)。
### torch to array
```python=
import torch
import numpy as np
np_data = np.arange(1,7).reshape((2,3))
torch_data = torch.from_numpy(np_data)
torch_to_array = torch_data.numpy()
print(np_data)
print()
print(torch_data)
print()
print(torch_to_array)
```
在地6行可以看到我們使用.numpy()將torch_data的tensor轉成array。
### 矩陣相乘
```python=
import torch
import numpy as np
matrix = [[1,2],[3,4]]
tensor_matrix = torch.IntTensor(matrix)
np_mul = np.matmul(matrix,matrix)
tor_mul = torch.mm(tensor_matrix,tensor_matrix)
print(np_mul)
print(tor_mul)
```
在numpy做矩陣相乘可使用np.matmul(),而在torch可以使用torch.mm()。
### torch.mean()
做平均。
### Variable
pytorch和tensorflow的差別是pytorch是動態計算圖,而tensorflow的計算圖是固定不變的。
```python=
import torch
from torch.autograd import Variable
tensor = torch.FloatTensor([[1,2],[3,4]])
variable = Variable(tensor, requires_grad = True)
ten = torch.mean(tensor*tensor)
var = torch.mean(variable*variable)
print(ten)
print(var)
var.backward()
print(variable.grad)
print(variable.data)
print(variable.data.numpy())
```
這邊不做說明,這邊僅是單純紀錄這段程式碼,方便之後複習,為何這邊不做說明呢?因為這邊有用到一種前饋神經網路,稱為back-propagation(BP),而這部分會運用到的數學太過艱澀,目前自學的我才高一,而這邊需要用到大量微積分,我無法承受,所以這邊就先略過。
## 重新塑造view()
```python=
import torch
tensor = torch.FloatTensor([1,2,3,4,5,6,7,8,9])
print(tensor.shape)
change = tensor.view(3,3)
print(change.shape)
```
output :
torch.Size([9])
torch.Size([3, 3])
## change device
```python=
import torch
cpu = torch.device("cpu")
gpu = torch.device("gpu")
x = torch.rand(10)
print(x)
x = x.to(gpu)
print(x)
x = x.to(cpu)
print(x)
```
## activation function(激勵函數)
這邊會介紹激勵函數,激勵函數主要是非線性函數,。那為什麼要使用激勵函數呢?其實在訓練model的時候不使用線性轉換的話,機器就不能學習到複雜的映射關係,如果今天沒有使用激勵函數,就算再多層神經網路也訓練不起來,可見激勵函數是非常重要的。
而常見的激勵函數有ReLU、Sigmoid、tanh、softplus,然而最常用的是ReLU,因為在做深度學習時時常會遇到梯度消失或者梯度爆炸(然而梯度爆炸較不常見),而ReLU可以解決這樣的問題,那詳細解決方式這邊就無法再提,因為那樣已經超出我的能力範圍。
接著說明以上常見的激勵函數的效果:
ReLU:ReLU會使正數大於等於0,而負數都變成0。
Sigmoid:會使數值保持在0~1之間,當一個正數已經大到趨近於正無限大時,數字會趨近於1,如果一個負數已經小到趨近於負無限大時,此數會趨近於0。
tanh:tanh解決了Sigmoid的不是zero-centered輸出問題。
softplus:則是在做分類(classification)時常用。
### 這邊將四個激勵函數的函數圖形畫出來。
```python=
import torch
import torch.nn.functional as nnf
from torch.autograd import Variable
import matplotlib.pyplot as plt
x = torch.linspace(-5,5,200)
x = Variable(x)
x_np = x.data.numpy()
activate_func_relu = torch.relu(x).data.numpy()
activate_func_sigmoid = torch.sigmoid(x).data.numpy()
activate_func_tanh = torch.tanh(x).data.numpy()
activate_func_softplus = nnf.softplus(x).data.numpy()
plt.figure(1,figsize = (8,6))
plt.subplot(221)
plt.plot(x_np,activate_func_relu,c = 'red', label = 'relu')
plt.ylim(-1,5)
plt.legend(loc = 'best')
plt.subplot(222)
plt.plot(x_np,activate_func_sigmoid,c = 'green', label = 'sigmoid')
plt.ylim(-0.2,1.2)
plt.legend(loc = 'best')
plt.subplot(223)
plt.plot(x_np,activate_func_tanh,c = 'blue', label = 'tanh')
plt.ylim(-1.2,1.2)
plt.legend(loc = 'best')
plt.subplot(224)
plt.plot(x_np,activate_func_softplus,c = 'black', label = 'softplus')
plt.ylim(-0.2,6)
plt.legend(loc = 'best')
plt.show()
```
![](https://i.imgur.com/3dlkC3Q.png)
### 程式介紹:
首先先引入torch來,接著import torch.nn.functional,由於softplus在torch.nn.functional裡,所以這邊引入torch.nn.functional便將它改名成nnf,由於我們的數據會存在變數裡,所以這邊要引用Variable,然後我們最後會將函數圖畫出來,所以這邊要import matplotlib.pyplot,方便進行數據可視化。
接著建造一段連續的數據,利用torch.linspace(),第一個參數是加入數據起始點,第二個參數是加入數據的終點,最後一個參數是表示要產生幾個數據。然後將此數據加入變成變數,但是這份數據是一個張量,而matplotlib不能使用tensor,所以這邊將張量轉成numpy()的array。
然後將張量x傳入各個激勵函數裡,再將它轉成numpy的array,最後再將它放入matplotlib做數據可視化,這邊講解一下plt.subplot()是將各個圖變成子圖,方便在一個視窗中看到所有圖形,第一個參數和第二個參數分別代表row和column所以大小是row\*column,第三個參數是代表編號,這樣就完成了。
### 接著將四個圖合成成一個來做比較。
```python=
import torch
import torch.nn.functional as nnf
from torch.autograd import Variable
import matplotlib.pyplot as plt
x = torch.linspace(-5,5,200)
x = Variable(x)
x_np = x.data.numpy()
activate_func_relu = torch.relu(x).data.numpy()
activate_func_sigmoid = torch.sigmoid(x).data.numpy()
activate_func_tanh = torch.tanh(x).data.numpy()
activate_func_softplus = nnf.softplus(x).data.numpy()
plt.plot(x_np,activate_func_relu,c = 'red', label = 'relu')
plt.legend(loc = 'best')
x_relu = -1.3
y_relu = 0
plt.annotate('ReLU',xy = (x_relu,y_relu),xycoords = 'data',xytext = (-80,-30),textcoords = 'offset points',fontsize = 20,arrowprops = dict(arrowstyle = '->',connectionstyle = 'arc3,rad = .2'))
plt.plot(x_np,activate_func_sigmoid,c = 'green', label = 'sigmoid')
plt.legend(loc = 'best')
x_sigmoid = 1.4
y_sigmoid = 0.8
plt.annotate('sigmoid',xy = (x_sigmoid,y_sigmoid),xycoords = 'data',xytext = (-20,-50),textcoords = 'offset points',fontsize = 20,arrowprops = dict(arrowstyle = '->',connectionstyle = 'arc3,rad = .2'))
plt.plot(x_np,activate_func_tanh,c = 'blue', label = 'tanh')
plt.legend(loc = 'best')
x_tanh = -0.5
y_tanh = -0.5
plt.annotate('tanh',xy = (x_tanh,y_tanh),xycoords = 'data',xytext = (+30,-30),textcoords = 'offset points',fontsize = 20,arrowprops = dict(arrowstyle = '->',connectionstyle = 'arc3,rad = .2'))
plt.plot(x_np,activate_func_softplus,c = 'black', label = 'softplus')
plt.legend(loc = 'best')
x_softplus = 0.5
y_softplus = 1
plt.annotate('softplus',xy = (x_softplus,y_softplus),xycoords = 'data',xytext = (-95,+15),textcoords = 'offset points',fontsize = 20,arrowprops = dict(arrowstyle = '->',connectionstyle = 'arc3,rad = .2'))
plt.show()
```
![](https://i.imgur.com/o1a12Yu.png)
這邊觀念和上面一個一樣只是有matplotlib做一些調整而已,這邊就不做贅述。
## Regression(回歸)
### code:
```python=
import torch
from torch.autograd import Variable
import torch.nn.functional as nnf
import matplotlib.pyplot as plt
x = torch.unsqueeze(torch.linspace(-1,1, 100),dim = 1)
y = x**2 + 0.2 * torch.rand(x.size())
x, y = Variable(x), Variable(y)
class NN(torch.nn.Module):
def __init__(self,n_features,n_hidden,n_output):
super(NN,self).__init__()
self.hidden = torch.nn.Linear(n_features, n_hidden)
self.predict = torch.nn.Linear(n_hidden, n_output)
def forward(self, data):##forward propagation
data = nnf.relu(self.hidden(data))
data = self.predict(data)
return data
network = NN(n_features = 1,n_hidden = 10,n_output = 1)
print(network)
optimization = torch.optim.SGD(network.parameters(),lr = 0.5)
loss_func = torch.nn.MSELoss()
plt.ion()
for i in range(1000):
prediction = network(x)
loss = loss_func(prediction, y)
optimization.zero_grad()
loss.backward()
optimization.step()
if i % 5 == 0:
plt.cla()
plt.scatter(x.data.numpy(),y.data.numpy())
plt.plot(x.data.numpy(),prediction.data.numpy(),'r-',lw = 5)
plt.text(-0.4, 1, 'Loss = %.4f' % loss.data.numpy(),fontdict = {'size': 20,'color': 'green'})
plt.pause(0.1)
plt.ioff()
plt.show()
```
### 函數圖:
![](https://i.imgur.com/vmlz1vD.png)
### code introduction:
首先先引入一些第三方套件,再來生成x,y兩個數據,而在pytorch是有維度的,所以這邊用unsqueeze()來為x增加一個維度,因為單純使用linspace()是一維的,用完unsqueeze()則可生成二維的數據,接著利用x帶入一個函數式來生成y,這邊多加一個常數,會更像真實數據,如果不加,會使此函數圖形太過於整齊,無法顯現現實世界的狀況,再來將x,y變成變數。
這邊建立一個類別(class),也就是我們的神經網路,這邊會使用的pytorch寫好的神經網路,這邊會使用類別的繼承,父類別是torch.nn.Module,首先先建立一個__init__函式,接著super(NN,self).__init__()其代表對繼承的那個父類別進行初始化,首先找到NN的父類別torch.nn.Module,然後把NN的物件self轉換為父類別torch.nn.Module的物件,然後被轉換的父類的物件A呼叫自己的__init__函式。接著建造linear function,第一個參數和第二參數都是代表維度,而weight和bias會隨機賦予。接著定義一個函式叫forward,代表正向傳播的意思,接著將傳入的data放入我們的神經網路,並使用ReLU這個激勵函數,再來將從隱藏層輸出的數值放入predict裡做預測,這邊不加激勵函數,因為我們的數據有可能是負無限大到正無限大,如果今天再使用激勵函數,例如ReLU,這份數據會因為激勵函數的特性,而使數值被壓縮在0~1之間。
接著開始建造神經網路,將我們要的神經元個數輸入到我們的NN裡,再來進行optimization,這邊使用SGD(stochastic gradient decent)也就是最單純的梯度下降方法,要使優化起作用需要知道當前網路的參數空間,而這邊的network.parameter()是要獲得參數的信息,而後面的lr就代表learning rate。
再來要建立loss function,這邊使用的方法是MSE(Mean Suare Error)。
接下來要進行訓練,順便把我們要的數據可視化畵出來,首先用plt.ion()代表我們要建立一個連續的圖,最後要記得加入plt.ioff,來關掉,再來用迴圈去跑1000遍,我們希望我們的loss function越小越好,所以將network回傳的predict值和我們實際的y值做MSE,由於計算完每次的loss,梯度都會保留,所以要用optimization.zero_grad()把梯度改成0,再將loss進行反向傳播(back-propagation),然後用optimization.step()來進行優化。
為了達成數據可視化,這邊將會每五次就更新一下目前的學習狀況,plt.cla()是清除目前座標軸,再用scatter把所有數據點畫出來,然後將訓練的線畫出來,同時並輸出當前的loss,並停頓0.1秒,即完成我們的regression。
## Classification(分類)
### code
```cpp=
import torch
import torch.nn.functional as nnf
import matplotlib.pyplot as plt
data = torch.ones(100, 2)
x0 = torch.normal(2*data, 1)
y0 = torch.zeros(100)
x1 = torch.normal(-2*data, 1)
y1 = torch.ones(100)
x = torch.cat((x0, x1), 0).type(torch.FloatTensor)
y = torch.cat((y0, y1), ).type(torch.LongTensor)
plt.figure(1)
plt.scatter(x.data.numpy()[:,0],x.data.numpy()[:, 1],c = y.data.numpy(),s = 100,lw = 0,cmap = 'RdYlGn')
plt.figure(2)
class NN(torch.nn.Module):
def __init__(self,n_features,n_hidden,n_output):
super(NN,self).__init__()
self.hidden = torch.nn.Linear(n_features, n_hidden)
self.predict = torch.nn.Linear(n_hidden, n_output)
def forward(self, data):##forward propagation
data = nnf.relu(self.hidden(data))
data = self.predict(data)
return data
network = NN(n_features = 2,n_hidden = 10,n_output = 2)
print(network)
optimization = torch.optim.SGD(network.parameters(),lr = 0.01)
loss_func = torch.nn.CrossEntropyLoss()
plt.ion()
for i in range(100):
prediction = network(x)
loss = loss_func(prediction, y)
optimization.zero_grad()
loss.backward()
optimization.step()
if i % 2 == 0:
plt.cla()
predict = torch.max(prediction, 1)[1]
pred_y = predict.data.numpy()
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 = float((pred_y == target_y).astype(int).sum()) / float(target_y.size)
plt.text(0.8,-4,'Accuracy = %.2f' % accuracy, fontdict = {'size':20, 'color': 'red'})
plt.pause(0.1)
plt.ioff()
plt.show()
```
### 輸出圖形
左側是正確的分類方式,右側是訓練的分類方式。
![](https://i.imgur.com/EYxHmiw.png)
### Neural Network
![](https://i.imgur.com/FLy5Obm.png)
### code introduction
首先創造一些假數據,data裡存的都是1,接著設定x0和x1,這裡利用離散正態度分佈抽取隨機數,第一個參數代表mean代表平均,第二個參數是std代表標準差,再來生成y0和y1,這裡將y0和y1分別生成都是1和都是0,再來將x0和x1合成成x,將y0和y1合成成y,由於要做對比,這裡開一個figure1代表正確的分類,並用散佈圖呈現,記得要將x和y轉成array,因為matplotlib並不支援tensor(張量),這裡使用到numpy的切片,x座標放的是x.data.numpy()[:, 0],就是說x是二維的,我們將一維的資料的第一個加入,而一維資料的第二個當y座標,這裡的顏色就用y來表示,稍後也是要訓練的像y的效果,由於這邊是將資料二分,所以1是一類,0是另一類,所以這邊將1設為一種顏色0設為另一種顏色,接著我們的神經網路的設計和上面的regression一樣,這邊就不做贅述了,只是這邊我將我們的輸入和輸出變成兩個神經元,這邊的優化一樣是用SGD(stochastic gradient decent),這裡將learning rate調成0.01是為了能更清楚的看到我們的model在學習的跡象,接著這裡我們的loss function不是使用MSE或MAE,而是使用CrossEntropy,因為在做classification時判斷的依據是機率,而MSE或MAE皆不是用機率的形式表現,所以這邊才使用CrossEntropy來計算loss function,至於CrossEntropy是如何做的呢?有機會的話後面會提到。
接著進入迴圈,有許多東西和前一個regression相同,這邊就不多贅述,我們將會每兩次輸出一個改變,然後前面提到說做classification主要是看機率,所以我將network回傳的prediction和1做比較,看誰比較大,我們選數字大的當我們的結果,將此結果存在predict裡,而後來判斷輸出的顏色就是以predict為基準的,最後要算我們的精確度,精確度的計算方法是如果預測的predict和實際結果y相同的話,將此值轉成int型態,並將此值除以實際結果y的大小就是我們要的accuracy,最後將所有要的東西輸出就完成這次的classification。
## 快速建造神經網路
### code
這是前幾個例子建造的神經網路,比較長,比較複雜:
```python=
class NN(torch.nn.Module):
def __init__(self,n_features,n_hidden,n_output):
super(NN,self).__init__()
self.hidden = torch.nn.Linear(n_features, n_hidden)
self.predict = torch.nn.Linear(n_hidden, n_output)
def forward(self, data):##forward propagation
data = nnf.relu(self.hidden(data))
data = self.predict(data)
return data
```
這個就是快速建造神經網路的方法:
```python=
NN2 = torch.nn.Sequential(
torch.nn.Linear(2,10),torch.nn.ReLU(),torch.nn.Linear(10,2),
)
```
將兩個神經網路輸出,看看會有哪裡不同:
```python=
import torch
import torch.nn.functional as nnf
class NN(torch.nn.Module):
def __init__(self,n_features,n_hidden,n_output):
super(NN,self).__init__()
self.hidden = torch.nn.Linear(n_features, n_hidden)
self.predict = torch.nn.Linear(n_hidden, n_output)
def forward(self, data):##forward propagation
data = nnf.relu(self.hidden(data))
data = self.predict(data)
return data
NN2 = torch.nn.Sequential(
torch.nn.Linear(2,10),torch.nn.ReLU(),torch.nn.Linear(10,2),
)
network = NN(n_features = 2,n_hidden = 10,n_output = 2)
print(network)
print(NN2)
```
### output:
![](https://i.imgur.com/p8CNa7l.png)
### code introduction
原本的神經網路就不介紹了,這裡只介紹第二種神經網路,第二種神經網路看起來簡單許多,只須使用pytorch的內建的class就可以完成,首先使用pytorch裡的class叫Sequential,Sequential是一個容器,可以加入其他的神經網路的class,而Sequential會照著順序執行它,接著加入torch.nn.Linear(2,10),這句就和原本NN裡的神經網路裡的self.hidden相同,只是可以看到輸出的位置NN2是顯示(0)而不是顯示(hidden),因為我們沒有幫它命名,接著加入激勵函數,這裡一樣使用ReLU,前面有說到Sequential會照順序執行,所以輸出的地方可以看到有一行ReLU(),看回NN的程式碼,在NN裡是使用nnf.relu(self.hidden(data))來定義激勵函數,但其實nnf.relu()和torch.nn.ReLU()效果是一樣的。最後是我們的預測輸出層,這裡使用torch.nn.Linear(10,2),和輸入層的觀念相同,這裡就不多做贅述了。
## 儲存先前已做好的訓練,並讀取
https://pytorch.org/tutorials/beginner/saving_loading_models.html
### code
```python=
import torch
import matplotlib.pyplot as plt
x = torch.unsqueeze(torch.linspace(-1,1, 100),dim = 1)
y = x**2 + 0.2 * torch.rand(x.size())
def save():
network = torch.nn.Sequential(
torch.nn.Linear(1,10),torch.nn.ReLU(),torch.nn.Linear(10,1),
)
optimization = torch.optim.SGD(network.parameters(), lr = 0.5)
loss_func = torch.nn.MSELoss()
for i in range(1000):
prediction = network(x)
loss = loss_func(prediction, y)
optimization.zero_grad()
loss.backward()
optimization.step()
plt.figure(1)
plt.subplot(131)
plt.title('origin')
plt.scatter(x.data.numpy(),y.data.numpy())
plt.plot(x.data.numpy(),prediction.data.numpy(),'r-',lw = 5)
torch.save(network,'NN.pkl')
torch.save(network.state_dict(), 'parameter.pkl')
def load_all_NN():
network2 = torch.load('NN.pkl')
prediction = network2(x)
plt.subplot(132)
plt.title('all NN')
plt.scatter(x.data.numpy(),y.data.numpy())
plt.plot(x.data.numpy(),prediction.data.numpy(),'r-',lw = 5)
def load_parameter_NN():
network3 = torch.nn.Sequential(
torch.nn.Linear(1,10),torch.nn.ReLU(),torch.nn.Linear(10,1),
)
network3.load_state_dict(torch.load('parameter.pkl'))
prediction = network3(x)
plt.subplot(133)
plt.title('parameters')
plt.scatter(x.data.numpy(),y.data.numpy())
plt.plot(x.data.numpy(),prediction.data.numpy(),'r-',lw = 5)
save()
load_all_NN()
load_parameter_NN()
plt.show()
```
### output
### code introduction
如果今天想要使用之前訓練的結果,但是如果要重新訓練會很麻煩又很耗時間,所以我們可以將之前的訓練結果存起來,以便下次直接取用,先來看code,我們這邊使用regression的例子,所以不講和之前一樣的東西,可以看到我們的code有三個函式,第一個函式是存取訓練完的資料,第二個函式是讀取全部的資料,第三個是讀取之前訓練的參數,這裡可能會很納悶,第二個和第三個函式不是很像嗎?為何有兩種不同函式呢?這邊是為了清楚顯現效果才將兩種方式顯現出來,否則擇一即可,而這兩種的不同稍後會說。
首先看到第21行,這邊由於要將三個圖放入一個figure裡,所以要建造子圖,再來看26行,這裡我們直接使用pytorch的函式來儲存,有兩種儲存方法,這也就是為什麼前面有三個自訂函式,第一種儲存方式是將所有資料儲存,包括圖(graph),因為pytorch是一種graph,另一種儲存方式是只儲存訓練的參數,而存取參數的時候要使用state_dict,在pytorch的官方文檔裡是這樣解釋state_dict:(A state_dict is simply a Python dictionary object that maps each layer to its parameter tensor. )也就是說state_dict是存取之前訓練的各層的張量資料,包括weights和bias,那為什麼要分兩種,其實第二種存參數的速度比較快,這邊記得要將檔案存成pkl檔。
最後兩個函式中我們將資料取出,使用load()函式,至於存取參數的那個需要多家load_state_dict(),這樣我們就完成了。
## Batch Training
### code
```python=
import torch
from torch.utils import data as data_
batch_sizes = 5
x = torch.linspace(1, 10, 10)
y = torch.linspace(10, 1, 10)
torch_dataset = data_.TensorDataset(x, y)
loader = data_.DataLoader(dataset = torch_dataset, batch_size = batch_sizes, shuffle = True, num_workers = 2,)
for epoch in range(3):
for step, (batch_x, batch_y) in enumerate(loader):
print('Epoch: ', epoch,' || Step: ', step, ' || batch x: ', batch_x.numpy(), ' || batch_y: ', batch_y.numpy())
```
### outuput
![](https://i.imgur.com/7irZP6N.png)
### code intorduction
在做深度學習時不一定是將所有資料全部一起訓練,時常會將資料做切割,進行分批訓練,以提高訓練速度,首先要先引用torch.utils裡的data,以便稍後做分批訓練,然後設定一個變數代表每批訓練的數量,再來創造假數據,一個數據為1到10,另一個則是10到1,接著利用TensorDataset()將x和y加入一個包裝數據和目標張量的集合裡,接著要做batch的分批訓練,我們直接繼承pytorch的類別,便可以直接使用DataLoader(),接著將參數輸入,就可以將資料分批,第一個參數是加入之前的torch_dataset,第個參數是每批幾個,第三個參數代表是否將資料打亂,True代表要,False代表不要,最後一個參數num_workers需要大於等於0,代表我一次要有幾個窗口來導入數據,這樣可以加快數據導入速度,當然加入的參數不只這幾個,這裡引用pytorch的官方文件:
<span style = "color:red">DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
batch_sampler=None, num_workers=0, collate_fn=None,
pin_memory=False, drop_last=False, timeout=0,
worker_init_fn=None, *, prefetch_factor=2,
persistent_workers=False)</span>
以上是可以輸入的參數,接著使用巢狀迴圈去跑,外迴圈代表我們要執行幾次,由於我的數據可以打亂且分為兩批做輸入,所有重複多次這個行為,可以使我們的model訓練的更好,因為每次都會打亂並分為兩批,所以每次訓練的資料不一樣,使每次訓練可以訓練到不同的feature,至於裡面那層迴圈是將我們每批數據都顯現出來,方便我們觀察資料實際的樣子,至於後面的enumerate是為了產生index,方便我們的step顯示,最後將x_batch和y_batch輸出,即完成這次的實作。
## Optimization
### code
```python=
import torch
from torch.utils import data as data_
import torch.nn.functional as nnf
from torch.autograd import Variable
import matplotlib.pyplot as plt
LR = 0.01
BATCH_SIZE = 32
EPOCH = 12
x = torch.unsqueeze(torch.linspace(-1,1, 100),dim = 1) ## regression example
y = x**2 + 0.2 * torch.rand(x.size())
plt.figure(1)
plt.scatter(x.numpy(),y.numpy())
plt.figure(2)
torch_dataset = data_.TensorDataset(x, y)
loader = data_.DataLoader(dataset = torch_dataset, batch_size = BATCH_SIZE, shuffle = True, num_workers = 2,)
class NN(torch.nn.Module):
def __init__(self):
super(NN,self).__init__()
self.hidden = torch.nn.Linear(1,20)
self.predict = torch.nn.Linear(20,1)
def forward(self, data):##forward propagation
data = nnf.relu(self.hidden(data))
data = self.predict(data)
return data
NN_SGD = NN()
NN_Momentum = NN()
NN_RMSprop = NN()
NN_Adam = NN()
network = [NN_SGD,NN_Momentum,NN_RMSprop,NN_Adam]
opt_SGD = torch.optim.SGD(NN_SGD.parameters(), lr= LR)
opt_Momentum = torch.optim.SGD(NN_Momentum.parameters(), lr= LR, momentum = 0.8)
opt_RMSprop = torch.optim.RMSprop(NN_RMSprop.parameters(), lr= LR, alpha = 0.9)
opt_Adam = torch.optim.Adam(NN_Adam.parameters(), lr= LR, betas = (0.9,0.99))
optimization = [opt_SGD,opt_Momentum,opt_RMSprop,opt_Adam]
loss_func = torch.nn.MSELoss()
loss_list =[[], [], [], []]
for epoch in range(EPOCH):
for step, (batch_x, batch_y) in enumerate(loader):
bx = Variable(batch_x)
by = Variable(batch_y)
for net, opt, l_list in zip(network, optimization, loss_list):
output = net(bx)
loss = loss_func(output, by)
opt.zero_grad()
loss.backward()
opt.step()
l_list.append(loss.data.numpy())
labels = ['SGD', 'Momentum', 'RMSprop', 'Adam']
for i, l_list in enumerate(loss_list):
plt.plot(l_list, label = labels[i])
plt.legend(loc = 'best')
plt.xlabel('Steps')
plt.ylabel('Loss')
plt.show()
```
### output
![](https://i.imgur.com/swSXnTG.jpg)
![](https://i.imgur.com/PAdVPkC.png)
### code intorduction
首先引入需要用到的套件,接著定義一些hyper parameter,hyper parameter就是給設計者進行調配更改的參數,通常以大寫的形式定義(沒有強制),而在做batch training時常常會看到epoch這個詞,所謂的一次epoch就代表所有數據的forward(正向傳遞)和backward(反向傳遞)更新參數的過程。
再來我們要製造假數據,這裡的假數據和前面的regression一樣,這邊就不多做贅述。
這邊我們一樣做batch training,方式和之前一樣,這邊就不多做贅述。
再來創建神經網路,方式和之前一樣,這邊就不多做贅述。😂😂
接著我們將會測試四種optimizer的優化效率,分別為SGD、Momentum、RMSprop、Adam,首先第一個SGD,就是一個常見且基礎的optimizer,利用微分方法找到梯度,並往梯度的方向更新數據,我們這邊參數只需加入learning rate即可,接著是Momentum,Momentum有運動量的意思,在同方線的時候學習速度會加快,在方向改變的時候學習速度下減小,所以有機會透過這個機制突破local minmum,而momuntum通常會設在0.8、0.9。
接著是RMSprop,如果沿著同一個方向梯度非常大,可以使用RMSprop,RMSprop更新learning rate時會前一次有關係,所以這邊會有一個alpha,可以自由調整新舊gradient的比重。
最後是Adam,Adam保留了Momentum對過去梯度的方向做梯度速度調整與Adam對過去梯度的平方值做learning rate的調整,再加上Adam有做參數的偏離校正,使得每一次的學習率都會有個確定的範圍,會讓參數的更新較為平穩。
接著創造loss function,這裡使用MSE,然後在loss_list裡存放每個optimizer的loss,再來進入迴圈,迴圈裡就是在做分批訓練,在第三層迴圈裡使用zip將可迭代的資料打包成一個,再由變數進行迭代,最後將圖做輸出就完成了。
##### by 中和高中吳振榮