手写数字识别入门实战

《手写数字识别实战:从逻辑回归到CNN的进阶之路(完整代码解析)》

## 📚 前言  
手写数字识别(MNIST)是机器学习领域的"Hello World"。本文将从最简单的逻辑回归模型出发,逐步实现ANN和CNN,通过**完整代码+可视化训练过程**,带你直观感受不同模型的性能差异。文末附完整代码和数据集!

---
## 🛠️ 环境准备  
```python
# 基础库
import torch
import torch.nn as nn
from torchvision import datasets, transforms

# 可视化
import matplotlib.pyplot as plt
%matplotlib inline

# 设置GPU加速
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
 

📈 模型效果对比

模型最高准确率训练时间(15 epochs)
逻辑回归85.85%~2分钟
ANN96.26%~2分钟
CNN98.26%~2分钟

一、逻辑回归:基础入门

1. 核心代码

class LogisticRegression(nn.Module):
    def __init__(self, input_dim, output_dim):
        super().__init__()
        self.linear = nn.Linear(input_dim, output_dim)
        
    def forward(self, x):
        return self.linear(x)

2. 训练过程

Epoch: 15/15 | Iteration: 9500 | Loss: 0.5245 | Accuracy: 85.85%

二、增强版ANN(三隐藏层)

1. 模型结构

class ANNModel(nn.Module):
    def __init__(self, input_dim, hidden_dim, output_dim):
        super().__init__()
        # 第一隐藏层:784 → 150
        self.fc1 = nn.Linear(input_dim, hidden_dim)
        self.relu1 = nn.ReLU()  # 首层使用ReLU快速收敛
        
        # 第二隐藏层:150 → 150(保持维度)
        self.fc2 = nn.Linear(hidden_dim, hidden_dim)
        self.tanh2 = nn.Tanh()  # 中间层使用Tanh增强特征表达
        
        # 第三隐藏层:150 → 150 
        self.fc3 = nn.Linear(hidden_dim, hidden_dim)
        self.elu3 = nn.ELU(alpha=1.0)  # 深层使用ELU防止梯度消失
        
        # 输出层:150 → 10(无激活函数)
        self.fc4 = nn.Linear(hidden_dim, output_dim)

    def forward(self, x):
        out = self.relu1(self.fc1(x))
        out = self.tanh2(self.fc2(out))
        out = self.elu3(self.fc3(out))
        return self.fc4(out)

 

关键改进说明

  • 通过堆叠三个隐藏层实现深度特征提取

  • 混合激活函数策略:ReLU(快速收敛) + Tanh(特征压缩) + ELU(深层稳定)

  • 使用.view()保持张量维度一致性

三、专业级CNN实现

class CNNModel(nn.Module):
    def __init__(self):
        super().__init__()
        # 卷积层1:1输入通道 → 16特征图(5x5核)
        self.cnn1 = nn.Conv2d(1, 16, kernel_size=5, stride=1, padding=0)
        self.relu1 = nn.ReLU()
        self.maxpool1 = nn.MaxPool2d(kernel_size=2)  # 输出尺寸:12x12
        
        # 卷积层2:16 → 32特征图(5x5核)
        self.cnn2 = nn.Conv2d(16, 32, kernel_size=5, stride=1, padding=0)
        self.relu2 = nn.ReLU()
        self.maxpool2 = nn.MaxPool2d(kernel_size=2)  # 输出尺寸:4x4
        
        # 全连接层:32*4*4=512 → 10
        self.fc1 = nn.Linear(32*4*4, 10)

    def forward(self, x):
        # 输入形状:[batch, 1, 28, 28]
        out = self.maxpool1(self.relu1(self.cnn1(x)))  # → [b,16,12,12]
        out = self.maxpool2(self.relu2(self.cnn2(out))) # → [b,32,4,4]
        out = out.view(out.size(0), -1)  # 展平 → [b,512]
        return self.fc1(out)

参数计算原理

  1. 卷积输出尺寸 = 【(输入尺寸 - 核尺寸 + 2×padding)/stride】 + 1

  2. 池化后尺寸 = 【(输入尺寸 - 核尺寸)/stride】 + 1

  3. 全连接输入维度需匹配最后一层特征图的展平尺寸

🚀 统一训练框架

# 通用训练流程
def train_model(model, train_loader, test_loader, num_epochs=15, lr=0.01):
    # 初始化优化器和损失函数
    optimizer = torch.optim.SGD(model.parameters(), lr=lr)
    criterion = nn.CrossEntropyLoss()
    
    # 训练监控
    history = {'loss': [], 'accuracy': [], 'iterations': []}
    iteration = 0
    
    for epoch in range(num_epochs):
        for images, labels in train_loader:
            # 数据预处理(统一处理逻辑)
            if isinstance(model, CNNModel):
                inputs = images.view(-1, 1, 28, 28)  # CNN需要保留空间维度
            else:
                inputs = images.view(-1, 28*28)  # ANN需要展平
            
            # 标准训练步骤
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            
            # 每50次迭代记录指标
            iteration += 1
            if iteration % 50 == 0:
                acc = evaluate(model, test_loader)
                history['loss'].append(loss.item())
                history['accuracy'].append(acc)
                history['iterations'].append(iteration)
                
    return history

# 统一评估函数
def evaluate(model, loader):
    correct = 0
    total = 0
    with torch.no_grad():
        for images, labels in loader:
            if isinstance(model, CNNModel):
                inputs = images.view(-1, 1, 28, 28)
            else:
                inputs = images.view(-1, 28*28)
            outputs = model(inputs)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    return 100 * correct / total

📊 性能对比更新

模型最高准确率参数量训练时间(epoch=15)
逻辑回归85.85%7,8402分钟
三隐藏层ANN96.26%784×150 + 150×150×2 + 150×10 = 121,8602分钟
双层CNN98.26%(5×5×1×16) + (5×5×16×32) + (32×4×4×10) = 27,2802分钟

关键发现

  1. CNN以更少参数量实现更高准确率 → 空间特征提取优势

  2. ANN参数量是CNN的4.5倍但精度低2% → 卷积的权重共享优势

  3. 训练时间与模型复杂度正相关 → CNN计算密度更高

🎯 关键代码差异说明

ANN改进点:

  1. 增加fc3隐藏层和ELU激活函数

  2. 使用.view(-1, 28*28)统一处理输入展平

  3. 学习率调整为0.02(原0.001)

CNN改进点:

  1. 明确卷积核参数计算逻辑

  2. 使用.view(100,1,28,28)保持batch维度

  3. 调整学习率为0.1(原0.001)

💻 完整代码获取

hyasdfghjkl/MNIST 分类基线

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值