一. 前言 #
你有沒有想過,ChatGPT、影像辨識、語音助理背後的核心技術是什麼?答案就是神經網路 (Neural Network)。
雖然「深度學習」聽起來很嚇人,但其實用 Python + PyTorch,幾十行程式碼就能建出一個能學習的模型。今天拍拍君就來手把手帶你從零開始,用 PyTorch 建立你的第一個神經網路!
🎯 本文目標:理解 Tensor、自動微分、模型定義、訓練迴圈,並完成一個手寫數字辨識模型。
二. 安裝 #
# 用 pip 安裝(CPU 版本,入門足夠)
pip install torch torchvision
# 或用 uv
uv pip install torch torchvision
確認安裝成功:
import torch
print(torch.__version__) # e.g., 2.6.0
print(torch.cuda.is_available()) # GPU 是否可用
💡 如果你有 NVIDIA GPU,可以裝 CUDA 版本加速訓練。Mac M 系列晶片則可以用
torch.backends.mps。
三. Tensor:PyTorch 的核心資料結構 #
Tensor(張量)就是 PyTorch 版本的多維陣列,跟 NumPy 的 ndarray 很像,但多了兩個超能力:自動微分和 GPU 加速。
import torch
# 建立 Tensor
x = torch.tensor([1.0, 2.0, 3.0])
print(x) # tensor([1., 2., 3.])
print(x.shape) # torch.Size([3])
print(x.dtype) # torch.float32
# 常用建立方式
zeros = torch.zeros(3, 4) # 3×4 全零
ones = torch.ones(2, 3) # 2×3 全一
rand = torch.randn(2, 3) # 2×3 標準常態分佈
arange = torch.arange(0, 10, 2) # [0, 2, 4, 6, 8]
Tensor 運算 #
a = torch.tensor([1.0, 2.0, 3.0])
b = torch.tensor([4.0, 5.0, 6.0])
# 元素運算
print(a + b) # tensor([5., 7., 9.])
print(a * b) # tensor([ 4., 10., 18.])
# 矩陣運算
A = torch.randn(3, 4)
B = torch.randn(4, 5)
C = A @ B # 矩陣乘法,結果 shape: (3, 5)
# reshape
x = torch.arange(12).reshape(3, 4)
print(x.shape) # torch.Size([3, 4])
NumPy 互轉 #
import numpy as np
# NumPy → Tensor
np_arr = np.array([1, 2, 3])
tensor = torch.from_numpy(np_arr)
# Tensor → NumPy
back_to_np = tensor.numpy()
⚠️ 兩者共享記憶體,改一個另一個也會變!如果不想共享,用
.clone()或.copy()。
四. Autograd:自動微分 #
神經網路的學習靠的是梯度下降 (Gradient Descent),而 PyTorch 的 autograd 系統會自動幫你算梯度,不用手推微分!
# 告訴 PyTorch 要追蹤這個變數的梯度
x = torch.tensor(3.0, requires_grad=True)
# 定義函數 y = x^2 + 2x + 1
y = x ** 2 + 2 * x + 1
# 反向傳播,計算 dy/dx
y.backward()
# dy/dx = 2x + 2 = 2(3) + 2 = 8
print(x.grad) # tensor(8.)
更實際的例子 — 向量梯度:
w = torch.randn(3, requires_grad=True)
x = torch.tensor([1.0, 2.0, 3.0])
# 前向計算
z = (w * x).sum()
z.backward()
print(w.grad) # 就是 x: tensor([1., 2., 3.])
🧠
backward()會沿著計算圖反向傳播,自動算出每個requires_grad=True變數的偏微分。這就是深度學習的數學基礎!
五. 建立神經網路模型 #
PyTorch 用 torch.nn 模組來定義模型。最常用的方式是繼承 nn.Module:
import torch.nn as nn
class SimpleNet(nn.Module):
def __init__(self, input_size, hidden_size, num_classes):
super().__init__()
self.layer1 = nn.Linear(input_size, hidden_size)
self.relu = nn.ReLU()
self.layer2 = nn.Linear(hidden_size, num_classes)
def forward(self, x):
x = self.layer1(x) # 線性變換
x = self.relu(x) # 激活函數
x = self.layer2(x) # 輸出層
return x
# 建立模型
model = SimpleNet(input_size=784, hidden_size=128, num_classes=10)
print(model)
輸出:
SimpleNet(
(layer1): Linear(in_features=784, out_features=128, bias=True)
(relu): ReLU()
(layer2): Linear(in_features=128, out_features=10, bias=True)
)
快速版:用 nn.Sequential
#
如果模型是簡單的堆疊結構,可以更精簡:
model = nn.Sequential(
nn.Linear(784, 128),
nn.ReLU(),
nn.Linear(128, 10),
)
常用層和激活函數 #
| 層 / 函數 | 用途 |
|---|---|
nn.Linear(in, out) |
全連接層(線性變換) |
nn.Conv2d(in_ch, out_ch, kernel) |
2D 卷積層(影像用) |
nn.ReLU() |
激活函數(最常用) |
nn.Sigmoid() |
輸出 0~1(二元分類) |
nn.Softmax(dim=1) |
輸出機率分佈(多類分類) |
nn.Dropout(p) |
隨機丟棄(防過擬合) |
nn.BatchNorm1d(n) |
批次正規化(加速訓練) |
六. 完整範例:MNIST 手寫數字辨識 #
把上面學到的全部串起來!我們用經典的 MNIST 資料集來訓練一個能辨識手寫數字的模型。
6.1 準備資料 #
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# 資料預處理:轉 Tensor + 正規化
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,)), # MNIST 的均值和標準差
])
# 下載 MNIST
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST('./data', train=False, transform=transform)
# DataLoader:批次載入 + 打亂
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)
6.2 定義模型 #
class MNISTNet(nn.Module):
def __init__(self):
super().__init__()
self.flatten = nn.Flatten() # 28×28 → 784
self.layers = nn.Sequential(
nn.Linear(784, 256),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(256, 128),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(128, 10),
)
def forward(self, x):
x = self.flatten(x)
return self.layers(x)
model = MNISTNet()
6.3 設定損失函數和優化器 #
criterion = nn.CrossEntropyLoss() # 多類分類的標準損失
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
| 優化器 | 特點 |
|---|---|
SGD |
最經典,需要調 learning rate |
Adam |
自適應學習率,大多數情況首選 |
AdamW |
Adam + weight decay,訓練大模型常用 |
6.4 訓練迴圈 #
這是深度學習最核心的部分:
def train(model, loader, criterion, optimizer, epoch):
model.train() # 設為訓練模式(啟用 Dropout 等)
total_loss = 0
for batch_idx, (data, target) in enumerate(loader):
optimizer.zero_grad() # 1. 清除舊梯度
output = model(data) # 2. 前向傳播
loss = criterion(output, target) # 3. 計算損失
loss.backward() # 4. 反向傳播(算梯度)
optimizer.step() # 5. 更新權重
total_loss += loss.item()
avg_loss = total_loss / len(loader)
print(f"Epoch {epoch}: avg loss = {avg_loss:.4f}")
🔁 訓練五步驟口訣:清梯度 → 前向 → 算損失 → 反向 → 更新
6.5 評估模型 #
def evaluate(model, loader):
model.eval() # 設為評估模式(關閉 Dropout)
correct = 0
total = 0
with torch.no_grad(): # 評估時不需要算梯度
for data, target in loader:
output = model(data)
pred = output.argmax(dim=1)
correct += (pred == target).sum().item()
total += target.size(0)
accuracy = 100.0 * correct / total
print(f"Test accuracy: {accuracy:.2f}%")
return accuracy
6.6 開始訓練! #
num_epochs = 10
for epoch in range(1, num_epochs + 1):
train(model, train_loader, criterion, optimizer, epoch)
evaluate(model, test_loader)
典型輸出:
Epoch 1: avg loss = 0.3521
Test accuracy: 95.42%
Epoch 5: avg loss = 0.0812
Test accuracy: 97.63%
Epoch 10: avg loss = 0.0398
Test accuracy: 97.89%
🎉 只用簡單的全連接網路,就能達到 ~98% 的準確率!用 CNN 可以輕鬆到 99%+。
七. 進階技巧 #
儲存和載入模型 #
# 儲存(只存權重,推薦)
torch.save(model.state_dict(), 'mnist_model.pth')
# 載入
model = MNISTNet()
model.load_state_dict(torch.load('mnist_model.pth'))
model.eval()
GPU 加速 #
# 選擇裝置
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# Mac M 系列:
# device = torch.device('mps' if torch.backends.mps.is_available() else 'cpu')
# 把模型和資料搬到 GPU
model = model.to(device)
# 訓練時也要搬資料
for data, target in train_loader:
data, target = data.to(device), target.to(device)
output = model(data)
# ...
Learning Rate Scheduler #
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.5)
for epoch in range(1, num_epochs + 1):
train(model, train_loader, criterion, optimizer, epoch)
evaluate(model, test_loader)
scheduler.step() # 每 5 個 epoch 把 lr 砍半
結語 #
今天我們從最基礎的 Tensor 開始,一路走到完成一個能辨識手寫數字的神經網路模型。回顧一下學到的重點:
- Tensor — PyTorch 的基本資料結構,支援 GPU 加速
- Autograd — 自動微分,不用手推梯度
- nn.Module — 模型定義的標準方式
- 訓練迴圈 — 清梯度 → 前向 → 損失 → 反向 → 更新
- 評估 —
model.eval()+torch.no_grad()
PyTorch 的設計哲學是 Pythonic + 動態計算圖,寫起來就像普通 Python,debug 也很直覺。這也是為什麼它成為學術界和業界最受歡迎的深度學習框架之一。
下一步你可以試試:
- 用 CNN (
nn.Conv2d) 來做影像辨識 - 用 RNN/LSTM 來處理序列資料
- 到 Hugging Face 玩預訓練模型
深度學習的世界很大,但第一步其實沒那麼難。加油!💪
延伸閱讀 #
- PyTorch 官方教學
- PyTorch 官方文件
- MNIST 資料集介紹
- 拍拍君:Python 數值積分 — 另一個數學 × Python 的應用