聊聊Pytorch中的dataloader

2022-01-10 極市平臺

今天為啥突然要寫一下pytorch的dataloader呢,首先來說說事情的來龍去脈。

起初,我最開始單獨訓練一個網絡來完成landmark點回歸任務和分類任務,訓練的數據是txt格式,在訓練之前對數據進行分析,發現分類任務中存在嚴重的數據樣本不均衡的問題,那麼我事先針對性的進行數據採樣均衡操作,重新得到訓練和測試的txt數據和標籤,保證了整個訓練和測試數據的樣本均衡性。由於我的整個項目是檢測+點回歸+分類,起初檢測和點回歸+分類是分兩步實現的,檢測是通過讀取XML格式來進行訓練,現在要統一整個項目的訓練和測試過程,要將點回歸+分類的訓練測試過程也按照讀取XML格式來進行,那麼就遇到一個問題,如何針對性的去給樣本偏少的樣本進行均衡,由於在dataset類中,返回的圖像和標籤都是針對每個index返回一個結果,在dataset類中進行操作似乎不太可行,那麼就想到在dataloader中進行操作,通過dataloader中的參數sample來完成針對性採樣。

還有一個問題是關於num_workers的設置,因為我有對比過,在我的單機RTX 2080Ti上和八卡伺服器TITAN RTX上(僅使用單卡,其它卡有在跑其它任務),使用相同的num_workers,在單機上的訓練速度反而更快,於是猜想可能和CPU或者內存有關係,下面會具體分析。

首先來看下下dataloader中的各個參數的含義。

類的定義為:torch.utils.data.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)

dataset:定義的dataset類返回的結果。

batchsize:每個bacth要加載的樣本數,默認為1。

shuffle:在每個epoch中對整個數據集data進行shuffle重排,默認為False。

sample:定義從數據集中加載數據所採用的策略,如果指定的話,shuffle必須為False;batch_sample類似,表示一次返回一個batch的index。

num_workers:表示開啟多少個線程數去加載你的數據,默認為0,代表只使用主進程。

collate_fn:表示合併樣本列表以形成小批量的Tensor對象。

pin_memory:表示要將load進來的數據是否要拷貝到pin_memory區中,其表示生成的Tensor數據是屬於內存中的鎖頁內存區,這樣將Tensor數據轉義到GPU中速度就會快一些,默認為False。

drop_last:當你的整個數據長度不能夠整除你的batchsize,選擇是否要丟棄最後一個不完整的batch,默認為False。

註:這裡簡單科普下pin_memory,通常情況下,數據在內存中要麼以鎖頁的方式存在,要麼保存在虛擬內存(磁碟)中,設置為True後,數據直接保存在鎖頁內存中,後續直接傳入cuda;否則需要先從虛擬內存中傳入鎖頁內存中,再傳入cuda,這樣就比較耗時了,但是對於內存的大小要求比較高。

下面針對num_workers,sample和collate_fn分別進行說明:

1. 設置num_workers:

pytorch中dataloader一次性創建num_workers個子線程,然後用batch_sampler將指定batch分配給指定worker,worker將它負責的batch加載進RAM,dataloader就可以直接從RAM中找本輪迭代要用的batch。如果num_worker設置得大,好處是尋batch速度快,因為下一輪迭代的batch很可能在上一輪/上上一輪...迭代時已經加載好了。壞處是內存開銷大,也加重了CPU負擔(worker加載數據到RAM的進程是進行CPU複製)。如果num_worker設為0,意味著每一輪迭代時,dataloader不再有自主加載數據到RAM這一步驟,只有當你需要的時候再加載相應的batch,當然速度就更慢。num_workers的經驗設置值是自己電腦/伺服器的CPU核心數,如果CPU很強、RAM也很充足,就可以設置得更大些,對於單機來說,單跑一個任務的話,直接設置為CPU的核心數最好。

2. 定義sample:(假設dataset類返回的是:data, label)

from torch.utils.data.sampler import WeightedRandomSamplerweights = [2 if label == 1 else 1 for data, label in dataset]sampler = WeightedRandomSampler(weights,num_samples=10, replacement=True)dataloader = DataLoader(dataset, batch_size=16, sampler=sampler)

