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

相关推荐
amhjdx16 分钟前
星巽短剧以科技赋能影视创新,构建全球短剧新生态!
人工智能·科技
听风南巷1 小时前
机器人全身控制WBC理论及零空间原理解析(数学原理解析版)
人工智能·数学建模·机器人
美林数据Tempodata1 小时前
“双新”指引,AI驱动:工业数智应用生产性实践创新
大数据·人工智能·物联网·实践中心建设·金基地建设
电科_银尘1 小时前
【大语言模型】-- 私有化部署
人工智能·语言模型·自然语言处理
翔云 OCR API3 小时前
人工智能驱动下的OCR API技术演进与实践应用
人工智能·ocr
南方者3 小时前
重磅升级!文心 ERNIE-5.0 新一代原生全模态大模型,这你都不认可它吗?!
人工智能·aigc
庄周迷蝴蝶4 小时前
旋转位置编码(Rotary Position Embedding,RoPE)
人工智能·机器学习
xier_ran4 小时前
深度学习:RMSprop 优化算法详解
人工智能·深度学习·算法
大千AI助手4 小时前
Prefix-Tuning:大语言模型的高效微调新范式
人工智能·神经网络·自然语言处理·llm·prefix-tuning·大千ai助手·前缀微调
雾江流4 小时前
RikkaHub 1.6.11 | 开源的本地大型语言模型聚合应用,支持多种AI服务提供商
人工智能·语言模型·自然语言处理·软件工程