nvidia.dali:深度学习加速神器!
??最近準備準備整合一個基于pytorch的深度學習平臺,把常用的訓練推理流程、模型、數據管理、metric測試以及各種有效的黑科技攢到一起,作為個人的使用工具可以提升算法開發和實驗效率。為了驗證不同特性的有效性選擇在比較有說服力的imagenet上進行實驗。之前也做過很多次imagenet的訓練和模型復現,但是訓練一次imagenet比較耗時。最近使用上了一個比較有效的數據預處理框架:nvidia.dali,感覺效果不錯。
DALI的概念
??Nvidia DALI,NVIDIA’s Data Loading Library, is a collection of highly optimized building blocks, and an execution engine, to accelerate the pre-processing of the input data for deep learning applications。這是DALI的定義,說白了就是一個在神經網絡中加速數據預處理(尤其是圖像數據)的庫。大家都知道,目前的神經網絡任務中,基本可以分為以下幾個步驟:
1、將數據(圖片、視頻、文本、音頻或其他形式的數據)準備好存放在服務器的硬盤上;
2、通過CPU將硬盤上的數據讀入內存,進行解碼、數據增強等操作,將數據準備成可被神經網絡使用的格式,這部分主要依賴于CPU的計算能力;
3、通過設備通信將內存中的數據傳入GPU的顯存中,進行神經網絡計算,這部分主要依賴于GPU的計算能力。
整個流程中CPU和GPU的計算延時應該基本相當,這樣CPU處理好的數據能被及時的被GPU利用,充分發揮二者的能力。如果CPU的處理速度大于GPU的計算速度,則會造成CPU算力浪費經常需要等待GPU;反之亦然。
??隨著黃教主不斷發力發布算力越來越強的GPU以及計算機的硬件架構原因,這個過程的平衡逐漸被打破了。目前的神經網絡應用中,經常看到GPU的算力較強而CPU較弱,數據預處理的過程耗時較長,成為了整個應用的bottleneck。而數據的預處理(以圖像數據為例,數據的解碼、圖像翻轉、resize等)大多都是計算密集型操作,這正是GPU擅長做的事情。那么為什么不考慮將這部分操作都從CPU挪到GPU上去做呢?于是nvidia做了dali這樣一個庫來實現這個transfer的過程。所以我們可以總結的說,DALI就做了一件事:把一部分數據處理的操作從CPU挪到了GPU上去做,挪動的比例得當的話,剛好可以實現CPU和GPU的延時同步,充分利用二者的計算能力。
一個DALI的使用sampler
??我們以一個圖像問題來簡單介紹dali的使用方法。在DALI中,一個完整的數據預處理過程叫做一個pipeline,在這個pipeline中用戶需要自己定義一些必須的內容,比如原始數據是什么格式(圖片?二進制文件?視頻?),需要做哪些數據預處理操作(圖像翻轉?圖像歸一化?)以及每個操作過程是在什么設備上運行(cpu?gpu?mixed?)。完成pipeline定以后需要將其build起來,然后打包成一個可被循環訪問的數據結構,就可以在for循環中不斷拿數據了。首先回顧一下,如果在Pytorch中使用原生API構建一個數據模塊,我們可以這么做:
class MyDataset(torch.utils.data.Dataset):def __init__(self, ...):...def __getitem__(self, idx):# logit code about how to get a data from disk into RAM, usually decode image first and data augmentation then. ... dataset = MyDataset(...) data_loader = torch.utils.data.DataLoader(dataset, ...) for index, data in enumerate(data_loader):images, labels = dataimages, labels = images.cuda(), labels.cuda()...這樣的代碼中,每張圖片的預處理都在CPU上進行,最后通過Tensor.cuda()將其推到GPU的顯存中參與運算。如果使用DALI,我們可以這么做:
from nvidia.dali.pipeline import Pipeline import nvidia.dali.ops as ops import nvidia.dali.types as types from nvidia.dali.plugin.pytorch import DALIClassificationIterator class SimplePipeline(Pipeline):def __init__(self, batch_size, num_threads, device_id):super(SimplePipeline, self).__init__(batch_size, num_threads, device_id, seed = 12)# 類似pytorch的torchvision.datasets.ImageFolder,dali也提供了諸如FileReader、TFRecordReader這樣的高階API,能夠解析不同類型的硬盤數據self.input = ops.FileReader(file_root = image_dir)# pipeline中定義了一個解碼圖像的模塊,并指定其運行狀態為CPU,輸出的格式為RGB順序self.decode = ops.ImageDecoder(device = 'cpu', output_type = types.RGB)# pipeline中定義了一個resize模塊,并指定其運行狀態為GPU,resize方式為保持ratio不變將其短邊resize為224self.resize = ops.Resize(device='gpu', resize_shorter=224)# pipeline中定義了一個數據增強三件套模塊,并指定其運行狀態為GPU,分別包括crop、mirror和normalizeself.cmn = ops.CropMirrorNormalize(device='gpu', crop=(224, 224), dtype=types.FLOAT, mean=[0.485 * 255, 0.465 * 255, 0.406 * 255], std=[0.229 * 255, 0.224 * 255, 0.225 * 255], mirror=1)# 這是pipeline中必須存在的一個函數,用來說明在調用該pipeline時,應該如何對數據進行實際的操作,可以理解為pytorch的module的forward函數。__init__中只定義了一些可使用的操作,具體怎么使用在這里說明def define_graph(self):jpegs, labels = self.input()images = self.decode(jpegs)images = self.resize(images)images = self.cmn(images)return (images, labels) pipe = SimplePipeline(8, 4, 0) pipe.build() train_loader = DALIClassificationIterator(pipe) for index, data in enumerate(train_loader):images, labels = data# 由于之前已經在gpu上對images進行操作了,所以images已經被處理成為gpu上的tensor了,而labels沒有進行過gpu上的操作,因此它還是內存中的tensor,需要.cuda()轉移到gpu上,可以通過對代碼的調試詳細分析labels = labels.squeeze(-1).cuda().long()...上面的代碼中,我們看到了一個dali處理圖像數據的很簡單的例子,希望大家從中理解dali的大概運行原理,而不用糾結每個參數或者每個API的接口具體形式,這些內容可以再dali的doc網站中全部查到。如果想要準確的使用某一個類型的數據,還是需要去查找每個API的接口參數及其對應的含義,甚至可能需要去看一下部分API的源碼。DALI支持多種數據類型,甚至連TFRecord類型的數據都支持,可以通過dali把tfrecord數據集成到一個pytorch框架下的應用中,確實還是很方便的。我把自己的渣渣CPU版本數據預處理換成DALI+TFRecord再搭配上DDP后,訓練速度直接飆了將近8倍,4塊Tesla P100上訓練一個epoch的imagenet只需要12分鐘,四塊卡的GPU利用率全部頂到100%,深深地治愈了我的強迫癥,感謝DALI!!!
??水平有限,歡迎討論。
總結
以上是生活随笔為你收集整理的nvidia.dali:深度学习加速神器!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机重复启动问题原因及修复
- 下一篇: 最简易上手的numpy学习笔记三