从0开始深度学习(27)——卷积神经网络(LeNet)

1 LeNet神经网络

LeNet是最早的卷积神经网络之一,由Yann LeCun等人在1990年代提出,并以其名字命名。最初,LeNet被设计用于手写数字识别,最著名的应用是在美国的邮政系统中识别手写邮政编码。LeNet架构的成功证明了卷积神经网络在解决实际问题中的有效性,为后续更复杂、更强大的CNN模型的发展奠定了基础。

结构如下:

先用pytorch代码实现该结构:

python 复制代码
import torch
from torch import nn

net=nn.Sequential(
    nn.Conv2d(1,6,kernel_size=5,padding=2),
    nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2,stride=2),
    nn.Conv2d(6,16,kernel_size=5),
    nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2,stride=2),
    nn.Flatten(),
    nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),
    nn.Linear(120, 84), nn.Sigmoid(),
    nn.Linear(84, 10)
)

我们知道手写数字识别数据集的数据,都是 28 × 28 28\times28 28×28的灰度图,下面我们将输入一个 28 × 28 28\times28 28×28的矩阵,看看经过这个模型过后,会输出什么。

python 复制代码
x=torch.rand(size=(1,1,28,28))
for layer in net:
    x=layer(x)
    print(layer.__class__.__name__,"output shape:",x.shape)
    

运行结果:

可以发现最后输出为 1 × 10 1\times10 1×10的张量,该维度与我们需要的结果分类数(0~9)匹配。

2 模型训练

检测一下LeNet-5在Fashion-MNIST数据集上的表现。

python 复制代码
import torch
from torch import nn,optim
import torchvision
from torch.utils import data
from torchvision import transforms,datasets
from torch.utils.data import DataLoader
from d2l import torch as d2l
import matplotlib.pyplot as plt

net=nn.Sequential(
    nn.Conv2d(1,6,kernel_size=5,padding=2),
    nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2,stride=2),
    nn.Conv2d(6,16,kernel_size=5),
    nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2,stride=2),
    nn.Flatten(),
    nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),
    nn.Linear(120, 84), nn.Sigmoid(),
    nn.Linear(84, 10)
)

batch_size=128

# 数据预处理
transform = transforms.Compose([
    transforms.ToTensor(),  
    transforms.Normalize((0.5,), (0.5,))  # 标准化到[-1, 1]区间,加快计算
])

# 加载Fashion-MNIST数据集
train_dataset = datasets.FashionMNIST(root='D:/DL_Data/', train=True, download=False, transform=transform)
test_dataset = datasets.FashionMNIST(root='D:/DL_Data/', train=False, download=False, transform=transform)

train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)

# 自定义 try_gpu 函数
def try_gpu(i=0):
    if torch.cuda.device_count() >= i + 1:
        return torch.device(f'cuda:{i}')
    return torch.device('cpu')

def evaluate_acc_gpu(net, data_iter, device=None):
    if isinstance(net, nn.Module):
        net.eval()
        if not device:
            device = next(iter(net.parameters())).device
        metric = d2l.Accumulator(2)
        with torch.no_grad():
            for X, y in data_iter:
                if isinstance(X, list):
                    X = [x.to(device) for x in X]
                else:
                    X = X.to(device)
                y = y.to(device)
                temp = net(X)
                acc = accuracy(temp, y)
                metric.add(acc, y.numel())
    return metric[0] / metric[1]

def train(net, train_iter, test_iter, num_epochs, lr, device, train_acc_list,test_acc_list):
    def init_weights(m):
        if type(m) == nn.Linear or type(m) == nn.Conv2d:
            nn.init.xavier_uniform_(m.weight)
    net.apply(init_weights)
    print("training on", device)
    net.to(device)
    optimizer = torch.optim.SGD(net.parameters(), lr=lr)
    loss = nn.CrossEntropyLoss()
    timer = d2l.Timer()
    train_acc_list = train_acc_list
    test_acc_list = test_acc_list
    print("init train_list nad test_list is ok")

    for epoch in range(num_epochs):
        metric = d2l.Accumulator(3)
        net.train()
        for i, (X, y) in enumerate(train_iter):
            optimizer.zero_grad()
            X, y = X.to(device), y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            l.backward()
            optimizer.step()
            with torch.no_grad():
                metric.add(l * X.shape[0], accuracy(y_hat, y), X.shape[0])
        train_l = metric[0] / metric[2]
        train_acc = metric[1] / metric[2]
        train_acc_list.append(train_acc)
        print(f"epoch: {epoch+1}, train_l: {train_l:.3f}, train_acc: {train_acc:.3f}")
        test_acc = evaluate_acc_gpu(net, test_iter)
        test_acc_list.append(test_acc)
        print(f"test acc: {test_acc:.3f}")
    return train_acc_list,test_acc_list

    


# 实现 accuracy 函数
def accuracy(y_hat, y):
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y
    return float(cmp.type(y.dtype).sum())
    
lr, num_epochs = 0.9, 10
train_acc_list=[]
test_acc_list=[]

train_acc_list,test_acc_list=train(net, train_loader, test_loader, num_epochs, lr, try_gpu(),train_acc_list,test_acc_list)

print(f"num_epochs: {num_epochs}")
print(f"train_acc_list: {train_acc_list}")
print(f"test_acc_list: {test_acc_list}")

try:
    # 绘制训练和测试准确率的折线图
    epochs = range(1, num_epochs + 1)
    plt.plot(epochs, train_acc_list, 'b', label='Training Accuracy')
    plt.plot(epochs, test_acc_list, 'r', label='Testing Accuracy')
    plt.title('Training and Testing Accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()
    plt.show()
except Exception as e:
    print(f"An error occurred: {e}")

运行结果

分析图像可以看出,准确率还没有稳定,说明还有提升空间,可以添加epoch继续训练以获得更准的分类效果

相关推荐
汗流浃背了吧,老弟!2 小时前
BPE 词表构建与编解码(英雄联盟-托儿索语料)
人工智能·深度学习
软件聚导航2 小时前
从 AI 画马到马年红包封面,我还做了一个小程序
人工智能·chatgpt
啊森要自信2 小时前
CANN ops-cv:AI 硬件端视觉算法推理训练的算子性能调优与实战应用详解
人工智能·算法·cann
要加油哦~2 小时前
AI | 实践教程 - ScreenCoder | 多agents前端代码生成
前端·javascript·人工智能
玄同7652 小时前
从 0 到 1:用 Python 开发 MCP 工具,让 AI 智能体拥有 “超能力”
开发语言·人工智能·python·agent·ai编程·mcp·trae
新缸中之脑2 小时前
用RedisVL构建长期记忆
人工智能
J_Xiong01172 小时前
【Agents篇】07:Agent 的行动模块——工具使用与具身执行
人工智能·ai agent
SEO_juper2 小时前
13个不容错过的SEO技巧,让您的网站可见度飙升
人工智能·seo·数字营销
小瑞瑞acd2 小时前
【小瑞瑞精讲】卷积神经网络(CNN):从入门到精通,计算机如何“看”懂世界?
人工智能·python·深度学习·神经网络·机器学习
CoderJia程序员甲3 小时前
GitHub 热榜项目 - 日榜(2026-02-06)
人工智能·ai·大模型·github·ai教程