3.2 DataLoader

dataloader簡介

<<AI人工智慧 PyTorch自學>> 3.2 DataL

按照上圖的順序,本小節就來到pytorch資料載入最核心模組——DataLoader

torch.utils.data.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, multiprocessing_context=None, generator=None, *, prefetch_factor=2, persistent_workers=False)

從以上可以看到,DataLoader類有14個變數,因此成為最核心模組,一點不為過。

DataLoader功能繁多,這裡根據官方文檔可總結為以下五大功能:

支持兩種形式資料集讀取

兩種形式的資料集分別是映射式(Map-style)與反覆運算式(Iterable-style),在3.1小結中講解的Dataset類就是映射式,因為它(getitem)提供了序號到資料的映射。反覆運算式則是編寫一個可反覆運算物件,從中依次獲取資料,此處不詳細展開,感興趣可以瞭解IterableDataset

注:往後不做特別說明,Dataset均表示映射式Dataset

自訂採樣策略

DataLoader可借助Sampler自訂採樣策略,包括為每個類別設置採樣權重以實現1:1的均衡採樣,或者是自訂採樣策略,關於Sampler會在後面小結詳細展開,它是一個漲點神奇。

自動組裝成批資料

mini-batch形式的訓練成為了深度學習的標配,如何把資料組裝成一個batch資料?DataLoader內部自動實現了該功能,並且可以通過batch_samplercollate_fn來自訂群組裝的策略,十分靈活。

多進程資料載入

通常GPU運算消耗資料會比CPU讀取載入資料要快,CPU“生產跟不上GPU“消費,因此需要多進程進行載入資料,以滿足GPU的消費需求。通常指要設置num_workers CPU核心數,如16核的CPU就設置為16

