epoch:1个epoch等于使用训练集中的全部样本训练一次,通俗的讲epoch的值就是整个数据集被轮几次。
batchsize:批大小,因为数据集太庞大,求所有样本的全局损失函数需要巨大的内存,故在深度学习中采用批训练,即每次梯度更新仅在训练集中取batchsize个样本综合;
iteration:1个iteration等于使用batchsize个样本(一个batch)训练一次;
换算公式:批数量 = 样本总数/批大小,向上取整。
举例:50000张训练图片的数据集CIFAR10,取batchsize = 256
batch数 = 50000/256=196
每个epoch需要完成196个batch(196个iteration)
每个epoch权重更新196次
用于数据预处理。
import torchvision.transforms as transforms
transform = transforms.Compose(
[transforms.Resize((32, 32)),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
上述实现的功能为:
1.图像缩放为32*32的尺寸。
2.将数据类型转换为Tensor格式,Tensor的数据存储顺序为[C,H,W],其中C为channel数,如RGB图像通道数为3。H与W分别是图像的长宽,当用四个参数时第一个参数为图像的张数。PIL的数据存储顺序为[H,W,C]。并且原始图片的像素值在0-255,转换后像素范围变为0-1。
3.对于每个channel,每个像素二维矩阵的像素值减去0.5,再除0.5。实现标准化。
input[channel] = (input[channel]-mean[channel])/std[channel]
输入总数据集(包括了标签),批大小,是否打乱(通常会打乱训练集顺序),载入数据集线程数(非Linux系统通常为0),制作批数据集。
import torch
train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,shuffle=True, num_workers=0)
val_loader与3中的train_loader一样,是划分好的测试集。通过iter迭代器,并以next对每个batch逐步进行访问。
val_data_iter = iter(val_loader)
val_image, val_label = val_data_iter.next()
如第一次运行第二行代码获取第一个batch的图像像素与标签,第二次运动获取下一个batch,以此类推。
先要对像素点进行反归一化,但是Tensor不可直接用于画图,取numpy数据后才能可视化,在取numpy后用np.transpose对通道的次序进行调整:[C,H,W]->[H,W,C]。
故原来索引1的分量放到索引0,原来索引2的分量放到索引1,原来索引0的分量放到索引2。
def imshow(img):
img = img/2+0.5 #去归一化
npimg = img.numpy()
plt.imshow(np.transpose(npimg,(1,2,0)))
plt.show()
imshow(torchvision.utils.make_grid(val_image))
其中val_image为某个batch的图像像素。
书写格式如下,需要搭建前向传播函数
import torch.nn as nn
import torch.nn.functional as F
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv1 = nn.Conv2d(3, 16, 5)
self.pool1 = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(16, 32, 5)
self.pool2 = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(32*5*5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = F.relu(self.conv1(x)) # input(3, 32, 32) output(16, 28, 28)
x = self.pool1(x) # output(16, 14, 14)
x = F.relu(self.conv2(x)) # output(32, 10, 10)
x = self.pool2(x) # output(32, 5, 5)
x = x.view(-1, 32*5*5) # output(32*5*5)
x = F.relu(self.fc1(x)) # output(120)
x = F.relu(self.fc2(x)) # output(84)
x = self.fc3(x) # output(10)
return x
import torch.optim as optim
import torch.nn as nn
from model import LeNet#从moldel.py文件导入模型
net = LeNet()
loss_function = nn.CrossEntropyLoss()#交叉熵损失函数
optimizer = optim.Adam(net.parameters(), lr=0.001)
交叉熵中包含了softmax,只需要将模型输出标签与真实标签直接填入即可,如6.2所述。
最外层循环为epoch,然后对每个epoch中的batch进行训练并更新梯度。
for step, data in enumerate(train_loader, start=0):
inputs, labels = data
start表示该epoch中第一个batch的序号是0,从0开始累加。
梯度更新:
optimizer.zero_grad()#梯度清0
outputs = net(inputs)
loss = loss_function(outputs, labels)#计算损失函数
loss.backward()#反向传播
optimizer.step()#优化器更新
首先要进行当前的梯度清零,然后计算损失函数,并进行反向传播。计算每个batch的损失函数之和时需要取loss.item(),loss的数据类型是Variable,若不取item值动态图会延伸导致显存爆炸。
模型储存
save_path = './Lenet.pth'
torch.save(net.state_dict(), save_path)
使用模型.state_dict()即可储存模型参数于对应路径,保存的文件为pth文件。
模型读取
net.load_state_dict(torch.load('Lenet.pth'))
使用模型.load_state_dict并填入相应的参数使用训练好的模型参数用于推理。
用于载入图片,仅需填入当前路径下的图片文件名称即可。
from PIL import Image
im = Image.open('1.png')#读取图片并转换为Tensor格式用于推理
im.show()#图片可视化
用于扩充维度。
x = torch.Tensor([[1, 2, 3, 4],
[5,6,7,8],
[9,10,11,12]])
torch.unsqueeze(x,0)#tensor([[[ 1., 2., 3., 4.],
#[ 5., 6., 7., 8.],
#[ 9., 10., 11., 12.]]])
torch.unsqueeze(x,0).size()#torch.Size([1, 3, 4])
torch.unsqueeze(x,1)#tensor([[[ 1., 2., 3., 4.]],
#[[ 5., 6., 7., 8.]],
#[[ 9., 10., 11., 12.]]])
torch.unsqueeze(x,1).size()#torch.Size([3, 1, 4])
torch.unsqueeze(x,2)"""tensor([[[ 1.],
[ 2.],
[ 3.],
[ 4.]],
[[ 5.],
[ 6.],
[ 7.],
[ 8.]],
[[ 9.],
[10.],
[11.],
[12.]]])"""
torch.unsqueeze(x,2).size()#torch.Size([3, 4, 1])
原矩阵为3×4矩阵。
在维度0进行扩张,每个3维张量包含1个3×4矩阵;在维度1进行扩招,每个3维向量包含3个1×4矩阵;在维度2进行扩张,每个3维向量包含3个4×1矩阵。
对维度进行压缩。
x = torch.zeros(3,2,4,1,2,1)
y = torch.squeeze(x,dim=0)#因为第0维的长度不为1,故此操作无效
y = torch.squeeze(x,dim=3)#因为第3维的长度为1,故此操作有效
#返回的尺寸为[3,2,4,2,1]
y = torch.squeeze(x)#去除全部长度为1的维度
#返回的尺寸为[3,2,4,2[
向量拉直。因为Tensor是以[S,C,H,W]格式储存的,S为图像的输入图像的数量(第0维),所以从第1维开始对每张图像的输出拉直成一维向量。
x = torch.flatten(x, start_dim=1)
包含在torchvision库中。调用该API时需要先将数据集按照文件夹进行分类,每个文件夹包含不同类别的图像。
数据格式如下,其中root为根目录,dog/cat等均为根目录下不同类别图像的文件夹。
root/dog/xxx.png
root/dog/xxy.png
root/dog/xxz.png
root/cat/123.png
root/cat/nsdf3.png
root/cat/asd932_.png
.jpg格式也可。
根据目录路径对训练集进行加载,并进行数据预处理。
from torchvision import datasets
train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),transform=data_transform["train"])
加载好的数据通过train_dataset.class_to_idx获取其类别的字典,格式为:{‘daisy’:0, ‘dandelion’:1, ‘roses’:2, ‘sunflower’:3, ‘tulips’:4}
字典的键为不同类别图像文件夹的名称。
之后再调用torch.utils.data.DataLoader进行batch的划分即可。