Notes_on_PyTorch


PyTorch学习笔记

Link: PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】

1. 函数

  1. dir(): 查询某个类或对象的所有属性
  2. help(): 打开某个module的详细信息,也可以直接对象名加’??’。如要查询torch.utils.Data中的 Dataset, 可以输入help(Dataset), 也可以输入Dataset??.
  3. os.listdir(dir_path):将某个路径下的文件转换为列表.
    os.path.join(root_dir, target_dir):将两个地址连起来(前者为根路径,后者为前者中的子文件夹)
  4. 按住 Ctrl 点击Module 可以打开对应的模块文件
  5. torch.reshape() : 转换格式/升维(unsqueeze()也可以)
  6. ReLU() 中的inplace指是否原地置换结果
  7. input = torch.tensor([1, 2, 3], dtype=torch.floag32): 将 tensor 中存放的数据的数据类型定义为浮点数 float32.
  8. print("some words here: {}".format(some param here)):python格式化字符串写法,将前边的花括号 {} 替换为 format 中的内容.
  9. 选中某行按 Ctrl+D可以直接粘贴。
  10. 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]
  11. state_time = time.time():记录当前时间,需要import time
  12. 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. 数据类型

  1. Dataset: 提供获取数据及其label的方式
  2. Dataloader: 数据打包,为网络提供不同的数据形式
  3. tensor:打包了训练样本中相关的数据类型,如图片(用多维矩阵存储)、标签(label)等

3. 类的创建

  1. 初始化函数 __init__(self)

  作用:为所在的 class 提供全局变量。

  1. 类中带双下划线 __ 的函数可以通过对象名直接调用,不需要通过 对象名.函数名(参数列表) 的形式来调用。

4. Tensorboard

  1. 作用:训练过程可视化、结果导出
  2. 导入方法:from torch.utils.tensorboard import SummaryWriter.
  3. 创建事件文件:writer = SummaryWriter("logs")
  4. 加载数据到事件文件:writer.add_image("tag", image, step)writer.add_scalar("tag", scalar, step

    如果没有对tag进行修改,重复操作后,后边的结果会覆盖在前边的结果上边,表现为曲线的重叠(中间有拟合)、图像堆叠为一个过程。

  5. 完成后记得关闭, 输入writer.close()

  1. 打开方法:tensorboard --logdir=logs(logs为事件文件所在文件夹名字)
    指定端口名:tensorboard --logdir=logs --port=xxxx(xxxx为端口名)

5. torchvision

  1. torchvision.transform
    • 作用:将图片的格式等进行操作(totensor, resize)
    • 导入方法:from torchvision import transforms.
  2. torchvision.dataset:包含很多标准数据集

6. NN的建立

  1. 库:import torch.nn as nnimport torch.nn.functional as F
  2. 定义模型类,继承 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

  1. 库:torch.nn
  2. 作用:①计算实际输出与目标之间的差距
       ②为网络参数的更新提供依据(误差反向传播)
  3. 计算方法:
    loss_result = cross_loss(outputs, target)   # 计算loss
    loss_result.backward()      # 反向传播计算梯度

8. 优化器

  1. 使用方法
for input, target in dataset:
    optimizer.zero_grad()   # 梯度清零,避免上一步的梯度影响这一步的计算
    output = model(input)
    loss = loss_fn(output, target)  # 计算loss
    loss.backward()     # 反向传播计算梯度
    optimizer.step()    # 优化器进行参数更新

9. 模型保存与加载

  1. 保存
    • 方式1: 保存完整的网络,包括网络结构与参数
      torch.save(model_name, "file_name.pth")
    • 方式2:通过字典保存网络的参数(推荐)
      torch.save(model_name.state_dict(), "file_name.pth")
  2. 加载
    • 方式1:
      model_name = torch.load("file_name.pth")
    • 方式2:
      model_name.load_state_dict(torch.load("file_name.pth"))
> 在不同的设备上保存的模型,读取时需要将模型映射到对应的设备(GPU保存CPU读取是需要的,CPU保存GPU读取没试过),即
> `model_name = torch.load("file_name.pth", map_location=torch.device('cpu))`.

10. 使用GPU进行训练

  1. 方式1:将训练相关的(如网络模型、损失函数、样本、标签)后面加上.cuda()即可实现在GPU上进行训练。但最好加上条件判断,即:

    model = Model()
    if torch.cuda.is_available():
      model = model.cuda()
  2. 方式2:
    device = torch.device("cuda") # 如果有多个GPU,则写`cuda: 索引`,索引若为0可以直接不写
    model = Model
    model = model.to(device)
    类似方式1,最好加判断,可以简写为:
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
  3. 可以用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()  # 关闭事件文件

文章作者: Vyron Su
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Vyron Su !