PyTorch学习笔记
Link: PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】
1. 函数
- dir(): 查询某个类或对象的所有属性
- help(): 打开某个module的详细信息,也可以直接对象名加’??’。如要查询
torch.utils.Data
中的Dataset
, 可以输入help(Dataset)
, 也可以输入Dataset??
. os.listdir(dir_path)
:将某个路径下的文件转换为列表.os.path.join(root_dir, target_dir)
:将两个地址连起来(前者为根路径,后者为前者中的子文件夹)- 按住
Ctrl
点击Module
可以打开对应的模块文件 torch.reshape()
: 转换格式/升维(unsqueeze()
也可以)ReLU()
中的inplace指是否原地置换结果input = torch.tensor([1, 2, 3], dtype=torch.floag32)
: 将tensor
中存放的数据的数据类型定义为浮点数float32
.print("some words here: {}".format(some param here))
:python格式化字符串写法,将前边的花括号{}
替换为format
中的内容.- 选中某行按
Ctrl+D
可以直接粘贴。 array.argmax(flag)
: 输出array的每一行/列中最大值所在的列/行索引,0按列1按行搜索。如array = [[0.1, 0.2], [0.05, 0.4]] print(array.argmax(0)) print(array.argmax(1)) 输出:[0, 1] [1, 1]
state_time = time.time()
:记录当前时间,需要import time
Variable
:位于torch.autograd
库中,封装了Tensor
,并整合了反向传播的相关实现。Variable
具有三个属性: ①data
:存储tensor
变量;②grad
: 存储data
反向传播的梯度;③grad_fn
: 存放计算梯度的方法. 实际创建变量时的语法:x_var = Variable(x, requires_grad=True)
.requires_grad
: 表示是否需要对该变量进行求导,默认为False
, 指定为True
时,print(x_var.grad)
则是其梯度值,否则为None
2. 数据类型
- Dataset: 提供获取数据及其label的方式
- Dataloader: 数据打包,为网络提供不同的数据形式
- tensor:打包了训练样本中相关的数据类型,如图片(用多维矩阵存储)、标签(label)等
3. 类的创建
- 初始化函数
__init__(self)
作用:为所在的 class
提供全局变量。
- 类中带双下划线
__
的函数可以通过对象名直接调用,不需要通过对象名.函数名(参数列表)
的形式来调用。
4. Tensorboard
- 作用:训练过程可视化、结果导出
- 导入方法:
from torch.utils.tensorboard import SummaryWriter
. - 创建事件文件:
writer = SummaryWriter("logs")
加载数据到事件文件:
writer.add_image("tag", image, step)
和writer.add_scalar("tag", scalar, step
如果没有对tag进行修改,重复操作后,后边的结果会覆盖在前边的结果上边,表现为曲线的重叠(中间有拟合)、图像堆叠为一个过程。
完成后记得关闭, 输入
writer.close()
- 打开方法:
tensorboard --logdir=logs
(logs为事件文件所在文件夹名字)
指定端口名:tensorboard --logdir=logs --port=xxxx
(xxxx为端口名)
5. torchvision
torchvision.transform
- 作用:将图片的格式等进行操作(totensor, resize)
- 导入方法:
from torchvision import transforms
.
torchvision.dataset
:包含很多标准数据集
6. NN的建立
- 库:
import torch.nn as nn
和import torch.nn.functional as F
定义模型类,继承
nn.Module
类,定义初始化函数__init__
和前向传播函数forward
class Model(nn.Module): def __init__(self): super(Module, self).__init__() self.conv1 = nn.Conv2d(1, 20, 5) # 卷积层 self.conv2 = nn.Conv2d(20, 20, 5) # 卷积层 def forward(self, x) x = F.Relu(self.conv1(x)) return F.Relu(self.conv2(x))
上边的代码块是建立NN时的模板,必须要重写
__init__
和forward
两个函数
7. Loss
- 库:
torch.nn
- 作用:①计算实际输出与目标之间的差距
②为网络参数的更新提供依据(误差反向传播) - 计算方法:
loss_result = cross_loss(outputs, target) # 计算loss loss_result.backward() # 反向传播计算梯度
8. 优化器
- 使用方法
for input, target in dataset:
optimizer.zero_grad() # 梯度清零,避免上一步的梯度影响这一步的计算
output = model(input)
loss = loss_fn(output, target) # 计算loss
loss.backward() # 反向传播计算梯度
optimizer.step() # 优化器进行参数更新
9. 模型保存与加载
- 保存
- 方式1: 保存完整的网络,包括网络结构与参数
torch.save(model_name, "file_name.pth")
- 方式2:通过字典保存网络的参数(推荐)
torch.save(model_name.state_dict(), "file_name.pth")
- 方式1: 保存完整的网络,包括网络结构与参数
- 加载
- 方式1:
model_name = torch.load("file_name.pth")
- 方式2:
model_name.load_state_dict(torch.load("file_name.pth"))
- 方式1:
> 在不同的设备上保存的模型,读取时需要将模型映射到对应的设备(GPU保存CPU读取是需要的,CPU保存GPU读取没试过),即
> `model_name = torch.load("file_name.pth", map_location=torch.device('cpu))`.
10. 使用GPU进行训练
方式1:将训练相关的(如网络模型、损失函数、样本、标签)后面加上
.cuda()
即可实现在GPU上进行训练。但最好加上条件判断,即:model = Model() if torch.cuda.is_available(): model = model.cuda()
- 方式2:
类似方式1,最好加判断,可以简写为:device = torch.device("cuda") # 如果有多个GPU,则写`cuda: 索引`,索引若为0可以直接不写 model = Model model = model.to(device)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
- 可以用google的colab进行GPU训练:colab.research.google.com
附录(基于CIFAR10数据集的图像识别代码)
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d,Linear,MaxPool2d,Flatten,Sequential
from torch.utils.data import DataLoader
from torchvision import datasets
from torch.utils.tensorboard import SummaryWriter
import time
# 数据集
# train为true则说明下载训练集,为false则下载测试集
train_set = torchvision.datasets.CIFAR10(root="./dataset",
train=True,
transform=torchvision.transforms.ToTensor(),
download=True) # 下载数据集
test_set = torchvision.datasets.CIFAR10(root="./dataset",
train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
# 设置设备类型
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 求数据集长度
train_set_size = len(train_set)
test_set_size = len(test_set)
# 将前边字符串中的花括号{}用format中的内容替换,如train_set_size = 10, 运行后
# print的内容为:"训练数据集的长度为:10"
print("训练数据集的长度为:{}".format(train_set_size))
print("测试数据集的长度为:{}".format(test_set_size))
# 利用dataloader加载数据集
train_data = DataLoader(train_set, batch_size=64)
test_data = DataLoader(test_set, batch_size=64)
# 搭建网络
class CIFAR10_Model(nn.Module):
def __init__(self) -> None:
super().__init__()
self.model1 = Sequential(
# in_channels输入通道数,out_channels输出通道数
# kernel_size卷积核, stride步长, padding拓展
Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=2), # 卷积层
MaxPool2d(2), # 池化层
Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2),
MaxPool2d(2),
Flatten(), # 展平
Linear(64 * 4 * 4, 64), # 线性层
Linear(64, 10)
)
def forward(self, x): # 前向传播
x = self.model1(x)
return x
# 创建网络
model = CIFAR10_Model()
model = model.to(device) # 转换到目标设备
# 优化器,需要指定模型的参数,即model.parameters(),以及学习率lr
optim = torch.optim.SGD(model.parameters(), lr=0.01) # 采用梯度下降优化器
# 损失函数
loss_fn = nn.CrossEntropyLoss() # 损失函数采用交叉熵损失函数
loss_fn = loss_fn.to(device) # 转换到目标设备
# 超参设置
total_train_step = 0 # 记录训练次数
total_test_step = 0 # 记录测试次数
epoches = 10 # 训练轮数
# 创建事件文件,记录训练以及测试过程中的性能指标
writer = SummaryWriter("CIFAR10_train_gpu")
for epoch in range(epoches):
start_time = time.time() # 记录每轮训练开始时的时间
print("------------第{}轮训练开始-------------".format(epoch+1))
# 训练步骤
for data in train_data:
imgs, targets = data
imgs = imgs.to(device) # 进行设备转换
targets = targets.to(device) # 进行设备转换
outputs = model(imgs) # 将batch喂给NN计算输出
episode_loss = loss_fn(outputs, targets) # 计算损失
# 优化器模型
optim.zero_grad() # 清空梯度,避免上一步的结果对这一步的影响
episode_loss.backward() # 误差反向传播(理解为计算梯度)
optim.step() # 优化器进行参数优化
total_train_step += 1
# episode_loss.item():打印内容,直接print可能会带上类型,如tensor
if total_train_step % 100 == 0: # 每100步打印一次指标
print("训练次数:{}, Loss:{}".format(total_train_step, episode_loss))
writer.add_scalar("train_loss", episode_loss.item(), total_train_step)
end_time = time.time() # 记录单轮训练的时间
train_time = end_time - start_time
print("Training Time for a Epoch: {}".format(train_time)) # 打印单轮训练时间
# 测试步骤
total_test_loss = 0 # 测试损失
total_accuracy = 0 # 测试准确率
with torch.no_grad(): # 测试过程不进行参数更新,把梯度关掉
for data in test_data:
imgs, targets = data # 读取样本及标签
imgs = imgs.to(device) # 转移到目标设备
targets = targets.to(device) # 转移到目标设备
outputs = model(imgs) # 将batch喂给NN输出预测结果
episode_loss = loss_fn(outputs, targets) # 计算测试损失
# argmax(1)是求每一行中最大值的列索引,argmax(0)是求每一列中最大值的行索引
accuracy = (outputs.argmax(1) == targets).sum() # 求batch中预测正确的样本数量
total_accuracy += accuracy # 求单轮测试中的准确样本数
total_test_loss += episode_loss.item() # 加.item是去除数据类型
print("整体测试集的Loss:{}".format(total_test_loss))
print("整体测试集的准确率:{}".format(total_accuracy/test_set_size))
writer.add_scalar("test_loss", total_test_loss, total_test_step) # 将训练过程中的性能指标写入事件文件
writer.add_scalar("test_accuracy", total_accuracy/test_set_size, total_test_step)
total_test_step += 1
# 保存每一轮的模型
torch.save(model.state_dict(), "CIFAR10_train_model{}.pth".format(epoch))
# torch.save(model, "CIFAR10_train_model{}.pth".format(epoch))
print("模型已保存")
# 读取模型
# model.load_state_dict(torch.load(“CIFAR10_train_model{}.pth”.format(epoch)))
# model = torch.load("CIFAR10_train_model{}.pth".format(epoch))
writer.close() # 关闭事件文件