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 timeVariable:位于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__和前向传播函数forwardclass 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()  # 关闭事件文件