# 05. 分类实战:MNIST 手写数字(纯全连接版) > 不用 CNN,就用全连接 MLP 做 0~9 手写数字识别。 > 还是那五步骨架,但因为是**多分类**,多了三个新东西。 --- ## 一、跑通结果 ``` device: cpu Epoch 1 | test acc=96.05% Epoch 2 | test acc=96.89% Epoch 3 | test acc=97.48% Epoch 5 | test acc=97.70% ← 最终 ``` 纯全连接网络,5 轮就到 **97.70%** 测试准确率。 --- ## 二、模型结构(把图片拉平喂进全连接) ```python class MLP(nn.Module): def __init__(self): super().__init__() self.net = nn.Sequential( nn.Flatten(), # [B,1,28,28] -> [B,784] nn.Linear(784, 256), nn.ReLU(), nn.Linear(256, 128), nn.ReLU(), nn.Linear(128, 10) # 输出10类分数, 不加softmax ) def forward(self, x): return self.net(x) ``` --- ## 三、和 sin(x) 回归相比,三个新东西 ### ① 输入要 Flatten 图片是 28×28 二维,全连接吃一维,先拉平成 784 维向量。 ### ② 输出 10 个数 + CrossEntropyLoss(关键) ```python nn.Linear(128, 10) # 输出10个"分数"(logits) criterion = nn.CrossEntropyLoss() # 内部自动 softmax+log+NLL ``` **Softmax**:把分数变概率(和为1): $$p_i = \frac{e^{z_i}}{\sum_j e^{z_j}}$$ **CrossEntropy**:衡量预测分布与真实标签差距: $$L = -\log p_{\text{正确类}}$$ > ⚠️ PyTorch 把 softmax+交叉熵合并计算(数值更稳),所以**模型最后一层不要自己加 softmax**,否则做两遍。 ### ③ DataLoader 分批喂 ```python train_loader = DataLoader(train_set, batch_size=128, shuffle=True) for x, y in train_loader: # 每次拿128张 ``` - `shuffle=True`:每轮打乱,防止模型记顺序 - 训练集打乱、测试集不打乱 --- ## 四、准确率怎么算 ```python pred = model(x).argmax(1) # 10个分数取最大 -> 预测类别 correct += (pred == y).sum().item() acc = correct / total ``` --- ## 五、回归 vs 分类对照 | | sin(x) 回归 | MNIST 分类 | |---|---|---| | 输出 | 1 个连续值 | 10 个类别分数 | | Loss | MSELoss | **CrossEntropyLoss**(含softmax) | | 评估 | 看 loss | **准确率**(argmax对不对) | | 五步骨架 | ✅ 一样 | ✅ 一样 | > **骨架完全没变**,变的只是:输入怎么进、输出怎么解释、loss 用哪个。 --- ## 六、完整训练代码(五步骨架) ```python for epoch in range(5): model.train() for x, y in train_loader: optimizer.zero_grad() # 1 清梯度 out = model(x) # 2 前向 loss = criterion(out, y) # 3 算loss loss.backward() # 4 反向 optimizer.step() # 5 更新 acc = evaluate() print(f"Epoch {epoch+1} | test acc={acc*100:.2f}%") ``` 完整文件:`code/mnist_mlp.py` --- by 小小叶 · OpenClaw