持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
上一篇博客主要内容介绍了如何利用pytorch构建一个简单的网络结构,其中包括网络结构的定义,以及定义一个forward函数完成网络的前向传播,也就是将整个网络结构从输入到输出,进行串联起来,这个是最简单的网络结构,随着后面的不断学习,网络结构也会逐渐复杂,有时为了能够在深层次的网络结构中获取浅层次的网络特征,也会出现网络结构并联的情况,也就是concat操作。
本博客利用CIFA10数据集作为例子,在学会构建网络结构的基础上,通过定义损失函数,梯度的反向传播、优化等训练自己的网络。
CIFA10与MNIST数据集类似,其中都有十个类别,因此对于昨天定义的简单的网络结构,对其进行微调后仍然可以胜任今天的任务。CIFA10与MNIST也可以直接通过torchvision中的datasets包进行下载和使用。
首先导入本实验所需要的所有包
import matplotlib.pyplot as plt
import torch.nn as nn
import torchvision.utils
from torchvision import datasets
import numpy as np
import torch.utils.data as Data
from torchvision import transforms
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
然后下载CIFA10的数据集,如果已经下载过CIFA10的数据集,可以将参数download设置为False.
# 设置一些变化的格式。将数据转化为tensor格式,将数据按照下面的格式进行标准化,mean表示均值,std表示方差
transform = transforms.Compose(
[
transforms.ToTensor(),
transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))
]
)
# 下载数据集
traindata = datasets.CIFAR10(
root="./data/CIFA10",
train=True,
download=True,
transform=transform
)
testdata = datasets.CIFAR10(
root="./data/CIFA10",
train=False,
download=True,
transform=transform
)
# 加载数据集
train = Data.DataLoader(dataset=traindata, batch_size=32, shuffle=True, num_workers=1)
test = Data.DataLoader(dataset=testdata, batch_size=32, shuffle=True, num_workers=1)
# CIFA10的类别标签
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# 查看数据集的一些照片
def showImage():
def imgshow(img):
img = img.permute(1, 2, 0)
img = img / 2 + 0.5
img_array = np.array(img)
plt.imshow(img_array)
plt.show()
image = iter(train)
images, labels = image.next()
imgshow(torchvision.utils.make_grid(images))
上面显示的是一个batch的图像数据,一共32张,其数量大小可以由Dataloader中的batch_size控制。
然后就是定义我们的网络模型,网络模型是(1)卷积后跟一个relu然后最大池化;(2)卷积后跟一个relu,然后最大池化;(3)经历一个全连接然后跟一个relu;(4)经历一个全连接然后跟一个relu;(5)最后一个全连接,将类别输出(10类)
class Net(nn.Module):
def __init__(self, in_channels, middle_channels, out_channels):
'''
定义卷积、全连接层等几个操作,无顺序可言
:param in_channels:
:param middle_channels:
:param out_channels:
'''
super(Net, self).__init__()
self.relu = nn.ReLU(inplace=True)
self.conv1 = nn.Conv2d(in_channels, middle_channels, 5)
# self.bn1 = nn.BatchNorm2d(middle_channels)
self.conv2 = nn.Conv2d(middle_channels, out_channels, 5)
# self.bn2 = nn.BatchNorm2d(out_channels)
self.fc1 = nn.Linear(out_channels*5*5, 120)
self.fc2 = nn.Linear(120, 60)
self.fc3 = nn.Linear(60, 10)
def forward(self, x):
'''
定义网络的结构顺序,完成前向的传播,这里的顺序就是整个网络结构的顺序
:param x:
:return:
'''
# 卷积层后跟一个池化层
x = F.max_pool2d(self.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(self.relu(self.conv2(x)), 2)
# view中给定第一个参数为-1表示,让系统自动计算前面的维度大小
yy = self.feature_size(x)
x = x.view(-1, yy)
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x)
return x
def feature_size(self, x):
'''
计算特征图的数量
:param x:
:return:
'''
numer = x.size()[1:]
num_feature = 1
for num in numer:
num_feature *= num
return num_feature
最后,就是通过实例化模型,然后将输入输入到模型中,根据输出进行一个交叉熵损失计算,利用随机梯度下降进行参数的更新。代码如下
if __name__ == "__main__":
showImage()
in_channels = 3
middle_channels = 6
out_channels = 16
model = Net(in_channels, middle_channels, out_channels).cuda(device=0)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=1e-3, momentum=0.7)
epoch = 3
for i in range(epoch):
loss_all = 0.0
for j, data in enumerate(train, 0):
inputs, labels = data
inputs, labels = Variable(inputs.cuda(0)), Variable(labels.cuda(0))
# 首先需要把梯度置为0
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
loss_all += loss.item()
if j == 0 or j % 100 == 0:
print("epoch: {}, loss: {}".format(j, loss_all / 100))
loss_all = 0
对于训练的epoch,可以自己进行设置,具体做法就是将最外层的for循环数值增大即可。网络训练的结果如下。
可以看到随着训练迭代次数的增加,损失函数在下降,但是明显没有收敛,说明我们设置的epoch数值太小。可以增大epoch来达到网络训练过程中最终的收敛。本次训练,我们仅使用了一个gpu进行训练,在后面的博客中我将继续更新对于数据集的一些操作,包括如何使用多个gpu进行训练等。