Day22 - 人工智慧 III.

斜槓學習 – 零基礎成為 AI 解夢大師秘笈

本文作者: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),讓我們繼續看下去!

向後傳播(backward propagation)

損失函數(Loss Funciton)

上圖中,成本函數為每一次loss總和的平均,而 y 上面有一個^符號,我們稱作y_hat為我們預測的結果。

損失函數是衡量神經網路預測能力的好壞,上述公式 binary_crossentropy - 交叉熵損失函式,在 y 和 y_hat 相等時,loss 會為0,否則 loss 就會是一個正值,且差值越大,loss 值會呈倍數差距,而這個度量概率距離的方式稱為交叉熵。

梯度下降(Gradient descent)

前一篇的實作中,我們有利用當y_p不等於y_t時,我們會提高W降低W,這個過程其實就是梯度下降的簡易操作方式,但是判斷邏輯過於簡單且不夠精確,在求出loss之後,我們需要得知神經元對於輸出的變化,用文字陳述可能比較難理解,從以下視覺化圖片,相信大家能有更好的見解。

上圖中在向前傳播的結果中,我們可以得知輸出是 33,但是我們無法得知每一個節點(輸入和神經元) 的運算對於結果的影響;因此,需要得知每個節點對於輸出的重要性(權重W),我們必須仰賴向後傳播。

而向後(反向)傳播其實就是一系列的偏微分,透過微分我們可以得知每個節點當下的變化,進而得到他的梯度(gradient),這邊就不詳細解釋偏微分的知識,我們僅需要知道欲得到梯度,我們必須透過偏微分;根據連鎖律(Chain Rule),假設你要得知a對J的影響,就要拿J對v的微分乘上v對a的微分,這樣就可以得知a的梯度。

而上面的公式,大家有沒有覺得很熟悉,這就是上次實作的更新的公式,只是這次的dw1上次是x1,而dw1就是w1的梯度,相對來說更加精準。

Pytorch 實作

基礎教學

我們先帶一些pytorch的基礎教學,後面文章到解夢模型的深度學習套件是用tensorflowkeras,但使用過keras的朋友們都知道,當用這個套件時,AI 就真的像黑盒子一樣,丟進去等結果就好,利於快速開發,但對於比較底層的邏輯在程式碼中無從得知,而pytorch這個套件底層 API 比較多,開發過程雖較為繁瑣,但是不熟悉 AI 基礎知識的開發者,較能快速去理解模型內的運作喔!

Pytorch官網的教學

Tensor

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

關於clone的用法可以參考

Autograd

在上面的範例中,我們手動算出了w1與w2的梯度,繼續看下面的範例

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_()

nn module

在前兩個實作中,我們都強調向後傳播的過程與簡化,其實pytorch向前傳播也有很方便的API

import 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

有沒有忽然覺得學 AI 輕鬆了許多呢?有了這些開發 AI 的框架,很多 function 都已經把複雜公式簡化,方便開發者進行快速的開發!

本次的實作先到這邊,希望大家都能更了解深度學習實作的方法,下篇將會帶來實際data的應用。

想更深入認識 AI . FREE Team ?

自由團隊 官方網站: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 挑戰賽部落格

(繼續閱讀下一篇教學...)