自動實現鎖頁記憶體(Pinning Memory

鎖頁記憶體的概念通常在作業系統課程裡才會涉及,非CS的同學可能不太瞭解,感興趣的可以去瞭解一下。Pinning Memory是空間換時間的做法,將指定的資料住,不會被系統移動(交換)到磁片中的虛擬記憶體,因此可以加快資料的讀取速率。簡單的可以理解為常用的衣服就在你的衣櫃裡,某些時候(如夏天),暫時不用的衣服——冬季大衣,則會移動到收納櫃裡,以騰出空間放其它常用的衣服,等到冬天來臨,需要用到大衣的時候,再從收納櫃裡把大衣放到衣櫃中。但是冬天拿大衣的時候就會慢一些,如果把它在你的衣櫃,那麼冬天獲取它的時候自然快了,但佔用了你的空間。這就是空間換時間的一個例子。這裡的就是固定的意思,大家可補充學習一下OS的內容。

DataLoader API

DataLoader提供了豐富的功能,下面介紹常用的功能,高階功能等到具體項目中再進行分析。

  • dataset:它是一個Dataset實例,要能實現從索引(indices/keys)到樣本的映射。(即getitem函數)
  • batch_size:每個batch的樣本量
  • shuffle:是否對打亂樣本順序。訓練集通常要打亂它!驗證集和測試集無所謂。
  • sampler:設置採樣策略。後面會詳細展開。
  • batch_sampler:設置採樣策略, batch_samplersampler二選一,具體選中規則後面代碼會體現。
  • num_workers 設置多少個子進程進行資料載入(data loading
  • collate_fn:組裝資料的規則, 決定如何將一批資料組裝起來。
  • pin_memory:是否使用鎖頁記憶體,具體行為是“the data loader will copy Tensors into CUDA pinned memory before returning them”
  • drop_last:每個epoch是否放棄最後一批不足batchsize大小的資料,即無法被batchsize整除時,最後會有一小批資料,是否進行訓練,如果資料量足夠多,通常設置為True。這樣使模型訓練更為穩定,大家千萬不要理解為某些資料被捨棄了,因為每個epochdataloader的採樣都會重新shuffle,因此不會存在某些資料被真正的丟棄。

下面通過配套代碼加深dataloader的理解,並且觀察DataLoader Dataset是如何配合使用的。

運行代碼,可看到輸出如下資訊:

0 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([1, 0])

1 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([0, 1])

2 torch.Size([1, 3, 224, 224]) torch.Size([1]) tensor([0])

0 torch.Size([3, 3, 224, 224]) torch.Size([3]) tensor([0, 0, 1])

1 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([1, 0])

0 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([0, 0])

1 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([0, 1])

這裡主要觀察batch_sizedrop_last的作用,以及圖片組裝成batch之後的shape

這裡構建一個資料量為5dataset,這樣可以採用batchsize=23來觀察drop_last的作用。

dataloader內部代碼

下一步,我們將採用debug模式,深入dataloader內部,觀察它是如何進行採樣的,如何調用datasetgetitem獲取資料,如何組裝一個batch的。這裡我們僅觀察單進程模式,因此大家的num_workers注意設置為0

首先在for i, (inputs, target) in enumerate(train_loader_bs2) 設置一個中斷點,然後debug模式運行代碼,接著持續採用 Step Into方式運行代碼,下面就列出依次會進入的代碼:

第一步:初始化dataloader反覆運算器

for i, (inputs, target) in enumerate(train_loader_bs2)

DataLoaderiter()

DataLoader_get_iterator()

SingleProcessDataLoaderIter_init

注:至此,僅完成了DataLoader的初始化,需要再一次進入dataloader才開始讀取資料。

第二步:依次迴圈該反覆運算器

來到 BaseDataLoaderIter_next:進入521行:data = self._next_data()

來到 _SingleProcessDataLoaderIter_next_data:此函式呼叫了兩個重要功能,第一個獲取一個batch的索引,第二個獲取此batch的資料。下面一個一個來看。

<<AI人工智慧 PyTorch自學>> 3.2 DataL

進入 _SingleProcessDataLoaderIter_next_data:進入560行, index = self._next_index()

來到 _BaseDataLoaderIter_next_index() 這裡是對sampler的包裝,調用sampler獲取一批索引,進入512

來到BatchSampleriter():函數中有yield,這是一個反覆運算器,從這裡可以看到sampler是如何工作的。預設情況下,這裡用的是RandomSampler 它會實現採樣的順序及頻率。在本函數中,對self.sampler依次反覆運算,拿到足夠一個batchsize的索引時,就yield

 

<<AI人工智慧 PyTorch自學>> 3.2 DataL

<<AI人工智慧 PyTorch自學>> 3.2 DataL

回到 _SingleProcessDataLoaderIter_next_data:第561行,經過index = self._next_index() ,已經獲得一個batch所對應的index,接著進入self._dataset_fetcher.fetch(index)

來到 _MapDatasetFetcherfetchmapdataset就是前面講到的map-style dataset。看到第49行,是一個列表生成式,在這裡,調用了我們自己寫的dataset,繼續進入。

來到 AntsBeesDatasetgetitem:進入到這裡,大家就豁然開朗了吧,知道dataset是如何被dataloader使用的。下面,直接跳出去,回到 fetch看看如何組裝的。

來到 _MapDatasetFetcherfetch:第52self.collate_fn(data) 這裡採用collate_fn對資料進行組裝,繼續進入。

來到 collate.pydefault_collate():這是pytorch預設的組裝函數,值得大家認真學習。這個函數通常是一個遞迴函數,第一次進入時可以發現會來到第84行:return [default_collate(samples) for samples in transposed]。會依次再進入一次default_collate()

這裡的邏輯是這樣的:

首先將dataset返回的一系列資料解包再zip,為的是將相同資料放到一起。即getitemreturn返回有imglabel,這裡就是為了將多個img放到一起,多個label放到一起,當然大家可以在getitem返回其它有用資訊(例如圖片的路徑)。

接著再次進入default_collate函數時,會對實際的資料進行組裝。例如img的資料會進入if isinstance(elem, torch.Tensor),然後會看到img資料是這樣被組裝起來的:torch.stack(batch, 0, out=out),因此可知一個batch的圖像資料第一個維度是B,整體是BCH*W

至此,一個batch資料已經讀取、載入完畢,依次跳出函數,可回到for i, (inputs, target) in enumerate(train_loader_bs2)

這個時候,再觀察inputs, target,一定會更清晰了,知道它們是如何從硬碟到模型需要的形式。並且通過上述debug過程,我們可以知道sampler的機制、collate_fn的機制,方便今後進行高級的改造。希望大家一定要debug幾遍上述過程,並且記錄。

小結

以上就是關於DataLoader的概念的介紹,通過兩個小節相信大家對資料讀取有了初步認識,可pytorch的資料處理遠不止於此,它還提供了很多使用的方法,例如資料集的拼接,資料集的截取,資料的劃分等,想瞭解怎麼使用,請接著往下看。

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 HCHUNGW 的頭像
    HCHUNGW

    HCHUNGW的部落格

    HCHUNGW 發表在 痞客邦 留言(0) 人氣()