PyTorch 实战:使用卷积神经网络对照片进行分类

栏目: 编程工具 · 发布时间: 5年前

PyTorch 实战:使用卷积神经网络对照片进行分类

本文任务

我们接下来需要用CIFAR-10数据集进行分类,步骤如下:

  1. 使用torchvision 加载并预处理CIFAR-10数据集

  2. 定义网络

  3. 定义损失函数和优化器

  4. 训练网络并更新网络参数

  5. 测试网络

对卷积不了解的同学建议先阅读

10分钟理解深度学习中的~卷积~

conv2d处理的数据是什么样的?

注意:文章末尾含有项目jupyter notebook实战教程下载可供大家课后实战操作

一、CIFAR-10数据加载及预处理

CIFAR-10 是一个常用的彩色图片数据集,它有 10 个类别,分别是 airplane、automobile、bird、cat、deer、dog、frog、horse、ship和 truck 。每张图片都是 3*32*32 ,也就是 三通道彩色图片,分辨率 32*32

import torchvision as tv
import torchvision.transforms as transforms
from torchvision.transforms import ToPILImage
import torch as t

#可以把Tensor转化为Image,方便可视化
show = ToPILImage()

#先伪造一个图片的Tensor,用ToPILImage显示
fake_img = t.randn(3, 32, 32)

#显示图片
show(fake_img)

第一次运行torchvision会自动下载CIFAR-10数据集,大约163M。这里我将数据直接放到项目 data文件夹 中。

cifar_dataset = tv.datasets.CIFAR10(root='data',
                                    train=True,
                                    download=True
                                  )

imgdata, label = cifar_dataset[90]
print('label: ', label)
print('imgdata的类型:',type(imgdata))
imgdata  

运行结果

Files already downloaded and verified
label:  2
imgdata的类型: <class 'PIL.Image.Image'>

注意,数据集中的照片数据是以 PIL.Image.Image类 形式存储的,在我们加载数据时,要注意将其转化为 Tensor类

def dataloader(train):

    transformer = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize(mean=(0.5, 0.5, 0.5),
                             std = (0.5, 0.5, 0.5))
    ])

    cifar_dataset = tv.datasets.CIFAR10(root='data',  #下载的数据集所在的位置
                                        train=train,  #是否为训练集。
                                        download=True, #设置为True,不用再重新下载数据
                                        transform=transformer
                                  )

    loader = t.utils.data.DataLoader(
        cifar_dataset,
        batch_size=4, 
        shuffle=True, #打乱顺序
        num_workers=2 #worker数为2
    )

    return loader

