如果觉得本篇文章对您的学习起到帮助作用,请 点赞 + 关注 + 评论 ,留下您的足迹
LeNet是早期最具代表性的卷积神经网络,虽然现在已经出现了很多性能优于LeNet的模型,但是我认为,学习LeNet还是很有必要的,追本溯源,因为这可以使我们弄清楚卷积神经网络乃至深度学习的历史发展进程。读者需要知道,本文所构建的卷积神经网络并非原始的LeNet,而是现代改进版。
本文所使用代码下载:
链接:https://pan.baidu.com/s/1ZVPVvrZl8YcEPtPep3RsJg
提取码:nsqb
本文相关推荐阅读:
一学就会 | PyTorch入门看这篇就够了
一学就会 | 基于PyTorch的TensorBoard可视化
LeNet设计的巧妙之处在于,它利用卷积核实现参数共享,使用池化操作提取特征,极大减少了参数的数量,节省了大量的计算成本,最后再使用全连接神经网络进行分类识别,后来如AlexNet、VGG等网络,都是在LeNet的基础上提出的。
LeNet的网络结构示意图如下所示:
这是在原论文摘取的图片,如果感兴趣,可以去看Gradient-Based Learning Applied to Document Recognition。
LeNet网络所识别的目标是灰色1通道的数据,像素为(1, 32, 32),而本实验要做的是3个通道RGB彩色图片,图片像素为(3, 32, 32)。
我们简单分析一下卷积神经网络结构:
构建LeNet网络模型如下:
import torch.nn as nn
import torch.nn.functional as F
class LeNet(nn.Module):
def __init__(self):
super(LeNet,self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool1 = nn.MaxPool2d(2,2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.pool2 = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(16*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))
x = self.pool1(x)
x = F.relu(self.contv2(x))
x = self.pool2(x)
x = x.view(-1, 16*5*5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
使用torchsummary工具检测网络及参数,有关torchsummary的内容,请点击这里,根据目录选择相关内容,进行学习。
model = LeNet()
from torchsummary import summary
summary(model, input_size=(3, 32, 32),device="cpu")
该数据集有60000张彩色图像,通道为3,这些图像像素为32 * 32的,分为10个类,每类有60000张图像。其中50000张图像作为训练集数据,10000张图像作为测试集数据。
该数据集内10个类别分别为:飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船、卡车。
下载数据集,点击CIFAR-10数据集。
使用Torchvision,可以很容易的加载CIFAR-10数据集。
1、导入需要的包:
import torch
import torchvision
import torchvision.transforms as transforms
2、数据处理
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
torchvision.transforms.ToTensor作用是:
torchvision.transforms.Normalize作用是:
3、加载数据
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=0)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=0)
1、卷积神经网络实例化
# 建立GPU设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
from model import LeNet
net = LeNet()
# 模型放置到GPU
net.to(device)
2、使用交叉熵损失函数和SGD优化器
import torch.nn as nn
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
3、使用TensorBoard绘制epoch折线图
from torch.utils.tensorboard import SummaryWriter
# logs保存训练10个epoch的日志
writer = SummaryWriter('logs/logs')
4、迭代训练与测试
# 开始训练标志
print('Start Training')
# 计算loss的和,为了计算平均loss值
training_loss = 0.0
testing_loss = 0.0
for epoch in range(10):
for data in trainloader:
# 将数据放在GPU上
inputs, labels = data[0].to(device), data[1].to(device)
# 清空梯度信息
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, labels)
# 反向传播,计算梯度
loss.backward()
# 更新一步参数
optimizer.step()
# 计算这个epoch的loss值得和
training_loss += loss.item()
# torch.no_grad()为PyTorch的上下文管理器,
# 测试将不会对梯度监控,节省内存空间
with torch.no_grad():
for data in testloader:
inputs, labels = data[0].to(device), data[1].to(device)
outputs = net(inputs)
loss = criterion(outputs, labels)
testing_loss += loss.item()
# 使用TensorBoard绘制折现图,对比训练集和测试机上的损失loss
writer.add_scalars('Train-Test Loss',
{'Train Loss': training_loss / len(trainloader),
'Test Loss': testing_loss / len(testloader)}, epoch)
# 每个epoch都把loss清零
testing_loss = 0.0
training_loss = 0.0
# 每完成一个epoch打印一次,可掌握训练进度
print("{} finished".format(epoch))
# 关闭TensorBoard
writer.close()
# 训练完成标志
print('Finished Training')
5、保存训练好的模型
PATH = './cifar_net_10.pth'
torch.save(net.state_dict(), PATH)
完成了这五步,接下来检测下我们的可视化结果:
我们可以观察到,在第4次迭代epoch之前,测试集loss值反而低于训练集。训练集loss始终在下降,而测试机loss在第8次loss值达到最低,之后呈现上升趋势,说明该模型出现了一定的过拟合现象。
我从网上找了四张图片,分别为狗,马,船,卡车。图片存放于test_images文件夹中。
展示下这四张图片:
提示:读者可以自己在网上随意下载图片,不过下载的图片要在CIFAR10数据集包含的10个类别之中。
1、加载预先训练好的模型
from model import LeNet
import torch
# 实例化模型
net = LeNet()
PATH = 'cifar_net_10.pth'
# 将训练好的参数导入
net.load_state_dict(torch.load(PATH))
2、数据处理模块
import torchvision.transforms as transforms
'''
我们设计的模型输入为32*32,所以在测试时,仍应该输入32*32大小的图片。
'''
transform = transforms.Compose(
[transforms.Resize((32,32)),
transforms.ToTensor(),
transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
from PIL import Image
# 将图片加载为PIL图片形式,分别存放为img1,img2,img3,img4
img0,img1,img2,img3 = map(Image.open,
('test_images/dog.jpg','test_images/horse.jpg',
'test_images/ship.jpg','test_images/truck.jpg'))
img0,img1,img2,img3 = map(transform, (img0,img1,img2,img3))
四张图片合成为一个批次:
# 加入表示批次大小的维度
img0 = torch.unsqueeze(img0, dim=0)
img1 = torch.unsqueeze(img1, dim=0)
img2 = torch.unsqueeze(img2, dim=0)
img3 = torch.unsqueeze(img3, dim=0)
# 四张图片合成一个批次
images = torch.cat((img0, img1, img2, img3),dim=0)
3、测试
with torch.no_grad():
outputs = net(images)
# pred为最大值的索引值
_, pred = torch.max(outputs,dim=1)
pred = pred.numpy()
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
for i in range(4):
print(classes[pred[i]])
Out:
cat
horse
ship
truck
太棒了!模型准确的预测出了在网上随便下载的图片。
如果您觉得这篇文章对你有帮助,记得 点赞 + 关注 + 评论 三连,您只需动一动手指,将会鼓励我创作出更好的文章,快留下你的足迹吧