ใน 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 ถัดไป

หลังจากนั้น เราจะมาเช็คกันดูว่า โมเดลของเราทำงานถูกต้อง แม่นยำ แค่ไหน ได้ Accuracy เท่าไร เมื่อเราเทสกับ Validation Set
เรามาเริ่มกันเลยดีกว่า
Neural Networks 104 - Training Loop
วิธีการเทรน นิวรอลเน็ตเวิร์ก ทีละขั้นตอน แบบเข้าใจง่าย
0. Magic¶
%load_ext autoreload
%autoreload 2
%matplotlib inline
1. Import¶
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¶
MNIST_URL='http://deeplearning.net/data/mnist/mnist.pkl'
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))
x_train, y_train, x_valid, y_valid = get_data()
3. เตรียม Data¶
# x = data, m = mean, s = standard deviation
def normalize(x, m, s):
return (x-m)/s
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)
n, m = x_train.shape
c = y_train.max()+1
n, m, c
4. สร้าง Model¶
โมเดลจะประกอบด้วยส่วนต่าง ๆ เช่น Architecture กำหนด Layers ต่าง ๆ, Initialization, Loss Function, อัลกอริทึมในการเทรน และ Metrics ที่เราเรียนรู้กันไปใน ep ก่อน ๆ
โมเดล
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
# loss function
loss_func = F.cross_entropy
ในเคสนี้เราจะใช้ metrics เป็น Accuracy ประกาศฟังก์ชัน accuracy
def accuracy(yhat, y):
return (torch.argmax(yhat, dim=-1) == y).float().mean()
กำหนด Hyperparameter
# batch size
bs = 64
# learning rate
lr = 0.03
epoch = 1
nh = 50
แบ่งข้อมูลออกมา 1 Batch
# x batch
xb = x_train[:bs]
yb = y_train[:bs]
สร้างโมเดล จาก Hyperparameter ด้านบน
model = Model(m, nh, c.numpy())
ลองให้โมเดล จำแนก MNIST โดยที่ยังไม่ได้เทรน
# predictions
preds = model(xb)
preds[0]
ได้ Output ออกมา 10 Class
preds[0].shape
ลองเทส Loss Fuction
loss = loss_func(preds, yb)
loss
เทส Accuracy ได้ประมาณ 10% เหมือน Random เพราะเรายังไม่ได้เทรน
accuracy(preds, yb)
5. Training Loop¶
เราจะเทรนโมเดล ด้วยอัลกอริทึม Stochastic Gradient Descent (SGD) และ เก็บ Loss, Accuracy เอาไว้พล็อตกราฟ
losses, metrics = [], []
ประกาศฟังก์ชัน แบ่งข้อมูลเป็น Mini-batch
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
# 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_()
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')
ลองเทสดู Loss และ Accuracy อีกครั้ง
loss_func(model(xb), yb), accuracy(model(xb), yb)
6. Validation Set¶
แต่การเทสกับข้อมูลที่ใช้เทรนโมเดล ไม่ใช่สิ่งที่ควรทำ เราควรเทสกับ Validation Set จากที่อธิบายไว้ใน ep Train / Test Split คืออะไร ทำไมเราต้องแยกชุดข้อมูล เป็น Training, Validation และ Test Set ใน Machine Learning
xb = x_valid[:bs*2]
yb = y_valid[:bs*2]
ได้ Accuracy พอ ๆ กัน
yhat = model(xb)
loss_func(yhat, yb), accuracy(yhat, yb)
ลองดูรูปภาพ เปรียบเทียบกับ ผลการทำนายจากโมเดล
plt.imshow(xb[99].view(28,28))
yhat[99].argmax()
plt.imshow(xb[55].view(28,28))
yhat[55].argmax()
7. สรุป¶
- เราได้สร้าง Neural Network ตั้งแต่ต้น ด้วยการประกอบชิ้นส่วนทุกอย่างเข้าด้วยกัน สร้าง Training Loop เพื่อเริ่มต้นเทรน Feed Forward, หาค่า Loss, เพื่อ Backpropagation, หา Gradient, ไปจนถึงอัพเดท Weight และ Bias, ก่อนเริ่มต้นเทรน Batch / Epoch ถัดไป
- เราได้คำนวน Metrics ว่าโมเดลของเราทำงานถูกต้อง แม่นยำ แค่ไหน มี Accuracy เท่าไร
- ในเคสนี้โมเดลเป็นแบบ Classification แต่ถ้าเป็นงาน Regression เราก็ปรับโมเดล เปลี่ยนแค่ Layer สุดท้าย, Loss Function และ Metrics เท่านั้น ชิ้นส่วนอื่น ๆ เราสามารถ Reuse ได้เกือบหมด
- ถ้ามีความรู้ความเข้าใจในทุกส่วนประกอบของ Neural Network เราจะสามารถออกแบบ สร้างโมเดลที่แก้ปัญหาซับซ้อนยิ่งขึ้นได้
- ในเคสนี้ Training Loop เป็นแบบง่ายที่สุด ยังสามารถ Optimize ได้อีกหลายจุด ทั้ง Training Loop และ สถาปัตยกรรมของโมเดล จะอธิบายต่อไป