• PyTorch上路


    PyTorch

    torch.autograd模块

    • 深度学习的算法本质上是通过反向传播求导数, PyTorch的autograd模块实现了此功能, 在Tensor上的所有操作, autograd都会为它们自动提供微分, 避免手动计算导数的复杂过程。
    • autograd.Variable是autograd的核心类, 它简单封装了Tensor(最新版PyTorch已经将Variable和Tensor的API合并, 以后直接使用Tensor即可, 不要使用Variable了)
      • backward: 一个Scalar变量调用backward方法可以实现反向传播, 计算出各个变量的梯度
      • data: 数据
      • grad: 梯度
      • grad_fn: 计算梯度的函数
      • 注意:
        • 方向传播backward中grad的值是累加的, 第二次反向传播的梯度就加上第一次反向传播的梯度, 这不是我们期望的, 所以进行了一次反向传播之后应该清空梯度, 使用grad.data.zero_()

    torch.nn模块

    • torch.autograd提供了反向传播的功能, 已经可以仅仅通过torch.autograd来写深度学习代码了, 但是在大多数情况下还是略显麻烦, torch.nn提供了神经网络设计的模块化接口, torch.nn模块是以torch.autograd为基础的。

    • torch.nn.Module是torch.nn模块的核心类

      • 使用torch.nn.Module搭建LeNet
      
      #!/usr/bin/env python
      # -*- coding: utf-8 -*-
      
      
      import torch as t
      from torch.autograd import Variable
      import torch.optim as optim
      import torch.nn as nn
      import torch.nn.functional as F
      
      
      class LeNet(nn.Module):
      
      
          def __init__(self):
              super(LeNet, self).__init__()
              # 需要学习的参数要写在__init__()中, 并且作为属性
              # 在__init__()并不是定义图, 是在设置要学习的参数(进行初始化), nn.Conv2d这么多的参数就是在确定卷积核的维度而已
              # 接着随机初始化参数W与b
              self.conv1 = nn.Conv2d(1, 6, 5)
              self.conv2 = nn.Conv2d(6, 16, 5)
              self.fc1 = nn.Linear(16 * 5 * 5, 120)
              self.fc2 = nn.Linear(120, 84)
              self.fc3 = nn.Linear(84, 10)
      
          def forward(self, x):
              # 这里才是搭建网络的步骤
              net = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
              net = F.max_pool2d(F.relu(self.conv2(net)), 2)
              net = net.view(net.size()[0], -1)
              net = F.relu(self.fc1(net))
              net = F.relu(self.fc2(net))
              net = self.fc3(net)
              return net
      
      
      def main(argv=None):
          # 创建网络
          lenet = LeNet()
          # PyTorch中维度是通道优先, 与TensorFlow不同
          # 伪造数据
          x = Variable(t.randn(1, 1, 32, 32))
          labels = Variable(t.arange(0, 10).type(t.FloatTensor))
          
          # 前向传播
          out = lenet(x)
          # 定义损失函数
          criterion = nn.MSELoss()
          # 计算损失
          loss = criterion(out, labels) 
          # 反向传播前先清空grad
          lenet.zero_grad()
          
          loss.backward()
          """
          # 手动实现SGD(最简单的优化策略)
          learning_rate = 0.01
          for f in lenet.parameters():
              f.data.sub_(f.data * learning_rate)
          """
          # 使用PyTorch提供的SGD
          optimizer = optim.SGD(lenet.parameters(), lr=0.01) 
          optimizer.step()
      
          if __name__ == '__main__':
              main()
      
      
    • 扩展PyTorch中的函数

      1. 可以选择继承PyTorch中的Function对象, 实现Function的staticmethod, 分别为forward, backward
      2. 上面的方法需要计算函数的反向传播, 其实也可以向TensorFlow一样, 直接写前向传播公式, PyTorch会通过自动求微分autograd机制实现反向传播
      3. 方法一和方法二的区别, 方法一, 采用的是面向对象的思维, 如果要自定义一个新的函数, 需要继承Function对象; 方法二, 一般定义的是一个函数。他们关系就像是PyTorch中的nn.ReLU和nn.funcional.relu一样
    • torchvision模块

      • 提供已经预训练的网络模型(AlexNet, VGG, ResNet等), 在torchvision.models中
      • 提供常用的数据集(MSCOCO, MNIST, CIFAR等), 在torchvision.datasets中
      • 图像预处理模块, torchvision.transform
        • CenterCrop
        • ToTensor
        • ToPILImage
        • RandomHorizontalFlip
        • Resize
        • Normalize
        • Compose用于将多个操作何为一个Sequence
      • 工具模块, torchvision.utils
        • make_grid, 将图像拼进去指定的网格中
        • save_image, 将Tensor转为Image并保存到磁盘中
    • 自定义数据集

    
    import os
    import numpy as np
    import torch as t
    import torch.nn as nn
    import PIL
    from PIL import Image
    from torch.utils.data.dataset import Dataset
    from torch.utils.data.dataloader import default_collate
    from torch.utils.data import DataLoader
    from torchvision import Transform as T
    
    class BaseCatVsDogDataset(Dataset):
        
        
        def __init__(self, root, transform=None):
            ims = os.listdir(root)
            self.ims = [os.path.join(root, im) for im in ims]
            self.transform = transform
            
        def __getitem__(self, idx):
            im_path = self.ims[idx]
            label = 1 if 'dog' in os.split('/')[0] else 0
            im = Image.open(im_path)
            if self.transform:
                data = self.transform(im)
            else:
                data = t.Tensor(np.asarray(im))
            return data, label
        
        def __len__(self):
            return len(self.ims)
            
            
    class CatVsDogDataset(BaseCatVsDogDataset):
        """
        
        Notes
        -----
        Subclass ``CatVsDogDataset`` are used to prevent abnormal exit of program caused by abnormal data
        """
        def __init__(self, root, transform=None):
            super(CatVsDogDataset, self).__init__(root, transform=transform)
        
        def __getitem__(self, idx):
            try:
                return super(CatVsDogDataset, self).__getitem__(idx)
            except:
                # An exception returns to None
                return None, None
    
    def collate_fn(batch):
        """
        Parameters
        ----------
        batch : list
            At this point, batch hasn't been integrated into [batch_size, C, H, W], 
            and each image is placed in a list [(im, label), (im, label), ...]
        """
        # Filter out None values
        batch = filter(lambda x: x[0] is not None, batch)
        # Splice the values in batch whose type is ``list`` into a matrix
        return default_collate(batch)
        
        
    def main(): 
        transform = T.Compose([T.Resize(224), T.CenterCrop(224), T.RandomHorizontalFlip(), T.ToTensor(), T.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])])
        dataset = CatVsDogDataset('./data', transform=transform)
        dataloader = DataLoader(dataset, shuffle=True, batch_size=64, drop_last=False, collate_fn=collate_fn) 
        for ims, labels in dataloader:
            print(ims.size(), labels.size())
        
    if __name__ == '__main__':
        main()
    
    • visdom工具

      • 先启动守护进程, nohup python -m visdom.server &
      • 创建vis对象, vis = visdom.Visdom(env='test'), env为环境名称
      • 绘制标量变化, vis.line(X=x, Y=y, win='sin', opts={'title': 'This is a title'}), win是窗口名称, opts提供额外的属性, title为标题
      • 绘制分布图, vis.histogram(X=x, Y=y, win='test', opts={'title': 'This is a new title'})
    • PyTorch规范

      • 目录结构
      .
      ├── README.md
      ├── checkpoints: 保存训练好的权重
      ├── config.py: 配置信息, 里面是一个DefaultConfig类, 还有一个parse方法, 用于解析命令行参数来覆盖DefaultConfig中的默认配置
      ├── data: 处理数据集
      │   ├── __init__.py
      │   └── dataset.py: 里面定义了一个数据集名称的类
      ├── dataset: 存放训练和测试的数据
      ├── main.py: 启动文件, 包含有train, val, test三个函数, 其中train会调用val在训练中进行交叉验证, test需要用户另外再启动一个进程执行
      ├── models: 存放网络模型
      │   ├── __init__.py: 在文件中导入模型, 方便在main.py中导入
      │   ├── basemodule.py: 扩展了nn.Module, 主要添加了load和save模型的方法
      │   └── resnet34.py: 不继承nn.Module, 而是继承BaseModule
      ├── requirements.txt: 程序依赖说明文件
      ├── tmp: 临时目录, 如果在该目录下创建一个debug文件, 则程序进入debug模式
      └── utils: 工具
          ├── __init__.py
          └── visualize.py: 封装了visdom的可视化
      
      • config.py中一般配置
        • num_workers
        • use_gpu
        • lr
        • max_epoch
        • batch_size
        • save_path
        • env
        • debug_file
        • data_root
    • 试手项目

      • DogVsCat
      • FastNeuralStyle
      • RNN写诗
    • 可能用到的函数

      • torch.bmm: batch matmul
      • nn.ReflectionPad2d(size): 镜像padding
      • clamp(min, max): if x < min: min, if x > max: max, else x
      • torchvision.transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])是现将图片归一化到0-1, 在减去均值除以标准差, 所以要可视化的话要乘以标准差, 加上均值
      • with torch.no_grad(): 类似于之前版本中的volatile=True, 比requires_grad优先级要高
    • 其他

      • 转置卷积维度计算公式
        • (H_{out}=(H_{in}-1) imes{stride}-2 imes{padding}+ksize)
      • 研究表明, 几乎所有神经网络的第一层学习到的都是关于线条和颜色的信息, 直观理解就是像素组成颜色, 点组成线, 这与人眼的感知特征十分相像, 在往上层, 神经网络开始关注一些复杂的特征, 例如拐角或者某些特殊的形状, 这些特征可以看成是低层次的特征组合, 随着深度的加深, 神经网络关注的信息逐渐抽象, 例如有一些卷积核关注的是这张图中有个鼻子, 或者是图中有张人脸, 以及对象之间的空间关系, 例如鼻子在人脸的中间等(这不是我说的, 是唐进民作者说的)
      • 取patch像素中的值并累加就会抛弃空间信息, 因为一张图片的像素随机打乱得到的累加也是一样的, 但是亮度信息还保留, 因此还会有纹理信息, 色彩信息等
      • PyTorch中网络中的__init__方法中定义了self.left = nn.Conv2d(in_ch, out_ch, 3, 1)等, 此时就会在使用的optimizer中会有一个param_group, PyTorch支持分层设置学习率, 使用
        
        for param_group in optimizer.param_groups:
            param_group['lr'] = lr * lr_decay
        
      • PyTorch中, 对于Scalar的Tensor, 使用item()可是直接返回里面的数值
      • torchnet.meter对象直接不能直接相加, 需要调用value()[0]获取里面的tensor才能相加, value()返回的是元组, [0]是我们期望的元素, [1]是Inf
      • visdom.line参数一般是这样设置的
        vis.line(X=np.array([x]), Y=np.array([y]), win=name, opts={'title': name}, update=None if not x else 'append')
        
  • 相关阅读:
    Java
    Leetcode 计划
    Java虚拟机
    浅谈 MVP in Android
    【PAT】B1074 宇宙无敌加法器(20 分)
    【PAT】B1076 Wifi密码(15 分)
    【PAT】B1075 链表元素分类(25 分)
    【PAT】B1077 互评成绩计算(20 分)
    【PAT】B1078 字符串压缩与解压(20 分)
    【PAT】B1079 延迟的回文数(20 分)
  • 原文地址:https://www.cnblogs.com/megachen/p/10860395.html
Copyright © 2020-2023  润新知