torchvision内置了常用数据集和最常见的模型,其中,最常见的两个包就是datasets和models,另外它还内置了转换数据集的办法,叫transforms,它是一系列的转换数据集的方法。
transforms提供的ToTensor()方法是最常用的转换,可以将我们读取的图片或者其他数据转换成tensor,它主要有三个作用:
将我们读取的图片或者其他类型的数据转换成一个tensor;会将数据归一化,转化到0~1之间;会将通道放到第一维度上,比如说用python的pillow库读入一张图片,它的表现形式是 [长,宽,通道数],经过ToTensor后通道数会放在第一个维度上,即[通道数,长,宽]。
加载数据集
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
# 定义数据要做哪些变换
# transforms为我们提供了Compose方法,我们可以将所有的这些变换都以列表的形式放在Compose里面。
transformation = transforms.Compose([
transforms.ToTensor(), # 这个转换最常用的转换,在处理图片的过程当中是非常重要的。
# transforms.Normalize(),# 标准化到-1~1之间。这一步并不是必须的。
])
加载数据集
# datasets.MNIST会返回一个dataset数据类型,可以用来创建DataLoader。
# 第一个参数是要把下载下来的数据集放在哪个位置
# 第二个参数表示这个数据是否是训练数据
# 第三个参数表示是否要做变换
# 第四个参数表示数据集是否要下载下来
train_ds = datasets.MNIST('data/',train=True,transform=transformation,download=True)
test_ds = datasets.MNIST('data/',train=False,transform=transformation,download=True)
#创建dataloader
train_dl = DataLoader(train_ds,batch_size=64,shuffle=True)
test_dl = DataLoader(test_ds,batch_size=256,shuffle=False) # 测试数据集并不需要做反向传播,占用的内存和显存都比较少,所以批次大小可以稍微大一些。
认识一下数据集
imgs,labels = next(iter(train_dl)) # 取出一个批次的数据。iter(train_dl)生成迭代器 # imgs.shape 是torch.Size([64, 1, 28, 28]),0维度是批次,一维度是channel # 在pytorch里面图片的表示形式: 【batchSize, channel(通道), height, width】 img = imgs[0] # 取出第一张图片绘图 img.shape # 结果是:torch.Size([1, 28, 28]),单张图片没有批次大小 img = img.numpy() #我们把img这张图片进行一个绘图,需要把tensor转换为ndarray """ img是 [1,28,28] ,第0个维度是通道数,其实并没有什么卵用,我们要squeeze掉 squeeze就是删除单维度条目,即把shape中为1的维度去掉 """ img = np.squeeze(img) img.shape # 结果:(28, 28) plt.imshow(img) # imshow就是imageShow,绘制图片 labels[0] # 查看img对应的标签
运行结果:
可以看到,标签与图片是一一对应的。
# 定义一个显示图片的函数
def imshow(img):
np_img = img.numpy()
np_img = np.squeeze(np_img)
plt.imshow(np_img)
#绘制返回一个批次数据的前10张图片
plt.figure(figsize=(10,1)) # 创建一张画布,画布大小 1行10列。figsize=(宽,高)
for i,img in enumerate(imgs[:10]):
plt.subplot(1,10,i+1)
imshow(img)
labels[:10]
运行结果:
创建模型
我们要将 [1,28,28] 这样的数据格式做展平,展平成 [28*28] 这么长的一个tensor,然后将这个tensor连接在linear层...,展平的处理实际上取消掉了模型中的空间维度,但还是可以做出一些效果出来。
创建模型。使用全连接的模型
将数据做展平处理,变成28X28的一个tensor
再把这个tensor连接到linear层,再连接到输出,创建一个多分类的模型。模版代码:
1. 创建输入(dataloader)
2. 创建模型(model)
3 创建损失函数
class Model(nn.Module):
def __init__(self):
#首先要继承父类中所有的变量
super().__init__()
# 开始初始化所有的层
self.linear1 = nn.Linear(28*28,120 ) # 第一个线性层。将输入的28*28个特征输出到120个隐藏层单元。
self.linear2 = nn.Linear(120,84)
self.linear3 = nn.Linear(84,10) # 输出层。因为是10分类,所以输出特征长度为10。
def forward(self,input):
# forward方法有一个input参数,你的输入是交给forward方法,然后forward方法调用这些层对输入数据处理。
# (1)将输入数据进行展平;展开成一个28x28这么长的一个tensor。
input = input.view(-1,28*28)
#(2)开始前向传播
x1 = F.relu(self.linear1(input)) # 中间的影藏层单元,需要进行一个relu激活,提升模型的非线性表达能力
x2 = F.relu(self.linear2(x1))
x3 = self.linear3(x2) # 损失函数是CrossEntropyLoss,输出不需要进行激活。
return x3
model = Model() # 创建模型
loss_fn = torch.nn.CrossEntropyLoss() # 损失函数。交叉熵
opt = torch.optim.Adam(model.parameters(),lr=0.001) # Adam优化器
epochs = 20 #训练批次大小
# 使用模板代码
def fit(epoch, model, trainloader, testloader):
correct = 0 # 记录模型正确预测对了多少个样本
total = 0 # 总共对多少个样本进行了预测
running_loss = 0 # 累加每一个batch的loss
for x, y in trainloader: # 返回的是每一个batch的数据
y_pred = model(x) # 前向传播
loss = loss_fn(y_pred, y)
opt.zero_grad()
loss.backward() # 反向传播
opt.step() # 优化模型参数
with torch.no_grad():
y_pred = torch.argmax(y_pred, dim=1) # 将预测结果转换为相应的预测值
correct += (y_pred == y).sum().item() # 单个tensor转python标量(取值)。每一个batch都将算对的个数加到correct里面。
total += y.size(0) # 每一个batch的样本个数
running_loss += loss.item() # 对每一个batch(批次)的loss进行累加
# dataloader有一个对象dataset,反映创建这个dataloader的那个dataset,使用len函数取得它的总的长度。
epoch_loss = running_loss / len(trainloader.dataset) # 每一个批次的loss/总的样本数==每个样本的平均loss
epoch_acc = correct / total # 预测对的样本数/总的预测样本数(对于一个epoch)。每个样本的平均acc
test_correct = 0
test_total = 0
test_running_loss = 0
with torch.no_grad():
for x, y in testloader:
y_pred = model(x)
loss = loss_fn(y_pred, y)
y_pred = torch.argmax(y_pred, dim=1)
test_correct += (y_pred == y).sum().item()
test_total += y.size(0)
test_running_loss += loss.item()
epoch_test_loss = test_running_loss / len(testloader.dataset)
epoch_test_acc = test_correct / test_total
print('epoch:', epoch,
' loss:', round(epoch_loss, 3),
' accuracy:', round(epoch_acc, 3),
' test_loss:', round(epoch_test_loss, 3),
' test_accuracy:', round(epoch_test_acc, 3)
)
# 返回一个epoch在训练集和测试数据集上的损失和准确率
return epoch_loss, epoch_acc, epoch_test_loss, epoch_test_acc
开始训练:
train_loss = []
train_acc = []
test_loss = []
test_acc = []
for epoch in range(epochs):
epoch_loss, epoch_acc, epoch_test_loss, epoch_test_acc = fit(epoch, model,train_dl,test_dl)
train_loss.append(epoch_loss)
train_acc.append(epoch_acc)
test_loss.append(epoch_test_loss)
test_acc.append(epoch_test_acc)
运行结果:
训练可视化
plt.plot(range(1, epochs+1), train_loss, label='train_loss') plt.plot(range(1, epochs+1), test_loss, label='test_loss') plt.legend()
plt.plot(range(1, epochs+1), train_acc, label='train_acc') plt.plot(range(1, epochs+1), test_acc, label='test_acc') plt.legend()



