目录
数据集准备(下载)
CIFAR-10数据集由10个类别的60000张32x32彩色图像组成,每个类别有6000张图像。有50000个训练图像和10000个测试图像。
使用torchvision自带的数据集
# 定义图像的预处理方法
transform = transforms.Compose(
[
transforms.ToTensor(),
transforms.Normalize(
mean=(0.5, 0.5, 0.5),
std=(0.5, 0.5, 0.5),
)
]
)
train_set = torchvision.datasets.CIFAR10(
root=r'D:\Cproject\ex001\CIFAR10', # 如果下载,下载的位置,也可以手动下载,放到这个位置来
train=True,
download=False, # 下载地址 https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
transform=transform
)
transform.Compose()是网络加载数据集的时候对图像进行预处理操作的处理流程的打包
例子中先是把加载的图像转成Tensor,并且调整hwc为chw格式,然后执行一个归一化操作在标准化处理(rgb灰度值/255得到0-1,再减去0.5(均值) 再 除以0.5(标准差)) 对原始数据归一化及标准化有助于网络的训练收敛
此处的均值及标准差是先验的,是统计的结果,人为设定的,并不是训练网络必须要使用这个参数,重要的是要理解标准化的数据对于训练的帮助
比如 ImageNet 图像常用的参数为mean=(0.485, 0.456, 0.406),std=(0.229, 0.224, 0.225)。而对于特定的数据集,选择这个值结果可能并不理想
注意如果手动下载,要把压缩包手动解压,并正确组织目录
例如
数据集加载(dataloader)
实例化Dataset后就可以实例化Dataloader了
from torch.utils.data import DataLoader
dataloader = DataLoader(
dataset=train_set, # 指定从哪个数据集实例中加载
batch_size=4,
shuffle=True, # 是否打乱顺序,训练一般开启,保证数据加载的随机性
num_workers=0, # 加载具体数据的时候工作线程数,因为一般读数据设计到大量io操作,比较耗时多线程加载可以加快速度,win下有bug
)
batchsize的选择可以根据硬件来设置(显存),尽可能大一些
创建完dataloader对象后,就可以使用for i in dataloader:..语句从中得到每一个批次的数据了
也可以使用iter(dataloader)封装成一个迭代器,使用next()直接获取一组数据进行展示
dataiter = iter(dataloader)
images,labels = next(dataiter)
next(dataiter)语句的返回值和Dataset中的__getitem__()方法有关,
在CIFAR10中相关代码如下
可以看到返回给我们的数据实际上是经过了一些变换(transform)的数据,
为了展示,对其进行还原
对于图像的数据 4,3,32,32 表示的是4张样本图片,3通道,高宽都是32
对于label的数据 表示4个样本的类别索引(标签索引的含义可以通过train_set.classes获取)
使用
imgs = torchvision.utils.make_grid(images) 把多张样本组合成一张图片
def im_show(img):
img = img / 2 + 0.5 # 反标准化过程,处理完后值的范围为0-1
npimg = img.numpy() # tensor->numpy
plt.imshow(np.transpose(npimg, (1, 2, 0))) # chw->hwc
plt.show()
再调用im_show(imgs)展示
关于Dataset/Dataloader将整理在另一篇博客中
训练过程代码
训练代码的主要组织结构为
网络加载,损失函数反向传播优化器的设置,按batchsize取出数据,进行前向传播,计算损失,反向传播更新网络,评估代码,日志代码,保存网络等
import numpy as np
import torch
import torchvision
from matplotlib import pyplot as plt
from torch import nn, optim
from torch.utils.data import DataLoader
from torchvision import transforms
from net import LeNet
def main():
# 定义图像的预处理方法
transform = transforms.Compose(
[
transforms.ToTensor(),
transforms.Normalize(
mean=(0.5, 0.5, 0.5),
std=(0.5, 0.5, 0.5),
)
]
)
train_set = torchvision.datasets.CIFAR10(
root=r'D:\Cproject\ex001\CIFAR10', # 如果下载,下载的位置,也可以手动下载,放到这个位置来
train=True,
download=False, # 下载地址 https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
transform=transform
)
train_dataloader = DataLoader(
dataset=train_set, # 指定从哪个数据集实例中加载
batch_size=128,
shuffle=True, # 是否打乱顺序,训练一般开启,保证数据加载的随机性
num_workers=0, # 加载具体数据的时候工作线程数,因为一般读数据设计到大量io操作,比较耗时多线程加载可以加快速度,win下有bug
)
test_set = torchvision.datasets.CIFAR10(
root=r'D:\Cproject\ex001\CIFAR10', # 如果下载,下载的位置,也可以手动下载,放到这个位置来
train=False,
download=False, # 下载地址 https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
transform=transform
)
test_dataloader = DataLoader(
dataset=test_set, # 指定从哪个数据集实例中加载
batch_size=128,
shuffle=False, # 是否打乱顺序,训练一般开启,保证数据加载的随机性
num_workers=0, # 加载具体数据的时候工作线程数,因为一般读数据设计到大量io操作,比较耗时多线程加载可以加快速度,win下有bug
)
# show_example(dataloader)
model = LeNet(10) # 实例化网络对象
loss_function = nn.BCEWithLogitsLoss() # 损失函数
optimizer = optim.Adam(params=model.parameters(), lr=0.01) # 优化器
epochs = 100
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 选择算法计算的硬件设置
model = model.to(device)
for epoch in range(epochs):
running_loss = []
training_acc = []
# train
model.train()
for step, (images, labels) in enumerate(train_dataloader):
images = images.to(device)
labels = labels.to(device)
labels = torch.nn.functional.one_hot(labels, num_classes=10).to(torch.float32) # 对label进行onehot编码
outputs = model(images) # 网络前向传播
loss = loss_function(outputs, labels) # 计算损失
optimizer.zero_grad() # 优化器梯度清零
loss.backward() # 计算梯度
optimizer.step() # 更新网络权重
running_loss.append(loss.detach().cpu().item()) # 保存当前 batch 的损失记录
predict_y = torch.max(outputs, dim=1)[1]
labels = torch.max(labels, dim=1)[1]
training_acc.extend((predict_y == labels).cpu().numpy().tolist()) # 保存当前 batch 预测的正确性的记录
# test
testing_loss = []
testing_acc = []
model.eval()
with torch.no_grad():
for step, (images, labels) in enumerate(test_dataloader):
images = images.to(device)
labels = labels.to(device)
labels = torch.nn.functional.one_hot(labels, num_classes=10).to(torch.float32) # 对label进行onehot编码
outputs = model(images) # 网络前向传播
loss = loss_function(outputs, labels) # 计算损失
testing_loss.append(loss.detach().cpu().item()) # 保存当前 batch 的损失记录
predict_y = torch.max(outputs, dim=1)[1]
labels = torch.max(labels, dim=1)[1]
testing_acc.extend((predict_y == labels).cpu().numpy().tolist()) # 保存当前 batch 预测的正确性的记录
# print log
print(
f'epoch:{epoch}/{epochs} '
f'train_loss:{sum(running_loss) / len(running_loss):.3f} '
f'test_loss:{sum(testing_loss) / len(testing_loss):.3f} '
f'train_acc:{sum(training_acc) / len(training_acc):.3f} '
f'test_acc:{sum(testing_acc) / len(testing_acc):.3f} '
)
torch.save(model.state_dict(), 'model.pth')
return 0
def show_example(dataloader):
dataiter = iter(dataloader)
images, labels = next(dataiter)
imgs = torchvision.utils.make_grid(images)
im_show(imgs)
def im_show(img):
img = img / 2 + 0.5 # 反标准化过程,处理完后值的范围为0-1
npimg = img.numpy() # tensor->numpy
plt.imshow(np.transpose(npimg, (1, 2, 0))) # chw->hwc
plt.show()
if __name__ == '__main__':
main()