Pytorch 官方教程 1
Training an image classifier
- 用
torchvision
模块加载和归一化CIFAR10数据集的图片数据 - 定义一个卷积神经网络
- 定义损失函数
- 训练集上训练网络
- 在测试集上测试网络
import torch
import torchvision
import torchvision.transforms as transforms
######################### 1. 用`torchvision`模块加载和归一化CIFAR10数据集的图片数据######################################
######################################################################################################################
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # 返回transforms.Compose 类
#可以接纳任何数量的参数
#参数是各种transforms
#将各种trasnsform组合到一起(此处为 :转化为tensor+归一化
#用于后面的加载CIFAR10数据集的函数的参数
#对于transform.Normalize()下篇文章我会详述
#这里暂且认为就是归一化输入数据
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform) #返回CIFAR10类,可以通过下面的DataLoader得到访问的接口
#参数root 数据集下载到本地的路径
#参数train = true下载的是CIFAR10数据集中的训练集
#参数transform 将下载的原始的训练集如何转化
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2) #返回DateLoader类,是访问trainset的接口
#参数 1 ‘trainset’指定是访问哪个数据集
#参数 batch_size=4,指定batch即一次访问4张图片
#参数 shuffle=True ,指访问前将数据集洗牌
#参数 num_workers=2.指定多少个子进程会访问这个数据集,只有一个主进程可以设置为0(通常)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform) #同上 只是下载的是测试集
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck') #创建tuple ,注:本程序使用的CIFAR10数据集包含10种类别的图片;即tuple中的10种。
已经加载好数据集下面我们来看一看数据集中测试集的一些图片
import matplotlib.pyplot as plt
import numpy as np
# functions to show an image
def imshow(img):
img = img / 2 + 0.5 # unnormalize 以显示数据集中的原图
npimg = img.numpy() #将Tensor形式的图片转化为numpy格式
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
# get some random training images
dataiter = iter(trainloader) #得到数据集的迭代器
images, labels = dataiter.next() #获取一份数据
# show images
imshow(torchvision.utils.make_grid(images)) #由于上面的加载数据的batch=4 ,一次获得四张图片
#torchvision.utils.make_grid()函数作用是将积分图拼接为一个网格图
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
输出为:
################################################## 2. 定义一个卷积神经网络########################################################
################################################################################################################################
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
class Net(nn.Module): #网络描述类继承于'nn.Module'类
def __init__(self): #构造函数
super(Net, self).__init__() #调用父类构造函数
self.conv1 = nn.Conv2d(3, 6, 5) #网络第一层为卷积层
#输入图片的通道为 3
#输出通道为 6 即共有6个卷积核
#卷积核大小为 5*5*3
#所以此处有需要训练的参数个数为:5*5*3*6=450个(不包括b参数的个数)
self.pool = nn.MaxPool2d(2, 2) #池化层
#最大值池化
#池化核大小为2*2
self.conv2 = nn.Conv2d(6, 16, 5) # 网络第二层卷积层
#上一层的输出为6通道,所以这一层的输入为6通道
#输出通道数为16 即共有16个卷积核
#卷积核的大小为 5*5*6
#所以此处有需要训练的参数个数为:5*5*6*16=2400个(不包括b参数的个数)
self.fc1 = nn.Linear(16 * 5 * 5, 120) #网络第三层 全连接层
#输入向量 为 16*5*5(16为上一层的输出的通道数,5*5为图片大小)
#输出向量 为 120 即含有120个神经元
#此处参数个数为 : 16*5*5*120=48000个(不包括b参数的个数)
self.fc2 = nn.Linear(120, 84) #网络第四成层 全连接层
#输入向量120
#输出向量 84
#参数个数 120*84=10080个(不包括b参数的个数)
self.fc3 = nn.Linear(84, 10) #网络第5层 全连接层(softMax)
#输入向量 84
# 输出向量10 即十种类别的可能性
#参数个数 10*84=840个(不包括b参数的个数)
def forward(self, x): #前向传播函数
x = self.pool(F.relu(self.conv1(x))) #第一层采用Relu激活函数
x = self.pool(F.relu(self.conv2(x))) #第二层采用Relu激活函数
x = x.view(-1, 16 * 5 * 5) #view()相当于reshape()操作,将维度于全连接层1的维度相匹配
x = F.relu(self.fc1(x)) #第三层采用Relu激活函数
x = F.relu(self.fc2(x)) #第四层采用Relu激活函数
x = self.fc3(x) #第五层采用恒等激活函数
return x
net = Net() #创建 Net类的对象net
############################################# 3. 定义损失函数###################################################################
###############################################################################################################################
criterion = nn.CrossEntropyLoss() #损失函数为 交叉熵 函数 因为我们做的是分类任务
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) #选取优化方法:带动量的随机梯度下降
############################################# 4. 训练集上训练网################################################################
###############################################################################################################################
for epoch in range(2): # 每次训练遍历两次训练集
running_loss = 0.0 #记录损失函数的值
for i, data in enumerate(trainloader, 0): #遍历训练集
#trainloader是迭代器
#enumerate 还会返回迭代的次数 i
inputs, labels = data #input 为一个mini_batch
#labels为对应的标签
optimizer.zero_grad() #将梯度清零,不然每次训练得到的梯度都会叠加
outputs = net(inputs) #完成前向传播
loss = criterion(outputs, labels) #计算损失值
loss.backward() #反向传播
optimizer.step() #跟新参数
running_loss += loss.item() # running_loss=running_loss+loss.item() 将损失叠加
if i % 2000 == 1999:
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000)) #每2000个损失值求平均输出
running_loss = 0.0
print('Finished Training')
我们来看下损失值得输出 ,由于训练过程真的很慢 (要多次执行训练)。。。输出很多,节选了其中几次
[1, 2000] loss: 0.776
[1, 4000] loss: 0.801
[1, 6000] loss: 0.822
[1, 8000] loss: 0.804
[1, 10000] loss: 0.846
[1, 12000] loss: 0.861
[2, 2000] loss: 0.744
[2, 4000] loss: 0.774
[2, 6000] loss: 0.790
[2, 8000] loss: 0.792
[2, 10000] loss: 0.814
[2, 12000] loss: 0.827
[1, 2000] loss: 0.630
[1, 4000] loss: 0.661
[1, 6000] loss: 0.683
[1, 8000] loss: 0.722
[1, 10000] loss: 0.723
[1, 12000] loss: 0.746
[2, 2000] loss: 0.616
[2, 4000] loss: 0.660
[2, 6000] loss: 0.696
[2, 8000] loss: 0.710
[2, 10000] loss: 0.698
[2, 12000] loss: 0.694
接下里我们将训练好得网络保存起来,以便后面调用
PATH = './cifar_net.pth' #保存路径
torch.save(net.state_dict(), PATH) #保存模型,这只是将模型得一些参数字典进行保存并不是保存整个模型
#本文的字典模型如下 后面输出的是Size
#conv1.weight torch.Size([6, 3, 5, 5])
#conv1.bias torch.Size([6])
#conv2.weight torch.Size([16, 6, 5, 5])
#conv2.bias torch.Size([16])
#fc1.weight torch.Size([120, 400])
#fc1.bias torch.Size([120])
#fc2.weight torch.Size([84, 120])
#fc2.bias torch.Size([84])
#fc3.weight torch.Size([10, 84])
# fc3.bias torch.Size([10])
现在我们已经将训练好得模型保存起来了,现在我们要加载我们得模型然后在测试集上进行评估模型训练得怎么样
###############################################先输出四个测试集的图片(这四个图片后面会放入到模型中去测试)#######################
dataiter = iter(testloader)
images, labels = dataiter.next()
# print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
输出
GroundTruth: cat ship ship plane
############################################### 5. 在测试集上测试网络#########################################################
net = Net() #初始化网络
net.load_state_dict(torch.load(PATH)) #加载保存的参数
outputs = net(images) #前向传播进行预测
_, predicted = torch.max(outputs, 1) #torch.max()返回 两个tensor,第一个是最大值,第二个表示最大值的位置
#参数 1 outputs 寻找最大值的对象
#参数2 dim=1 指定按照行寻找
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
for j in range(4)))
Predicted: cat ship ship plane
从预测的结果可以看到我们的网络的训练已经很好了,准确率非常好,这四个测试集的数据全部都预测正确了