Oct 19, 2018 原创文章

  Hello Pytorch 零 -- 搭建年轻人的第一个神经网络:LeNet

使用 Pytorch 搭建 LeNet 网络,实现对CIFAR-10 数据集的分类

分享到: 0

请保证您的浏览器支持MathJax插件,以免数学公式无法显示


经典网络模型:LeNet

LeNet 这一经典网络模型由LeCun 于1988年提出,发表在论文《Gradient-Based Learning Applied to Document Recognition》中。最初是用来进行手写字体识别,现已经被扩展到多个应用场景中。这里使用LeNet网络,实现对CIFAR-10数据集的分类识别。

网络结构

LeCun在论文中提出网络结构

在这个网络中,使用了三个卷积层,一个全连接层和两个下采样层。

输入:

网络的输入是一个32×32的单通道图片。

卷积层:

网络中使用了两个卷积层,C1、C3和C5。

在C1中,卷积核的大小为5,输出通道数设置为6,因此这一层得到的计算结果为6个28×28的特征映射(feature map)。连接数为(5×5+1)×6×28×28 = 122304,参数数量为(5×5+1)×6=156。

在C3中,卷积核的大小为5,输出通道数设置为16,因此这一层得到的计算结果为16个10×10的特征映射。连接数为(5x5x3+1)x6+(5x5x4+1)x9+(5x5x6+1)x10x10=151600。参数数量为(5x5x3+1)x6+(5x5x4+1)x9+(5x5x6+1)=1516。

在C5中,卷积核的大小为5,输出通道数设置为120,因此这一层得到的计算结果为120个1×1的特征映射。参数数量为120x(5x5x16+1)=48120。

全连接层:

网络中使用了一个全连接层F6,其输入大小为120,输出为84。共有(120+1)×84个连接,其中+1为偏置。

下采样(池化)层:

网络中使用了两个下采样层,分别为S2和S4,这里是通过最大池化实现下采样,池化大小为2×2。对于S2,连接数为6x14x14x(2x2+1)=5880,参数数量为6x(1+1)=12。对于S4,连接数为5x5x16x(2x2+1)=2000,参数数量为16x2=32。

池化的过程:首先对输入的图像进行分块,根据池化大小则每个块的大小为2×2,然后将每个块中的最大的值作为下采样的新像素。

输出层:

网络中的输出层共有10个节点,分别代表数字0到9。如果第i个节点的值为0,则表示网络识别的结果是数字i。

理解网络

如果将整个视为一个函数,其输入是一个大小为32×32的图像,输出为一个具有10个元素的向量。通过对比向量中元素的大小确定预测分类。

CIFAR-10训练集

CIFAR-10 是一个常用的彩色图片数据集,拥有十个类别,分别为’plane’,’car’,’bird’,’cat’,’deer’,’dog’,’frog’,’horse’,’ship’,’truck’。数据集中每个图片的大小都是3×32×32,其中的图片数为6000个,在这6000个图片中,有5000个是训练集,1000个测试集。

下载地址:https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz

搭建网络

在原始的LeNet中,输入数据是单通道32×32的图像,而CIFAR-10数据集中的样本是三通道32×32。因此需要对LeNet进行简单的修改。C1的输入改为3×32×32即可得到所需的网络。

代码示例

导入所需的包

import torch as t
from torch import optim

# 导入torchvision 包
import torchvision as tv
import torchvision.transforms as transforms
from torchvision.transforms import ToPILImage

# 导入torch.nn 包
import torch.nn as nn

# 导入import torch.nn.functional  包
import torch.nn.functional as F

torch.optim

torch.optim 包中封装了多种常用的优化算法

参见:https://pytorch.org/docs/stable/optim.html

torchvision

torchvision 包中封装了各种计算机视觉领域常用的数据集、模型结构和图像变换

参见:https://pytorch.org/docs/stable/torchvision/index.html

torchvision.transforms

torchvision.transforms 包中封装了多种图像变换算法,使用 torchvision.transforms.Compose 可以将多种变换组合使用。

参见:https://pytorch.org/docs/stable/torchvision/transforms.html

torch.nn

torch.nn 包中封装了多种神经网络的结构

参见:https://pytorch.org/docs/stable/nn.html

torch.nn.functional

torch.nn.functional 包中封装了网络层结构

参见:https://pytorch.org/docs/stable/nn.html#torch-nn-functional

定义对数据的预处理

transform = transforms.Compose([
        transforms.ToTensor(),                          # 转为 Tensor
        transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5)),# 归一化
])

class torchvision.transforms.ToTensor()

将PIL Image 或 numpy.ndarray 转换 tensor、

参见:https://pytorch.org/docs/stable/torchvision/transforms.html#torchvision.transforms.ToTensor

class torchvision.transforms.Normalize(mean, std)

用于对数据的所有通道逐一进行归一化处理。

参见:https://pytorch.org/docs/stable/torchvision/transforms.html#torchvision.transforms.Normalize

定义训练集


