深度學習筆記(三)用Torch實現多層感知器
阿新 • • 發佈:2019-02-03
上一次我們使用了輸出節點和輸入節點直接相連的網路。網路裡只有兩個可變引數。這種網路只能表示一條直線,不能適應複雜的曲線。我們將把它改造為一個多層網路。一個輸入節點,然後是兩個隱藏層,每個隱藏層有3個節點,每個隱藏節點後面都跟一個非線性的Sigmoid函式。如圖所示,這次我們使用的網路是有2個隱藏層,每層有3個節點的多層神經網路。
那麼這樣的結構用程式碼如何表示呢?我們來直接在上一次的程式碼上修改。 這個網路結構是一層一層的疊加起來的,nn庫裡有一個型別叫做Sequential序列,正好適合我們。這個Sequential是一個容器類,我們可以在它裡面新增一些基本的模組。
優化函式的呼叫方法有一點特殊,需要你先提供一個目標函式,這個函式相當於C++裡的回撥函式,他的輸入是一組網路權重引數w,輸出有兩個,第一個是網路使用引數w時,其輸出結果與實際結果之間的差別,也可以叫loss損失,另一個是w中每個引數對於loss的偏導數。
看一下結果,這次的結果看起來就好多了。綠線(預測值)幾乎和藍線(實際值)重合在一起了。 下一節,我們將介紹如何用卷積神經網路識別MNIST手寫數字影象。 本節的完整程式碼:
那麼這樣的結構用程式碼如何表示呢?我們來直接在上一次的程式碼上修改。 這個網路結構是一層一層的疊加起來的,nn庫裡有一個型別叫做Sequential序列,正好適合我們。這個Sequential是一個容器類,我們可以在它裡面新增一些基本的模組。
model = nn.Sequential()
第一個我們要新增的是輸入節點和第一個隱藏層的連線,它是一個Linear線性的型別,它的輸入是1個節點,輸出是3個節點。
model:add(nn.Linear(1,3))
然後我們在他後面新增一個Sigmoid層,它的節點個數會自動和前一層的輸出個數保持一致。
接下來我們新增第一和第二隱藏層中間的線性連線,輸入是3,輸出也是3。model:add(nn.Sigmoid())
model:add(nn.Linear(3,3))
再新增一個Sigmoid層。
model:add(nn.Sigmoid())
最後是第二隱藏層和輸出節點之間的線性連線,輸入是3,輸出是1。
model:add(nn.Linear(3,1))
所以完整的建立模型的程式碼看起來是這樣的
好,理論上講我們已經改造完了網路,可以開始訓練了。我們執行一下,看一下結果。我們會很意外的發現這個結果還不如我們上一次的結果。 其實這裡面存在兩個問題: 一個是我們的訓練資料,輸入的月份取值範圍從1到10,輸出的價格取值範圍是幾萬。這樣開始訓練的時候後面幾層的梯度會受到輸出值的影響,變得非常大,迅速的把前面幾層的引數推到一個很大的數值。而Sigmoid函式在遠離零點的位置幾乎梯度為零,所以就一直固定在一個位置不動了。 解決的方法是把輸入和輸出的取值範圍調整到合適的區間,我這裡把輸入除以10,輸出除以50000。預測時再把50000乘回去。在程式碼裡面體現,就是在開頭和結尾加兩個輔助層,nn.MulConstant,這種型別的模組是對網路中的每個元素乘上一個常數。在輸入進入之前先乘以0.1,在輸入之後乘以50000。 這樣一來,建立模型的程式碼就變成了這樣:model = nn.Sequential() model:add(nn.Linear(1,3)) model:add(nn.Sigmoid()) model:add(nn.Linear(3,3)) model:add(nn.Sigmoid()) model:add(nn.Linear(3,1))
資料預處理問題現在解決了,還有一個問題是訓練的速度很慢。因為我們現在的優化方法用的是最原始梯度下降法。 其實Torch已經給我們提供了各種先進的優化演算法,都放在optim這個庫裡。我們在檔案的頭部新增包含optim庫:model = nn.Sequential() model:add(nn.MulConstant(0.1)) --在輸入進入之前先乘以0.1 model:add(nn.Linear(1,3)) model:add(nn.Sigmoid()) model:add(nn.Linear(3,3)) model:add(nn.Sigmoid()) model:add(nn.Linear(3,1)) model:add(nn.MulConstant(50000)) --在輸入之後乘以50000
require 'optim'
另外,還需要把model裡面的引數找出來方便隨時呼叫。
w, dl_dw = model:getParameters()
w是model裡面所有可調引數的集合,dl_dw是每個引數對loss的偏導數。需要注意的是這裡的w和dl_dw都相當於C++裡面的“引用”,一旦你對他們進行了操作,模型裡的引數也會跟著改變。優化函式的呼叫方法有一點特殊,需要你先提供一個目標函式,這個函式相當於C++裡的回撥函式,他的輸入是一組網路權重引數w,輸出有兩個,第一個是網路使用引數w時,其輸出結果與實際結果之間的差別,也可以叫loss損失,另一個是w中每個引數對於loss的偏導數。
feval = function(w_new)
if w ~= w_new then w:copy(w_new) end
dl_dw:zero()
price_predict = model:forward(month_train)
loss = criterion:forward(price_predict, price_train)
model:backward(month_train, criterion:backward(price_predict, price_train))
return loss, dl_dw
end
這個回撥函式可以參照這個例子來寫,同樣是“例行公事”,呼叫一下反向傳播的演算法。
有了這個目標函式,優化迭代的過程就簡單多了。只需要一句optim.rprop(feval, w, params)。 rprop是一種改進的梯度下降法,它只看梯度的方向,不管大小,只要方向不變,它會無限的增大步長,所以他速度非常快。迭代的程式碼如下:
params = {
learningRate = 1e-2
}
for i=1,3000 do
optim.rprop(feval, w, params)
if i%10==0 then
gnuplot.plot({month, price}, {month_train:reshape(10), price_predict:reshape(10)})
end
end
其中每10次迭代會把結果用gnuplot畫出來。
我們來執行一下。
在命令列鍵入
th mlp.lua
看一下結果,這次的結果看起來就好多了。綠線(預測值)幾乎和藍線(實際值)重合在一起了。 下一節,我們將介紹如何用卷積神經網路識別MNIST手寫數字影象。 本節的完整程式碼:
require 'torch'
require 'nn'
require 'optim'
require 'gnuplot'
month = torch.range(1,10)
price = torch.Tensor{28993,29110,29436,30791,33384,36762,39900,39972,40230,40146}
model = nn.Sequential()
model:add(nn.MulConstant(0.1))
model:add(nn.Linear(1,3))
model:add(nn.Sigmoid())
model:add(nn.Linear(3,3))
model:add(nn.Sigmoid())
model:add(nn.Linear(3,1))
model:add(nn.MulConstant(50000))
criterion = nn.MSECriterion()
month_train = month:reshape(10,1)
price_train = price:reshape(10,1)
gnuplot.figure()
w, dl_dw = model:getParameters()
feval = function(w_new)
if w ~= w_new then w:copy(w_new) end
dl_dw:zero()
price_predict = model:forward(month_train)
loss = criterion:forward(price_predict, price_train)
model:backward(month_train, criterion:backward(price_predict, price_train))
return loss, dl_dw
end
params = {
learningRate = 1e-2
}
for i=1,3000 do
optim.rprop(feval, w, params)
if i%10==0 then
gnuplot.plot({month, price}, {month_train:reshape(10), price_predict:reshape(10)})
end
end
month_predict = torch.range(1,12)
local price_predict = model:forward(month_predict:reshape(12,1))
print(price_predict)
gnuplot.pngfigure('plot.png')
gnuplot.plot({month, price}, {month_predict, price_predict})
gnuplot.plotflush()