# 深度學習與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 中和高中吳振榮