iT邦幫忙

2022 iThome 鐵人賽

DAY 16
0
AI & Data

PyTorch 生態鏈實戰運用系列 第 16

[Day16] 資料預處理機制與優化 - 軟體篇 - 土法煉鋼

  • 分享至 

  • xImage
  •  

前言

一樣承襲前幾日的主題,繼續討論資料前處理優化的方式。同樣是Cached到記憶體內,今天會利用比較土法煉鋼的方式來進一步優化效能。

此篇始於意外

實際上有跑前一篇利用Monai來進行Cached的朋友應該有發現,每次的訓練前,都需要花費一段時間進行Cached。於是就會發生「80M的東西要讀取好幾分鐘」這樣十分不合理的事情。於是我本身在實驗的時候,對於前一篇所提到的「monai.data.CacheDataset與直接把mednist的.npz讀進來的效率差不多」這件事情開始懷疑,經過測試以後發現果然還有優化的空間,因此意外誕生了這一篇。

直接使用.npz

直接讀取.npz其實會發現,不過就是幾秒鐘的事情,而且已經直接把三個切分全部讀進來了。

>>> npz_files = np.load('data/chestmnist.npz')
>>> for key in npz_files.files:
>>>     print(key, f'shape {npz_files[key].shape}')
train_images shape (78468, 28, 28)
val_images shape (11219, 28, 28)
test_images shape (22433, 28, 28)
train_labels shape (78468, 14)
val_labels shape (11219, 14)
test_labels shape (22433, 14)

上一篇情況中,會比較久,主要是因為需要把10多萬張png分別進行讀取,再整成檔案花費了許多時間。而.npz的狀況,則是因為是單一個連續的檔案,因此可以只進行一次的I/O直接全部讀取進來。

所以若是原始比較大型的檔案,如果前處理以後的影像不大(例如把X光壓成224x224),再壓成像.npz這樣的array實際上應該是可行的。

轉成dataloader

為了讓先前dict-based的Transforms以及Dataloader可以延續使用,首先我們要把資料點從整個大矩陣切個出來,像是train set就是把78468x28x28 切成 78,468個28x28,可以利用np.split直接進行快速的分割:

for key in npz_files.files:
    sliced_data[key] = np.split(npz_files[key], len(npz_files[key]))
  • 注意一點是,在這裡千萬不要使用python內建的List Comprehension來對array進行slice進行分割(e.g. [npz_files[key][i] for i in range(len(npz_files[key]))] 很慢,可以體驗看看...),會受到python慢的原罪影響,要跑非常久。矩陣運算的任務可以的話一定要交給優化過效能的numpy來執行。

接著把slice後的array製程dict就完成了!

datasets = {
    split : [{ 
        'img' : sliced_data[f'{split_mapping[split]}_images'][i].transpose(0,2,1),
        'labels' : sliced_data[f'{split_mapping[split]}_labels'][i][0]}
        
    for i in range(len(sliced_data[f'{split_mapping[split]}_images']))]
  • 這裡會對image進行transpose是因為跟我原本使用的Image reader方向不一致,因此我轉置一下,實務上基本沒有差異。
  • 因應資料格式,Transform也會需要更改。

具體可以參考preprocess.py,執行後可以得到下圖,基本上與先前無異。

實際差異

在修改了前處理以後,可以實際跑一次train.py看看。這邊大概要注意幾點是:

  • 由於整個Array已經讀進來了,所以使用一般的Dataset就可以,沒必要再Cache一次
  • 使用以後大幅縮短前面Cached2的時間,不需要等待好幾分鐘的png cache process!

一樣只訓練5個epoch,大概跑個幾分鐘就可以得到結果了:

  • 本來是期望可以縮短Cached,但訓練時間居然又縮短了!
  • 推測可能是monai.data.CacheDataset有進行更多一些meta data的I/O,或是讀取Cached的機制沒有這麼直接,造成實際上產生資料的速度會花上更多的時間。

結語

  • 有點意外,但之後實驗可以省更多時間!
  • 矩陣好、矩陣妙、矩陣呱呱叫!
  • 土法煉鋼有土法煉鋼的好

上一篇
[Day15] 資料預處理機制與優化 - 軟體篇 - 空間換取時間
下一篇
[Day17] Optimizations for GPU computation
系列文
PyTorch 生態鏈實戰運用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言