卷积神经网络CNN-part2-简单的CNN
卷积神经网络CNN-part1-组成和构建-CSDN博客
摘要:本文介绍了经典的卷积神经网络LeNet及其实现。LeNet由Yann LeCun于1989年提出,是最早的CNN之一,主要用于手写数字识别。其结构包含两个卷积块(每个含卷积层、Sigmoid激活和平均池化)和三个全连接层。文中详细展示了PyTorch实现代码,并演示了28x28输入图像在各层的维度变化。同时提供了模型训练方法,包括数据加载、评估函数和GPU训练过程。最后介绍了一个3D可视化工具,可交互式观察LeNet-5各层处理过程,包括输入填充、卷积、池化及全连接层的具体参数变化。该可视化直观展示了CNN。
1.简单的CNN-LeNet
1.1结构解析
LeNet是最早发布的卷积神经网络之一,因其在计算机视觉任务中的高性能表现收到关注。LeNet是AT&T贝尔实验室的Yann LeCun在1989年发布的,目的是识别图像中的手写数字。LeNet被广泛应用在ATM机上。
LeNet有以下两个部分组成:
卷积编码器:由两个卷积层。
全连接层稠密块:由3个全连接层组成。
结构如下图所示(来自网络)。
每个卷积块都包含了一个卷积层,一个激活函数和一个池化层。第一个卷积层由6个通道,第二个卷积层由16个通道。池化层具有2x2的窗口和2的步距,通过空间采样将维数减少4倍。稠密块有3个全连接层,分别是120、84、10。
1.2构建
实现程序:
#模型
net=nn.Sequential(#卷积层1,输入1转6通道,核函数5x5,填充2,激活函数Sigmoidnn.Conv2d(1,6,kernel_size=5,padding=2),nn.Sigmoid(),#池化层1,平均池化,窗口2x2,步幅2nn.AvgPool2d(kernel_size=2,stride=2),#卷积层2,6通道变16通道,核函数5x5,激活函数Sigmoidnn.Conv2d(6,16,kernel_size=5),nn.Sigmoid(),#池化层2,平均池化,窗口2x2,步幅2nn.AvgPool2d(kernel_size=2,stride=2), nn.Flatten(),#全连接层1,输出120,激活函数Sigmoidnn.Linear(16*5*5,120),nn.Sigmoid(),#全连接层2,输出84,激活函数Sigmoidnn.Linear(120,84),nn.Sigmoid(),#全连接层3,输出10nn.Linear(84,10))
输入时28x28像素的单通道图像通过LeNet做前向计算。我们查看下每一层的数据。
X=torch.rand(size=(1,1,28,28),dtype=torch.float32)
for layer in net:X=layer(X)print(layer.__class__.__name__,'output shape:\t',X.shape)
结果:
Conv2d output shape: torch.Size([1, 6, 28, 28])
Sigmoid output shape: torch.Size([1, 6, 28, 28])
AvgPool2d output shape: torch.Size([1, 6, 14, 14])
Conv2d output shape: torch.Size([1, 16, 10, 10])
Sigmoid output shape: torch.Size([1, 16, 10, 10])
AvgPool2d output shape: torch.Size([1, 16, 5, 5])
Flatten output shape: torch.Size([1, 400])
Linear output shape: torch.Size([1, 120])
Sigmoid output shape: torch.Size([1, 120])
Linear output shape: torch.Size([1, 84])
Sigmoid output shape: torch.Size([1, 84])
Linear output shape: torch.Size([1, 10])
2. 模型训练
加载数据集
#模型训练
batch_size=256
train_iter,test_iter=d2l.load_data_fashion_mnist(batch_size=batch_size)
评估准确率函数
def evalute_accuracy_gpu(net,data_iter,device=None):#@save"""使用GPU计算模型在数据集上的精度"""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):#BERT微调所需的X=[x.to(device) for x in X]else:X=X.to(device)y=y.to(device)metric.add(d2l.accuracy(net(X),y),y.numel())return metric[0]/metric[1]
训练函数
def train_ch6(net,train_iter,test_iter,num_epochs,lr,device):"""用GPU训练模型"""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)#优化函数选择了SGD,损失函数选择交叉熵optimizer=torch.optim.SGD(net.parameters(),lr=lr)loss=nn.CrossEntropyLoss()animator=d2l.Animator(xlabel='epoch',xlim=[1,num_epochs],legend=['train loss','train acc','test acc'])timer,num_batches=d2l.Timer(),len(train_iter)for epoch in range(num_epochs):"""训练损失之和,训练准确率之和,样本数"""metric=d2l.Accumulator(3)net.train()for i,(X,y) in enumerate(train_iter):timer.start() #计时器开始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],d2l.accuracy(y_hat,y),X.shape[0])timer.stop() #计时器停止train_l=metric[0]/metric[2] train_acc=metric[1]/metric[2]if (i+1)%(num_batches//5)==0 or i==num_batches-1:animator.add(epoch+(i+1)/num_batches,(train_l,train_acc,None))test_acc=evalute_accuracy_gpu(net,test_iter)animator.add(epoch+1,(None,None,test_acc))print(f'loss {train_l:.3f},train acc {train_acc:.3f},' f'test acc {test_acc:.3f}')print(f'{metric[2]*num_epochs/timer.sum():.1f} examples/sec' f'on{str(device)}')
训练模型
lr,num_epochs=0.9,10
train_ch6(net,train_iter,test_iter,num_epochs,lr,d2l.try_gpu())
3.CNN可视化
在资料查找中发现一个网址:3D Visualization of a Convolutional Neural Network
该网站提供了一个训练好的CNN网络,和基本的LeNet-5的结果类似。左上角可以用鼠标拖动输入数字,右侧控制每一层的的显示与否,中部是每一层每个像素的具体参数。该页面是在三维中开发,可通过不同视觉观察。CNN的具体配置如下:
输入:28x28,但是四周填充了2列,共32x32=1024个单元;
卷积层1:6通道,28x28=784个单元,tanh激活函数;
池化层1:6通道,14x14=196单元,最大池化;
卷积层2:16通道,10x10=100单元,tanh激活函数;
池化层2:16通道,5x5=25单元,最大池化;
稠密层1:120单元,tanh激活函数;
稠密层2:100单元,tanh激活函数;
稠密层3:10单元,tanh激活函数;
最终输出第一结果和第二结果。