本文整理总结自博客用portorch训练自己的数据集,在pytorch官网例程的基础上将自己的数据放到其模型下,实现一个识别手写数字的简易分类器。
1.环境配置及模块导入:
首先配置pytorch的运行环境,然后导入各种模块。
import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import torch.optim as optim
import matplotlib.pyplot as plt
import numpy as np
2.处理自定义数据集
(1)处理训练集
首先调用ImageFolder()函数处理训练数据。
将我们要训练的数据放在一个路径为path的文件夹下,假如这个文件夹名为train。在我们的train文件夹下有N个子文件夹,每个子文件夹代表一个分类,一共有N类。
transforms.Compose()的功能是合并几个transform语句。
transforms.Resize()的功能是缩放图片的尺寸。Resize()函数有两种参数设置方式,第一种是Resize(h,w),将图片缩放到(h,w)大小,这种方式会改变图像的长宽比;第二种是Resize(h),这种方式不改变图像的长宽比,使图片经过缩放后最短的边长度等于传入的参数h。
transforms.CenterCrop(size)的功能是依据给定的size从中心裁剪。
transforms.ToTensor()的功能是转换数据类型,使得到的自定义数据集trainset和官网例程中的trainset格式相同。
def loadtraindata():
# 设置路径
path = r"/home/********/folder/train"
# 将图片缩放到指定大小
trainset = torchvision.datasets.ImageFolder(path,transform=transforms.Compose([
transforms.Resize((32, 32)),
transforms.CenterCrop(32),
transforms.ToTensor()]))
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,shuffle=True,
num_workers=2)
return trainloader
本文用到的模型结构完全照搬官网例程,因为全连接层是固定尺寸的输入输出,所以trainset的图像大小也设置为与例程相同的32×32。如果需要其他尺寸的输入需要修改网络模型的内部结构。
(2)处理测试集
测试集和训练集相似,这里将batch_size改成了25,在测试阶段每次预测25张图片并以5×5的排列显示。
def loadtestdata():
path = r"/home/********/folder/test"
testset = torchvision.datasets.ImageFolder(path,transform=transforms.Compose([
transforms.Resize((32, 32)),
transforms.ToTensor()])
)
testloader = torch.utils.data.DataLoader(testset, batch_size=25,
shuffle=True, num_workers=2)
return testloader
3.定义网络结构
本文用到的模型结构完全照搬官网例程,注意在这里输出是10种分类,与例程保持一致。如果分类数不同需要修改网络结构的参数。
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5) # 卷积层
self.pool = nn.MaxPool2d(2, 2) # 池化层
self.conv2 = nn.Conv2d(6, 16, 5) # 卷积层
self.fc1 = nn.Linear(16 * 5 * 5, 120) # 全连接层
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10) # 10个输出
def forward(self, x): # 前向传播
x = self.pool(F.relu(self.conv1(x))) # F就是torch.nn.functional
x = self.pool(F.relu(self.conv2(x)))
# .view( )是一个tensor方法,使得tensor改变size但是元素的总数保持,实现从卷基层到全连接层的维度转换
x = x.view(-1, 16 * 5 * 5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return
本文数字识别的分类情况为classes = ('0','1', '2', '3', '4','5', '6', '7', '8', '9'),分类数为10类,与例程相同。
4.训练网络
用trainandsave()函数来训练CNN的参数并用torch.save保存其参数,保存后参数文件.pkl会放在py文件的同一个路径下。
如果只进行训练阶段,那么只需要执行trainandsave()函数。
def trainandsave():
trainloader = loadtraindata()
# 【1】定义神经网络结构
net = Net()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) # 学习率为0.001
criterion = nn.CrossEntropyLoss() # 损失函数(交叉熵)
# 【2】训练部分
# 训练的数据量为5个epoch,每个epoch为一个循环
for epoch in range(5):
# 每个epoch训练所有图片,每训练完成200张打印一次训练损失值查看训练效果
# 定义一个变量方便对loss进行输出
running_loss = 0.0
# 这里用到trailoader,代码传入数据
# enumerate是python的内置函数,既获得索引也获得数据
for i, data in enumerate(trainloader, 0):
# data是从enumerate返回的data,包含数据和标签信息,分别赋值给inputs和labels
inputs, labels = data
# 转换数据格式用Variable
inputs, labels = Variable(inputs), Variable(labels)
# 梯度置零,因为反向传播过程中梯度会累加上一次循环的梯度
optimizer.zero_grad()
# 把数据输进网络net
outputs = net(inputs)
# 计算损失值
loss = criterion(outputs, labels)
# loss反向传播
loss.backward()
# 反向传播后参数更新
optimizer.step()
# 损失累加
running_loss += loss.data[0]
if i % 200 == 199:
# 除以200,获得两百次的平均损失值
print('[%d, %5d] loss: %.3f' %(epoch + 1, i + 1, running_loss / 200)) # 这一个200次结束后,就把running_loss归零,下一个200次继续使用
running_loss = 0.0
print('Finished Training')
# 【3】保存神经网络
torch.save(net, 'net.pkl') # 保存整个神经网络的结构和模型参数
torch.save(net.state_dict(), 'net_params.pkl') # 只保存神经网络的模型参数
5.测试网络
执行测试阶段首先需要一个根目录里已经训练好了的net.pkl文件,再执行test()函数。
def reload_net():
trainednet = torch.load('net.pkl')
return trainednet
def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
在imshow()中的transpose()的作用可以简单理解为格式转换,不进行格式转换的数据无法正常读取。
def test():
testloader = loadtestdata()
net = reload_net()
dataiter = iter(testloader)
images, labels = dataiter.next()
# nrow为每行显示的图片数量
imshow(torchvision.utils.make_grid(images,nrow=5))
# 打印前25个test集里图片的标签
print('GroundTruth: ', " ".join('%5s' % classes[labels[j]] for j in range(25)))
outputs = net(Variable(images))
_, predicted = torch.max(outputs.data, 1)
# 打印前25个预测值
print('Predicted: ', " ".join('%5s' % classes[predicted[j]] for j in range(25)))