<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          PyTorch DataSet 和 Dataloader 加載步驟

          共 7617字,需瀏覽 16分鐘

           ·

          2022-01-01 09:28


          ?戳我,查看GAN的系列專輯~!

          等你著陸!【GAN生成對抗網(wǎng)絡(luò)】知識星球!

          來源:知乎—端莊的湯湯? 侵刪

          地址:https://zhuanlan.zhihu.com/p/381224748
          在訓(xùn)練模型讀取數(shù)據(jù)的時候一開始總是搞不清楚這兩個接口的原理,這次就好好的整理整理,把整個過程一步一步梳理清楚,細(xì)節(jié)拉滿。(內(nèi)容來自深度之眼和自己的debug)
          開局兩張圖,內(nèi)容全靠編?。?!

          from torch.utils.data import Dataset

          class MyDataset__(Dataset): def __init(self, *args, **kargs): pass def __len__(self): pass def __getitem__(self, idx):????????pass
          在寫自己的dataset的時候這三個魔術(shù)方法是必須要寫的,其余方法看自己需求。Dataset寫法多樣,此處就不表了。我在下面這個鏈接里寫了要用到的魔術(shù)方法的解釋。
          https://zhuanlan.zhihu.com/p/381229502
          實(shí)例化對象md = MyDataset(...)之后,len(md)會返回實(shí)例化dataset對象md的數(shù)據(jù)長度,md[idx]就可以索引該對象的數(shù)據(jù),這就是兩個魔術(shù)方法的非常方便的應(yīng)用,和python的內(nèi)置函數(shù)就聯(lián)系起來了。強(qiáng)調(diào):是因?yàn)槟g(shù)方法的改寫,自己建立的dataset才能使用len()函數(shù)還有數(shù)組索引。示例代碼結(jié)果如下。
          DataLoader

          這個類有那么多參數(shù),左邊這幾個是常用的。dataset=train_data,來自上邊黃色代碼圖片。num_workers代表多進(jìn)程讀取數(shù)據(jù),windows下設(shè)置為0,因?yàn)閜ytorch多進(jìn)程是fork,windows不是這種方式,所以不能使用多進(jìn)程,Linux可以,一般設(shè)置為4或8都見過。shuffle在訓(xùn)練的時候?yàn)門rue,提高模型泛化性,驗(yàn)證或者推理一般就False,沒必要多一步操作。
          其他的參數(shù)在官方dataloader.py里面都有介紹,就不多說了,太抽象也不好理解?,F(xiàn)在就一步一步debug下去,碰到什么就搞清楚什么,一路趟平過去。下圖看到初始狀態(tài),還有參數(shù),batchsize = 16。
          創(chuàng)建Dataloader對象進(jìn)來之后在def __init__(self,...)初始化方法中,有這么一個判斷,傳進(jìn)來的dataset是不是迭代器,如果是迭代器不支持目前pytorch的采樣器sampler等。目前傳進(jìn)來的dataset都不是迭代器。后面還有其他的判斷,不影響主線,就不表了,感興趣的朋友可以自己看看。初始化方法中后面的代碼也不多,我就搬上來看一下邏輯。
          首先這個第一行else就是接上圖的如果dataset不是迭代器,if isinstance(...):。正常的我們傳入的dataset,設(shè)置這么一個實(shí)例化屬性,后面的那個變量_DatasetKind.Map==0。
          然后我們看一下這個類,這個類也在dataloader.py中,32行開始的??梢钥吹竭@個類有兩個參數(shù)Map = 0,當(dāng)這個參數(shù)等于0的時候,會采用_MapDatasetFetcher的采樣方法,進(jìn)去看一下這個類到底是個什么玩意兒。
          可以看到這是一個在fetch.py中的類,這里面有一個fetch方法,定義了收集數(shù)據(jù)的方法,這個一會兒估計會用到,先放到這里,標(biāo)記為①。
          現(xiàn)在我們繼續(xù)回到dataloader.py中,剛才到216行代碼,我把初始化方法中下面的內(nèi)容都粘貼過來,不多,看看邏輯是什么樣的。
          到這個地方247行,就是初始化方法的全部內(nèi)容了。從上面197~216行代碼中看一下,如果自己指定了sampler即采樣器,那么shuffle=True就不行了,如果指定了batchsampler,也是有一些參數(shù)會沖突,先往后看,一會兒再看看這個采樣器是什么,batch_size不會不設(shè)置的,就不看了,也是異常處理語句。
          來到上面這兩張圖片,dataloader.py里面的217~247,還是初始化方法里面的(def __init__(self,...))。第218行代碼,如果sampler==None,這個條件一般是成立的,很少自己指定采樣器,訓(xùn)練的時候shuffle==True,就是if shuffle:了,此時采用隨機(jī)采樣器,驗(yàn)證或者推理的時候shuffle==False,就采用順序采樣器,這個晚會兒再去看。
          228行代碼,指定了batchsize并且batch_sampler沒有指定的情況下(這就是通常的情況),定義了一個batchsampler的采樣器類,看參數(shù)傳入的就是上面沒指定sampler時,系統(tǒng)傳入的或著隨機(jī)采樣器或者順序采樣器。下面都是定義實(shí)例屬性了,把剛才的判斷都用屬性引用一下。不慌,接著看下一步的debug。
          上面有三張圖片,按照我的代碼debug,shuffle=True直接進(jìn)入隨機(jī)采樣器的,但是順序采樣器就在隨機(jī)采樣器的上面,就一起看看。debug就會進(jìn)入sampler.py文件,先看一下第一張圖SequentialSampler類。這個類很簡單就是三個內(nèi)置方法。首先初始化會傳入一個dataset進(jìn)去也就是58行的datasource。
          dataset本質(zhì)是什么呢?dataset是我們自己創(chuàng)建的MyDataset類的實(shí)例對象,系統(tǒng)給他分配了一塊兒內(nèi)存,這塊兒內(nèi)存里有實(shí)例屬性,實(shí)例方法啥的。舉個例子,實(shí)例屬性有一個變量存儲了一個列表,這個列表里存儲的是所有圖片的路徑。def __getitem__(self, idx): 這個方法呢就可以索引這個列表讀取圖片。當(dāng)然這個方法里面的過程是需要自己寫的。比如上面說了有個列表是存儲了圖片的路徑,我們可以對該列表進(jìn)行索引,取出一個路徑,然后讀取圖片,然后處理圖片,最后返回圖片。這些代碼都是存儲在內(nèi)存中的。
          正如最開始我們寫的那樣,getitem這個魔術(shù)方法讓dataset實(shí)例對象這塊內(nèi)存可以像數(shù)組那樣進(jìn)行索引dataset[idx],可以使用len(dataset)函數(shù)直接返回長度。所以dataset可以理解為一塊兒內(nèi)存。
          創(chuàng)建順序采樣器實(shí)例時,把dataset傳入到SequentialSampler類,就是創(chuàng)建了一個dataset長度的迭代器,__iter__這個魔術(shù)方法最開始也提到了,可以返過去看一下。所以這個順序采樣器的本質(zhì)我們就完全了解了,其實(shí)就是個迭代器,可以理解為跟range(n)沒有什么太大的區(qū)別。
          現(xiàn)在看隨機(jī)采樣器RandomSampler(Sampler)類。別的不用看,直接看__iter__()方法,有個if,如果要替代傳進(jìn)來的dataset,那么就返回一個指定了樣本數(shù)量的列表。這個列表就是從[0, n-1]每次取一個值,取了指定的num_samples個,一般我們不會選這個,那返回的就是另一個。
          其中,n是dataset的大小,返回的是一個[0, n-1]的亂序的列表的迭代器。這個列表中包含0~n-1的每一個值,但是是亂序的。這一步到現(xiàn)在也非常的明了了??椿豥ataloader.py的224行,得到sampler是個迭代器,迭代器里面是亂序的[0~n-1]的數(shù)值,繼續(xù)往下看。
          剛才說了dataloader.py的228行就是我們遇見的通常情況,所以debug就會進(jìn)入到230行,然后創(chuàng)建實(shí)例對象,現(xiàn)在看一下這個類,是怎么對sampler迭代器進(jìn)行操作的,返回的又是什么。
          首先是初始化方法,創(chuàng)建一些實(shí)例屬性,然后還是__iter/len__(self)這兩個方法,這個batch采樣器也很明了了,就是一個生成器。
          過程就是:先創(chuàng)建一個列表,然后從我們存儲有亂序的索引的迭代器sampler中取值,每取batchsize個就返回batch這個列表(里面存儲的是數(shù)字),然后停在這里,等待下一次next()方法調(diào)用,下一次從yield處開始執(zhí)行,先把batch這個列表置空,然后重新取值,知道最后取完,然后判斷batch的長度到達(dá)設(shè)置的batchsize沒有。如果有就返回,沒有就完成循環(huán)了。然后執(zhí)行下面的判斷,drop_last是否為True,看看是舍棄還是繼續(xù)返回。
          就這樣BatchSampler類的原理也了解了,就這,非常的簡單。
          然后繼續(xù)debug,現(xiàn)在到了dataloader.py的232行了。
          debug這些屬性的時候都會跳到另一個魔術(shù)方法里,就下面這個
          這個方法可以看一下這里面的解釋python 中__setattr__, __getattr__,__getattribute__, __call_使用方法。重寫_setattr__方法,意味著每次對實(shí)例屬性進(jìn)行賦值都會調(diào)用該方法。我們在debug的過程實(shí)例已經(jīng)創(chuàng)建了,這個方法的目的是控制括號里的那5個參數(shù)不能更改。如果在創(chuàng)建實(shí)例之后,又重新對這幾個實(shí)例屬性賦值,當(dāng)然或者對其他實(shí)例屬性賦值,都會調(diào)用這個方法,只不過調(diào)用之后在方法內(nèi)的邏輯是,如果是括號內(nèi)的這幾個實(shí)例屬性,就會報錯??聪洛e誤,意思就是xx屬性不應(yīng)該在實(shí)例對象初始化之后再被設(shè)置。
          現(xiàn)在就來到了238行collate_fn,然后再239行進(jìn)行判斷的時候就來到了下圖295行這個方法。
          上面的代碼可以看到,self.batch_sampler是有值的,右值為BatchSampler類的實(shí)例對象,所以返回True。所以collate_fn = _utils.collate.default_collate,這個方法是怎么運(yùn)行的一會兒再說。
          現(xiàn)在整個dataset和dataloader的原理和內(nèi)容基本都了解的差不多了,那下面就在循環(huán)代碼中,看看它整個的過程是怎么一步一步取值的。下圖是一個比較簡單的常規(guī)訓(xùn)練代碼,建立DataLoader的實(shí)例,起名字叫train_loader,進(jìn)去看一下,跳到第二張圖。
          類中實(shí)現(xiàn)了__iter__方法,實(shí)例對象就是個迭代器。對括號內(nèi)self的理解參見第一個卡片鏈接,幾個特殊方法那個。在迭代過程中確認(rèn)是單進(jìn)程還是多進(jìn)程加載數(shù)據(jù),上面提到過了,在windows上是不支持多進(jìn)程加載數(shù)據(jù)的,所以進(jìn)去看一下單進(jìn)程是怎么加載數(shù)據(jù)的。
          這是一個比較簡單的類,看下代碼,首先看一下初始化方法,定義了一個fetcher的實(shí)例屬性,看下右值是不是比較熟悉,在標(biāo)記為①處的地方,已經(jīng)說過這個類還有方法了。
          在它實(shí)例方法里面有一個index的變量,這個右值在初始化方法中沒有,應(yīng)該是繼承父類的,我們進(jìn)去其父類看一下,這個右值在下面說明。
          從上面三個框可以看到,剛才說沒有的那個右值self._next_index()來自train_loader的_index_sampler,這是一個實(shí)例方法,返回BatchSampler類的實(shí)例對象(還記得這個對象返回的是什么嗎?這是一個迭代器,每次返回一個batch亂序索引),看下圖。
          這個就是單進(jìn)程類里面的東西,初始化完成之后,就返回這么一個實(shí)例對象。
          for … in… 這個語法有兩個功能。一是獲得一個迭代器,即調(diào)用了__iter__()方法。第二個功能是循環(huán)調(diào)用__next__()方法。剛才是實(shí)現(xiàn)了第一個功能,獲得一個迭代器,現(xiàn)在繼續(xù)單步調(diào)試下去就是調(diào)用__next__()方法。對于迭代器來講,這就是取數(shù)據(jù)的過程。
          Iter()與 __iter__ 用于產(chǎn)生 iterator(迭代器),__iter__ 迭代器協(xié)議,凡是實(shí)現(xiàn)__iter__協(xié)議的對象,皆是迭代器對象。(next()也得實(shí)現(xiàn),不然沒法產(chǎn)生數(shù)據(jù))。
          Iter()迭代器工廠函數(shù),凡是有定義有__iter__()函數(shù),或者支持序列訪問協(xié)議,也就是定義有__getitem__()函數(shù)的對象 皆可以通過 iter()工廠函數(shù) 產(chǎn)生迭代器(iterable)對象。
          原文鏈接:https://blog.csdn.net/weixin_36670529/article/details/106641754
          剛才說過了,這一步會調(diào)用對象的__next__()方法,現(xiàn)在的對象是_SingleProcessDataLoaderIter類的實(shí)例對象,這個方法是父類的,直接繼承了。我們看到data = self._next_data(),這個是實(shí)例方法。
          如圖所示,進(jìn)入了這個類的這個方法。上面剛剛說過了,self._next_index()是父類的一個實(shí)例方法,可以往上翻看一下,經(jīng)過一系列調(diào)用,到了BatchSampler類,返回的是一個batchsize的亂序的索引列表。
          從上圖debug的index值可以看到具體列表,我的樣本是199個,batchsize=16,所以列表內(nèi)的值是無序的16個,不超過198的數(shù)值。
          現(xiàn)在只是索引取完了,我們要的是數(shù)據(jù)和標(biāo)簽,所以下面到了真正的按照亂序索引取數(shù)據(jù)的過程。
          到了fetch.py的這個類的fetch方法,這個方法上面提到過2次,if條件為真,所以直接執(zhí)行下面代碼,傳進(jìn)來的形參是剛才那個index(16個無序的不超過198的數(shù)字列表),然后就是一個列表生成式,索引的形式讀取dataset返回的數(shù)據(jù),這也是為什么上面一直強(qiáng)調(diào),自定義的dataset類必須寫__getiitem__()方法的原因。再強(qiáng)調(diào)一遍,只有寫了這個魔術(shù)方法,才能和python的內(nèi)置函數(shù)功能對應(yīng)起來,實(shí)例對象才能夠以索引的方式取數(shù)據(jù)。下一步debug就會直接調(diào)到我們自定義的dataset類的__getiitem__()方法讀取數(shù)據(jù)。如下圖所示。
          在fetch方法中,得到那16個索引的dataset數(shù)據(jù)之后,data是一個含有image和label數(shù)據(jù)的長度為16的列表,每對image和label數(shù)據(jù)構(gòu)成一個元組。return時對data有一個self.collate_fn(data)的操作,看一下這個操作什么樣子的。
          在dataloader的初始化方法中,就有這個函數(shù),上面也提到過,這個函數(shù)一直作為參數(shù)傳遞,直到現(xiàn)在終于用到這個函數(shù)了,看下到底是個啥玩意兒。
          紅框的路徑正是上面collate_fn的右值,collate_fn是在dataloader.py中的,dataloader.py是在torch.utils.data中的,所以這個右值就是是一個相對路徑,并且它不是一個實(shí)例方法,它是一個函數(shù)。
          這個函數(shù)的形參是個batch,其實(shí)就是我們剛才的data,data是一個含有image和label數(shù)據(jù)的長度為16的列表,每對image和label數(shù)據(jù)構(gòu)成一個元組,下圖可以看到一個示例。
          default_collate這個函數(shù)代碼邏輯很簡單,就是各種判斷,如果都不是,最后就raise個類型錯誤。顯然,elem變量是個tuple類型數(shù)據(jù),里面含有兩個元素,如上所示一個tensor,一個數(shù)字,elem_type必然是tuple了。所以一路判斷下來,進(jìn)入了下圖這個判斷里面,下面注釋著這個判斷是檢查batch或者說我們的data中的元素是不是尺寸一致的。因?yàn)槲覀兠總€元素都是tuple,每個tuple里面都是2個元素,每個tuple里面的兩個元素的各自形式都是一致的。所以進(jìn)入了這個判斷。
          https://blog.csdn.net/csdn15698845876/article/details/73411541
          將batch這個列表變成迭代器,然后,取出一個數(shù)據(jù),即一個elem,elem_size = 2,就是一個元組有幾個元素嘛,有兩個。然后判斷是否所有elem的size都是2。然后解包再打包,這個過程看上面鏈接里的內(nèi)容去理解。就是把batch這個列表解包,形成了16個元組,每個元組有2個元素,分別是image和label數(shù)據(jù),然后再zip。元組每個有2個元素,所以zip完的迭代器如果轉(zhuǎn)換為list形式就是2個元組,每個元組里分別有16個image和label元素。
          zip函數(shù)返回的是一個迭代器,使用for這個語法取的時候,分別取出來兩個參數(shù),然后又送入到當(dāng)前的default_collate函數(shù)進(jìn)行判斷。
          第一個肯定是個tensor了,get_workerinfo()返回None,進(jìn)入到return,對batch這組數(shù)據(jù)在0維進(jìn)行stack操作,(batch是個元組,里面有16個tensor,在第0維進(jìn)行stack相當(dāng)于是增加了一維,這一維度的個數(shù)是元組的長度,也就是數(shù)據(jù)的個數(shù))返回4維的tensor(n*c*h*w),這也是為什么返回的數(shù)據(jù)第一維是n(batch_size大?。?。然后zip后的第二組數(shù)據(jù)也是元組,取出一個elem就是int,所以進(jìn)入到如下代碼段中。
          講label數(shù)據(jù)轉(zhuǎn)換為tensor返回到調(diào)用zip后迭代器的那個列表中,然后進(jìn)行return,return的是一個列表,里面有兩個tensor元素。每個tensor元素都有16個數(shù)據(jù)。
          然后進(jìn)行內(nèi)存加速操作,這個是什么鎖頁內(nèi)存,可以對數(shù)據(jù)處理進(jìn)行加速,然后返回data(十個列表),返回到哪兒了呢?
          其實(shí)是返回到_SingleProcessDataLoaderIter的__next__(self)方法中,S繼承了父類_BaseDataLoaderIter類的這個方法。然后再進(jìn)行判斷,此時判斷條件不成立,我們的數(shù)據(jù)不是_DatasetKind.Iterable,而是_DatasetKind.Map,這個上面已經(jīng)說過了。然后再返回data,此時返回的data就是轉(zhuǎn)了一圈回來了。
          終于傳回來了,此時data就是for語句中的那個data,data里面有兩組數(shù)據(jù),將其解包,圖像數(shù)據(jù)給inputs,標(biāo)簽數(shù)據(jù)給label,此時數(shù)據(jù)維度也正確了,然后進(jìn)行后續(xù)的前向計算和反向傳播等操作。



          猜您喜歡:

          超110篇!CVPR 2021最全GAN論文匯總梳理!

          超100篇!CVPR 2020最全GAN論文梳理匯總!

          拆解組新的GAN:解耦表征MixNMatch

          StarGAN第2版:多域多樣性圖像生成


          附下載 |?《可解釋的機(jī)器學(xué)習(xí)》中文版

          附下載 |《TensorFlow 2.0 深度學(xué)習(xí)算法實(shí)戰(zhàn)》

          附下載 |《計算機(jī)視覺中的數(shù)學(xué)方法》分享


          《基于深度學(xué)習(xí)的表面缺陷檢測方法綜述》

          《零樣本圖像分類綜述: 十年進(jìn)展》

          《基于深度神經(jīng)網(wǎng)絡(luò)的少樣本學(xué)習(xí)綜述》


          瀏覽 21
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  喷水人妻在线播放视频 | 99免费热播视频 | 蜜桃av资源 | 国产乱伦a片视频 | の夫婦交換中中文字幕 |