1、定义
LeNet是深度学习领域的一个经典卷积神经网络模型,由Yann LeCun等人于1998年提出,被广泛应用于手写数字识别和其他图像识别任务。
LeNet的网络结构相对简单,包含两个卷积层和三个全连接层,是卷积神经网络的基础。
LeNet对于现代的图像识别任务来说可能过于简单,但其对于深度学习的发展有着重要的贡献,是深度学习领域不可或缺的一部分。
总体来看,LeNet(LeNet-5)由两个部分组成:
卷积编码器:由两个卷积层组成;
全连接层密集块:由三个全连接层组成。
输入层:接收输入的图像数据,一般为灰度图或者RGB彩色图像。 第一个卷积层:对输入进行卷积操作,使用的卷积核的大小为5x5,输出通道数为6,不使用填充,步幅为1。 第一个池化层:对第一个卷积层的输出进行下采样,使用的窗口大小为2x2,步幅为2,池化方式为平均池化或最大池化。 第二个卷积层:对第一个池化层的输出进行卷积操作,使用的卷积核的大小为5x5,输出通道数为16,不使用填充,步幅为1。 第二个池化层:对第二个卷积层的输出进行下采样,使用的窗口大小为2x2,步幅为2,池化方式为平均池化或最大池化。 全连接层1:将第二个池化层的输出展开成向量,并经过一个全连接层,输出大小为120。 全连接层2:对第一层全连接层的输出进行处理,输出大小为84。 输出层:对第二层全连接层的输出进行处理,输出大小为分类任务的类别数目。
手写数字识别的架构如图所示。
在此图示中,输入图片的尺寸为32*32,图示有误
C1层(卷积层):6@28×28 该层使用了6个卷积核,每个卷积核的大小为5×5,这样就得到了6个feature map(特征图)。 每个卷积核(5×5)与原始的输入图像(32×32)进行卷积,这样得到的feature map(特征图)大小为(32-5+1)×(32-5+1)= 28×28 S2层(下采样层,也称池化层):6@14×14 这一层主要是做池化或者特征映射(特征降维),池化单元为2×2,因此,6个特征图的大小经池化后即变为14×14。
回顾本文刚开始讲到的池化操作,池化单元之间没有重叠,在池化区域内进行聚合统计后得到新的特征值,因此经2×2池化后,每两行两列重新算出一个特征值出来,相当于图像大小减半,
因此卷积后的28×28图像经2×2池化后就变为14×14。 C3层(卷积层):16@10×10 C3层有16个卷积核,卷积模板大小为5×5。每个卷积核都与S2的6@14*14进行互关系运算,得到16个通道 与C1层的分析类似,C3层的特征图大小为(14-5+1)×(14-5+1)= 10×10 S4(下采样层,也称池化层):16@5×5 与S2的分析类似,池化单元大小为2×2,因此,该层与C3一样共有16个特征图,每个特征图的大小为5×5。 C5层(卷积层/全连接层):120 该层有120个卷积核,每个卷积核的大小仍为5×5,因此有120个特征图。由于S4层的大小为5×5,而该层的卷积核大小也是5×5,
因此特征图大小为(5-5+1)×(5-5+1)= 1×1。这样该层就刚好变成了全连接,这只是巧合,如果原始输入的图像比较大,则该层就不是全连接了。 F6层(全连接层):84 F6层有84个单元,之所以选这个数字的原因是来自于输出层的设计,对应于一个7×12的比特图。 OUTPUT层(输出层):10 Output层也是全连接层,共有10个节点,分别代表数字0到9。如果第i个节点的值为0,则表示网络识别的结果是数字i。
2、实现
模型
1 import torch 2 from torch import nn 3 from d2l import torch as d2l 4 5 net = nn.Sequential( 6 nn.Conv2d(1, 6, kernel_size=5, padding=2), nn.Sigmoid(), 7 nn.AvgPool2d(kernel_size=2, stride=2), 8 nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(), 9 nn.AvgPool2d(kernel_size=2, stride=2), 10 nn.Flatten(), 11 nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(), 12 nn.Linear(120, 84), nn.Sigmoid(), 13 nn.Linear(84, 10))
或者是
1 import torch.nn as nn 2 import torch.nn.functional as F 3 4 class LeNet(nn.Module): 5 def __init__(self): 6 super(LeNet, self).__init__() 7 self.conv1 = nn.Conv2d(3, 16, 5) # in_channels=3 out_channels=16 kernel=5 8 self.pool1 = nn.MaxPool2d(2, 2) 9 self.conv2 = nn.Conv2d(16, 32, 5) 10 self.pool2 = nn.MaxPool2d(2, 2) 11 self.fc1 = nn.Linear(32*5*5, 120) 12 self.fc2 = nn.Linear(120, 84) 13 self.fc3 = nn.Linear(84, 10) 14 # 调用上面定义的函数 15 def forward(self, x): 16 x = F.relu(self.conv1(x)) # input(3, 32, 32) output(16, 28, 28) 17 x = self.pool1(x) # output(16, 14, 14) 18 x = F.relu(self.conv2(x)) # output(32, 10, 10) 19 x = self.pool2(x) # output(32, 5, 5) 20 x = x.view(-1, 32*5*5) # output(32*5*5) 21 x = F.relu(self.fc1(x)) # output(120) 22 x = F.relu(self.fc2(x)) # output(84) 23 x = self.fc3(x) # output(10) 24 return x
训练
1 import torch 2 import torchvision 3 import torch.nn as nn 4 5 from model import LeNet 6 import torch.optim as optim 7 import torchvision.transforms as transforms 8 from torch.utils.data import DataLoader 9 10 def main(): 11 transform = transforms.Compose( 12 [transforms.ToTensor(), 13 transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) 14 15 # 50000张训练图片 16 # 第一次使用时要将download设置为True才会自动去下载数据集 17 train_set = torchvision.datasets.CIFAR10(root='./data', train=True, 18 download=False, transform=transform) 19 train_loader = DataLoader(train_set, batch_size=36, 20 shuffle=True, num_workers=0) 21 22 # 10000张验证图片 23 # 第一次使用时要将download设置为True才会自动去下载数据集 24 val_set = torchvision.datasets.CIFAR10(root='./data', train=False, 25 download=False, transform=transform) 26 val_loader = DataLoader(val_set, batch_size=5000, 27 shuffle=False, num_workers=0) 28 val_data_iter = iter(val_loader) 29 val_image, val_label = val_data_iter.next() 30 31 # classes = ('plane', 'car', 'bird', 'cat', 32 # 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') 33 34 # 定义模型和损失函数 35 net = LeNet() 36 loss_function = nn.CrossEntropyLoss() 37 38 # 定义优化器 39 optimizer = optim.Adam(net.parameters(),lr=0.001) 40 41 # 对训练集迭代5次 42 epochs = 5 43 for epoch in range(epochs): 44 running_loss = 0 45 for step,data in enumerate(train_loader,start=0): # step从start开始 46 inputs,labels = data 47 # 梯度清零 48 optimizer.zero_grad() 49 50 outputs = net(inputs) 51 # 计算损失函数 52 loss = loss_function(outputs,labels) 53 # 反向传播并更新参数 54 loss.backward() 55 optimizer.step() 56 57 running_loss += loss.item() 58 if step%500 == 499: 59 with torch.no_grad(): 60 outputs = net(val_image) 61 predict_y = torch.max(outputs,dim=1)[1] 62 accuracy = (predict_y==val_label).sum().item() / val_label.size(0) 63 print('[%d %3d] train_loss: %.3f test_accuracy: %.3f' % 64 (epoch+1,step+1,running_loss/500,accuracy)) 65 running_loss = 0 66 67 print('Finished Training') 68 save_path = './Lenet.pth' 69 torch.save(net.state_dict(),save_path) 70 if __name__ == '__main__': 71 main()
这段代码是一个基于LeNet网络结构进行CIFAR10分类的训练代码,主要步骤如下:
导入必要的Python包,包括PyTorch中的torch、torchvision、torch.nn等模块,以及用于数据加载的DataLoader。
定义数据预处理,包括将图像转换为Tensor格式、标准化处理等操作。
加载训练集和验证集,使用DataLoader将数据分批次读取。
定义LeNet网络结构和损失函数,此处使用交叉熵损失函数。
定义Adam优化器。
进行训练,共进行5个epoch,每个epoch对训练集进行一次完整的遍历,每次遍历对数据分批次读取并进行网络的前向计算、反向传播、参数更新等操作。
每500个batch输出一次训练损失和测试精度。
保存训练好的模型参数。
测试
1 import torch 2 import torchvision.transforms as transforms 3 from PIL import Image 4 5 from model import LeNet 6 7 8 def main(): 9 transform = transforms.Compose( 10 [transforms.Resize((32, 32)), # 对输入的图片尺寸进行调整 11 transforms.ToTensor(), # Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor. (H x W x C)->(C x H x W) 12 transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) 13 14 classes = ('plane', 'car', 'bird', 'cat', 15 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') 16 17 net = LeNet() 18 net.load_state_dict(torch.load('Lenet.pth')) # 加载模型 19 20 im = Image.open('data/plane.jpg') 21 im = transform(im) # [C, H, W] 22 # 增加一个维度,batch 23 im = torch.unsqueeze(im, dim=0) # [N, C, H, W] 增加一个batch维度 24 25 with torch.no_grad(): 26 outputs = net(im) 27 predict = torch.max(outputs, dim=1)[1].numpy() 28 print(classes[int(predict)]) 29 30 if __name__ == '__main__': 31 main()
标签:nn,16,卷积,32,self,torch,神经网络,理解 From: https://www.cnblogs.com/Zhouce/p/17996677