pytorch數據處理的一個核心部分。主要支持:
- map-style和iterable-style的數據集。
- 定制數據載入模式
- 自動批量化
- 單或多線程數據載入
- 自動memory pinning
用法:
DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None, num_workers=0, collate_fn=None, pin_memory=False, drop_last=False, timeout=0, worker_init_fn=None)
可以看到dataloader中的dataset是關鍵參數,就是數據的來源。pytorch主要支持兩種不同風格的dataset:
map-style datasets和iterable-style datasets。
Map-style datasets
map-style風格的數據是實現__getitem__()和__len__()方法,實現一種映射:從索引/keys(可以是非這個整型)到data samples的過程。
例如:dataset[idx]可以實現從硬盤里讀取第idx個圖像和其對應的label。
Iterable-style datasets
iterable-style風格的數據是類IterableDataset的子類實例,需要實現__iter__()方法。作為一種在數據樣本上的迭代器。這種風格適用於隨機讀取數據的代價較大或者是不合適的、或適用於批量大小取決於獲取的數據。例如,通過iter(dataset)語句實現從數據庫、遠程服務器或實時生成的logs中返回一個數據流。
Data Loading Order and Sampler
對於iterable-style datasets,數據載入順序完全由用戶定義的迭代器所控制。這種風格對於chunk-reading和動態批量是很容易實現的。由於迭代風格的數據沒有key和indx(只是每次輸出圖像),所以沒有sampler或batch_sampler參數。
所以對於載入順序sampler主要考慮第一種風格,就是map-style。torch.utils.data.Sampler
類常被指定用來數據載入中的indices/keys(其實就是數據的索引或鍵,可以和圖像一一對應起來)的順序。該類sampler就是實現了一種在索引上的迭代器類。所以通過影響索引/鍵就可以來確定圖像載入的順序以及形式。sampler(迭代器)就可以隨機組合indices/keys列表的順序,從而實現每次yield一個樣本或yield一組樣本。
dataloader中的參數shuffle是一個自動構建的sampler。當指定shuffle=true時就是隨機的sampler,序列隨機產生。當指定shuffle=false時就是順序sampler,序列順序產生。當然,除了這個默認的操作,用戶可以自己指定sampler,來定制序列的產生方法。當定制sampler產生一組序列時,就可以作為參數傳給batch_sampler中。自動batching可以通過batch_size和drop_last參數來指定。
Loading Batched and Non-Batched Data
默認和常見的方法就是批量載入數據。當batch_size和drop_last作為參數從sampler來構建bath_sampler。對於map-style的數據,sampler要不是用戶制定,要不來源於shuffle參數。所以sampler和shuffle參數是互斥的。當從sampler中提供的序列index獲取到一批樣本后,參數collate_fn就可以用來整理這個批量數據了。有人會問:獲取到批量樣本不就好了嗎,用這個collate_fn干嘛?實際上多數情況下這個參數本來也是默認為None的,但是對於特殊情況:批量中的圖像有的是壞的,需要篩選、批量中的序列不等長,需要處理到等長等等情況。
map-style的數據大約等價於:
for indices in batch_sampler: yield collate_fn([dataset[i] for i in indices])
iterable-style的數據大致等價於:
dataset_iter = iter(dataset) for indices in batch_sampler: # 這里的sampler沒啥用的。 yield collate_fn([next(dataset_iter) for _ in indices])
Disable automatic batching
就是禁止默認的自動批量化。在某些情況下,用戶可能希望在數據集代碼中手動處理批處理,或只是加載單個樣本。例如,直接加載批處理數據(例如,從數據庫批量讀取或讀取連續內存塊)可能更容易,或者批處理大小依賴於數據,或者程序設計用於處理單個樣本。在這些場景下,最好不要使用自動批處理(collate_fn用於整理樣本),而是讓數據加載器直接返回dataset對象的每個成員。
當batch_size和batch_sampler是None時,自動batching就被禁止了。這時候每個樣本就直接進入collate_fn函數的處理中了。當自動批量被禁止,默認的collate_fn就只是將numpy數組轉為pytorch tensor,然后保持其他不變。
此時的map-style大致等價於:
for index in sampler: # 沒有了batch yield collate_fn(dataset[index])
iterable-style的代碼大致為:
for data in iter(dataset): yield collate_fn(data)
所以自動批量被禁后,就變成了單個樣本。
Single- and Multi-process Data Loading
在Python進程中,全局解釋器鎖(GIL)防止在線程之間完全並行化Python代碼。為了避免在數據加載時阻塞計算代碼,PyTorch提供了一個簡單的開關,通過將參數num_workers設置為正整數來執行多進程數據加載。
單進程數據加載(默認)
在此模式下,數據獲取在初始化數據加載器的同一進程中完成。因此,數據加載可能會阻塞計算。但是,當用於在進程(例如,共享內存、文件描述符)之間共享數據的資源有限時,或者當整個數據集很小並且可以完全加載到內存中時,可以優選此模式。此外,單進程加載通常顯示更可讀的錯誤跟蹤,因此對調試很有用。
多進程數據加載
將參數num_workers設置為正整數將啟用具有指定數量的加載程序工作進程的多進程數據加載。
在這種模式下,每次創建數據加載器的迭代器(例如,當調用enumerate(DataLoader))時,都會創建num_workers工作進程。此時,dataset、collate_fn和worker_init_fn將傳遞給每個worker,在那里它們用於初始化和獲取數據。這意味着數據集訪問及其內部IO、轉換(包括collate fn)在工作進程中運行。
torch.utile.data.get_worker_info()返回工作進程中的各種有用信息(包括工作進程id、數據集副本、初始種子等),在主進程中不返回任何信息。用戶可以在數據集代碼和/或worker_init_fn中使用此函數來分別配置每個數據集副本,並確定代碼是否在工作進程中運行。這對於切分數據集特別有用。
對於map-style數據集,主進程使用sampler生成索引並將它們發送workers。因此,任何隨機shuffle都是在主進程中完成的,該進程通過將索引分配給加載來指導加載。
對於iterable-style數據集,由於每個工作進程都得到一個dataset對象的副本,因此簡單的多進程加載通常會導致重復的數據。使用torch.utile.data.get_worker_info()和/或worker_init_fn,用戶可以獨立配置每個副本。(有關如何實現這一點,請參閱IterableDataset文檔。)出於類似的原因,在多進程加載中,drop_last參數將刪除每個worker的iterable樣式數據集副本的最后一批非完整數據。
一旦到達迭代的末尾,或者當迭代器成為垃圾回收器時,將關閉工作進程。