LeNet训练Cifar-10数据集代码详解以及输出结果

首先讲一下交叉熵损失函数,里面包含了Softmax函数和NLL损失函数

接下来讲一下NLL损失函数

Legative Log Likelihood Loss,中文名称是最大似然或者log似然代价函数

似然函数是什么呢?

似然函数就是我们有一堆观察所得得结果,然后我们用这堆观察结果对模型的参数进行估计

举个例子:

抛一个硬币,假设正面朝上的概率是θ,那么反面朝上的概率就是1-θ

但是我们不知道θ是多少,这个θ就是模型的参数

我们为了获得θ的值,我们抛了十次,得到一个序列x=正正反反正反正正正正,获得这个序列的概率是θ⋅θ⋅(1-θ)⋅(1-θ)⋅θ⋅(1-θ)⋅θ⋅θ⋅θ⋅θ = θ⁷ (1-θ)³,我们尝试所有θ可能的值,绘制了一个图(θ的似然函数)

LeNet训练Cifar-10数据集代码详解以及输出结果_第1张图片

我们发现这个函数有最大值,当θ=0.7的时候,得到这个序列的概率最大,当我们实验的次数越来越多,这个最大值约接近真实值0.5。

损失函数的用途是衡量当前参数下模型的预测值和真实label的差距。似然函数损失函数当然也是如此。

在PyTorch中,CrossEntropyLoss其实是LogSoftMax和NLLLoss的合体

交叉熵损失函数

 

softmax函数

LeNet训练Cifar-10数据集代码详解以及输出结果_第2张图片 

softmax一般用于多分类过程中,它将多个神经元的输出,映射到(0,1)区间内,可以看成概率来理解,从而来进行多分类! 

loss(x,class)=−log⁡(exp⁡(x[class])∑jexp⁡(x[j]))=−x[class]+log⁡(∑jexp⁡(x[j]))

什么叫做梯度下降法?

顺着梯度下滑,找到最陡的方向,迈一小步,然后再找当前位,置最陡的下山方向,再迈一小步…

LeNet训练Cifar-10数据集代码详解以及输出结果_第3张图片

通过比较以上两个图,可以会发现,由于初始值的不同,会得到两个不同的极小值,所以权重初始值的设定也是十分重要的,通常的把W全部设置为0很容易掉到局部最优解,一般可以按照高斯分布的方式分配初始值。

当误差越大,梯度就越大,参数w(神经元之间的连接权重)调整得越快,训练速度也就越快

使用softmax函数之后,再使用交叉熵作为损失函数,也会使得求梯度变得十分简单,

学习率

将输出误差反向传播给网络参数,以此来拟合样本的输出。本质上是最优化的一个过程,逐步趋向于最优解。但是每一次更新参数利用多少误差,就需要通过一个参数来控制,这个参数就是学习率(Learning rate),也称为步长。

在这里插入图片描述

以上部分是10月25号写的,昨天满课,没时间写。今天早上10点的课,同门都8点就来实验室学习,卷的我好害怕O.o


import torch
import torchvision
import torch.nn as nn
from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np


#def main():
transform = transforms.Compose(#transform函数对图像进行预处理  compose:将下面两个函数打包成一个整体
        [transforms.ToTensor(),#Totensor函数 将H*W*C转换为C*H*W 并将像素值转换成0到1之间
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])#Normalize函数 标准化 使用均值和标准差来标准化这个tensor 
        #标准化处理:output = (input-0.5)/0.5
        #反标准化处理: input = output/2+0.5
#     # 50000张训练图片
#     # 第一次使用时要将download设置为True才会自动去下载数据集  下载完之后设置为False
train_set = torchvision.datasets.CIFAR10(root='./data', train=True,#train=true 导入cifar10训练集的样本
                                             download=False, transform=transform)#transform 是对图像进行预处理的一个函数
train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,#将训练集导入进来,然后分成一个批次一个批次的,每一批拿出36张图片进行训练
                                               shuffle=True, num_workers=0)#shuffle是否将数据集进行一个打乱 num_works载入数据的线程数win系统里面只能设置为0,不然报错

#     # 10000张验证图片 测试集
#     # 第一次使用时要将download设置为True才会自动去下载数据集
val_set = torchvision.datasets.CIFAR10(root='./data', train=False,
                                           download=False, transform=transform)
val_loader = torch.utils.data.DataLoader(val_set, batch_size=10000,
                                             shuffle=False, num_workers=0)
val_data_iter = iter(val_loader)#iter函数将参数转换成可迭代的迭代器   测试集
val_image, val_label = val_data_iter.next()#next方法就能够获取一批数据
    