trainset = tv.datasets.CIFAR10(root='/cifar10/',
                                   train = True,
                                   download = False,
                                   # 建议提前将数据集下载好,放入根目录下
                                   # 否则这里应设置为True
                                   transform = transform
                                  )

trainLoader = t.utils.data.DataLoader(
                                    trainset,
                                    batch_size=4,
                                    shuffle=True,
                                    num_workers=2
    )

class torchvision.datasets.CIFAR10(root, train, transform, target_transform download)

使用CIFAR-10数据集。

参见:https://pytorch.org/docs/stable/torchvision/datasets.html#torchvision.datasets.CIFAR10

class torch.utils.data.DataLoader(dataset, batch_size, shuffle, sampler, batch_sampler, num_workers, collate_fn, pin_memory, drop_last, timeout, worker_init_fn)

设置训练集的数据加载器。

1、设置为训练集

2、设置每个batch有4个样本数据

3、设置对每个epoch将数据集打乱

3、设置使用2个子进程用来加载数据

参见 :https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader

定义测试集

testset = tv.datasets.CIFAR10(root='/cifar10/',
                                   train = False,
                                   download = False,# 同上
                                   transform = transform
                                  )

testLoader = t.utils.data.DataLoader(
                                    testset,
                                    batch_size=4,
                                    shuffle=False,
                                    num_workers=2
    )

同上一部分相同

定义网络

class Net(nn.Module):
    # 定义网络结构
    def __init__(self):
        super(Net,self).__init__()
        self.conv1 = nn.Conv2d(3,6,5)    # 卷积层C1
        self.conv2 = nn.Conv2d(6,16,5)   # 卷积层C3
        self.fc1 = nn.Linear(16*5*5,120) # 全连接层S2
        self.fc2 = nn.Linear(120,84)     # 全连接层S4
        self.fc3 = nn.Linear(84,10)      # 全连接层OUTPUT   
    
    # 定义前向传播函数   
    def forward(self,x):
        x = F.max_pool2d(F.relu(self.conv1(x)),(2,2))
        x = F.max_pool2d(F.relu(self.conv2(x)),2)
        x = x.view(x.size()[0],-1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        
        return x


net = Net()

定义损失函数和优化器

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(
                    net.parameters(),
                    lr = 0.001,
                    momentum=0.9)

class torch.nn.CrossEntropyLoss(weight, size_average, ignore_index, reduce, reduction)

将损失函数设置为Cross-Entropy 函数

参见:https://pytorch.org/docs/stable/nn.html#torch.nn.CrossEntropyLoss

class torch.optim.SGD(params, lr, momentum=0, dampening=0, weight_decay=0, nesterov)

使用螺旋梯度下降。

参见:https://pytorch.org/docs/stable/optim.html#torch.optim.SGD

训练网络

for epoch in range(70): # 设置训练的迭代次数
    running_loss = 0.0
    for i,data in enumerate(trainLoader,0):
        # 输入数据
        inputs,labels = data
        # 梯度清零
        optimizer.zero_grad()
        # forward + backward
        outputs = net(inputs)
        # 计算损失值
        loss = criterion(outputs,labels)
        loss.backward()
        # 更新参数
        optimizer.step()
        # 打印log信息
        running_loss += loss.data[0]
        if i%2000 == 1999:
            print('[%d,%5d] loss:%.3f' %(epoch+1,i+1,running_loss/2000))
            running_loss = 0.0
            
print('Finished Training')

测试效果

# 计算图片在每个类别上的分数
dataiter = iter(testLoader)
images, labels = dataiter.next() # 一个batch返回4张图片
outputs = net(images)
# 得分最高的那个类
_, predicted = t.max(outputs.data, 1)

print('预测结果: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))

在测试集中测试准确率

correct = 0 # 预测正确的图片数
total = 0 # 总共的图片数


# 由于测试的时候不需要求导,可以暂时关闭autograd,提高速度,节约内存
with t.no_grad():

    # 在测试集中迭代数据
    for data in testLoader:
        # 读取数据的数据内容和标签
        images, labels = data
        # 得到网络的输出
        outputs = net(images)
        # 得到预测值
        _, predicted = t.max(outputs, 1)
        # 累积计算数据集的大小
        total += labels.size(0)
        # 累积计算预测正确的数据集的大小
        correct += (predicted == labels).sum()

print('10000张测试集中的准确率为: %d %%' % (100 * correct / total))


打赏


感谢您的支持,我会继续努力的!

扫码支持

长按识别二维码或打开支付宝扫一扫 完成打赏
或 使用<支付宝链接>打赏


关闭
相关文章:
Hello Pytorch 肆 -- 激活函数 # , , Nov 02, 2018 原创文章
Hello Pytorch 叁 -- 简单理解生成对抗网络(GAN) # , , , Oct 29, 2018 原创文章
Hello Pytorch 贰 -- 常用损失函数 # , , , Oct 20, 2018 原创文章
Hello Pytorch 壹 -- 卷积层原理及实现 # , , Oct 20, 2018 原创文章

分享到: 0