动手学深度学习之lenet

妖精的绣舞 提交于 2020-02-18 16:33:39

参考伯禹学习平台《动手学深度学习》课程内容内容撰写的学习笔记
原文链接:https://www.boyuai.com/elites/course/cZu18YmweLv10OeV/lesson/cuwTT4MTwpHpKfaKKDcYQt
感谢伯禹平台,Datawhale,和鲸,AWS给我们提供的免费学习机会!!
总的学习感受:伯禹的课程做的很好,课程非常系统,每个较高级别的课程都会有需要掌握的前续基础知识的介绍,因此很适合本人这种基础较差的同学学习,建议基础较差的同学可以关注伯禹的其他课程:
数学基础:https://www.boyuai.com/elites/course/D91JM0bv72Zop1D3
机器学习基础:https://www.boyuai.com/elites/course/5ICEBwpbHVwwnK3C

course content

  1. lenet 模型介绍
  2. lenet 网络搭建
  3. 运用lenet进行图像识别-fashion-mnist数据集

Convolutional Neural Networks

使用全连接层的局限性:(数据集中图像大小28*28,展开成一个764)

  • 图像在同一列邻近的像素在这个向量中可能相距较远。它们构成的模式可能难以被模型识别。
  • 对于大尺寸的输入图像,使用全连接层容易导致模型过大。

使用卷积层的优势:

  • 卷积层保留输入形状。
  • 卷积层通过滑动窗口将同一卷积核与不同位置的输入重复计算,从而避免参数尺寸过大。

含有卷积层的神经网络叫做卷积神经网络

LeNet 模型

LeNet分为卷积层块和全连接层块两个部分。下面我们分别介绍这两个模块。(池化层2*2)

Image Name

卷积层块里的基本单位是卷积层后接平均池化层:卷积层用来识别图像里的空间模式,如线条和物体局部,之后的平均池化层则用来降低卷积层对位置的敏感性。

卷积层块由两个这样的基本单位重复堆叠构成。在卷积层块中,每个卷积层都使用5×55 \times 5的窗口,并在输出上使用sigmoid激活函数。第一个卷积层输出通道数为6,第二个卷积层输出通道数则增加到16(加多通道6-16,尽可能的保留特征)。

全连接层块含3个全连接层。它们的输出个数分别是120、84和10,其中10为输出的类别个数。

下面我们通过Sequential类来实现LeNet模型。
#import
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
import torch
import torch.nn as nn
import torch.optim as optim
import time
#net
class Flatten(torch.nn.Module): #展平操作
def forward(self, x):
return x.view(x.shape[0], -1)

class Reshape(torch.nn.Module): #将图像大小重定型
def forward(self, x):
return x.view(-1,1,28,28) #(B x C x H x W)

net = torch.nn.Sequential( #Lelet L(nh28-kh5+ph4+sh1)/sh_J1× L(nv-kv+pv+sv)/swJ
Reshape(),
nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2), #b12828 =>b62828
nn.Sigmoid(), #激活
nn.AvgPool2d(kernel_size=2, stride=2), #平均池化 #b62828 =>b61414
nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5), #b61414 =>b161010
nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2), #b161010 => b1655
Flatten(), #展平操作 #b1655 => b400
nn.Linear(in_features=1655, out_features=120),
nn.Sigmoid(),
nn.Linear(120, 84),
nn.Sigmoid(),
nn.Linear(84, 10)
)
接下来我们构造一个高和宽均为28的单通道数据样本,并逐层进行前向计算来查看每个层的输出形状。

#print
X = torch.randn(size=(1,1,28,28), dtype = torch.float32)
for layer in net:
X = layer(X)
print(layer.class.name,‘output shape: \t’,X.shape)
可以看到,在卷积层块中输入的高和宽在逐层减小。卷积层由于使用高和宽均为5的卷积核,从而将高和宽分别减小4,而池化层则将高和宽减半,但通道数则从1增加到16。全连接层则逐层减少输出个数,直到变成图像的类别数10。

Image Name

获取数据和训练模型

下面我们来实现LeNet模型。我们仍然使用Fashion-MNIST作为训练数据集。

数据

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(
batch_size=batch_size, root=’/home/kesci/input/FashionMNIST2065’)
print(len(train_iter)) #每个批次包含256张图片

为了使读者更加形象的看到数据,添加额外的部分来展示数据的图像

#数据展示
import matplotlib.pyplot as plt #画图库
def show_fashion_mnist(images, labels): # 定义画图函数,传入的是图像和对应的标签
d2l.use_svg_display()
# 这里的_表示我们忽略(不使用)的变量
_, figs = plt.subplots(1, len(images), figsize=(12, 12)) #1行多少张图片就有多少列
for f, img, lbl in zip(figs, images, labels):
f.imshow(img.view((28, 28)).numpy())
f.set_title(lbl) #设置标签
f.axes.get_xaxis().set_visible(False) #轴不可见
f.axes.get_yaxis().set_visible(False)
plt.show() #展示所有图像

