本讲目标:
介绍Pytorch搭建LeNet5网络的流程。
Pytorch八股法搭建LeNet5网络
- 1.LeNet5网络介绍
- 2.Pytorch搭建LeNet5网络
- 2.1搭建LeNet网络
- 2.2测试LeNet网络输出
- 2.3下载数据集
- 2.4加载并配置网络
- 2.5训练并保存网络
- 2.6测试图片
1.LeNet5网络介绍
借鉴点:共享卷积核,减少网络参数(通过共享卷积参数,避免了像全连接层那样存在大量参数)。
LeNet由Yann LeCun于1998年提出,是卷积网络的开篇之作。LeNet-5提出以后,卷积神经网络成功的被商用,广泛的应用在邮政编码、支票号码识别等相关任务中。本节用LeNet网络实现Cifar10数据集的识别任务。
在统计卷积神经网络的层数时,一般只统计卷积计算层和全连接层,其余操作可以认为是卷积层的附属。
LeNet共5层网络(如下图),两层卷积层(C1、C2,前面两个卷积层,我们定义卷积层为包含了conv
、batch normalization
、activation
、pool
、dropout
等层的单元,这五部分不一定全部具备)和三层全连接层(D1、D2、D3,最后三个Dense层)。
卷积就是特征提取器:CBAPB
特征提取器(卷积层):
C1:
C(核k:6x5x5,步长s:1,填充p:valid)
B(None)
A(sigmoid)
P(max,核k:2x2,步长s:2,填充p:valid)
D(None)
C2:
C(核k:16x5x5,步长s:1,填充k:valid)
B(None)
A(sigmoid)
P(max,核k:2x2,步长s:2,填充p:valid)
D(None)
分类器(全连接层):
D1:
Dense(神经元:120,激活:sigmoid)
D2:
Dense(神经元: 84,激活:sigmoid)
D3:
Dense(神经元: 10,激活:softmax)
2.Pytorch搭建LeNet5网络
2.1搭建LeNet网络
搭建网络在model.py
文件中。
import torch
import torch.nn as nn
import torch.nn.functional as Fclass LeNet(nn.Module):def __init__(self):super(LeNet,self).__init__()# N=(W-F+2P)/S+1self.conv1=nn.Conv2d(in_channels=3,out_channels=16,kernel_size=5)self.pool1=nn.MaxPool2d(2,2)self.conv2=nn.Conv2d(16,32,5)self.pool2=nn.MaxPool2d(2,2)self.fc1=nn.Linear(32*5*5,120)self.fc2=nn.Linear(120,84)self.fc3=nn.Linear(84,10)def forward(self,x):x=F.relu(self.conv1(x)) # (3,32,32) -> (16,28,28)x=self.pool1(x) # (16,28,28) -> (16,14,14)x=F.relu(self.conv2(x)) # (16,14,14) -> (32,10,10)x=self.pool2(x) # (32,10,10) -> (32,5,5)x=x.view(-1,32*5*5) # (32,5,5) -> 35*5*5x=F.relu((self.fc1(x))) # 120x=F.relu((self.fc2(x))) # 84x=self.fc3(x) # 10return x
2.2测试LeNet网络输出
我们输入的数据是一个32x32大小的3通道图像(3,32,32):input(3,32,32)->C1(16,28,28)->P1(16,14,14)->C2(32,10,10)->P2(32,5,5)->reshape(1,3255)->fc1(120)->fc2(80)->fc3(10)->output
测试网络如下:生成一个[1,3,32,32]大小的张量,其中1为batch_size,3为通道数,32为图像尺寸。观察输出是否为[1,10]。
input=torch.rand([1,3,32,32])
model=LeNet()
output=model(input)
print(model)
print(output.shape)
输出结果如下:
LeNet((conv1): Conv2d(3, 16, kernel_size=(5, 5), stride=(1, 1))(pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(conv2): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1))(pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(fc1): Linear(in_features=800, out_features=120, bias=True)(fc2): Linear(in_features=120, out_features=84, bias=True)(fc3): Linear(in_features=84, out_features=10, bias=True)
)
torch.Size([1, 10])
2.3下载数据集
下载数据集在train.py
文件中。
import torch.optim as optim
import torch.utils.data
import torchvision.datasets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import torch.nn as nnfrom model import LeNettransform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5,),(0.5,0.5,0.5))]
)#50000张训练图片
trainset=torchvision.datasets.CIFAR10(root='./data',train=True,download=True,transform=transform)trainloader=torch.utils.data.DataLoader(trainset,batch_size=36,shuffle=True,num_workers=0)#10000张训练图片
testset=torchvision.datasets.CIFAR10(root='./data',train=False,download=True,transform=transform)testloader=torch.utils.data.DataLoader(testset,batch_size=10000,shuffle=False,num_workers=0)
test_data_iter=iter(testloader)
test_image,test_label=test_data_iter.next()
2.4加载并配置网络
仍然在train.py
文件中。
net=LeNet()
loss_function=nn.CrossEntropyLoss()
optimizer=optim.Adam(net.parameters(),lr=0.001)
2.5训练并保存网络
仍然在train.py
文件中。
总共训练5个epoch,之前将batch_size设置为32,训练过程中,每100次batch_size,就打印以此在testloader上的损失。最后保存训练好的模型。
for epoch in range(5):running_loss=0.0for step,data in enumerate(trainloader,start=0):inputs,labels=dataoptimizer.zero_grad()outputs=net(inputs)loss=loss_function(outputs,labels)loss.backward()optimizer.step()running_loss+=loss.item()if step % 100==99:with torch.no_grad():outputs=net(test_image)predict_y=torch.max(outputs,dim=1)[1]accuracy=torch.eq(predict_y,test_label).sum().item()/test_label.size(0)print('[%d,%5d train_loss:%.3f test_accuracy:%.3f'%(epoch+1,step+1,running_loss/500,accuracy))running_loss=0.0
print('Finish Training')save_path= 'LeNet.pth'
torch.save(net.state_dict(),save_path)
最后一次epoch的打印信息如下,可以观察到识别精度已经达到了65.8%,对于一个古老的网络,这个精度已经是非常高了。
[5, 100 train_loss:0.163 test_accuracy:0.649
[5, 200 train_loss:0.171 test_accuracy:0.658
[5, 300 train_loss:0.172 test_accuracy:0.658
[5, 400 train_loss:0.173 test_accuracy:0.654
[5, 500 train_loss:0.170 test_accuracy:0.647
[5, 600 train_loss:0.175 test_accuracy:0.655
[5, 700 train_loss:0.176 test_accuracy:0.668
[5, 800 train_loss:0.172 test_accuracy:0.658
[5, 900 train_loss:0.168 test_accuracy:0.640
[5, 1000 train_loss:0.173 test_accuracy:0.658
[5, 1100 train_loss:0.173 test_accuracy:0.643
[5, 1200 train_loss:0.169 test_accuracy:0.661
[5, 1300 train_loss:0.167 test_accuracy:0.658
Finish Training
2.6测试图片
测试图片程序在predict.py
文件中。
import torch
import torchvision.transforms as transforms
from PIL import Image
from model import LeNettransform=transforms.Compose([transforms.Resize((32,32)),transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5,),(0.5,0.5,0.5))]
)classes=('plane','car','bird','cat','deer','dog','frog','horse','ship','truck')net = LeNet()
net.load_state_dict(torch.load('LeNet.pth'))
im=Image.open('1.jpg')
im=transform(im)
im=torch.unsqueeze(im,dim=0)
with torch.no_grad():outputs=net(im)predict=torch.softmax(outputs,dim=1)predict_class=torch.max(outputs,dim=1)[1].numpy()
#打印类别名称和准确率
print(classes[predict_class[0]],predict[0][predict_class[0]].numpy())
输出结果:
plane 0.8187075