本文作者:Ovien
自由團隊將從0到1 手把手教各位讀者學會(1)Python基礎語法、(2)Python Web 網頁開發框架 – Django 、(3)Python網頁爬蟲 – 周易解夢網、(4)Tensorflow AI語言模型基礎與訓練 – LSTM、(5)實際部屬AI解夢模型到Web框架上。
AI . FREE Team 讀者專屬福利 → Python Basics 免費學習資源
經過前兩篇的講述人工智慧基礎知識的文章後,相信大家對於 AI 的起源都有初步的理解,為了讓大家可以對類神經網路有更深的理解,本篇將帶大家使用pytorch這個深度學習的框架,來進行今天的實作。
一開始先為大家詳解一下向後傳播的概念,上次有說到透過預測與答案之間的差距(loss function),了解模型的學習狀況,再利用調整W,降低預測與答案之間的差距,進而提高正確率,但是其實在向後傳播的過程中,還有一個更重要的過程,便是梯度下降(Gradient descent),讓我們繼續看下去!

loss總和的平均,而 y 上面有一個^符號,我們稱作y_hat為我們預測的結果。損失函數是衡量神經網路預測能力的好壞,上述公式 binary_crossentropy - 交叉熵損失函式,在 y 和 y_hat 相等時,loss 會為0,否則 loss 就會是一個正值,且差值越大,loss 值會呈倍數差距,而這個度量概率距離的方式稱為交叉熵。
前一篇的實作中,我們有利用當y_p不等於y_t時,我們會提高W降低W,這個過程其實就是梯度下降的簡易操作方式,但是判斷邏輯過於簡單且不夠精確,在求出loss之後,我們需要得知神經元對於輸出的變化,用文字陳述可能比較難理解,從以下視覺化圖片,相信大家能有更好的見解。

上圖中在向前傳播的結果中,我們可以得知輸出是 33,但是我們無法得知每一個節點(輸入和神經元) 的運算對於結果的影響;因此,需要得知每個節點對於輸出的重要性(權重W),我們必須仰賴向後傳播。
而向後(反向)傳播其實就是一系列的偏微分,透過微分我們可以得知每個節點當下的變化,進而得到他的梯度(gradient),這邊就不詳細解釋偏微分的知識,我們僅需要知道欲得到梯度,我們必須透過偏微分;根據連鎖律(Chain Rule),假設你要得知a對J的影響,就要拿J對v的微分乘上v對a的微分,這樣就可以得知a的梯度。

而上面的公式,大家有沒有覺得很熟悉,這就是上次實作的更新的公式,只是這次的dw1上次是x1,而dw1就是w1的梯度,相對來說更加精準。
我們先帶一些pytorch的基礎教學,後面文章到解夢模型的深度學習套件是用tensorflow與keras,但使用過keras的朋友們都知道,當用這個套件時,AI 就真的像黑盒子一樣,丟進去等結果就好,利於快速開發,但對於比較底層的邏輯在程式碼中無從得知,而pytorch這個套件底層 API 比較多,開發過程雖較為繁瑣,但是不熟悉 AI 基礎知識的開發者,較能快速去理解模型內的運作喔!
Pytorch官網的教學import torchd
type = torch.float
device = torch.device("cpu") # 表示目前使用CPU來跑
# device = torch.device("cuda:0") # 這是for GPU的程式
# N is batch size; D_in 是輸入維度;
# H is hidden dimension; D_out 是輸出維度.
N, D_in, H, D_out = 64, 1000, 100, 10
# Create random input and output
datax = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype) # Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)
learning_rate = 1e-6
for t in range(500): # 向前傳播
h = x.mm(w1)
h_relu = h.clamp(min=0)
y_pred = h_relu.mm(w2)
# 計算出loss 這邊loss function使用的是 MSE公式
loss = (y_pred - y).pow(2).sum().item()
if t % 100 == 0:
print(t, loss)
# 向後傳播計算出w1與w2的梯度
grad_y_pred = 2.0 * (y_pred - y)
grad_w2 = h_relu.t().mm(grad_y_pred) # t()是將矩陣轉置
grad_h_relu = grad_y_pred.mm(w2.t())
grad_h = grad_h_relu.clone() #clone是賦值之外並且讓這個變數具有可計算梯度的功能
grad_h[h < 0] = 0
grad_w1 = x.t().mm(grad_h)
# 更新權重
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w20 1.4500995348498691e-05100 1.0990642294927966e-05200 8.501140655425843e-06300 6.994195700826822e-06400 5.831332146044588e-06
dtype = torch.float
device = torch.device("cpu")
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)
learning_rate = 1e-6
for t in range(500):
y_pred = x.mm(w1).clamp(min=0).mm(w2)
loss = (y_pred - y).pow(2).sum()
if t % 100 == 0:
print(t, loss.item()) # 這裡我們使用backward()來自動找出w1與w2的梯度,是不是很輕鬆呢~
loss.backward()
with torch.no_grad():
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad # 更新完權重後,我們需要將每個梯度手動歸0,以免下次的梯度加到這次的
w1.grad.zero_()
w2.grad.zero_()pytorch向前傳播也有很方便的APIimport torch N, D_in, H, D_out = 64, 1000, 100, 10 x = torch.randn(N, D_in) y = torch.randn(N, D_out) # 這裡使用nn.Sequential()這個function來宣告我們向前傳播的模型 model = torch.nn.Sequential( torch.nn.Linear(D_in, H), torch.nn.ReLU(), torch.nn.Linear(H, D_out),) # loss也有funciton可以做喔 loss_fn = torch.nn.MSELoss(reduction='sum') learning_rate = 1e-4 for t in range(500): # 這裡只要將x丟進我們的model就好 y_pred = model(x) # 將預測與答案丟進剛剛宣告的loss funciton就可以得到loss loss = loss_fn(y_pred, y) if t % 100 == 0: print(t, loss.item()) loss.backward() # 更新權重也有簡化的API with torch.no_grad(): for param in model.parameters(): param -= learning_rate * param.grad
自由團隊 官方網站:https://aifreeblog.herokuapp.com/
自由團隊 Github:https://github.com/AI-FREE-Team/
自由團隊 粉絲專頁:https://www.facebook.com/AI.Free.Team/
自由團隊 IG:https://www.instagram.com/aifreeteam/
自由團隊 Youtube:https://www.youtube.com/channel/UCjw6Kuw3kwM_il39NTBJVTg/
文章同步發布於:第十二屆 IT 挑戰賽部落格