classes=('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

#训练集和测试集的加载器
trainloader = dataloader(train=True)
testloader = dataloader(train=False)

运行结果

Files already downloaded and verified
Files already downloaded and verified

DataLoader是一个可迭代的对象,它将dataset返回的每一条数据样本拼接成一个batch,并提供多线程加速优化和数据打乱等操作。当程序对 cirfar_dataset 的所有数据遍历完一遍, 对Dataloader也完成了一次迭代。

dataiter = iter(trainloader)

#返回四张照片及其label
images, labels = dataiter.next()

#打印多张照片
show(tv.utils.make_grid(images))

PyTorch 实战:使用卷积神经网络对照片进行分类

#显示images中的第三张照片
show(images[2])

二、定义网络

最早的卷积神经网络LeNet为例,学习卷积神经网络。

PyTorch 实战:使用卷积神经网络对照片进行分类

2.1 第一个convolutions层

图中显示是单通道照片,但是由于我们的数据集中的照片是三通道照片。所以

该层输入的是 三通道图片 ,图片长宽均为32,那么通过kernel_size=5的卷积核卷积后的尺寸为(32-5+1)=28

同时要注意,第一个convolution中,图片由 三通道变为6通道 , 所以在此卷积过程中,in_channels=3, out_channels=6

nn.Conv2d(in_channels=3, 
          out_channels=6, 
          kernel_size=5)

2.2 第一subsampling层

该层输入数据是6通道,输出还为6通道,但是图片的长宽从28变为14,我们可以使用池化层来实现尺寸缩小一倍。这里我们使用MaxPool2d(2, 2)

nn.MaxPool2d(kernel_size=2,
             stride=2)

2.3 第二个convolutions层

该层输入的是6通道数据,输出为16通道数据,且图片长宽从14变为10。这里我们使用

nn.Conv2d(in_channels=6,
          out_channels=16,
          kernel_size=5)

2.4 全连接层作用

在此之前的卷积层和池化层都属于特征工程层,用于从数据中抽取特征。而之后的多个全连接层,功能类似于机器学习中的模型,用于学习特征数据中的规律,并输出预测结果。

2.5 第一全连接层full connection

第二个convolutions层输出的 数据形状为 (16, 5, 5) 的数组 ,是一个三维数据。

而在全连接层中,我们需要将其 展平为一个一维数据(样子类似于列表,长度为16\*5\*5)

nn.Linear(in_features=16*5*5,
          out_features=120) #根据图中,该输出为120

2.6 第二全连接层

该层的输入是一维数组,长度为120,输出为一维数组,长度为84.

nn.Linear(in_features=120,
          out_features=84) #根据图中,该输出为84

2.7 第三全连接层

该层的输入是一维数组,长度为84,输出为一维数组,长度为10,该层网络定义如下

nn.Linear(in_features=84,
          out_features=10) #根据图中,该输出为10

注意:

这里的长度10的列表,可以看做输出的label序列。例如理想情况下

output = [1, 0, 0, 0, 0, 0, 0 ,0, 0 ,0]

该output表示 input数据 经过该神经网络运算得到的 预测结果 显示的 类别是 第一类

同理,理想情况下

output2 = [0, 1, 0, 0, 0, 0, 0 ,0, 0 ,0]

该output2表示 input数据 经过该神经网络运算得到的 预测结果 显示的 类别是 第二类

根据前面对LeNet网络的解读,现在我们用pytorch来定义LeNet网络结构

import torch
import torch.nn as nn

class LeNet(nn.Module):

    def __init__(self):
        #Net继承nn.Module类,这里初始化调用Module中的一些方法和属性
        nn.Module.__init__(self) 

        #定义特征工程网络层,用于从输入数据中进行抽象提取特征
        self.feature_engineering = nn.Sequential(
            nn.Conv2d(in_channels=3,
                      out_channels=6,
                      kernel_size=5),


            #kernel_size=2, stride=2,正好可以将图片长宽尺寸缩小为原来的一半
            nn.MaxPool2d(kernel_size=2,
                         stride=2),

            nn.Conv2d(in_channels=6,
                      out_channels=16,
                      kernel_size=5),


            nn.MaxPool2d(kernel_size=2,
                        stride=2)
        )

        #分类器层,将self.feature_engineering中的输出的数据进行拟合
        self.classifier = nn.Sequential(
            nn.Linear(in_features=16*5*5,
                      out_features=120),


            nn.Linear(in_features=120,
                      out_features=84),


            nn.Linear(in_features=84,
                      out_features=10),

        )



    def forward(self, x):
        #在Net中改写nn.Module中的forward方法。
        #这里定义的forward不是调用,我们可以理解成数据流的方向,给net输入数据inpput会按照forward提示的流程进行处理和操作并输出数据
        x = self.feature_engineering(x)
        x = x.view(-1, 16*5*5)
        x = self.classifier(x)
        return x

实例化神经网络LeNet

net = LeNet()
net

运行结果

LeNet(
  (feature_engineering): Sequential(
    (0): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
    (1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (classifier): Sequential(
    (0): Linear(in_features=400, out_features=120, bias=True)
    (1): Linear(in_features=120, out_features=84, bias=True)
    (2): Linear(in_features=84, out_features=10, bias=True)
  )
)

我们随机传入一批照片(batch_size=4) ,将其输入给net,看输出的结果是什么情况。

注意:

pytorch中输入的数据必须是batch数据(批数据)

dataiter = iter(trainloader)

#返回四张照片及其label
images, labels = dataiter.next()

outputs = net(images)

outputs

运行结果

tensor([[ 0.1963,  0.0203,  0.0887, -0.0789, -0.0027, -0.0429, -0.1119,  0.0080,
          0.0007, -0.0901],
        [ 0.2260,  0.0246,  0.0498, -0.0188,  0.0207, -0.0541, -0.0943,  0.0431,
         -0.0204, -0.1023],
        [ 0.2168,  0.0280,  0.0463, -0.0055, -0.0017, -0.0504, -0.0897,  0.0385,
         -0.0229, -0.1030],
        [ 0.2025,  0.0579,  0.0527, -0.0038, -0.0300, -0.0474, -0.0952,  0.0698,
         -0.0145, -0.0620]], grad_fn=<ThAddmmBackward>)

t.max(input, dim)

  • input:传入的tensor

  • dim: tensor的方向。dim=1表示按照行方向计算最大值

t.max(outputs, dim=1)

运行结果

(tensor([0.1963, 0.2260, 0.2168, 0.2025], grad_fn=<MaxBackward0>),
 tensor([0, 0, 0, 0]))

上述的操作,找到了outputs中四个最大的值,及其对应的index(该index可以理解为label)

三、定义损失函数和优化器

神经网络强大之处就在于 反向传播 ,通过比较 预测结果真实结果 , 修整 网络参数

这里的 比较 就是 损失函数 ,而 修整网络参数 就是 优化器

这样充分利用了每个训练数据,使得网络的拟合和预测能力大大提高。

from torch import optim

#定义交叉熵损失函数
criterion = nn.CrossEntropyLoss()

#随机梯度下降SGD优化器
optimizer = optim.SGD(params = net.parameters(),
                      lr = 0.001) 

四、训练网络

所有网络的训练的流程都是类似的,不断执行(轮):

  • 给网络输入数据

  • 前向传播+反向传播

  • 更新网络参数

遍历完一遍数据集称为一个epoch,这里我们进行 2个epoch 轮次的训练。

epochs = 10

average_loss_series = []

for epoch in range(epochs):

    running_loss = 0.0

    for i, data in enumerate(trainloader):
        inputs, labels = data
        #inputs, labels = Variable(inputs), Variable(labels)

        #梯度清零
        optimizer.zero_grad()

        #forward+backward
        outputs = net(inputs)

        #对比预测结果和labels,计算loss
        loss = criterion(outputs, labels)

        #反向传播
        loss.backward()

        #更新参数
        optimizer.step()

        #打印log  
        running_loss += loss.item()
        if i % 2000 == 1999: #每2000个batch打印一次训练状态
            average_loss = running_loss/2000
            print("[{0},{1}] loss:  {2}".format(epoch+1, i+1, average_loss))
            average_loss_series.append(average_loss)
            running_loss = 0.0

运行结果

[1,2000] loss:  2.284719424366951
[1,4000] loss:  2.1300598658323286
[1,6000] loss:  2.0143098856806754
[1,8000] loss:  1.9478365245759488
[1,10000] loss:  1.9135449583530426
[1,12000] loss:  1.8653237966001033
[2,2000] loss:  1.8014366626143457
[2,4000] loss:  1.737443323969841
[2,6000] loss:  1.6933535016775132
[2,8000] loss:  1.6476907352507115
[2,10000] loss:  1.6234023304879666
[2,12000] loss:  1.5863604183495044
[3,2000] loss:  1.5544855180978776
[3,4000] loss:  1.539060534775257
[3,6000] loss:  1.5500386973917484
[3,8000] loss:  1.5407403408288955
[3,10000] loss:  1.493699783280492
[3,12000] loss:  1.4957395897060632
[4,2000] loss:  1.4730096785128117
[4,4000] loss:  1.4749664356559515
[4,6000] loss:  1.4479290856420994
[4,8000] loss:  1.445657522082329
[4,10000] loss:  1.4586472637057304
[4,12000] loss:  1.4320134285390378
[5,2000] loss:  1.406113230422139
[5,4000] loss:  1.4196837954670192
[5,6000] loss:  1.3951636335104705
[5,8000] loss:  1.3933502195328473
[5,10000] loss:  1.3908299638181925
[5,12000] loss:  1.3908768535405398
[6,2000] loss:  1.3397984126955271
[6,4000] loss:  1.3737898395806551
[6,6000] loss:  1.360704499706626
[6,8000] loss:  1.3652801268100738
[6,10000] loss:  1.334371616870165
[6,12000] loss:  1.312294240474701
[7,2000] loss:  1.3097571679353714
[7,4000] loss:  1.3236577164530754
[7,6000] loss:  1.310647354334593
[7,8000] loss:  1.3016219032108785
[7,10000] loss:  1.2931814943552018
[7,12000] loss:  1.2910259604007006
[8,2000] loss:  1.2796987656354903
[8,4000] loss:  1.2650054657310248
[8,6000] loss:  1.2713083022236824
[8,8000] loss:  1.258927255064249
[8,10000] loss:  1.275728213787079
[8,12000] loss:  1.2612977192252874
[9,2000] loss:  1.2273035216629504
[9,4000] loss:  1.25000972096622
[9,6000] loss:  1.2236297953873874
[9,8000] loss:  1.2251979489773512
[9,10000] loss:  1.2623697004914283
[9,12000] loss:  1.2501848887503146
[10,2000] loss:  1.2257770787626505
[10,4000] loss:  1.2277075409144163
[10,6000] loss:  1.2050671626776457
[10,8000] loss:  1.2159633481949568
[10,10000] loss:  1.210464821562171
[10,12000] loss:  1.2225491935014725

五、测试网络

5.1 打印误差曲线

%matplotlib inline
import matplotlib.pyplot as plt

x = range(0, 60)
plt.figure()
plt.plot(x, average_loss_series)

PyTorch 实战:使用卷积神经网络对照片进行分类

5.2 查看训练的准确率

我们使用测试集检验训练的神经网络的性能。

def correct_rate(net, testloader):
    correct = 0
    total = 0

    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = t.max(outputs.data, 1)
        total += labels.size(0) 
        correct += (predicted==labels).sum()

    return 100*correct/total

correct = correct_rate(net, testloader)
print('10000张测试集中准确率为: {}%'.format(correct))

运行结果

10000张测试集中准确率为: 57%

数据集一共有10种照片,且每种照片数量相等。所以理论上,我们猜测对每一张照片的概率为10%。

而通过我们神经网络LeNet预测的准确率达到 57% ,证明网络确实学习到了规律。

往期文章

《用 Python 做文本分析》视频教程  

大邓强力推荐-jupyter notebook使用小技巧   

10分钟理解深度学习中的~卷积~

深度学习之 图解LSTM

Pytorch实战:使用RNN网络对姓名进行分类  

100G Python学习资料(免费下载)

100G 文本分析语料资源(免费下载)     

typing库:让你的代码阅读者再也不用猜猜猜

Seaborn官方教程中文教程(一)

数据清洗 常用正则表达式大全

PySimpleGUI: 开发自己第一个软件

深度特征合成:自动生成机器学习中的特征

Python 3.7中dataclass的终极指南(一)

Python 3.7中dataclass的终极指南(二)

15个最好的数据科学领域Python库

使用Pandas更好的做数据科学

[计算消费者的偏好]推荐系统与协同过滤、奇异值分解

机器学习: 识别图片中的数字

应用PCA降维加速模型训练

如何从文本中提取特征信息?

使用sklearn做自然语言处理-1

使用sklearn做自然语言处理-2

机器学习|八大步骤解决90%的NLP问题      

Python圈中的符号计算库-Sympy

Python中处理日期时间库的使用方法  

视频讲解】Scrapy递归抓取简书用户信息

美团商家信息采集神器

用chardect库解决网页乱码问题

PyTorch 实战:使用卷积神经网络对照片进行分类


以上所述就是小编给大家介绍的《PyTorch 实战:使用卷积神经网络对照片进行分类》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Transcending CSS

Transcending CSS

Andy Clarke、Molly E. Holzschlag / New Riders / November 15, 2006 / $49.99

As the Web evolves to incorporate new standards and the latest browsers offer new possibilities for creative design, the art of creating Web sites is also changing. Few Web designers are experienced p......一起来看看 《Transcending CSS》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具