ใน ep นี้ เราจะมาสร้าง Neural Network สำหรับงาน Classification ด้วยการประกอบชิ้นส่วนทุกอย่างใน ep ก่อน ๆ เข้าด้วยกัน ขึ้นมาเป็น 2 Layers Deep Neural Network ใช้ ReLU Activation Function พร้อม Initialize Weight และ Bias

แล้วสร้าง Training Loop เพื่อเริ่มต้นเทรน, ป้อนข้อมูลตัวอย่าง Batch แรกให้ Feed Forward, นำ Output yhat ไปเทียบกับ y หาค่า Cross Entropy Loss, Backpropagation, หา Gradient, อัพเดท Weight และ Bias ด้วย SGD, ก่อนเริ่มต้นเทรน Batch / Epoch ถัดไป

gradient descent Credit: Lecture 2 | Machine Learning (Stanford) https://www.youtube.com/watch?v=5u4G23_OohI
gradient descent Credit: Lecture 2 | Machine Learning (Stanford) https://www.youtube.com/watch?v=5u4G23_OohI

หลังจากนั้น เราจะมาเช็คกันดูว่า โมเดลของเราทำงานถูกต้อง แม่นยำ แค่ไหน ได้ Accuracy เท่าไร เมื่อเราเทสกับ Validation Set

เรามาเริ่มกันเลยดีกว่า

Open In Colab

Neural Networks 104 - Training Loop

วิธีการเทรน นิวรอลเน็ตเวิร์ก ทีละขั้นตอน แบบเข้าใจง่าย

0. Magic

In [0]:
%load_ext autoreload
%autoreload 2
%matplotlib inline

1. Import

In [0]:
import torch
from torch import tensor
from torch import nn
import torch.nn.functional as F
import matplotlib.pyplot as plt

from pathlib import Path
from IPython.core.debugger import set_trace
from fastai import datasets
import pickle, gzip, math, torch
import operator

2. Data

In [0]:
MNIST_URL='http://deeplearning.net/data/mnist/mnist.pkl'
In [0]:
def get_data():
    path = datasets.download_data(MNIST_URL, ext='.gz')
    with gzip.open(path, 'rb') as f:
        ((x_train, y_train), (x_valid, y_valid), _) = pickle.load(f, encoding='latin-1')
    return map(tensor, (x_train, y_train, x_valid, y_valid))
In [0]:
x_train, y_train, x_valid, y_valid = get_data()

3. เตรียม Data

In [0]:
# x = data, m = mean, s = standard deviation
def normalize(x, m, s): 
    return (x-m)/s
In [0]:
train_mean, train_std = x_train.mean(), x_train.std()
x_train = normalize(x_train, train_mean, train_std)
x_valid = normalize(x_valid, train_mean, train_std)
In [0]:
n, m = x_train.shape
c = y_train.max()+1
n, m, c
Out[0]:
(50000, 784, tensor(10))

4. สร้าง Model

โมเดลจะประกอบด้วยส่วนต่าง ๆ เช่น Architecture กำหนด Layers ต่าง ๆ, Initialization, Loss Function, อัลกอริทึมในการเทรน และ Metrics ที่เราเรียนรู้กันไปใน ep ก่อน ๆ

โมเดล

In [0]:
class Model(nn.Module):
    # number of input feature, number of hidden feature, number of output feature
    def __init__(self, n_in, nh, n_out):
        super().__init__()
#         set_trace()
        self.layers = [nn.Linear(n_in, nh), nn.ReLU(), nn.Linear(nh, n_out)]        
    def __call__(self, x):
        for l in self.layers:
            x = l(x)
        return x

เคสนี้เป็นงาน Classification เราจะใช้ Cross Entropy Loss Fucntion

In [0]:
# loss function
loss_func = F.cross_entropy

ในเคสนี้เราจะใช้ metrics เป็น Accuracy ประกาศฟังก์ชัน accuracy

In [0]:
def accuracy(yhat, y):
    return (torch.argmax(yhat, dim=-1) == y).float().mean()

กำหนด Hyperparameter

In [0]:
# batch size
bs = 64
# learning rate
lr = 0.03
epoch = 1
nh = 50

แบ่งข้อมูลออกมา 1 Batch

In [0]:
# x batch
xb = x_train[:bs]
yb = y_train[:bs]

สร้างโมเดล จาก Hyperparameter ด้านบน

In [0]:
model = Model(m, nh, c.numpy())

ลองให้โมเดล จำแนก MNIST โดยที่ยังไม่ได้เทรน

In [0]:
# predictions
preds = model(xb)
preds[0]
Out[0]:
tensor([-0.0638,  0.0474,  0.1880,  0.2819, -0.1330, -0.0390, -0.1642, -0.1861,
         0.5124, -0.2557], grad_fn=<SelectBackward>)

ได้ Output ออกมา 10 Class

In [0]:
preds[0].shape
Out[0]:
torch.Size([10])

ลองเทส Loss Fuction

In [0]:
loss = loss_func(preds, yb)
loss
Out[0]:
tensor(2.3129, grad_fn=<NllLossBackward>)

เทส Accuracy ได้ประมาณ 10% เหมือน Random เพราะเรายังไม่ได้เทรน

In [0]:
accuracy(preds, yb)
Out[0]:
tensor(0.1406)

5. Training Loop

