• pytorch再次学习


    基础

    数据可视化

    import torch
    from torch.utils.data import Dataset
    from torchvision import datasets
    from torchvision.transforms import ToTensor
    import matplotlib.pyplot as plt
    
    
    training_data = datasets.FashionMNIST(
        root="data1",
        train=True,
        download=True,
        transform=ToTensor()
    )
    
    test_data = datasets.FashionMNIST(
        root="data1",
        train=False,
        download=True,
        transform=ToTensor()
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    labels_map = {
        0: "T-Shirt",
        1: "Trouser",
        2: "Pullover",
        3: "Dress",
        4: "Coat",
        5: "Sandal",
        6: "Shirt",
        7: "Sneaker",
        8: "Bag",
        9: "Ankle Boot",
    }
    cols, rows = 3, 3
    for i in range(1, cols * rows + 1):
        sample_idx = torch.randint(len(training_data), size=(1,)).item() #用于随机取出一个training_data
        img, label = training_data[sample_idx]
        plt.subplot(3,3,i) #此处i必须是1开始
        plt.title(labels_map[label])
        plt.axis("off")
        plt.imshow(img.squeeze(), cmap="gray")
    plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    切换设备device

    device = (
        "cuda"
        if torch.cuda.is_available()
        else "mps"
        if torch.backends.mps.is_available()
        else "cpu"
    )
    print(f"Using {device} device")
    model.to(device)
    #或者
    model.to('mps')
    model.to('cuda')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    定义神经网络的类

    class NeuralNetwork(nn.Module):
        def __init__(self):
            super().__init__()
            self.flatten = nn.Flatten()
            self.linear_relu_stack = nn.Sequential(
                nn.Linear(28*28, 512),
                nn.ReLU(),
                nn.Linear(512, 512),
                nn.ReLU(),
                nn.Linear(512, 10),
            )
    
        def forward(self, x):
            x = self.flatten(x)
            logits = self.linear_relu_stack(x)
            return logits
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    打印每层的参数大小

    print(f"Model structure: {model}\n\n")
    
    for name, param in model.named_parameters():
        print(f"Layer: {name} | Size: {param.size()} | Values : {param[:2]} \n")
    
    • 1
    • 2
    • 3
    • 4

    自动微分

    详见文章Variable
    需要优化的参数需要加requires_grad=True,会计算这些参数对于loss的梯度

    import torch
    
    x = torch.ones(5)  # input tensor
    y = torch.zeros(3)  # expected output
    w = torch.randn(5, 3, requires_grad=True)
    b = torch.randn(3, requires_grad=True)
    z = torch.matmul(x, w)+b
    loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    计算梯度

    计算导数

    loss.backward()
    print(w.grad)
    print(b.grad)
    
    • 1
    • 2
    • 3

    禁用梯度追踪

    训练好后进行测试,也就是不要更新参数时使用

    z = torch.matmul(x, w)+b
    print(z.requires_grad)
    
    with torch.no_grad():
        z = torch.matmul(x, w)+b
    print(z.requires_grad)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    优化模型参数

    1. 调用optimizer.zero_grad()来重置模型参数的梯度。梯度会默认累加,为了防止重复计算(梯度),我们在每次遍历中显式的清空(梯度累加值)。
    2. 调用loss.backward()来反向传播预测误差。PyTorch对每个参数分别存储损失梯度。
    3. 我们获取到梯度后,调用optimizer.step()来根据反向传播中收集的梯度来调整参数。
    optmizer.zero_grad()
    loss.backward()
    optmizer.step()
    
    • 1
    • 2
    • 3

    模型保存

    import torch
    import torchvision.models as models
    
    model = models.vgg16(weights='IMAGENET1K_V1')
    torch.save(model.state_dict(), 'model_weights.pth')
    
    • 1
    • 2
    • 3
    • 4
    • 5

    模型加载

    model = models.vgg16() # we do not specify ``weights``, i.e. create untrained model
    model.load_state_dict(torch.load('model_weights.pth'))
    model.eval()
    
    • 1
    • 2
    • 3

    迁移学习

    常见的迁移学习方式

    1. 载入权重后训练所有参数
    2. 载入权重后只训练最后几层参数
    3. 载入权重后在原网络基础上再添加一层全连接层,仅训练最后一个全连接层

    固定随机数种子

    torch.manual_seed(64)
    
    • 1

    详见文章
    详见B导

    修改维度位置

    tensor.permute(3,2,1,0)
    
    • 1

    原本位置为0,1,2,3

    torch.einsum(爱因斯坦)

    爱因斯坦求和约定:用于简洁的表示乘积、点积、转置等方法

    >>> import torch
    >>> A = torch.randn(3, 4)
    >>> B = torch.randn(4, 5)
    >>> C = torch.einsum('ik,kj->ij', A, B)
    >>> C.shape
    torch.Size([3, 5])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    A代表ik,B代表kj,C代表ij

    进阶

    padding更准确的补法

    input=torch.randn(1,1,3,3)
    m=torch.nn.ZeroPad2d((1,1,2,0)) #左,右,上,下
    m(input)
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    ReLU增加计算量但是减少内存消耗的办法(对低维度特征信息造成大量损失)

    torch.nn.ReLU(inplace=True)
    
    • 1

    输出合并

    output=[branch1,branch2,branch3,branch4]
    torch.cat(output,1) #在1维度上合并
    
    • 1
    • 2

    自适应平均池化(将输入shape变成指定的输出shape)

    avgpool=torch.nn.AdaptiveAvgPool2d((1,1)) #输出shape为(1,1)
    
    • 1

    深度可分离卷积(相比普通卷积参数量大大减小)

    将卷积分为dw卷积和pw卷积(pw实际就是普通卷积,只是卷积核大小为1)
    将groups设置为输入的深度(通道数),就是深度可分离卷积

    nn.Conv2d(groups=1)
    
    • 1

    在这里插入图片描述

    倒残差模块

    在这里插入图片描述

    模型权重保存后继续训练

    1. 保存模型时保存模型权重,优化器权重,epoch
    #原本
    torch.save(
            model.state_dict(),
            os.path.join(proj_save_dir, 'iterations{}.pth'.format(whole_iter_num))
        )
        
    #修改
    checkpoint = {
            "model_state_dict": model.state_dict(),
            "optimizer_state_dict": optimizer.state_dict(),
            "epoch": epoch,
        }
    torch.save(checkpoint, os.path.join(proj_save_dir, 'iterations{}.pth'.format(whole_iter_num)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    注意:checkpoint不要写在iteration里面,要写在epoch循环中
    2. 在实例化模型代码后加上加载模型的权重等

    path_checkpoint = "./checkpoint/ORSI_COSOD/iterations102.pth"
    checkpoint = torch.load(path_checkpoint)
    
    model.load_state_dict(checkpoint['model_state_dict'])
    
    optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
    
    start_epoch = checkpoint['epoch']
    
    lr_scheduler.last_epoch = start_epoch
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 在测试阶段的代码可能也需要相应修改
    #原本
    model.load_state_dict(torch.load(os.path.join(model_dir, model_name, "iterations102.pth")))
    
    #修改
    model.load_state_dict(torch.load(os.path.join(model_dir, model_name, "iterations102.pth"))['model_state_dict'])
    
    • 1
    • 2
    • 3
    • 4
    • 5

    模型可视化

    torchsummary

    from torchsummary import summary
    
    net=vggnet()
    print(summary(net,(3,224,224),8))
    
    • 1
    • 2
    • 3
    • 4

    注意:8是batch size

    netron

    在线版

    选择要可视化的模型文件上传即可
    网站

    本地版

    终端进行安装pip install netron
    安装完成后,在脚本中 调用包 import netron
    运行程序 netron.start("model.onnx") ,会自动打开浏览器进行可视化

    net = Alexnet()
    img = torch.rand((1, 3, 224, 224))
    torch.onnx.export(model=net, args=img, f='model.onnx', input_names=['image'], output_names=['feature_map'])
    onnx.save(onnx.shape_inference.infer_shapes(onnx.load("model.onnx")), "model.onnx") #用来在onnx显示中间层的一些尺寸
    netron.start("model.onnx")
    
    • 1
    • 2
    • 3
    • 4
    • 5

    args是指输入,f是指文件名

    TensorboardX

    之前自己是用Tensorboard来可视化,链接在此,此处是用TensorboardX

    from tensorboardX import SummaryWriter as SummaryWriter
    
    net=vggnet()
    img=torch.randn(1,3,224,224)
    with SummaryWriter(log_dir='logs') as w:
        w.add_graph(net,img)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    终端cd到logs目录所在的统计目录,输入下面代码

    tensorboard --logdir ./logs --port 6006
    
    • 1

    然后打开终端提示的网站

    定义网络中可学习的参数

    register_parameter()

    作用:保存到网络对象的参数中,被优化器作用进行学习,进而保存到网络参数文件

    import torch
    import torch.nn as nn
    
    
    class MyModule(nn.Module):
        def __init__(self):
            super(MyModule, self).__init__()
            self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=1, bias=False)
            self.conv2 = nn.Conv2d(in_channels=6, out_channels=9, kernel_size=3, stride=1, padding=1, bias=False)
    
            self.register_parameter('weight', torch.nn.Parameter(torch.ones(10, 10)))
            self.register_parameter('bias', torch.nn.Parameter(torch.zeros(10)))
    
    
        def forward(self, x):
            x = self.conv1(x)
            x = self.conv2(x)
            x = x * self.weight + self.bias
            return x
    
    
    net = MyModule()
    for name, param in net.named_parameters():
        print(name, param.shape)
    
    print('\n', '*'*40, '\n')
    
    for key, val in net.state_dict().items():
        print(key, val.shape) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    DataLoader

    在这里插入图片描述

    提高训练效率小技巧(啥都生)

    onecyclelr学习率(周期性学习率策略)

    data_loader = torch.utils.data.DataLoader(...)
    optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
    scheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr=0.01, steps_per_epoch=len(data_loader), epochs=10)
    for epoch in range(10):
        for batch in data_loader:
            train_batch(...)
            scheduler.step()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    adamw(比adam好)

    torch.optim.AdamW()
    
    • 1

    batchsize

    经验:当batchsize加倍时,通常学习率也需要加倍

    num_worker

    使用多少子进程进行加载,一般设置为可用GPU数量的4倍

    amp(自动混合精度训练)

    在这里插入图片描述

    梯度裁剪(避免梯度爆炸)

    已证明在transformer和resnets等架构非常有用
    在这里插入图片描述

    BN层使用时注意的问题

    (1)训练时要将traning参数设置为True,在验证时将trainning参数设置为False。在pytorch中可通过创建模型的model.train()和model.eval()方法控制。

    (2)batch size尽可能设置大点,设置小后表现可能很糟糕,设置的越大求的均值和方差越接近整个训练集的均值和方差。

    (3)建议将bn层放在卷积层(Conv)和激活层(例如Relu)之间,且卷积层不要使用偏置bias,因为没有用

    python方法

    读取图片

    from PIL import Image
    im=Image.open('1.jpg')
    
    • 1
    • 2

    获取绝对路径方法

    import os
    data_root=os.path.abspath(os.getcwd())
    data_root
    #或者
    pwd
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    数据加载(pytorch的用法)

    data_transform={
        'train':transforms.Compose([transforms.RandomResizedCrop(224),
                                  transforms.ToTensor(),
                                  transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))]),
        'val':transforms.Compose([transforms.ToTensor(),
                                  transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
    }
    
    from torchvision import transforms,datasets,utils
    train_dataset=datasets.ImageFolder(root=data_root+'train',
                                      transform=data_transform['train'])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    获取数据的分类索引,并进行翻转

    train_dataset.class_to_idx
    cla_dict=dict((val,key) for key,val in flower_list.items())
    
    • 1
    • 2

    将类别写入json文件

    import json
    json_str=json.dumps(cla_dict,indent=4) #转成json文件
    with open('class_indices.json','w') as json_file: #写入json文件
        json_file.write(json_str)
    
    • 1
    • 2
    • 3
    • 4

    读取json文件

    try:
        json_file=open('./class_indices.json','r')
        class_indict=json.load(json_file)
    except Exception as e:
        print(e)
        exit(-1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    打印训练时间

    import time
    t1=time.time()
    time.sleep(1)
    t2=time.time()
    print(t2-t1)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    更精确用法

    import time
    t1=time.perf_counter()
    time.sleep(1)
    t2=time.perf_counter()
    print(t2-t1)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    导入同级文件的包

    from ..utils.train_utils import get_info
    
    • 1

    命令行参数argparse

    作用:让使用者以类似 Unix/Linux 命令参数的方式输入参数(在终端以命令行的方式指定参数),argparse 会自动将命令行指定的参数解析为 Python 变量,从而让使用者更加快捷的处理参数

    使用步骤如下

    导入argparse模块,并创建解释器

    import argparse
    
    # 创建解释器
    parser = argparse.ArgumentParser(description="可写可不写,此处会在命令行参数出现错误的时候,随着错误信息打印出来。")
    
    • 1
    • 2
    • 3
    • 4

    添加所需参数

    parser.add_argument('-gf', '--girlfriend', choices=['jingjing', 'lihuan'])
    # --girlfriend 代表完整的参数名称,可以尽量做到让人见名知意,需要注意的是如果想通过解析后的参数取出该值,必须使用带--的名称
    # -gf 代表参数名缩写,在命令行输入 -gf 和 --girlfriend 的效果是一样的,用简称的作用是简化参数输入
    # choices 代表输入参数的值只能是这个choices里面的内容,其他内容则会保错
    
    parser.add_argument('food')
    # 该种方式则要求必须输入该参数; 输入该参数不需要指定参数名称,指定反而会报错,解释器会自动将输入的参数赋值给food
    
    parser.add_argument('--house', type=int, default=0)
    # type  代表输入参数的类型,从命令行输入的参数,默认是字符串类型
    # default 如果不指定该参数的值,则会使用该默认值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    解析参数

    # 进行参数解析
    args = parser.parse_args() 
    
    • 1
    • 2
  • 相关阅读:
    【附源码】计算机毕业设计SSM社区团购服务系统
    短链接网站系统设计与实践
    国际版腾讯云/阿里云:云解析DNS是什么
    QT 绘画功能的时钟
    构建未来交通:香橙派OPI Airpro上的智能交通监管系统
    Vue中的侦听器 Watch
    100天精通Python(数据分析篇)——第51天:numpy函数进阶
    《混沌工程》读书笔记
    第一章 STM32 CubeMX (CAN通信发送)基础篇
    【C】—文件版本通讯录的实现
  • 原文地址:https://blog.csdn.net/qq_40527560/article/details/132722317