pytorch加速加載方案


pytorch沒有像mxnet的RecordIO文件,每次讀大量小圖很是吃力,硬盤不給力的話耗時基本堵在加載數據上了,試過lmdb,快則快矣,然不支持訓練過程中隨機shuffle,終放棄。

-----2020.05.01更新------

nvidia-dali最好用沒有之一,版本更新很快,越新的支持的擾動越多,再也不用手寫多線程加載數據了。

蓋天下苦其久矣,早有各路神仙獻策,此地有個簡單匯總,遂拾人牙慧,總結如下:

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)

torchvision自帶數據增強,可以參考這里

報錯解決方案:

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. 內存當硬盤,貌似對SSD不明顯,機械硬盤有效,勝在操作簡單,內存空間大的話就用吧

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

經測試這個小trick+dali加速可以在遠程硬盤上快到飛起,gpu利用率幾乎到100%。

這個操作需要先分出一塊區域,再向tmp_data內拷貝數據,不能對一個有數據的文件夾執行這個命令,否則數據就全丟了......

解掛直接umount /data03/xxx/tmp_data即可,或重啟自動消失

4. 數據預取,不明顯,沒有試出作者說的打雞血效果.....

5. 手寫多線程加載數據,陷進去好幾天,終放棄......

之前寫過一次性多線程加載所有訓練數據到內存的,當時訓練數據少(2萬),一次加載所有內存也能撐住,然后訓練速度飛起,gpu完全不浪費。當時也就花了一天,想想改成在線加載也不難,不想困住了很久,現象就是多線程里返回torch tensor會崩潰,可能是自己工程能力太挫了吧,不搞了。

 

有些存儲不支持存放大量散文件,也許最終還是應該尋覓一種類似RecordIO/TFRecord的方式......


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM