前言
LeNet是最早的卷积神经网络之一,并且推动了深度学习领域的发展。自从1988年开始,这项由Yann LeCun完成的开拓性成果被命名为LeNet5。这个网络也是深度学习的入门要学的第一个网络。
网络主要部分
1、model.py ------ 模型的架构
2、train.py ——加载数据集并训练,训练集计算loss,测试集计算accuracy,保存训练好的网络参数
3、predict.py——得到训练好的网络参数后,随便找一张cifar10的图像进行分类测试
1、model.py
# 使用torch.nn包构建神经网络
import torch.nn as nn
import torch.nn.functional as F
# 定义LeNet网络
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv1 = nn.Conv2d(3, 16, 5) # 3代表输入通道,16个卷积核,卷积核大小为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)) # input(3, 32, 32) output(16, 28, 28)
x = self.pool1(x) # output(16, 14, 14)
x = F.relu(self.conv2(x)) # output(32, 10, 10)
x = self.pool2(x) # output(32, 5, 5)
x = x.view(-1, 32*5*5) # output(32*5*5)
x = F.relu(self.fc1(x)) # output(120)
x = F.relu(self.fc2(x)) # output(84)
x = self.fc3(x) # output(10)
return x
每一层详解
(1)INPUT层—输入层
首先是数据输入层,输入大小为32×32的图片。
(2)C1卷积层
输入图片大小:32×32
卷积核大小:5×5
卷积核个数:16
输出featuremap大小:2828 (32-5+1)=28
可训练参数:(55+1) * 6(每个滤波器55=25个unit参数和一个bias参数,一共6个滤波器)
连接数:(55+1)62828=122304
(3)S2池化层(下采样)
输入:28×28
采样区域:2×2
输出featureMap大小:14×14(28/2)
(4)C3卷积层
输入:S2中所有6个或者几个特征map组合
卷积核大小:5×5
卷积核种类:16
输出featureMap大小:10×10 (14-5+1)=10
(5)S4池化层(下采样)
输入:10×10
采样区域:2×2
采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid
采样种类:16
输出featureMap大小:5×5(10/2)
神经元数量:5×5×16=400
可训练参数:2×16=32
连接数:16×(2×2+1)×5×5=2000
(6)C5卷积层
输入:S4层的全部16个单元特征map(与s4全相连)
卷积核大小:5×5
卷积核种类:120
输出featureMap大小:1×1(5-5+1)
可训练参数连接:120(1655+1)=48120
(7)F6全连接层
输入:c5 120维向量
计算方式:计算输入向量和权重向量之间的点积,再加上一个偏置,结果通过sigmoid函数输出。
可训练参数:84*(120+1)=10164
(8)OUTPUT层–全连接层
Output层也是全连接层,共有10个节点,分别代表数字0到9,且如果节点i的值为0,则网络识别的结果是数字i
2、train.py
导入数据集
# 导入各种包
import torch
import torchvision
import torch.nn as nn
from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms
对数据进行处理
transform = transforms.Compose( # transforms.Compose()主要作用是串联多个图片变换的操作
# ToTensor() 能够把灰度范围从0-255变换到0-1之间,而后面的transform.Normalize()则把0-1变换到(-1,1).
# 具体地说,对每个通道而言,Normalize执行以下操作:
# image=(image-mean)/std
# 其中mean和std分别通过(0.5,0.5,0.5)和(0.5,0.5,0.5)进行指定。原来的0-1最小值0则变成(0-0.5)/0.5=-1,而最大值1则变成(1-0.5)/0.5=1.
# 而transforms.Compose()负责将这两个对图像的操作串联起来。
[transforms.ToTensor(), # convert a "PIL Image" or "numpy.ndarray(H×W×C)" to tensor(C×H×W) in the range[0.0,1.0]
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # 标准化的过程
导入、加载训练集
# 50000张训练图片
# 第一次使用时要将download设置为True才会自动去下载数据集,下载好后改为False
train_set = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
# 分批次训练,每批次随机拿出36个图片进行训练,shuffle=True表示对数据进行打乱
train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,
shuffle=True, num_workers=0)
导入、加载测试集
# 10000张验证图片
# 第一次使用时要将download设置为True才会自动去下载数据集
# torchvision.datasets: 一些加载数据的函数及常用的数据集接口
val_set = torchvision.datasets.CIFAR10(root='./data', train=False,
download=False, transform=transform)
# torch.utils.data.DataLoader()的作用就是实现数据以什么方式输入到什么网络中,训练数据的加载器
val_loader = torch.utils.data.DataLoader(val_set, batch_size=5000,
shuffle=False, num_workers=0)
# 获取测试集中的图像和标签,用于accuracy计算
val_data_iter = iter(val_loader) # 转换为迭代器
val_image, val_label = val_data_iter.next() # 通过next()获得一批数据,数据中包含了图像以及图像对应的标签值
训练过程
net = LeNet() # 实例化模型
loss_function = nn.CrossEntropyLoss() # 损失函数
optimizer = optim.Adam(net.parameters(), lr=0.001) # 优化器
# 训练过程
for epoch in range(5): # loop over the dataset multiple times
running_loss = 0.0 # 累加迭代过程中的损失
for step, data in enumerate(train_loader, start=0):
# get the inputs; data is a list of [inputs, labels]
inputs, labels = data
# zero the parameter gradients
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs)
loss = loss_function(outputs, labels)
loss.backward() # 反向传播
optimizer.step()
# print statistics
running_loss += loss.item()
if step % 500 == 499: # print every 500 mini-batches
with torch.no_grad(): # 不用计算误差梯度
outputs = net(val_image) # [batch, 10]
predict_y = torch.max(outputs, dim=1)[1]
accuracy = torch.eq(predict_y, val_label).sum().item() / val_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('Finished Training')
save_path = './Lenet.pth'
torch.save(net.state_dict(), save_path)
if __name__ == '__main__':
main()
打印信息如下:
[1, 1000] train_loss: 1.537 test_accuracy: 0.541
35.345407 s
[2, 1000] train_loss: 1.198 test_accuracy: 0.605
40.532376 s
[3, 1000] train_loss: 1.048 test_accuracy: 0.641
44.144097 s
[4, 1000] train_loss: 0.954 test_accuracy: 0.647
41.313228 s
[5, 1000] train_loss: 0.882 test_accuracy: 0.662
41.860646 s
Finished Training
3、predict.py
# 导入包
import torch
import torchvision.transforms as transforms
from PIL import Image
from model import LeNet
# 数据预处理
def main():
transform = transforms.Compose(
[transforms.Resize((32, 32)), # 把给定的图片resize到和训练集图片相同大小
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) # [C, H, W]
im = torch.unsqueeze(im, dim=0) # [N, C, H, W] 增加一个维度,
# 预测
with torch.no_grad():
outputs = net(im)
predict = torch.max(outputs, dim=1)[1].data.numpy()
print(classes[int(predict)])
if __name__ == '__main__':
main()
输出为预测结果
这是本人在学习过程中的一些学习笔记,希望能给大家带来帮助,有不对的地方还请各位大神指教~
参考资料:https://blog.csdn.net/m0_37867091/article/details/107136477
https://blog.csdn.net/qq_42570457/article/details/81460807?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163719754416780269827865%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163719754416780269827865&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-81460807.first_rank_v2_pc_rank_v29&utm_term=lenet&spm=1018.2226.3001.4187