for Xdata,ylabel in train_iter: #举出一个
break
X, y = [], []
for i in range(10):
print(Xdata[i].shape,ylabel[i].numpy())
X.append(Xdata[i]) # 将第i个feature加到X中
y.append(ylabel[i].numpy()) # 将第i个label加到y中
show_fashion_mnist(X, y)
在这里插入图片描述

因为卷积神经网络计算比多层感知机要复杂,建议使用GPU来加速计算。我们查看看是否可以用GPU,如果成功则使用cuda:0,否则仍然使用cpu

This function has been saved in the d2l package for future use

#use GPU
def try_gpu():
“”“If GPU is available, return torch.device as cuda:0; else return torch.device as cpu.”""
if torch.cuda.is_available():
device = torch.device(‘cuda:0’)
else:
device = torch.device(‘cpu’)
return device

device = try_gpu()
device

我们实现evaluate_accuracy函数,该函数用于计算模型net在数据集data_iter上的准确率。
#计算准确率
‘’’
(1). net.train()
启用 BatchNormalization 和 Dropout,将BatchNormalization和Dropout置为True
(2). net.eval()
不启用 BatchNormalization 和 Dropout,将BatchNormalization和Dropout置为False
‘’’

def evaluate_accuracy(data_iter, net,device=torch.device(‘cpu’)): #data_iter表示测试集 net表示训练的网络
“”“Evaluate accuracy of a model on the given data set.”""
acc_sum,n = torch.tensor([0],dtype=torch.float32,device=device),0 #acc_sum预测正确的总数,预测的总数
for X,y in data_iter: #一个批次一个批次的从测试集中取出
# If device is the GPU, copy the data to the GPU.
X,y = X.to(device),y.to(device)
net.eval() #该网络正在进行预测
with torch.no_grad(): #该区域所涉及的计算数据不需要计算梯度,也不会进行反向传播
y = y.long()
acc_sum += torch.sum((torch.argmax(net(X), dim=1) == y)) #[[0.2 ,0.4 ,0.5 ,0.6 ,0.8] ,[ 0.1,0.2 ,0.4 ,0.3 ,0.1]] => [ 4 , 2 ]
# 一个批次的训练数据X通过net得到我们的输出,在通过argmax得到预测值,特定维度上最大值对应的索引,
#将其中正确的相加就得到预测正确的总数
n += y.shape[0]
return acc_sum.item()/n #预测准确率
我们定义函数train_ch5,用于训练模型。
#训练函数
def train_ch5(net, train_iter, test_iter,criterion, num_epochs, batch_size, device,lr=None): #criterion损失函数
“”“Train and evaluate a model with CPU or GPU.”""
print(‘training on’, device)
net.to(device)
optimizer = optim.SGD(net.parameters(), lr=lr) #优化函数
for epoch in range(num_epochs):
train_l_sum = torch.tensor([0.0],dtype=torch.float32,device=device)
train_acc_sum = torch.tensor([0.0],dtype=torch.float32,device=device)
n, start = 0, time.time()
for X, y in train_iter: #按批训练
net.train()

        optimizer.zero_grad()   #梯度清零
        X,y = X.to(device),y.to(device)   #copy到device
        y_hat = net(X)            #得到预测值
        loss = criterion(y_hat, y)  #计算损失函数
        loss.backward()     #梯度回传
        optimizer.step()    
        
        with torch.no_grad():
            y = y.long()
            train_l_sum += loss.float()
            train_acc_sum += (torch.sum((torch.argmax(y_hat, dim=1) == y))).float()
            n += y.shape[0]
    test_acc = evaluate_accuracy(test_iter, net,device)
    print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, '
          'time %.1f sec'
          % (epoch + 1, train_l_sum/n, train_acc_sum/n, test_acc,
             time.time() - start))

我们重新将模型参数初始化到对应的设备device(cpu or cuda:0)之上,并使用Xavier随机初始化。损失函数和训练算法则依然使用交叉熵损失函数和小批量随机梯度下降。

训练

lr, num_epochs = 0.9, 10 #学习率0.9,学习次数10

def init_weights(m): #定义初始化函数
if type(m) == nn.Linear or type(m) == nn.Conv2d:
torch.nn.init.xavier_uniform_(m.weight)

net.apply(init_weights)
net = net.to(device)

criterion = nn.CrossEntropyLoss() #交叉熵描述了两个概率分布之间的距离,交叉熵越小说明两者之间越接近
train_ch5(net, train_iter, test_iter, criterion,num_epochs, batch_size,device, lr)

test #更加清楚看到我们的预测结果

for testdata,testlabe in test_iter:
testdata,testlabe = testdata.to(device),testlabe.to(device)
break
print(testdata.shape,testlabe.shape)
net.eval()
y_pre = net(testdata) #进行预测
print(torch.argmax(y_pre,dim=1)[:10]) #预测值
print(testlabe[:10]) # 真实标签

总结:

卷积神经网络就是含卷积层的网络。
LeNet交替使用卷积层和最大池化层后接全连接层来进行图像分类。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!