PyTorch中提供的這個sampler模塊,用來對數據進行採樣。默認採用SequentialSampler,它會按順序一個一個進行採樣。常用的有隨機採樣器:RandomSampler,當dataloader的shuffle參數為True時,系統會自動調用這個採樣器,實現打亂數據。這裡使用另外一個很有用的採樣方法:WeightedRandomSampler,它會根據每個樣本的權重選取數據,在樣本比例不均衡的問題中,可用它來進行重採樣。replacement用於指定是否可以重複選取某一個樣本,默認為True,即允許在一個epoch中重複採樣某一個數據。

3. 定義collate_fn:

def detection_collate(batch):    """Custom collate fn for dealing with batches of images that have a different    number of associated object annotations (bounding boxes).
Arguments: batch: (tuple) A tuple of tensor images and lists of annotations
Return: A tuple containing: 1) (tensor) batch of images stacked on their 0 dim 2) (list of tensors) annotations for a given image are stacked on 0 dim """ targets = [] imgs = [] for sample in batch: imgs.append(sample[0]) targets.append(torch.FloatTensor(sample[1])) return torch.stack(imgs, 0), targets

使用dataloader時加入collate_fn參數,即可合併樣本列表以形成小批量的Tensor對象,如果你的標籤不止一個的話,還可以支持自定義,在上述方法中再額外添加對應的label即可。

data_loader = torch.utils.data.DataLoader(dataset, args.batch_size,    num_workers=args.num_workers, sampler=sampler, shuffle=False,     collate_fn=detection_collate, pin_memory=True, drop_last=True)

參考連結:

