本文作者: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_w2
0 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 挑戰賽部落格