本文任务
我们接下来需要用CIFAR-10数据集进行分类,步骤如下:
-
使用torchvision 加载并预处理CIFAR-10数据集
-
定义网络
-
定义损失函数和优化器
-
训练网络并更新网络参数
-
测试网络
对卷积不了解的同学建议先阅读
注意:文章末尾含有项目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))
#显示images中的第三张照片
show(images[2])
二、定义网络
最早的卷积神经网络LeNet为例,学习卷积神经网络。
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)
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%
,证明网络确实学习到了规律。
往期文章
以上所述就是小编给大家介绍的《PyTorch 实战:使用卷积神经网络对照片进行分类》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
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》 这本书的介绍吧!