classes = ('plane', 'car', 'bird', 'cat',#标签  元组类型,值是不能改变的 index 0就代表plane  index 1就代表car
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# #展示数据集的图片
# def imshow(img):
#     img = img / 2 + 0.5     # unnormalize反标准化处理
#     npimg = img.numpy()
#     plt.imshow(np.transpose(npimg, (1, 2, 0)))#因为之前的totensor做了处理c,w,h=0,1,2,现在还原成w,h,c=1,2,0
#     plt.show()

# # print labels
# print(' '.join('%5s' % classes[val_label[j]] for j in range(4)))

# # show images
# imshow(torchvision.utils.make_grid(val_image))


net = LeNet()#实例化模型
loss_function = nn.CrossEntropyLoss()#定义损失函数  里面包含了softmax函数和NLL损失函数,所以在网络输出层的时候不需要再添加softmax函数
#Legative Log Likelihood Loss,中文名称是最大似然或者log似然代价函数
optimizer = optim.Adam(net.parameters(), lr=0.001)#优化器 这里使用的就是Adam优化器  net.parameter就是将LeNet所有可训练的参数都进行训练  lr代表的是learning rate学习率

for epoch in range(5):  # loop over the dataset multiple times 会将训练集迭代5次

        running_loss = 0.0#用来累加训练过程中的一些损失
        for step, data in enumerate(train_loader, start=0):#遍历训练集样本 enumerate(枚举)函数不仅返回每一批的数据data 它还会返回data的步数index ,循环从0开始
            # get the inputs; data is a list of [inputs, labels]
            inputs, labels = data#得到数据之后,将它分为inputs,即输入的图像,还有对应的标签

            # zero the parameter gradients
            optimizer.zero_grad()#zero_grad函数的作用是将历史损失梯度给清0
            # forward + backward + optimize
            outputs = net(inputs)#将图片输入到网络进行正向传播,得到输出
            loss = loss_function(outputs, labels)#计算损失,output是网络预测的值,labels是真实标签
            loss.backward()#然后将损失进行反向传播
            optimizer.step()#通过优化器的step函数进行参数的更新

            # print statistics
            running_loss += loss.item()#每当计算完损失之后,就累加到running_loss这个变量里面来
            if step % 500 == 499:    # print every 500 mini-batches 每隔500步就打印一次数据的信息
                with torch.no_grad():  #with是一个上下文管理器  在接下来的计算中,不用去计算误差损失梯度
                    outputs = net(val_image)  # [batch, 10]  正向传播
                    predict_y = torch.max(outputs, dim=1)[1]# 寻找输出最大的index在什么位置 (网络预测最可能归于哪个类别) 维度为1(输出的10个节点)维度为0对应的是batch [1]表示只输出index值,就是它在哪个位置
                    accuracy = torch.eq(predict_y, val_label).sum().item() / val_label.size(0)
                            #将预测的标签类别和真实的标签类别进行比较 在相同的地方返回true(1) 不同的地方返回false(0) 然后通过sum函数知道本次预测中预测到了多少个样本
                    print('[%d, %5d] train_loss: %.3f  test_accuracy: %.3f' %
                          (epoch + 1, step + 1, running_loss / 500, accuracy))#这几个参数分别是第几轮,多少步,训练过程中累加的损失(500步当中平均的训练误差),测试样本的准确率
                    running_loss = 0.0# 将running_loss清零 来进行下一个500步的计算

print('Finished Training')

save_path = './Lenet.pth'
torch.save(net.state_dict(), save_path)#save函数来将网络的参数进行保存


# if __name__ == '__main__':
#     main()

这个是训练结果,作为几十年前的网络,有68%的精度已经不错了

LeNet训练Cifar-10数据集代码详解以及输出结果_第4张图片

 接下来找一张图片来测试网络模型,输出图片的类别

LeNet训练Cifar-10数据集代码详解以及输出结果_第5张图片

下面是预测模块的代码

#predict 就是调用模型权重进行预测的脚本
import torch
import torchvision.transforms as transforms
from PIL import Image

from model import LeNet


def main():
    transform = transforms.Compose(
        [transforms.Resize((32, 32)),#将下载的图片转换为32*32大小  因为LeNet模型里面就是32*32大小
         transforms.ToTensor(),#totensor 转换为c h w
         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] unsqueeze函数的作用是增加一个新的维度batch

    with torch.no_grad():#不需要求损失梯度
        outputs = net(im)
        predict = torch.max(outputs, dim=1)[1].numpy()#可能性最高的类别索引
    print(classes[int(predict)])#索引对应的类别


if __name__ == '__main__':
    main()

预测结果

LeNet训练Cifar-10数据集代码详解以及输出结果_第6张图片

你可能感兴趣的:(机器学习,深度学习,人工智能)