zoukankan      html  css  js  c++  java
  • 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写诗
    • 可能用到的函数

      • nn.InstanceBatchNorm2d(ch, affine=True)
      • nn.ReLU(inplace=True)
      • tensor.item()
      • nn.ReflectionPad2d(ch, scale_factor=2)
      • meter.value()[0]
      • 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优先级要高
      • 如果使用已经预训练好的模型, 拿来用的话, 需要调用eval()方法来去掉一些DropOut层, 在使用如下方式不计算梯度
      
      for param in model.parameters():
          param.requires_grad = False
      
    • NLP

      • 像素的大小天生就具有色彩信息, 但是词的数值大小很难表征词的含义, 最初人们为了方便, 采用 One-Hot 的编码方式, 一般来说中文语料库的字平均在 8000-50000, 而词则在几十万左右, 使用 One-Hot 的编码方式很方便, 但是冗余很多, 并且无法体现词于词之间的关系 ( 使用 One-Hot 编码之后的每一个词都是正交的, 即任意两个词不相关 ), 如果语料库很大, One-Hot 的维度会非常的高, 在深度学习中容易造成维度灾难, 于是诞生了词向量 (Word Vector/Word Embedding)
      • 词向量从 One-Hot 发展过来, 将 One-Hot 表示的高纬度映射到低纬度, 同时保留词与词之间的相关性, 如果特征相近, 则词与词之间靠的近; Embedding 是一个映射关系 ( 一个矩阵 ), 如果有 N 个词, 每一个词的维度降维到了 W, 则矩阵的维度为 (N, W). 假如输入了一组序列 (N, W), 其中 N 为 Batch Size, W 为序列的长度, 也就是词的个数, 通过词向量 (矩阵的维度为 (NumberOfWords, Dimension)) 映射得 (N, W * Dimension); 在 PyTorch 中, 词向量模块在 torch.nn.Embedding
    • 其他

      • 转置卷积维度计算公式
        • (H_{out}=(H_{in}-1) imes{stride}-2 imes{padding}+ksize)就是卷积维度公式的逆过程
      • PyTorch 的 CrossEntropy 可以自动转 One-Hot
      • 获取 Tensor 中的变量值, 调用 item() 方法
      • 研究表明, 几乎所有神经网络的第一层学习到的都是关于线条和颜色的信息, 直观理解就是像素组成颜色, 点组成线, 这与人眼的感知特征十分相像, 在往上层, 神经网络开始关注一些复杂的特征, 例如拐角或者某些特殊的形状, 这些特征可以看成是低层次的特征组合, 随着深度的加深, 神经网络关注的信息逐渐抽象, 例如有一些卷积核关注的是这张图中有个鼻子, 或者是图中有张人脸, 以及对象之间的空间关系, 例如鼻子在人脸的中间等(这不是我说的, 是唐进民作者说的)
      • 取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')
        
      • 一般上采样的方法
        • 使用转置卷积, 但是当ksize / stride无法整除时容易导致checkerboard artifact(棋盘效应), 虽然理论上神经网络可以小心地学习从而消除棋盘效应
        • 先对features map进行插值上采样, 将大小resize到我们期望的大小, 再使用padding或者reflectionpadding进行卷积
  • 相关阅读:
    阿里消息队列中间件 RocketMQ 源码分析 —— Message 拉取与消费(上)
    数据库中间件 ShardingJDBC 源码分析 —— SQL 解析(三)之查询SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(六)之删除SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(五)之更新SQL
    消息队列中间件 RocketMQ 源码分析 —— Message 存储
    源码圈 300 胖友的书单整理
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(一)分库分表配置
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(四)之插入SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(二)之分库分表路由
    C#中Math类的用法
  • 原文地址:https://www.cnblogs.com/megachen/p/11114644.html
Copyright © 2011-2022 走看看