最近终于跑通了第一个模型,在这里浅记一下PyTorch的入门学习记录。
首先声明,因为我是做NLP的,但是第一个项目实践是关于图像的,所以在图像数据处理这里不做过多声明,主要谈网络怎么搭建,模型怎么跑,怎么保存和怎么用。
必须要安装的:(安装视频参考:PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】)
anaconda
pytorch
pycharm
自己在构建数据集时,应将数据构造成(数据,标签)的形式。构建自己数据集的方法可以参考:【ML】数据集的构建。
在本次实验中,我们的数据集选用CIFAR10
,准备数据集的代码如下:
import torchvision
# DataSet 准备数据集
train_data = torchvision.datasets.CIFAR10(root="./dataset", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10(root="./dataset", train=False, transform=torchvision.transforms.ToTensor(),
download=True) # debug此处可以查看train_data的分类
train_data_size = len(train_data)
test_data_size = len(test_data)
print("the length of train_data:{}".format(train_data_size))
print("the length of test_data:{}".format(test_data_size))
其中,transforms.ToTensor()
是将数据集转换成Tensor形式。
batch_size
就是一次拿多少数据的意思,等于1就代表每次从数据集中取一个,具体大小自己根据情况设置。
from torch.utils.data import DataLoader
# DataLoader 加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
不同的数据有不同的训练模型,我们选取的CIFAR10
已经有了较为成熟的网络结构了,如下图。
在创建网络模型的时候,我们只需要将网络结构转换成对应的函数,像这个处理过程就是:
卷积层、(最大)池化层、卷积层、(最大)池化层、卷积层、(最大)池化层、Flatten、全连接层。
对应的函数是:
Conv2d()
MaxPool2d()
Flatten()
Linear()
关于函数的参数,我们需要查阅相关文档,见pytorch官网:torch.nn(只要是和pytorch有关的都可以读官方文献)。以Conv2d()
为例:
可以看到前三个参数是要自定义的,在网络结构图中已经给出了。
标蓝色的两个参数:
从公式可以看出,stride[0] = 1
, padding[0] = 2
是最符合条件的一组参数。(越小越好)
那么我们就可以写出第一个卷积层的代码:
Conv2d(3, 32, 5, padding=2) # stride default = 1
其余同理,将网络结构图转换成下面的代码。这段代码一般都单独放在一个文件中,我的文件命名为Tudui_model
:
import torch
from torch import nn
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = nn.Sequential(
nn.Conv2d(3, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
在主程序中,我们只需要以下代码就可以进行调用:
tudui = Tudui()
# 设置训练网络的参数
total_train_step = 0 # 记录训练次数
total_test_step = 0 # 记录测试次数
# 1 epoch = see all the batches once
epoch = 10 # 训练次数
epoch
就是我们常说的轮次。注意:epoch
≠ update
。比如:N
= 10000
, batch_size
= 10
, 则 1 epoch
= 1e3 updates
# loss
loss_fn = nn.CrossEntropyLoss()
# Optimizer
learning_rate = 1e-2 # 10^(-2)
optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate) # 随机梯度下降
只要理解了训练的步骤就可以理解下面的代码:
从dataloader
里取出(数据,标签)、求损失函数loss
、反向传播backward
、参数调优。(这里看不懂的去补理论)
在损失函数和反向传播之间,需要加入:
optimizer.zero_grad()
进行梯度清零。(因为上一次的梯度对本次梯度更新没有用)
下面是代码:
# 训练步骤
tudui.train()
for data in train_dataloader:
imgs, targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs, targets)
optimizer.zero_grad() # 梯度清零(上一次的梯度对本次梯度更新没有用)
loss.backward() # 得到每一个可调节参数的梯度
optimizer.step() # 对每个参数调优
total_train_step = total_train_step + 1
测试集并不参与训练,所以在测试的时候就不需要反向传播和参数调优了,别忘了加上:
with torch.no_grad():
下面是测试部分的代码:
# 测试步骤
tudui.eval()
total_test_loss = 0
total_accuracy = 0 # 整体正确率
with torch.no_grad(): # 不希望测试时计算梯度,即:在该过程中不使用优化器更改参数,只使用训练好的模型
for data in test_dataloader:
imgs, targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs, targets) # 局部loss
# 整体loss
total_test_loss = total_test_loss + loss.item()
accuracy = (outputs.argmax(1) == targets).sum()
total_accuracy = total_accuracy + accuracy
print("整体测试集上的loss:{}".format(total_test_loss))
print("整体测试集上正确率:{}".format(total_accuracy/test_data_size))
total_test_step = total_test_step + 1
训练集和测试集跑完一轮后,就可以保存模型了:
torch.save(tudui, "tudui_{}.pth".format(i))
print("model saved")
但是跑一轮肯定是不够的,所以在训练和测试集外面应该加上大循环:
for i in range(epoch):
print("-----第 {} 轮训练开始-----".format(i+1))
```训练步骤```
```测试步骤```
```保存```
GPU训练的速度一定是会快上许多的。
GPU训练只需要改三个地方:
使用GPU有两种方法:
.cuda()
.to(device)
,其中:# device = torch.device("cpu")
# device = torch.device("cuda")
# device = torch.device("cuda:0")
以第二种方法为例,网络模型、数据、损失函数分别如下:
# 定义训练的设备
device = torch.device("cuda") # cpu or cuda
tudui = Tudui()
tudui = tudui.to(device) # 也可以省去赋值
# loss
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.to(device)
import torch
from torch import nn
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = nn.Sequential(
nn.Conv2d(3, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from Tudui_model import *
# 哪些可以用gpu:网络模型、数据(输入,标注)、损失函数
# 怎样使用gpu:.to(device)
# device = torch.device("cpu")
# device = torch.device("cuda")
# device = torch.device("cuda:0")
# 定义训练的设备
device = torch.device("cuda") # cpu or cuda
# DataSet 准备数据集
train_data = torchvision.datasets.CIFAR10(root="./dataset", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10(root="./dataset", train=False, transform=torchvision.transforms.ToTensor(),
download=True) # debug此处可以查看train_data的分类
train_data_size = len(train_data)
test_data_size = len(test_data)
print("the length of train_data:{}".format(train_data_size))
print("the length of test_data:{}".format(test_data_size))
# DataLoader 加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
# 创建网络模型
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = nn.Sequential(
nn.Conv2d(3, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
tudui = Tudui()
tudui = tudui.to(device) # 也可以省去赋值
# loss
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.to(device)
# Optimizer
learning_rate = 1e-2 # 10^(-2)
optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate)
# 设置训练网络的参数
total_train_step = 0 # 记录训练次数
total_test_step = 0 # 记录测试次数
# 1 epoch = see all the batches once
epoch = 10 # 训练次数 epoch ≠ update.eg:N = 1000, batch_size = 10, 则 1 epoch = 1e3 updates
# 添加tensorboard
writer = SummaryWriter("logs") # tensorboard --logdir = logs
for i in range(epoch):
print("-----第 {} 轮训练开始-----".format(i+1))
# 训练步骤
tudui.train()
for data in train_dataloader:
imgs, targets = data
imgs = imgs.to(device)
targets = targets.to(device)
outputs = tudui(imgs)
loss = loss_fn(outputs, targets)
optimizer.zero_grad() # 梯度清零
loss.backward()
optimizer.step()
total_train_step = total_train_step + 1
# 为方便显示,每100次记录一次
if total_train_step % 100 == 0:
print("训练次数:{}, loss = {}".format(total_train_step, loss.item())) # item():tensor -> int
writer.add_scalar("train_loss", loss.item(), total_train_step)
# 测试步骤
tudui.eval()
total_test_loss = 0
total_accuracy = 0 # 整体正确率
with torch.no_grad(): # 不希望测试时计算梯度,即:在该过程中不使用优化器更改参数,只使用训练好的模型
for data in test_dataloader:
imgs, targets = data
imgs = imgs.to(device)
targets = targets.to(device)
outputs = tudui(imgs)
loss = loss_fn(outputs, targets) # 局部loss
# 整体loss
total_test_loss = total_test_loss + loss.item()
accuracy = (outputs.argmax(1) == targets).sum()
total_accuracy = total_accuracy + accuracy
print("整体测试集上的loss:{}".format(total_test_loss))
print("整体测试集上正确率:{}".format(total_accuracy/test_data_size))
writer.add_scalar("test_loss", total_test_loss, total_test_step)
writer.add_scalar("test_accuracy", total_accuracy/test_data_size, total_test_step)
total_test_step = total_test_step + 1
torch.save(tudui, "tudui_{}_gpu2.pth".format(i))
print("model saved")
writer.close()
我们训练的模型保存结果如下图:(当然是越往后训练的越好了,懒得再训练截图了hh)
我们在保存模型后,就可以用模型进行图片的分类了。
观察网络模型图,最终给出的outputs是10,代表会输出对10种类型的预测概率,我们可以调用argmax(1)
,来输出概率最大的那个分类。
import torch
import torchvision
from PIL import Image
from torch import nn
# image_path = "dog.png"
image_path = "airplane.png"
image = Image.open(image_path)
# print(image)
image = image.convert('RGB') # 适应png,jpg各种格式的图片
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
torchvision.transforms.ToTensor()])
image = transform(image)
# print(image.shape)
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = nn.Sequential(
nn.Conv2d(3, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
# cpu训练
# model = torch.load("tudui_0.pth")
# print(model)
# gpu训练
model = torch.load("tudui_9_gpu.pth", map_location=torch.device('cpu')) # gpu训练的东西在cpu上运行应该映射(map)到cpu设备
image = torch.reshape(image, (1, 3, 32, 32))
model.eval()
with torch.no_grad():
output = model(image)
# print(output)
print(output.argmax(1)) # tensor([6])/tensor([5])
最核心的还是这段代码:
model.eval()
with torch.no_grad():
output = model(image)
print(output.argmax(1))
最后的结果是,训练次数少,正确率低的时候给不出正确结果,反之可以。
真正试一次才知道什么是训练模型,以及训练模型的核心到底在哪(搭建网络并转换成代码)。之前我以为NLP
的特点在跑模型,后来发现我错了,NLP
的特点应该是前置工作,也就是dataset
的构建。这次的数据集是图像,用transforms
可以解决,那以后输入如果是文本呢,怎样对文本进行处理,然后输入到网络中呢?这类问题或许是NLP
的特色所在吧。(小白瞎说的hhh)
[1] b站up主:我是土堆
[2] pytorch官网