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

相关推荐
人工智能AI酱4 分钟前
【AI深究】逻辑回归(Logistic Regression)全网最详细全流程详解与案例(附大量Python代码演示)| 数学原理、案例流程、代码演示及结果解读 | 决策边界、正则化、优缺点及工程建议
人工智能·python·算法·机器学习·ai·逻辑回归·正则化
爱喝可乐的老王9 分钟前
机器学习监督学习模型--逻辑回归
人工智能·机器学习·逻辑回归
Ao0000009 分钟前
机器学习——逻辑回归
人工智能·机器学习·逻辑回归
智算菩萨20 分钟前
【How Far Are We From AGI】3 AGI的边界扩张——数字、物理与智能三重接口的技术实现与伦理困境
论文阅读·人工智能·深度学习·ai·agi
智算菩萨21 分钟前
【How Far Are We From AGI】2 大模型的“灵魂“缺口:当感知、记忆与自我意识的迷雾尚未散去
人工智能·ai·agi·感知
deepxuan32 分钟前
Day1--python三大库-Pandas
人工智能·python·pandas
剑穗挂着新流苏3121 小时前
Pytorch加载数据
python·深度学习·transformer
阿泽·黑核1 小时前
Easy Vibe Coding 学习心得(二):从产品原型到 AI 应用——我的第一个完整项目
人工智能·ai编程·vibe coding
qq_452396231 小时前
【Python × AI】多智能体协作:从 AutoGPT 到 CrewAI 的组织进化论
大数据·人工智能·python·ai
老李的森林1 小时前
杂谈--如何与AI高效率的对话
人工智能·stm32·嵌入式硬件·机械