一、前言
最近開始重新記載我學習的pytorch筆記。
今天講的是加載數據的模塊,爲什麼要寫這個模塊呢?
因爲我最近自己生成了個全新的數據集,需要加載,所以順便把這個部分複習整理一下,列出了我覺得需要知道的一些技術點。
提醒:這篇文章可能會出現大量的代碼。
二、初時DataSet
研究事情咱們還是要歸於本身,所以我們直接先看這個類的源碼部分。
class Dataset(object):
"""An abstract class representing a Dataset.
All other datasets should subclass it. All subclasses should override
``__len__``, that provides the size of the dataset, and ``__getitem__``,
supporting integer indexing in range from 0 to len(self) exclusive.
"""
def __getitem__(self, index):
raise NotImplementedError
def __len__(self):
raise NotImplementedError
def __add__(self, other):
return ConcatDataset([self, other])
註釋裏說得清清楚楚,全部的其他數據集類都要繼承這個類,裏面的__len__
、__getitem__
是必須要重寫的,不重寫直接報錯,也是可以想想看,你做個數據集還不帶整體長度,也不帶按照index得到你的每條信息,那要你這個dataset幹啥呢,也就沒不存在bachsize,遍歷等等啦。
這裏有個直接給出的TensorDataset
我們可以先簡單用下,這也算是官方給的一個案例了。
class TensorDataset(Dataset):
"""Dataset wrapping tensors.
Each sample will be retrieved by indexing tensors along the first dimension.
Arguments:
*tensors (Tensor): tensors that have the same size of the first dimension.
"""
def __init__(self, *tensors):
assert all(tensors[0].size(0) == tensor.size(0) for tensor in tensors)
self.tensors = tensors
def __getitem__(self, index):
return tuple(tensor[index] for tensor in self.tensors)
def __len__(self):
return self.tensors[0].size(0)
這裏我採用一個我經常拿來做時序demo的數據集,北京pm2.5數據集,這裏爲了顯示方便只取了128個數據,正好兩個batch.
因爲代碼很easy,這裏我也不做很多的註釋了,會發現我在這裏做了很多的類型的print,無非就是想告訴大家利用TensorDataset
的時候傳入的應該是tensor類型,如果是df需要先轉換成numpy.array在轉換成tensor,輸出的也是tensor,事情其實可以分爲以下三步:
加載數據,提取出feature和label,並轉換成tensor
傳入
TensorDataset
中,實例化TensorDataset
爲datsset再將dataset傳入到Dataloader中,最後通過enumerate輸出我們想要的經過shuffle的bachsize大小的feature和label數據
ps:這裏DataLoader
的源碼就先不介紹了,比較多,如果讀者們想一起讀這個源碼的可以留言,我閒下來再寫一篇一起學習交流,共同進步。
三、自定義DataSet
我個人覺得結構化的數據和圖片維度的數據dataset寫法稍有不同,這裏做個簡單的分類,並給出我寫的demo
3.1 結構化數據
這裏的話 主要在__init__
做的事情差不多就是在前面所寫的輸入到TensorDataset
之前做的一些事情,不過多了一個數據的len,之後在__getitem__
中返回通過index得到的數據(X, Y)
這裏補充幾句,結構化的相對於圖片的要好寫很多,並且其實寫這個類很自由的,如果你是對應於特定的數據集的話你就可以仿效我寫的這個demo這樣,直接在裏面就表明你的路徑了,當然你也可以把path寫成傳參的,如果你不寫成特定的,那就在類外面進行一波操作,轉換成tensor之後傳入到MyDataset的這個類中,其實還是看具體你的需求了。
3.2 圖片類數據
這裏我挑了個簡單一些的圖片分類數據集,自己稍微刪了刪文件夾中的數據,爲了方便演示,我只留了很少的數目。
代碼demo如下:但是會報這個錯誤這個錯誤的主要原因:
這種錯誤有兩種可能:(來自於 https://www.cnblogs.com/zxj9487/p/11531888.html)
1.你輸入的圖像數據的維度不完全是一樣的,比如是訓練的數據有100組,其中99組是256×256,但有一組是384×384,這樣會導致Pytorch的檢查程序報錯
2.比較隱晦的batchsize的問題,Pytorch中檢查你訓練維度正確是按照每個batchsize的維度來檢查的,比如你有1000組數據(假設每組數據爲三通道256px×256px的圖像),batchsize爲4,那麼每次訓練則提取(4,3,256,256)維度的張量來訓練,剛好250個epoch解決(250×4=1000)。但是如果你有999組數據,你繼續使用batchsize爲4的話,這樣999和4並不能整除,你在訓練前249組時的張量維度都爲(4,3,256,256)但是最後一個批次的維度爲(3,3,256,256),Pytorch檢查到(4,3,256,256) != (3,3,256,256),維度不匹配,自然就會報錯了,這可以稱爲一個小bug。
這裏出的錯誤仔細一讀,65和85,應該是圖片的大小不一致造成的,因爲我一共才留了,20多張圖片。我也隨手打開我的圖片文件夾,發現
兩個圖片肉眼可見,像素應該不一樣,看一下圖片信息詳情確實是這樣。
解決方案這裏我加了一個resize就好了。
輸出,看來現在已經不存在上面所說的第二種問題了,不是整除也可以一樣輸出,問題不大。
我們把shuffle=True看下結果
除了上面那個錯誤之後還有個常見的錯誤
錯誤在於你在MyDataset中通過index得到的數據不是tensor類型,這裏進行轉換一下就好了,說白了就是dataloader輸出的每個bachsize大小的數據最好是張量,我也建議大家這麼寫,這樣也方便直接拿出來去模型訓練了。
四、總結
首先我們要去構建自己繼承Dataset的MyDataSet
傳入到Dataloader中,最後進行enumerate遍歷每個batchsize
Dataset通過index輸出的最好是tensor
整體的Dataset和Dataloader中,基本上的工作過程是Dataloader每次給你返回一個shuffle過的index(如果shuffle=True,如果是false則返回的index就是按照順序依次得到batchsize大小的index集合),當然這個index是在你的__len__之內的範圍中的,之後以這些index遍歷數據集,通過 getitem(self, index)返回一組你要的(feature, label)
公衆號後臺回覆:dataloader 返回這篇文章所有的代碼以及數據連接
更多精彩內容(請點擊圖片進行閱讀)
公衆號:AI蝸牛車
保持謙遜、保持自律、保持進步
個人微信
備註:暱稱+學校/公司+方向
如果沒有備註不拉羣!
拉你進AI蝸牛車交流羣
點個在看,麼麼噠!