zoukankan      html  css  js  c++  java
  • PyTorch--常用的工具

    PyTorch–常用的工具

    在训练神经网络的过程中需要用到很多工具,其中最重要的三部分是数据,可视化和GPU加速

    一.数据处理

    数据的处理对训练神经网络来说十分重要,良好的数据处理不仅加速模型训练,也会提高模型效果

    数据加载

    在PyTorch中,数据加载可通过自定义的数据集对象实现。数据集对象被抽象为Dataset类,实现自定义的数据集需要继承Dataset,并实现两个python魔法方法:

    • getitemgetitem**(index)
    • lenlen**()
    import torch as t
    from torch.utils import data
    
    import os
    from PIL import Image
    import numpy as np
    
    class DogCat(data.Dataset):
        def __init__(self,root):
            imgs=os.listdir(root)
            # 所有图片的绝对路径
            # 这里不实际加载图片,只是指定路径,当调用__getitem__时才会真正读图片
            self.imgs=[os.path.join(root,img) for img in imgs]
            
        def __getitem__(self,index):
            img_path=self.imgs[index]
            # dog->1,cat->0
            label=1 if 'dog' in img_path.split('/')[-1] else 0
            pil_img=Image.open(img_path)
            array=np.asarray(pil_img)
            data=t.from_numpy(array)
            return data,label
        
        def __len__(self):
            return len(self.imgs)
    
    dataset=DogCat('./data/dogcat/')
    img,label=dataset[0]   # 相当于调用dataset.__getitem__(0)
    for img,label in dataset:
        print(img.size(),img.float().mean(),label)
    
    torch.Size([500, 497, 3]) tensor(106.4915) 0
    torch.Size([499, 379, 3]) tensor(171.8085) 0
    torch.Size([236, 289, 3]) tensor(130.3004) 0
    torch.Size([374, 499, 3]) tensor(115.5177) 0
    torch.Size([375, 499, 3]) tensor(116.8139) 1
    torch.Size([375, 499, 3]) tensor(150.5080) 1
    torch.Size([377, 499, 3]) tensor(151.7174) 1
    torch.Size([400, 300, 3]) tensor(128.1550) 1
    

    通过上面的代码,我们学习了如何自定义自己的数据集,并可以,依次获取。但这里,返回的数据不适合实际使用,因其具有如下两方面问题:

    • 返回样本的形状不一,每张图片的大小不一样,这对于需要取batch训练的神经网络来说很不友好
    • 返回样本的数值较大,未归一化至[-1,1]

    针对上述问题,PyTorch提供了torchvision。它是一个视觉工具包,提供了很多视觉图像处理的工具,其中transforms模块提供了对PIL Image对象和Tensor对象的常用操作

    对PIL Image的常见操作如下:

    • Scale:调整图片尺寸,长宽比保持不变
    • CenterCrop,RandomGrop,RandomSizedGrop:裁剪图片
    • Pad:填充
    • ToTensor:将PIL Image对象转成Tensor,会自动将[0,255]归一化至[0,1]

      对Tensor的常见操作如下:
    • Normalize:标准化,即减均值,除以标准差
    • ToPILImage:将Tensor转为PIL Image对象

    下面我们就用transforms的这些操作来优化上面实现的dataset

    import os
    from PIL import Image
    import numpy as np
    from torchvision import transforms as T
    
    transform=T.Compose([
        # 缩放图片(Image),保持长宽比不变,最短边为224像素
        T.Scale(224),
        # 从图片中间切出224*224的图片
        T.CenterCrop(224),
        # 将图片(Image)转成Tensor,归一化至[0,1]
        T.ToTensor(),
        # 标准化至[-1,1],规定均值和标准差
        T.Normalize(mean=[.5,.5,.5],std=[.5,.5,.5])
    ])
    
    class DogCat(data.Dataset):
        def __init__(self,root,transforms=None):
            imgs=os.listdir(root)
            # 所有图片的绝对路径
            # 这里不实际加载图片,只是指定路径,当调用__getitem__时才会真正读图片
            self.imgs=[os.path.join(root,img) for img in imgs]
            self.transforms=transforms
            
        def __getitem__(self,index):
            img_path=self.imgs[index]
            label=0 if 'dog' in img_path.split('/')[-1] else 1
            data=Image.open(img_path)
            if self.transforms:
                data=self.transforms(data)
            return data,label
        
        def __len__(self):
            return len(self.imgs)
        
    dataset=DogCat('./data/dogcat/',transforms=transform)
    img,label=dataset[0]   # 相当于调用dataset.__getitem__(0)
    for img,label in dataset:
        print(img.size(),img.float().mean(),label)
    
    E:Anacondalibsite-packages	orchvision	ransforms	ransforms.py:207: UserWarning: The use of the transforms.Scale transform is deprecated, please use transforms.Resize instead.
      warnings.warn("The use of the transforms.Scale transform is deprecated, " +
    
    
    torch.Size([3, 224, 224]) tensor(-0.1654) 1
    torch.Size([3, 224, 224]) tensor(0.3892) 1
    torch.Size([3, 224, 224]) tensor(0.0711) 1
    torch.Size([3, 224, 224]) tensor(-0.0462) 1
    torch.Size([3, 224, 224]) tensor(-0.0649) 0
    torch.Size([3, 224, 224]) tensor(0.1176) 0
    torch.Size([3, 224, 224]) tensor(0.2234) 0
    torch.Size([3, 224, 224]) tensor(-0.0267) 0
    

    除了上述操作之外,transforms还可以通过lambda封装自定义的转换策略。例如,想对PIL Image进行随机旋转,则可写成trans=T.Lambda(lambda img:img.rotate(random()*360))

    torchvision已经预先实现了常用的Dataset,包括前面使用过的CIFAR-10,以及ImageNet,COCO,MNIST,LSUN等数据集,可通过调用torchvision.datasets下相应对象来调用相关数据集

    ImageFolder假设所有的文件按文件夹保存,每个文件夹下存储同一个类别的图片,文件夹名为类名,其构造函数如下:

    ImageFolder(root,transform=None,target_transform=None,loader=default_loader)
    

    它主要有以下四个参数:

    • root:在root指定的路径下寻找图片
    • transform:对PIL Image进行转换操作,transform的输入是使用loader读取图片的返回对象
    • target_transform:对label的转换
    • loader:指定加载图片的函数,默认操作是读取为PIL Image对象

    label是按照文件夹名顺序后存成字典的,即{类名:类序号(从0开始)},一般来说最好直接将文件夹命名为从0开始的数字,这样会和ImageFolder实际的label一致,如果不是这种命名规范,建议通过self.class_to_idx属性了解label和文件夹名的映射关系

    from torchvision.datasets import ImageFolder
    dataset=ImageFolder("data/dogcat_2/")
    
    # cat文件夹的图片对应label 0,dog对应1
    dataset.class_to_idx
    
    {'cat': 0, 'dog': 1}
    
    # 所有图片的路径和对应的label
    dataset.imgs
    
    [('data/dogcat_2/cat\cat.12484.jpg', 0),
     ('data/dogcat_2/cat\cat.12485.jpg', 0),
     ('data/dogcat_2/cat\cat.12486.jpg', 0),
     ('data/dogcat_2/cat\cat.12487.jpg', 0),
     ('data/dogcat_2/dog\dog.12496.jpg', 1),
     ('data/dogcat_2/dog\dog.12497.jpg', 1),
     ('data/dogcat_2/dog\dog.12498.jpg', 1),
     ('data/dogcat_2/dog\dog.12499.jpg', 1)]
    
    # 没有任何的transform,所以返回的还是PIL Image对象
    # 第一维是第几张图,第二维为1返回label
    dataset[0][1]
    # 为0返回图片数据,返回的Image对象
    dataset[0][0]
    

    output_22_0.png

    from torchvision import transforms as T
    # 加上transform
    normalize=T.Normalize(mean=[0.4,0.4,0.4],std=[0.2,0.2,0.2])
    
    transform=T.Compose([
        T.RandomSizedCrop(224),
        T.RandomHorizontalFlip(),
        T.ToTensor(),
        normalize,
    ])
    
    dataset=ImageFolder("data/dogcat_2/",transform=transform)
    
    # 深度学习中图片数据一般保存成CxHxW,即通道数x图片高x图片宽
    dataset[0][0].size()
    
    torch.Size([3, 224, 224])
    
    to_img=T.ToPILImage()
    # 0.2和0.4是标准差和均值的近似
    to_img(dataset[0][0]*0.2+0.4)
    

    output_26_0.png

    Dataset只负责数据的抽象,一次调用getitem只返回一个样本。前面提到过,在训练神经网络时,是对一个batch的数据操作,同时还需要对数据进行shuffle和并行加速。对此,PyTorch提供了DataLoader帮助我们实现这些功能

    DataLoader的函数定义如下:

    DataLoader(dataset,batch_size=1,shuffle=False,smapler=None,num_workers=0,collate_fn=default_collate,pin_memory=False,drop_last=False)
    
    • dataset:加载的数据集
    • batch_size:batch size(批大小)
    • shuffle:是否将数据打乱
    • sampler:样本抽样
    • num_workers:使用多进程加载的进程数,0代表不使用多进程
    • collate_fn:如何将多个样本数据拼接成一个batch,一般使用默认的拼接方式即可
    • pin_memory:是否将数据保存在pin memory区,pin memory中的数据转到GPU会快一些
    • drop_last:dataset中的数据个数可能不是batch_size的整数倍,drop_last为True会将多出来不足一个batch的数据丢弃
    from torch.utils.data import DataLoader
    
    dataloader=DataLoader(dataset,batch_size=3,shuffle=True,num_workers=0,drop_last=False)
    
    dataiter=iter(dataloader)
    imgs,labels=next(dataiter)
    # batch_size,channel,height,weight
    imgs.size()
    
    torch.Size([3, 3, 224, 224])
    

    dataloader是一个可迭代的对象,我们可以像使用迭代器一样使用它,例如:

    for batch_datas,batch_labels in dataloader:
        train()
        
    # 或
    dataiter=iter(dataloader)
    batch_datas,batch_labelsl=next(dataiter)
    

    在数据处理中,有时会出现某个样本无法读取等问题。这时在getitem函数中将出现异常,此时最好的解决方案即是将出错的样本剔除。如果遇到这种情况实在无法处理,则可以返回None对象,然后在DataLoader中实现自定义的collate_fn,将空对象过滤掉。但要注意,在这种情况下dataloader返回的一个batch的样本数目会少于batch_size

    class NewDogCat(DogCat):
        def __getitem__(self,index):
            try:
                # 调用父类的获取函数,即DogCat.__getitem__(self,index)
                return super(NewDogCat,self).__getitem__(index)
            except:
                return None,None
    
    # 导入默认的拼接方式
    from torch.utils.data.dataloader import default_collate
    
    def my_collate_fn(batch):
        """
        batch中的每个元素形如(data,label)
        """
        # 过滤为None的数据
        batch=list(filter(lambda x:x[0] is not None,batch))
        return default_collate(batch)
    
    dataset=NewDogCat("data/dogcat_wrong/",transforms=transform)
    
    dataset[5]
    
    (tensor([[[-0.5098, -0.8824, -1.1176,  ...,  1.8431,  1.4902,  0.4314],
              [-0.6078, -0.9412, -1.0196,  ...,  1.8431,  1.5098,  0.4706],
              [-0.7647, -1.0196, -0.9608,  ...,  1.8235,  1.5294,  0.4902],
              ...,
              [ 2.9216,  2.9608,  2.9020,  ...,  1.0588,  1.0588,  1.0588],
              [ 2.8824,  2.9216,  2.8431,  ...,  1.4118,  1.3725,  1.3725],
              [ 2.8431,  2.8824,  2.8039,  ...,  1.5294,  1.5294,  1.5490]],
     
             [[-0.0392, -0.4314, -0.6275,  ...,  1.4706,  1.1176,  0.1373],
              [-0.1373, -0.4902, -0.5294,  ...,  1.4706,  1.1569,  0.1569],
              [-0.2941, -0.5686, -0.5098,  ...,  1.4902,  1.1765,  0.1765],
              ...,
              [ 2.9216,  2.9608,  2.9020,  ...,  1.3725,  1.3529,  1.3529],
              [ 2.9216,  3.0000,  2.9216,  ...,  1.6667,  1.6078,  1.6078],
              [ 2.9020,  2.9804,  2.9216,  ...,  1.7843,  1.7843,  1.8039]],
     
             [[ 0.8824,  0.5294,  0.3529,  ...,  0.4902,  0.1373, -0.7647],
              [ 0.7843,  0.4706,  0.4314,  ...,  0.5098,  0.1961, -0.7255],
              [ 0.6078,  0.3922,  0.4706,  ...,  0.5098,  0.2157, -0.6863],
              ...,
              [ 2.9216,  2.9608,  2.9020,  ...,  1.0196,  1.0392,  1.0784],
              [ 2.9020,  2.9804,  2.9020,  ...,  1.4706,  1.4118,  1.4118],
              [ 2.8824,  2.9608,  2.8824,  ...,  1.5882,  1.5882,  1.6078]]]), 0)
    
    dataloader=DataLoader(dataset,2,collate_fn=my_collate_fn,num_workers=1)
    for batch_datas,batch_labels in dataloader:
        print(batch_datas.size(),batch_labels.size())
    

    二.视觉工具包:torchvision

    torchvision主要包括以下三部分:

    • models:提供深度学习中各种经典网络的网络结构及预训练好的模型,包括AlexNet,VGG系列,ResNet系列,Inception系列等
    • datasets:提供常用的数据集加载,设计上都是继承torch.utils.data.Dataset,主要包括MNIST,CIFAR10/100,ImageNet,COCO等
    • transforms:提供常用的数据预处理操作,主要包括对Tensor及PIL Image对象的操作
    from torchvision import models
    from torch import nn
    
    # 加载预训练好的模型,如果不存在会下载
    # 预训练好的模型保存在~/.torch/models/下面
    resnet34=models.resnet34(pretrained=True,num_classes=1000)
    
    # 修改最后的全连接层为10分类问题(默认是ImageNet上的1000分类)
    resnet34.fc=nn.Linear(512,10)
    
    from torchvision import datasets
    
    # 指定数据集路径为data,如果数据集不存在则进行下载
    # 通过train=False获取测试集
    dataset=datasets.MNIST("data/",download=True,train=False,transform=transform)
    
    Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to data/MNIST
    aw	rain-images-idx3-ubyte.gz
    
    
    100%|█████████▉| 9871360/9912422 [00:13<00:00, 1188747.47it/s]
    
    Extracting data/MNIST
    aw	rain-images-idx3-ubyte.gz
    Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to data/MNIST
    aw	rain-labels-idx1-ubyte.gz
    
    
    
    0it [00:00, ?it/s]
      0%|          | 0/28881 [00:00<?, ?it/s]
     57%|█████▋    | 16384/28881 [00:00<00:00, 59945.44it/s]
    32768it [00:00, 38427.66it/s]                           
    
    Extracting data/MNIST
    aw	rain-labels-idx1-ubyte.gz
    Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to data/MNIST
    aw	10k-images-idx3-ubyte.gz
    
    
    
    0it [00:00, ?it/s]
      0%|          | 0/1648877 [00:00<?, ?it/s]
      1%|          | 16384/1648877 [00:00<00:26, 62465.55it/s]
      3%|▎         | 49152/1648877 [00:01<00:21, 74382.81it/s]
      6%|▌         | 98304/1648877 [00:01<00:16, 92296.04it/s]
     10%|▉         | 163840/1648877 [00:01<00:12, 115207.62it/s]
     14%|█▍        | 229376/1648877 [00:01<00:10, 138903.86it/s]
     24%|██▍       | 393216/1648877 [00:01<00:06, 182372.38it/s]
     26%|██▋       | 434176/1648877 [00:02<00:07, 172328.72it/s]
     41%|████      | 679936/1648877 [00:02<00:04, 231085.39it/s]
     48%|████▊     | 794624/1648877 [00:02<00:03, 273772.31it/s]
     56%|█████▌    | 917504/1648877 [00:02<00:02, 320331.10it/s]
     64%|██████▎   | 1048576/1648877 [00:03<00:01, 410840.31it/s]
     68%|██████▊   | 1122304/1648877 [00:03<00:01, 408075.09it/s]
     73%|███████▎  | 1196032/1648877 [00:03<00:01, 393825.71it/s]
     81%|████████  | 1335296/1648877 [00:03<00:00, 435269.59it/s]
     90%|████████▉ | 1482752/1648877 [00:03<00:00, 475917.06it/s]
     99%|█████████▉| 1630208/1648877 [00:04<00:00, 560651.29it/s]
    1654784it [00:04, 395803.27it/s]                             
    
    Extracting data/MNIST
    aw	10k-images-idx3-ubyte.gz
    Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to data/MNIST
    aw	10k-labels-idx1-ubyte.gz
    
    
    
    0it [00:00, ?it/s]
      0%|          | 0/4542 [00:00<?, ?it/s]
    8192it [00:00, 16627.37it/s]            
    
    Extracting data/MNIST
    aw	10k-labels-idx1-ubyte.gz
    Processing...
    Done!
    
    
    9920512it [00:30, 1188747.47it/s]
    

    Transform中涵盖了大部分对Tensor和PIL Image的常用处理。需要注意的是转换分为两步,第一步:构建转换操作,例如transform=transforms.Normalize(mean=x,std=y);第二步:执行转换操作,例如ouput=transf(input).另外还可将多个处理操作用Compose拼接起来,构成一个处理转换流程

    from torchvision import transforms
    
    to_pil=transforms.ToPILImage()
    to_pil(t.randn(3,64,64))
    

    output_44_0.png

    torchvision还提供了两个常用的函数。一个是make_grid,它能将多张图片拼接在一个网格中;另一个是save_image,它能将Tensor保存成图片

    len(dataset)
    
    10000
    
    dataloader=DataLoader(dataset,shuffle=True,batch_size=16)
    
    from torchvision.utils import make_grid,save_image
    
    dataiter=iter(dataloader)
    img=make_grid(next(dataiter)[0],4)
    
    to_img(img)
    
    save_image(img,'a.png')
    
    Image.open('a.png')
    

    三.可视化工具

    介绍两个深度学习中常用的可视化工具:Tensorboard和visdom

    1.Tensorboard

    最初,Tensorboard是作为Tensorflow的可视化工具迅速流行起来的。作为和TensorFlow深度集成的工具,Tensorboard能够展现Tensorflow网络计算图,绘制图像生成的定量指标图及附加数据。在PyTorch中使用Tensorboard_logger进行训练损失的可视化。Tensorboard_logger是TeamHG-Memex开发的一款轻量级工具,它将Tensorboard的功能抽取出来,使非Tensorflow用户也能使用它进行可视化,但其支持的功能有限

    tensorboard_logger的安装主要分为以下两步:

    • 安装tensorflow:建议安装CPU-Only的版本
    • 安装tensorboard_logger:使用pip install tensorboard_logger

    tensorboard_logger的使用非常简单。首先用如下命令启动Tensorboard:

    tensorboard --logdir <your/running/dir> --port <your_bind_port>
    
    from tensorboard_logger import Logger
    
    # 构建logger对象,logdir用来指定log文件的保存路径
    # flush_secs用来指定刷新同步间隔
    logger=Logger(logdir='experimient_cnn',flush_secs=2)
    
    for ii in range(100):
        logger.log_value('loss',10-ii**0.5,step=ii)
        logger.log_value('accuracy',ii**0.5/10)
    

    输入:tensorboard --logdir F:PythonPytestDLpytorchexperimient_cnn --port 6006,打开浏览器http://localhost:6006

    image.png

    左侧的Horizontal Axis下有如下三个选项:

    • step:根据步长来记录,log_value是指如果有步长,则将其作为x轴坐标描点画线
    • Relative:用前后相对顺序描点画线,可认为logger自己维护了一个step属性,每调用一次log_value就自动加1
    • Wall:按时间排序描点画线

    左侧的Smoothing条可以左右拖动,用来调节平滑的幅度。单击页面右上角的刷新按钮可立即刷新结果,默认是每30秒自动刷新新数据。Tensorboard_logger的使用十分简单,但它只能统计简单的数值信息,不支持其他功能

    除了tensorboard_logger,还有专门针对PyTorch开发的TensorboardX,它封装更多的Tensorboard接口,支持记录标量,图片,直方图,声音,文本,计算图和embedding等信息,几乎包括和Tensorflow的Tensorboard完全一样的功能

    2.visdom

    visdom是Facebook专门为PyTorch开发的一款可视化工具,visdom十分轻量级,却支持非常丰富的功能,能胜任大多数的科学运算可视化任务

    visdom中有以下两个重要概念:

    • env:环境。不同环境的可视化结果相互隔离,互不影响,在使用时如果不指定env,默认使用main
    • pane:窗格。窗格可用于可视化图像,数值或打印文本等,其可以拖动,缩放,保存和关闭

    通过命令pip install visdom即可完成visdom的安装。安装完成后,需通过python -m visdom.server命令启动visdom服务

    import visdom
    
    # 新建一个连接客户端
    # 指定env=u'test1',默认端口为8097,host为'localhost'
    vis=visdom.Visdom(env=u'test1')
    
    x=t.arange(1,30,0.01)
    y=t.sin(x)
    vis.line(X=x,Y=y,win='sinx',opts={'title':'y=sin(x)'})
    
    WARNING:root:Setting up a new session...
    
    'sinx'
    

    37244307c7fa7a.svg

    下面我们逐一分析这几行代码:

    • vis=visdom.Visdom(env=u’test1’):用于构建一个客户端,客户端除指定env外,还可以指定host,port等参数
    • vis作为一个客户端对象,可以使用如下常见的画图函数
      • line:类似MATLAB中的plot操作,用于记录某些标量的变化,例如损失,准确率等
      • image:可视化图片,可以是输入的图片,也可以是GAN生成的图片,还可以是卷积核的信息
      • text:用于记录日志等文字信息,支持HTML格式
      • histgram:可视化分布,主要是查看数据,参数的分布
      • scatter:绘制散点图
      • bar:绘制柱状图
      • pie:绘制饼状图

    3724469a9a9450.svg

    image的画图功能可分为如下两类:

    • iamge接收一个二维或三维向量,H_W或3_H*W,前者是黑白图像,后者是彩色图像
    • images接收一个四维向量N_C_H*W,C可以是1或3,分别代表黑白和彩色图像
    # 可视化一张随机的黑白图片
    vis.image(t.randn(64,64).numpy())
    
    # 可视化一张随机的彩色图片
    vis.image(t.randn(3,64,64).numpy(),win='random2')
    
    # 可视化36张随机的彩色图片,每一张6张
    vis.images(t.randn(36,3,64,64).numpy(),nrow=6,win='random3',opts={'title':'random_imgs'})
    
    'random3'
    

    random_imgs.jpg

    vis.text用于可视化文本,它支持所有的html标签

    vis.text(u'''<h1>Hello visdom</h1><br>visdom是Facebook专门
    为<b>PyTorch</b>开发的一款可视化工具,
    visdom十分轻量级,却支持非常丰富的功能,
    能胜任大多数的科学运算可视化任务''',win='visdom',opts={'title':u'vidsom简介'})
    
    'visdom'
    

    image.png

  • 相关阅读:
    4412--PWM驱动部分
    Linux中进程与线程
    Linux 进程间通信 --信号量
    Linux 进程间通信 --消息队列
    Linux 进程间通信 --共享内存
    QByteArray转成十六进制的QString
    网易C++设计模式笔记(二)面向设计对象的原则
    网易C++设计模式笔记(一)
    windows安装系统记录
    设计模式解析第二版 课后习题自我解答
  • 原文地址:https://www.cnblogs.com/LQ6H/p/10500123.html
Copyright © 2011-2022 走看看