เราจะเทรนโมเดล ด้วยอัลกอริทึม Stochastic Gradient Descent (SGD) และ เก็บ Loss, Accuracy เอาไว้พล็อตกราฟ

In [0]:
losses, metrics = [], []

ประกาศฟังก์ชัน แบ่งข้อมูลเป็น Mini-batch

In [0]:
def get_databatch(b):
    start_b = b*bs
    end_b = start_b+bs
    return x_train[start_b:end_b], y_train[start_b:end_b]

วนลูปเทรน ตามจำนวน epoch สังเกตว่าระหว่างอัพเดท Weight และ Bias มีการเรียก torch.no_grad เพื่อให้ไม่คำนวน Gradient และหลังจากอัพเดทเรียบร้อยแล้ว จะ Set Gradient ให้เป็น 0

In [0]:
# e = epoch number
for e in range(epoch):
    # b = batch number
    for b in range((n-1)//bs + 1):
        
        # Feedforward
        xb, yb = get_databatch(b)
        yhatb = model(xb)
        loss = loss_func(yhatb, yb)

        # Metrics
        acc = accuracy(yhatb, yb)
        losses.append(loss); metrics.append(acc)
        
        # Backpropagation
        loss.backward()
        
        # Update Wight and Bias with SGD
        with torch.no_grad():
            for l in model.layers:
                if hasattr(l, 'weight'):
                    l.weight -= l.weight.grad * lr
                    l.bias   -= l.bias.grad * lr 
                    l.weight.grad.zero_()
                    l.bias.grad.zero_()       
                    
In [0]:
x = torch.arange(len(losses)).numpy()
fig,ax = plt.subplots(figsize=(9, 9))
ax.grid(True)
ax.plot(x, losses, label="Loss")
ax.plot(x, metrics, label="Accuracy")
ax.legend(loc='upper right')
Out[0]:
<matplotlib.legend.Legend at 0x7f5ab07de390>

ลองเทสดู Loss และ Accuracy อีกครั้ง

In [0]:
loss_func(model(xb), yb), accuracy(model(xb), yb)
Out[0]:
(tensor(0.1482, grad_fn=<NllLossBackward>), tensor(1.))

6. Validation Set

แต่การเทสกับข้อมูลที่ใช้เทรนโมเดล ไม่ใช่สิ่งที่ควรทำ เราควรเทสกับ Validation Set จากที่อธิบายไว้ใน ep Train / Test Split คืออะไร ทำไมเราต้องแยกชุดข้อมูล เป็น Training, Validation และ Test Set ใน Machine Learning

In [0]:
xb = x_valid[:bs*2]
yb = y_valid[:bs*2]

ได้ Accuracy พอ ๆ กัน

In [0]:
yhat = model(xb)
loss_func(yhat, yb), accuracy(yhat, yb)
Out[0]:
(tensor(0.2949, grad_fn=<NllLossBackward>), tensor(0.9141))

ลองดูรูปภาพ เปรียบเทียบกับ ผลการทำนายจากโมเดล

In [0]:
plt.imshow(xb[99].view(28,28))
Out[0]:
<matplotlib.image.AxesImage at 0x7f5ab0708390>
In [0]:
yhat[99].argmax()
Out[0]:
tensor(9)
In [0]:
plt.imshow(xb[55].view(28,28))
Out[0]:
<matplotlib.image.AxesImage at 0x7f5ab0668ef0>
In [0]:
yhat[55].argmax()
Out[0]:
tensor(1)

7. สรุป

  1. เราได้สร้าง Neural Network ตั้งแต่ต้น ด้วยการประกอบชิ้นส่วนทุกอย่างเข้าด้วยกัน สร้าง Training Loop เพื่อเริ่มต้นเทรน Feed Forward, หาค่า Loss, เพื่อ Backpropagation, หา Gradient, ไปจนถึงอัพเดท Weight และ Bias, ก่อนเริ่มต้นเทรน Batch / Epoch ถัดไป
  2. เราได้คำนวน Metrics ว่าโมเดลของเราทำงานถูกต้อง แม่นยำ แค่ไหน มี Accuracy เท่าไร
  3. ในเคสนี้โมเดลเป็นแบบ Classification แต่ถ้าเป็นงาน Regression เราก็ปรับโมเดล เปลี่ยนแค่ Layer สุดท้าย, Loss Function และ Metrics เท่านั้น ชิ้นส่วนอื่น ๆ เราสามารถ Reuse ได้เกือบหมด
  4. ถ้ามีความรู้ความเข้าใจในทุกส่วนประกอบของ Neural Network เราจะสามารถออกแบบ สร้างโมเดลที่แก้ปัญหาซับซ้อนยิ่งขึ้นได้
  5. ในเคสนี้ Training Loop เป็นแบบง่ายที่สุด ยังสามารถ Optimize ได้อีกหลายจุด ทั้ง Training Loop และ สถาปัตยกรรมของโมเดล จะอธิบายต่อไป
In [0]:
 

แชร์ให้เพื่อน:

Keng Surapong on FacebookKeng Surapong on GithubKeng Surapong on Linkedin
Keng Surapong
Project Manager at Bua Labs
The ultimate test of your knowledge is your capacity to convey it to another.

Published by Keng Surapong

The ultimate test of your knowledge is your capacity to convey it to another.

Enable Notifications.    Ok No thanks