PyTorch Basics

Key Concept: Neural Network คือการนำ Layer มาต่อกัน

โครงข่ายประสาทเทียม (Neural Networks) สร้างขึ้นจากการนำ เลเยอร์ (Layers) หรือโมดูลต่างๆ มาต่อเข้าด้วยกันเพื่อทำการคำนวณ ใน PyTorch เนมสเปซ torch.nn ได้เตรียมเลเยอร์พื้นฐานทั้งหมดที่คุณต้องใช้ในการสร้างโครงข่ายของคุณเองไว้ให้หมดแล้ว ทุกๆ โมดูลใน PyTorch จะสืบทอดมาจากคลาส nn.Module

1. Flatten

คลี่ข้อมูล 2D เป็น 1D Vector

2. Linear

คูณน้ำหนัก (Weights) + Bias

3. ReLU (Activation)

แปลงค่าติดลบให้เป็นศูนย์

import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

การเลือกอุปกรณ์สำหรับ Training

เราต้องการเทรนโมเดลบนตัวเร่งความเร็วฮาร์ดแวร์ (hardware accelerator) เช่น GPU หรือ MPS (Mac) หากมีให้ใช้งาน โค้ดด้านล่างใช้สำหรับตรวจสอบอุปกรณ์ที่มีและตั้งค่า

device = (
    "cuda"
    if torch.cuda.is_available()
    else "mps"
    if torch.backends.mps.is_available()
    else "cpu"
)
print(f"Using {device} device")
Output:
Using cuda device

การกำหนดคลาสโมเดล (Define the Class)

เราจะกำหนดโครงสร้างของโมเดลโดยสร้างคลาสใหม่ที่สืบทอดมาจาก nn.Module:

  • __init__ : กำหนดเลเยอร์ต่างๆ ที่จะนำมาใช้งานในโมเดล
  • forward : กำหนดขั้นตอนการไหลของข้อมูล (Data flow) ว่าเมื่อข้อมูลเข้าสู่โมเดลแล้วจะผ่านเลเยอร์ใดบ้างตามลำดับ
class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork().to(device)
print(model)
Output:
NeuralNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=True)
  )
)

การใช้งานโมเดล

หากเราต้องการใช้งานโมเดล เราเพียงแค่ส่งข้อมูลป้อนเข้าไป (ตัวอย่างเช่น model(X)) ระบบจะทำการเรียกใช้งานฟังก์ชัน forward ให้อัตโนมัติ (ห้ามเรียก model.forward(X) โดยตรง)

X = torch.rand(1, 28, 28, device=device)
logits = model(X)

# ผลลัพธ์จะเป็นคะแนนดิบ เราใช้ Softmax เพื่อแปลงเป็นความน่าจะเป็น
pred_probab = nn.Softmax(dim=1)(logits)
y_pred = pred_probab.argmax(1)
print(f"Predicted class: {y_pred.item()}")
Output:
Predicted class: 7

เจาะลึกการทำงานของ Model Layers

มาดูกันว่าเมื่อเราส่งรูปภาพเสื้อผ้าขนาด 28x28 พิกเซลจำนวน 3 รูปแบบมินิแบทช์ (shape: [3, 28, 28]) ข้อมูลจะถูกแปลงรูปร่างไปอย่างไรบ้าง

1. nn.Flatten

แปลงภาพ 2D ให้กลายเป็น 1D array โดยในที่นี้ ภาพ 28x28 จะถูกคลี่ออกกลายเป็นข้อมูล 784 ค่าเรียงกัน (3 คือจำนวนภาพใน batch คงไว้เหมือนเดิม)

input_image = torch.rand(3,28,28)
flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size()) # torch.Size([3, 784])

2. nn.Linear

คือเลเยอร์ที่ทำการ คูณเมทริกซ์และบวกค่า Bias (y = xA^T + b) มันรับข้อมูลขนาด 784 และแปลงออกเป็นขนาด 512

layer1 = nn.Linear(in_features=28*28, out_features=512)
hidden1 = layer1(flat_image)
print(hidden1.size()) # torch.Size([3, 512])

3. nn.ReLU

Activation Function ที่จัดการความไม่เป็นเชิงเส้น (Non-linearity) ให้โมเดลเรียนรู้ฟังก์ชันซับซ้อนได้ โดย ReLU จะกำหนดค่าที่ติดลบให้กลายเป็นศูนย์ทั้งหมด

hidden1 = nn.ReLU()(hidden1)

Model Parameters (พารามิเตอร์ของโมเดล)

หลายเลเยอร์ใน Neural Network มี พารามิเตอร์ (Weights และ Biases) ที่จะถูกปรับเปลี่ยนระหว่างการเทรน เราสามารถดูพารามิเตอร์ทั้งหมดในโมเดลได้ผ่าน model.parameters()

print(f"Model structure: {model}\n\n")

for name, param in model.named_parameters():
    print(f"Layer: {name} | Size: {param.size()} | Values : {param[:2]} \n")
Output:
Layer: linear_relu_stack.0.weight | Size: torch.Size([512, 784]) | Values : tensor([[ 0.0273,  0.0296, -0.0084, ...
Layer: linear_relu_stack.0.bias | Size: torch.Size([512]) | Values : tensor([-0.0142,  0.0093])
...
Layer: linear_relu_stack.4.weight | Size: torch.Size([10, 512]) | Values : tensor([[-0.0415, -0.0204,  0.0101,...