torch.utils.data - PyTorch master documentationpytorch.org(https://pytorch.org/docs/stable/data.html?highlight=dataloader#torch.utils.data.DataLoader)

Guidelines for assigning num_workers to DataLoaderdiscuss.pytorch.org(https://discuss.pytorch.org/t/guidelines-for-assigning-num-workers-to-dataloader/813)

相關焦點

  • PyTorch源碼解析與實踐(1):數據加載Dataset,Sampler與DataLoader
    可以通過get_worker_info方法,在某一當前進程中調用,獲得當前進程信息。這個方法要麼在dataset類的__iter__方法中使用,要麼在DataLoader的worker_init_fn方法中設置並使用。
  • PyTorch常見的坑匯總
    好像扯遠了,回歸pytorch,首先讓我比較尷尬的是pytorch並沒有一套屬於自己的數據結構以及數據讀取算法,dataloader個人感覺其實就是類似於tf中的feed,並沒有任何速度以及性能上的提升。
  • pytorch dataloader之從入門到入魔(CV方向篇)
    其實這就是pytorch(或者說其他框架)關於dataset的真相,核心就是有固定長度 __len__(self),且能迭代其中的元素__getitem__(self, index)。就這樣,一個真·樸實無華的dataloader就這樣被創造出來了。
  • Pytorch常見的坑匯總
    ——————————————————————————好像扯遠了,回歸pytorch,首先讓我比較尷尬的是pytorch並沒有一套屬於自己的數據結構以及數據讀取算法,dataloader個人感覺其實就是類似於tf中的feed,並沒有任何速度以及性能上的提升。1.
  • 【他山之石】pytorch常見的坑匯總
    好像扯遠了,回歸pytorch,首先讓我比較尷尬的是pytorch並沒有一套屬於自己的數據結構以及數據讀取算法,dataloader個人感覺其實就是類似於tf中的feed,並沒有任何速度以及性能上的提升。
  • 【他山之石】Pytorch Lightning 完全攻略
    另外,這樣的話,你需要給每個模型都加上一些相似的代碼,如training_step,validation_step。顯然,這並不是我們想要的,如果真的這樣做,不但不易於維護,反而可能會更加雜亂。同理,如果把每個數據集類都直接轉換成pl的DataModule,也會面臨相似的問題。基於這樣的考量,我建議使用上述架構:data和modle兩個文件夾中放入__init__.py文件,做成包。這樣方便導入。
  • pytorch常見的坑匯總
    首先作為tensorflow的骨灰級玩家+輕微強迫症患者,一路打怪升級,從0.6版本用到1.2,再用到1.10,經歷了tensorfow數個版本更迭,這裡不得不說一下tf.data.dataset+tfrecord使用起來效率遠比dataloader高的多。
  • 【pytorch】常見的坑匯總
    首先作為tensorflow的骨灰級玩家+輕微強迫症患者,一路打怪升級,從0.6版本用到1.2,再用到1.10,經歷了tensorfow數個版本更迭,這裡不得不說一下tf.data.dataset+tfrecord使用起來效率遠比dataloader高的多。
  • 【小白學PyTorch】3.淺談Dataset和Dataloader
    一般是把數據直接保存在這個類的屬性中。像是self.data,self.label2.2 getitemindex是一個索引,這個索引的取值範圍是要根據__len__這個返回值確定的,在上面的例子中,__len__的返回值是4,所以這個index會在0,1,2,3這個範圍內。
  • PyTorch常見踩坑匯總
    首先作為tensorflow的骨灰級玩家+輕微強迫症患者,一路打怪升級,從0.6版本用到1.2,再用到1.10,經歷了tensorfow數個版本更迭,這裡不得不說一下tf.data.dataset+tfrecord使用起來效率遠比dataloader高的多。
  • 【PyTorch修煉】二、帶你詳細了解並使用Dataset以及DataLoader
    一、前言最近開始重新記載我學習的pytorch筆記。今天講的是加載數據的模塊,為什麼要寫這個模塊呢?因為我最近自己生成了個全新的數據集,需要加載,所以順便把這個部分複習整理一下,列出了我覺得需要知道的一些技術點。
  • 【他山之石】pytorch_lightning 全程筆記
    「他山之石,可以攻玉」,站在巨人的肩膀才能看得更高,走得更遠。
  • 一文讀懂 PyTorch 中 Dataset 與 DataLoader
    Dataset什麼時候使用DatasetCIFAR10是CV訓練中經常使用到的一個數據集,在PyTorch中CIFAR10是一個寫好的Dataset,我們使用時只需以下代碼:data = datasets.CIFAR10(".
  • 一文讀懂PyTorch中Dataset與DataLoader
    Dataset什麼時候使用DatasetCIFAR10是CV訓練中經常使用到的一個數據集,在PyTorch中CIFAR10是一個寫好的Dataset,我們使用時只需以下代碼:data = datasets.CIFAR10(".
  • 實操教程|Pytorch-lightning的使用
    一些比較麻煩但是需要的功能通常如下:resume training 即重載訓練,我們希望可以接著上一次的epoch繼續訓練記錄模型訓練的過程(通常使用tensorboard)好在這些功能在pl中都已經實現。由於doc上的很多解釋並不是很清楚,而且網上例子也不是特別多。
  • Pytorch中的分布式神經網絡訓練
    在前向傳遞中,模型在每個設備上複製,每個副本處理批次的一部分。在向後傳遞過程中,將每個副本的梯度求和以生成最終的梯度,並將其應用於主gpu(上圖中的GPU-1)以更新模型權重。在下一次迭代中,主GPU上的更新模型將再次複製到每個GPU設備上。在PyTorch中,只需要一行就可以使用nn.DataParallel進行分布式訓練。
  • PyTorch  深度學習新手入門指南
    你可能對理解 tensorflow 中的會話,變量和類等有困擾,並且計劃轉向 pytorch,很好,你來對地方了。3. 如果你能夠用 pytorch 構建重要、複雜的模型,並且現在正在找尋一些實現細節,不好意思,你可以直接跳到最後一部分。  讓我們開始吧!
  • 從零學PyTorch:DataLoader構建高效的自定義數據集
    Torch中可以創建一個DataSet對象,並與dataloader一起使用,在訓練模型時不斷為模型提供數據Torch中DataLoader的參數如下DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
  • pytorch 入門4-簡單圖片分類
    , optimizer, epoch) test(model, device, test_dataloader) torch.save(model.state_dict(), "mnist_cnn.pt")NLL loss的定義
  • 用Pytorch Lightning重構代碼速度更慢,修復後速度倍增
    默認情況下,Pytorch 在兩個 epoch 之間會 kill 掉運行中的進程(worker)並重新加載,因而需要重新加載數據集。在我這個例子中,加載數據集非常慢。我將 DataLoader 裡的 persistent_workers 參數設置為 True,以防止運行中的進程被殺死,進而防止重新加載數據。