从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继续训练以获得更准的分类效果

相关推荐
枫叶林FYL41 分钟前
【机器学习与智慧医疗】T2DM-EWS: 2型糖尿病早期预警系统(多参数集成分类模型)完整实现
人工智能·机器学习·分类
南屹川42 分钟前
【缓存技术】Redis实战:从缓存策略到分布式锁
人工智能
Li emily7 小时前
解决了加密货币api多币种订阅时的数据乱序问题
人工智能·python·api·fastapi
山川绿水7 小时前
bugku——PWN——overflow2
人工智能·web安全·网络安全
程序员cxuan7 小时前
微信读书官方发了 skills,把我给秀麻了。
人工智能·后端·程序员
fake_ss1987 小时前
AI时代学习全栈项目开发的新范式
java·人工智能·学习·架构·个人开发·学习方法
nassi_7 小时前
对AI工程问题的一些思考
大数据·人工智能·hadoop
AI技术控8 小时前
《Transformers are Inherently Succinct》论文解读:从“能表达什么”到“多紧凑地表达”
人工智能·python·深度学习·机器学习·自然语言处理
蔡俊锋8 小时前
AI记忆压缩术:从305GB到7.4GB的魔法
人工智能·ai·ai 记忆
Upsy-Daisy8 小时前
AI Agent 项目学习笔记(二):Spring AI 与 ChatClient 主链路解析
人工智能·笔记·学习