ในการเทรน Machine Learning การทดสอบว่าโมเดล Neural Network ทำงานเป็นอย่างไร ที่ถูกต้องเราไม่ควรเทสกับข้อมูล ใน Training Set ที่เราป้อนให้โมเดลในขณะเทรน เพราะจะทำให้ไม่ได้ค่าที่แท้จริง ถ้าโมเดลใช้วิธีจำข้อสอบ เรียกว่า Overfit เมื่อเทสแล้วจะได้คะแนนสูงผิดปกติ

ที่ถูกคือ เราควรเทสกับข้อมูลที่โมเดลไม่เคยเห็นมาก่อน ใน Validation Set ที่เรากันเอาไว้ก่อนหน้าที่จะเริ่มต้นเทรน

Noisy (roughly linear) data is fitted to a linear function and a polynomial function. Credit https://en.wikipedia.org/wiki/File:Overfitted_Data.png
Noisy (roughly linear) data is fitted to a linear function and a polynomial function. Credit https://en.wikipedia.org/wiki/File:Overfitted_Data.png

เมื่อเราใช้ข้อมูลที่โมเดลไม่เคยเห็น ใน Validation Set มาเทสโมเดล แล้วได้ผลดี ทำให้เราสามารถคาดหวังได้ว่า โมเดลจะทำงานกับข้อมูลจากโลกภายนอก ที่ไม่เคยเห็นมาก่อนได้ดี เช่นกัน Train/Validation/Test Split คืออะไร คลิก

เมื่อเกิด Overfit โมเดลจะจำข้อสอบ ทำให้เมื่อเทรนไปเรื่อย ๆ Loss และ Accuracy ของ Training Set จะดีขึ้นเรื่อย ๆ (เส้นสีน้ำเงิน) ต่างกับเมื่อเราไปเทสกับ Validation Set ผล Loss และ Accuracy จะยิ่งแย่ลงเรื่อย ๆ (เส้นสีแดง)

Overfitting/overtraining in supervised learning. Credit https://en.wikipedia.org/wiki/File:Overfitting_svg.svg
Overfitting/overtraining in supervised learning. Credit https://en.wikipedia.org/wiki/File:Overfitting_svg.svg

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

Open In Colab

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
from torch.utils import data 
import matplotlib.pyplot as plt

from pathlib import Path
from IPython.core.debugger import set_trace
from fastai import datasets
from fastai.metrics import accuracy
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]:
class Dataset(data.Dataset):
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __len__(self):
        return len(self.x)
    def __getitem__(self, i):
        return self.x[i], self.y[i]
In [0]:
# x = data, m = mean, s = standard deviation
def normalize(x, m, s): 
    return (x-m)/s
In [8]:
n, m = x_train.shape
c = (y_train.max()+1).numpy()
n, m, c
Out[8]:
(50000, 784, array(10))
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]:
# batch size
bs = 64
train_ds, valid_ds = Dataset(x_train, y_train), Dataset(x_valid, y_valid)
train_dl, valid_dl = data.DataLoader(train_ds, bs, shuffle=True), data.DataLoader(valid_ds, bs, shuffle=False)

4. สร้าง Model

Hyperparameter ของโมเดล

In [0]:
# learning rate
lr = 0.03
epoch = 10
nh = 50

ประกาศฟังก์ชันเอาไว้สร้างโมเดล

In [0]:
def get_model():
    # loss function
    loss_func = F.cross_entropy
    model = nn.Sequential(nn.Linear(m, nh), nn.ReLU(), nn.Linear(nh,c))
    return model, loss_func

5. Training Loop

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

ประกาศฟังก์ชัน fit เอาไว้เรียกเทรนเวลาที่ต้องการ

In [0]:
def fit():
    losses, metrics = [], []
    # e = epoch number
    for e in range(epoch):
        for xb, yb in train_dl:

            # Feedforward
            yhatb = model(xb)
            loss = loss_func(yhatb, yb)

            # Metrics
            acc = accuracy(yhatb, yb)
            losses.append(loss); metrics.append(acc)

            # Backpropagation
            loss.backward()

            optim.step()
            optim.zero_grad()

    plot_metrics(losses, metrics)

ประการฟัง์ชัน ไว้พล็อตกราฟ Loss และ Accuracy

In [0]:
def plot_metrics(losses, metrics):
    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')

6. Refactor Training Loop ส่วน Metrics

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

Train/Validation/Test Split คืออะไร คลิก

In [0]:
def fit2():
    # e = epoch number
    for e in range(epoch):

        # Set Model in Train Mode
        model.train()

        for xb, yb in train_dl:
            yhatb = model(xb)
            loss = loss_func(yhatb, yb)
            loss.backward()
            optim.step()
            optim.zero_grad()

        # Set Model in Evaluation Mode
        model.eval()

        # Metrics
        with torch.no_grad():
            # tot_loss = total loss, tot_acc = total accuracy
            tot_loss, tot_acc = 0., 0.
            for xb, yb in valid_dl:
                yhatb = model(xb)
                tot_acc += accuracy(yhatb, yb)
                tot_loss += loss_func(yhatb, yb)
            # nv = number of validation batch
            nv = len(valid_ds)/bs
            print(f'epoch={e}, valid_loss={tot_loss/nv}, valid_acc={tot_acc/nv}')            
    return tot_loss/nv, tot_acc/nv
    

ลองเทรนด้วย SGD

In [24]:
model, loss_func = get_model()
optim = torch.optim.SGD(model.parameters(), lr=lr)
fit2()
epoch=0, valid_loss=0.28046083450317383, valid_acc=0.9236999750137329
epoch=1, valid_loss=0.2053385227918625, valid_acc=0.9484000205993652
epoch=2, valid_loss=0.16833384335041046, valid_acc=0.958899974822998
epoch=3, valid_loss=0.16855786740779877, valid_acc=0.9544000029563904
epoch=4, valid_loss=0.13522052764892578, valid_acc=0.9678000211715698
epoch=5, valid_loss=0.1309860646724701, valid_acc=0.9656000137329102
epoch=6, valid_loss=0.12342768907546997, valid_acc=0.9689000248908997
epoch=7, valid_loss=0.11911877244710922, valid_acc=0.9704999923706055
epoch=8, valid_loss=0.11267219483852386, valid_acc=0.9722999930381775
epoch=9, valid_loss=0.11112724244594574, valid_acc=0.9726999998092651
Out[24]:
(tensor(0.1111), tensor(0.9727))

7. สรุป

  1. ใน Training Loop เราได้ Refactor ส่วนของ Metrics ให้ใช้ Validation Set หา Loss และ Metrics หลังจากที่เทรนใน Epoch นั้นเรียบร้อยแล้ว
  2. เรารวม Total Loss ไว้ใน tot_loss แล้วจึงหารด้วย nv (จำนวนข้อมูล / batch size) เนื่องจาก batch สุดท้ายอาจจะไม่เต็ม batch ก็ได้ เราจึงไม่สามารถหารด้วย len(valid_dl) ได้
  3. เมื่อเราใช้ข้อมูลที่โมเดลไม่เคยเห็น ใน Validation Set มาเทสโมเดล แล้วได้ผลดี ทำให้เราสามารถคาดหวังได้ว่า โมเดลจะทำงานกับข้อมูลจากโลกภายนอกที่ไม่เคยเห็นมาก่อนได้ดี เช่นกัน

Credit

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