pytorch加速加载方案

Posted zhengmeisong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pytorch加速加载方案相关的知识,希望对你有一定的参考价值。

pytorch没有像mxnet的RecordIO文件,每次读大量小图很是吃力,硬盘不给力的话耗时基本堵在加载数据上了,试过lmdb,快则快矣,然不支持训练过程中随机shuffle,终放弃。

盖天下苦其久矣,早有各路神仙献策,此地有个简单汇总,遂拾人牙慧,总结如下:

1. dali - 当前最靠谱方案,连数据增广也提供了

参考链接pip install安装,根据手册写几行代码就可以了,跟pytorch自带的dataloader用法基本一样,支持数据增广,常用的都有了,需要搜索函数名看好具体用法,需要用dali自带的Uniform和CoinFlip做随机,如果用python自带random生成随机数,然后if _rand == 1的话只能生成一次,也就是说如果random生成了1那对于本次训练所有数据都做增广,而生成0就都不进行增广,这地方调了一个晚上,最后好好看手册里说明和git issue修改如下,感觉随机性可以了。

class reader_pipeline(Pipeline):
    def __init__(self, image_dir, batch_size, num_threads, device_id):
        super(reader_pipeline, self).__init__(batch_size, num_threads, device_id)
        self.input = dali_ops.FileReader(file_root = image_dir, random_shuffle = False)
        self.decode = dali_ops.ImageDecoder(device = mixed, output_type = dali_types.RGB)
       
        self.cmn_img = dali_ops.CropMirrorNormalize(device = "gpu",
                                           crop=(112, 112),  crop_pos_x=0, crop_pos_y=0,
                                           output_dtype = dali_types.FLOAT, image_type=dali_types.RGB,
                                           mean=[0.5*255, 0.5*255, 0.5*255],
                                           std=[0.5*255, 0.5*255, 0.5*255]
                                           )
       
        self.brightness_change = dali_ops.Uniform(range=(0.6,1.4))
        self.rd_bright = dali_ops.Brightness(device="gpu")
        self.contrast_change = dali_ops.Uniform(range=(0.6,1.4))
        self.rd_contrast = dali_ops.Contrast(device = "gpu")
        self.saturation_change = dali_ops.Uniform(range=(0.6,1.4))
        self.rd_saturation = dali_ops.Saturation(device = "gpu")
        self.jitter_change = dali_ops.Uniform(range=(1,2))
        self.rd_jitter = dali_ops.Jitter(device = "gpu")
        self.disturb = dali_ops.CoinFlip(probability=0.3)       #以0.3的概率生成1, 0.3的概率生成0
        self.hue_change = dali_ops.Uniform(range = (-30,30))    #以-30,30之间的随机数
        self.hue = dali_ops.Hue(device = "gpu")
       
    def define_graph(self):
        jpegs, labels = self.input(name="Reader")
        images = self.decode(jpegs)
        brightness = self.brightness_change()
        images = self.rd_bright(images, brightness=brightness)
        contrast = self.contrast_change()
        images = self.rd_contrast(images, contrast = contrast)
        saturation = self.saturation_change()
        images = self.rd_saturation(images, saturation = saturation)
        jitter = self.jitter_change()
        disturb = self.disturb()
        images = self.rd_jitter(images, mask = disturb)
        hue = self.hue_change()
        images = self.hue(images, hue = hue)
       
        imgs = self.cmn_img(images)
        return (imgs, labels)

报错解决方案:

1.1. AttributeError: module ‘nvidia.dali.ops‘ has no attribute ‘ImageDecoder‘是版本没有装对

import torch
torch.version.cuda    

根据打印版本信息选择对应的安装

1.2. RuntimeError: CUDA error: an illegal memory access was encountered
terminate called after throwing an instance of ‘dali::CUDAError‘
  what():  CUDA runtime API error cudaErrorIllegalAddress (77):
an illegal memory access was encountered

很诡异,batchsize=120没出现过,改大到240就报这个错,去掉数据增强,num_workers从1改成2,这个报错就没了。加上数据增强,num_workers怎么改都不行,应该是dali的bug,貌似还没有解决。

1.3 按这里描述,dali是支持分类样本以list作为输入的,但是自己用的时候还是报错,不确定是否list格式不对。

2. jpeg4py解码,ubuntu上大概能加速30%,centos安装失败

def pil_loader(path):
    with open(path, rb) as f:
        img = Image.open(f)
        return img.convert(RGB)
def jpeg4py_loader(path):
    with open(path, rb) as f:
        img = jpeg.JPEG(f).decode()
        return Image.fromarray(img) 

def __getitem__(self, index):
        path, target = self.samples[index]
        img = pil_loader(path)
        # img = jpeg4py_loader(path)
        if self.transform is not None:
            img = self.transform(img)
        return img, int(target)

参考git安装即可,代码改动也比较小,如果用ubuntu机器是比较划算的方案。

3. 内存当硬盘,不明显,但操作起来比较简单,内存空间大的话就用吧

sudo mount -t tmpfs -o size=100g tmpfs /data03/xxx/tmp_data

这个操作需要先分出一块区域,再向tmp_data内拷贝数据,不能对一个有数据的文件夹执行这个命令,否则数据就全丢了......

4. 数据预取,不明显,没有试出作者说的打鸡血效果.....

5. 手写多线程加载数据,陷进去好几天,终放弃......

之前写过一次性多线程加载所有训练数据到内存的,当时训练数据少(2万),一次加载所有内存也能撑住,然后训练速度飞起,gpu完全不浪费。当时也就花了一天,想想改成在线加载也不难,不想困住了很久,现象就是多线程里返回torch tensor会崩溃,可能是自己工程能力太挫了吧,不搞了。

 

有些存储不支持存放大量散文件,也许最终还是应该寻觅一种类似RecordIO/TFRecord的方式......

 

 

 

以上是关于pytorch加速加载方案的主要内容,如果未能解决你的问题,请参考以下文章

LMDB数据库加速Pytorch文件读取速度

模型推理加速系列BERT加速方案对比 TorchScript vs. ONNX

优化pytorch DataLoader提升数据加载速度

模型推理加速系列04:BERT加速方案对比 TorchScript vs. ONNX

PyTorch简易入门

Chrome-Devtools代码片段中的多个JS库