過去的部分帶我們參觀了計算機視覺的現代網絡設計。我們涵蓋的所有工作的共同點是它嚴重依賴科學家的直覺。許多架構在很大程度上受到了人類創造力的啟發,而在很大程度上受到了對深度網絡提供的設計空間的系統探索的影響。盡管如此,這種網絡工程方法已經取得了巨大的成功。
由于 AlexNet(第 8.1 節)在 ImageNet 上擊敗了傳統的計算機視覺模型,因此通過堆疊卷積塊構建非常深的網絡變得很流行,所有這些都是由相同的模式設計的。尤其,3×3卷積由 VGG 網絡(第 8.2 節)推廣。NiN(第 8.3 節)表明即使1×1通過添加局部非線性,卷積可能是有益的。此外,NiN 通過跨所有位置的聚合解決了在網絡頭部聚合信息的問題。GoogLeNet(8.4節)在其Inception block中加入了多個不同卷積寬度的分支,結合了VGG和NiN的優點。ResNets(第 8.6 節)改變了對身份映射的歸納偏差(來自f(x)=0).這允許非常深的網絡。將近十年后,ResNet 設計仍然流行,證明了它的設計。最后,ResNeXt(第 8.6.5 節)添加了分組卷積,在參數和計算之間提供了更好的權衡。擠壓和激發網絡 (SENets) 是用于視覺的變形金剛的前身,可實現位置之間的高效信息傳輸(Hu等人,2018 年)。他們通過計算每個通道的全局注意力函數來實現這一點。
到目前為止,我們省略了通過神經架構搜索(NAS)獲得的網絡(Liu等人,2018 年,Zoph 和 Le,2016 年)。我們之所以選擇這樣做,是因為它們的成本通常很高,依賴于蠻力搜索、遺傳算法、強化學習或某種其他形式的超參數優化。給定一個固定的搜索空間,NAS 使用搜索策略根據返回的性能估計自動選擇架構。NAS 的結果是單個網絡實例。EfficientNets 是這次搜索的顯著成果(Tan 和 Le,2019 年)。
下面我們討論一個與尋求單一最佳網絡完全不同的想法。它在計算上相對便宜,它會在途中產生科學見解,并且在結果質量方面非常有效。讓我們回顧一下Radosavovic等人的策略。(2020)設計網絡設計空間。該策略結合了手動設計和 NAS 的優勢。它通過對網絡分布進行操作 并以某種方式優化分布以獲得整個網絡系列的良好性能來實現這一點。它的結果是RegNets,特別是 RegNetX 和 RegNetY,以及一系列用于設計高性能 CNN 的指導原則。
importtorchfromtorchimportnnfromtorch.nnimportfunctionalasFfromd2limporttorchasd2l
frommxnetimportinit,np,npxfrommxnet.gluonimportnnfromd2limportmxnetasd2lnpx.set_np()
fromflaximportlinenasnnfromd2limportjaxasd2l
importtensorflowastffromd2limporttensorflowasd2l
8.8.1.AnyNet 設計空間
下面的描述緊跟Radosavovic等人的推理 。(2020)加上一些縮寫以使其符合本書的范圍。首先,我們需要一個供網絡系列探索的模板。本章設計的共同點之一是網絡由主干、主體和頭部組成。.stem 執行初始圖像處理,通常通過具有較大窗口大小的卷積。主體由多個塊組成,執行從原始圖像到對象表示所需的大量轉換。最后,頭部將其轉換為所需的輸出,例如通過用于多類分類的 softmax 回歸器。反過來,身體由多個階段組成,以降低的分辨率對圖像進行操作。事實上,主干和每個后續階段都占空間分辨率的四分之一。最后,每個階段由一個或多個塊組成。這種模式對所有網絡都很常見,從 VGG 到 ResNeXt。事實上,對于通用 AnyNet 網絡的設計,Radosavovic等人。(2020)使用了 ResNeXt 塊圖 8.6.5。
圖 8.8.1AnyNet 設計空間。號碼(c,r)沿每個箭頭指示通道數c和決議r×r當時的圖像。從左到右:由主干、主體和頭部組成的通用網絡結構;身體由四個階段組成;階段的詳細結構;塊的兩種替代結構,一種沒有下采樣,另一種將每個維度的分辨率減半。設計選擇包括深度di, 輸出通道數ci, 組數gi和瓶頸比ki對于任何階段i.
讓我們詳細回顧一下圖 8.8.1中概述的結構。如前所述,AnyNet 由主干、主體和頭部組成。詞干將 RGB 圖像(3 通道)作為輸入,使用3×3與 stride 的卷積2,然后是批量規范,將分辨率減半r×r到r/2×r/2.此外,它生成c0作為身體輸入的通道。
由于該網絡旨在與形狀的 ImageNet 圖像配合使用224×224×3,身體用于將其減少到7×7×c4通過 4 個階段(回想一下224/21+4=7), 每個最終的步幅為2.最后,head 通過全局平均池采用完全標準的設計,類似于 NiN(第 8.3 節),然后是一個完全連接的層以發出一個n維向量為n-類分類。
大多數相關的設計決策都是網絡主體固有的。它分階段進行,每個階段都由我們在第 8.6.5 節中討論的相同類型的 ResNeXt 塊組成。那里的設計再次完全通用:我們從一個塊開始,通過使用一個步長將分辨率減半2(圖8.8.1最右邊)。為了匹配這一點,ResNeXt 塊的剩余分支需要通過1×1卷積。此塊后跟可變數量的附加 ResNeXt 塊,這些塊使分辨率和通道數均保持不變。請注意,常見的設計實踐是在卷積塊的設計中添加一個小瓶頸。因此,瓶頸比ki≥1我們提供一些渠道ci/ki在每個階段的塊內i(正如實驗所示,這并不是真正有效,應該跳過)。最后,由于我們正在處理 ResNeXt 塊,我們還需要選擇組數gi對于階段的分組卷積i.
這個看似通用的設計空間仍然為我們提供了許多參數:我們可以設置塊寬度(通道數)c0,…c4,每個階段的深度(塊數)d1,…d4, 瓶頸比k1,…k4和組寬度(組數)g1,…g4.這總共加起來多達 17 個參數,導致不合理的大量配置值得探索。我們需要一些工具來有效地縮小這個巨大的設計空間。這就是設計空間概念美的來源。在我們這樣做之前,讓我們先實現通用設計。
classAnyNet(d2l.Classifier):defstem(self,num_channels):returnnn.Sequential(nn.LazyConv2d(num_channels,kernel_size=3,stride=2,padding=1),nn.LazyBatchNorm2d(),nn.ReLU())
classAnyNet(d2l.Classifier):defstem(self,num_channels):net=nn.Sequential()net.add(nn.Conv2D(num_channels,kernel_size=3,padding=1,strides=2),nn.BatchNorm(),nn.Activation('relu'))returnnet
classAnyNet(d2l.Classifier):arch:tuplestem_channels:intlr:float=0.1num_classes:int=10training:bool=Truedefsetup(self):self.net=self.create_net()defstem(self,num_channels):returnnn.Sequential([nn.Conv(num_channels,kernel_size=(3,3),strides=(2,2),padding=(1,1)),nn.BatchNorm(notself.training),nn.relu])
classAnyNet(d2l.Classifier):defstem(self,num_channels):returntf.keras.models.Sequential([tf.keras.layers.Conv2D(num_channels,kernel_size=3,strides=2,padding='same'),tf.keras.layers.BatchNormalization(),tf.keras.layers.Activation('relu')])
每個階段由depthResNeXt 塊組成,其中num_channels指定塊寬度。請注意,第一個塊將輸入圖像的高度和寬度減半。
@d2l.add_to_class(AnyNet)defstage(self,depth,num_channels,groups,bot_mul):blk=[]foriinrange(depth):ifi==0:blk.append(d2l.ResNeXtBlock(num_channels,groups,bot_mul,use_1x1conv=True,strides=2))else:blk.append(d2l.ResNeXtBlock(num_channels,groups,bot_mul))returnnn.Sequential(*blk)
@d2l.add_to_class(AnyNet)defstage(self,depth,num_channels,groups,bot_mul):net=nn.Sequential()foriinrange(depth):ifi==0:net.add(d2l.ResNeXtBlock(num_channels,groups,bot_mul,use_1x1conv=True,strides=2))else:net.add(d2l.ResNeXtBlock(num_channels,num_channels,groups,bot_mul))returnnet
@d2l.add_to_class(AnyNet)defstage(self,depth,num_channels,groups,bot_mul):blk=[]foriinrange(depth):ifi==0:blk.append(d2l.ResNeXtBlock(num_channels,groups,bot_mul,use_1x1conv=True,strides=(2,2),training=self.training))else:blk.append(d2l.ResNeXtBlock(num_channels,groups,bot_mul,training=self.training))returnnn.Sequential(blk)
@d2l.add_to_class(AnyNet)defstage(self,depth,num_channels,groups,bot_mul):net=tf.keras.models.Sequential()foriinrange(depth):ifi==0:net.add(d2l.ResNeXtBlock(num_channels,groups,bot_mul,use_1x1conv=True,strides=2))else:net.add(d2l.ResNeXtBlock(num_channels,groups,bot_mul))returnnet
將網絡的主干、主體和頭部放在一起,我們完成了 AnyNet 的實現。
@d2l.add_to_class(AnyNet)def__init__(self,arch,stem_channels,lr=0.1,num_classes=10):super(AnyNet,self).__init__()self.save_hyperparameters()self.net=nn.Sequential(self.stem(stem_channels))fori,sinenumerate(arch):self.net.add_module(f'stage{i+1}',self.stage(*s))self.net.add_module('head',nn.Sequential(nn.AdaptiveAvgPool2d((1,1)),nn.Flatten(),nn.LazyLinear(num_classes)))self.net.apply(d2l.init_cnn)
@d2l.add_to_class(AnyNet)def__init__(self,arch,stem_channels,lr=0.1,num_classes=10):super(AnyNet,self).__init__()self.save_hyperparameters()self.net=nn.Sequential()self.net.add(self.stem(stem_channels))fori,sinenumerate(arch):self.net.add(self.stage(*s))self.net.add(nn.GlobalAvgPool2D(),nn.Dense(num_classes))self.net.initialize(init.Xavier())
@d2l.add_to_class(AnyNet)defcreate_net(self):net=nn.Sequential([self.stem(self.stem_channels)])fori,sinenumerate(self.arch):net.layers.extend([self.stage(*s)])net.layers.extend([nn.Sequential([lambdax:nn.avg_pool(x,window_shape=x.shape[1:3],strides=x.shape[1:3],padding='valid'),lambdax:x.reshape((x.shape[0],-1)),nn.Dense(self.num_classes)])])returnnet
@d2l.add_to_class(AnyNet)def__init__(self,arch,stem_channels,lr=0.1,num_classes=10):super(AnyNet,self).__init__()self.save_hyperparameters()self.net=tf.keras.models.Sequential(self.stem(stem_channels))fori,sinenumerate(arch):self.net.add(self.stage(*s))self.net.add(tf.keras.models.Sequential([tf.keras.layers.GlobalAvgPool2D(),tf.keras.layers.Dense(units=num_classes)]))
8.8.2.設計空間的分布和參數
正如在第 8.8.1 節中討論的那樣,設計空間的參數是該設計空間中網絡的超參數。考慮在 AnyNet 設計空間中識別好的參數的問題。我們可以嘗試為給定的計算量(例如,FLOPs 和計算時間)找到單個最佳參數選擇。如果我們只允許每個參數有兩種可能的選擇,我們將不得不探索217=131072組合以找到最佳解決方案。由于其過高的成本,這顯然是不可行的。更糟糕的是,就應該如何設計網絡而言,我們并沒有真正從這個練習中學到任何東西。下次我們添加 X 階段或移位操作或類似操作時,我們需要從頭開始。更糟糕的是,由于訓練中的隨機性(舍入、混洗、位錯誤),沒有兩次運行可能產生完全相同的結果。更好的策略是嘗試確定參數選擇應如何關聯的一般準則。例如,瓶頸率、通道、塊、組的數量,或者它們在層之間的變化,理想情況下應該由一組簡單的規則來控制。Radosavovic等人的方法。(2019)依賴于以下四個假設:
我們假設通用設計原則確實存在,這樣許多滿足這些要求的網絡應該提供良好的性能。因此,識別網絡分布可能是一個很好的策略。換句話說,我們假設大海撈針很多。
在我們評估網絡是否良好之前,我們不需要訓練網絡收斂。相反,使用中間結果作為最終精度的可靠指導就足夠了。使用(近似)代理來優化目標被稱為多重保真度優化(Forrester等人,2007 年)。因此,根據僅幾次通過數據集后獲得的準確性進行設計優化,從而顯著降低成本。
在較小規模(對于較小網絡)下獲得的結果可以推廣到較大的網絡。因此,對結構相似但塊數少、通道數少等的網絡進行優化。只有到最后,我們才需要驗證這樣發現的網絡是否也能在規模上提供良好的性能。
設計的各個方面可以近似分解,這樣就可以在某種程度上獨立地推斷出它們對結果質量的影響。換句話說,優化問題比較容易。
這些假設使我們能夠廉價地測試許多網絡。特別是,我們可以從配置空間中均勻采樣并評估它們的性能。隨后,我們可以通過查看上述網絡可以實現的誤差/準確度分布來評估參數選擇的質量。表示為F(e)給定設計空間網絡錯誤的累積分布函數 (CDF),使用概率分布繪制p.那是,
(8.8.1)F(e,p)=defPnet~p{e(net)≤e}.
我們現在的目標是找到一個分布p通過網絡使得大多數網絡的錯誤率非常低,并且支持p簡潔。當然,這在計算上無法準確執行。我們求助于網絡樣本Z=def{net1,…netn}(有錯誤e1,…,en,分別)從p并使用經驗 CDFF^(e,Z)反而:
(8.8.2)F^(e,Z)=1n∑i=1n1(ei≤e).
每當一組選擇的 CDF 主要(或匹配)另一個 CDF 時,它的參數選擇是優越的(或無關緊要的)。因此,Radosavovic等人。(2020)試驗共享網絡瓶頸比ki=k對于所有階段i的網絡。這擺脫了3的4控制瓶頸比的參數。要評估這是否(負面)影響性能,可以從受約束和不受約束的分布中提取網絡并比較相應的 CDF。事實證明,這個約束根本不影響網絡分布的準確性,如圖8.8.2的第一幅圖所示。同樣,我們可以選擇選擇相同的組寬度gi=g發生在網絡的各個階段。同樣,這不會影響性能,如圖8.8.2的第二個面板所示。這兩個步驟結合起來減少了自由參數的數量6.
圖 8.8.2比較設計空間的誤差經驗分布函數。AnyNetA是原設計空間;AnyNetB聯系瓶頸比率,AnyNetC也聯系組寬度,AnyNetD跨階段增加網絡深度。從左到右:(i) 綁定瓶頸比率對性能沒有影響,(ii) 綁定組寬度對性能沒有影響,(iii) 增加跨階段的網絡寬度(通道)可以提高性能,(iv) 增加跨階段的網絡深度階段提高性能。圖由Radosavovic等人提供 。(2020 年)。
接下來,我們尋找方法來減少階段寬度和深度的多種潛在選擇。一個合理的假設是,隨著我們深入,通道的數量應該增加,即ci≥ci?1(wi+1≥wi根據他們在圖 8.8.2中的符號),產生AnyNetXD.同樣,同樣合理的假設是隨著階段的進展,它們應該變得更深,即,di≥di?1, 屈服AnyNetXE.這可以分別在圖 8.8.2的第三和第四圖中進行實驗驗證。
8.8.3.注冊網
所結果的AnyNetXE設計空間由遵循易于理解的設計原則的簡單網絡組成:
分享瓶頸比例ki=k對于所有階段i;
共享組寬度gi=g對于所有階段i;
增加跨階段的網絡寬度:ci≤ci+1;
跨階段增加網絡深度:di≤di+1.
這給我們留下了最后一組選擇:如何為最終的上述參數選擇特定值AnyNetXE設計空間。通過研究分布在AnyNetXE可以觀察到:理想情況下,網絡的寬度隨著網絡中的塊索引線性增加,即cj≈c0+caj, 在哪里j是塊索引和斜率ca>0.鑒于我們只能在每個階段選擇不同的塊寬度,我們得到了一個分段常數函數,該函數被設計為匹配這種依賴性。其次,實驗還表明,瓶頸比為k=1表現最好,即我們建議根本不要使用瓶頸。
我們建議有興趣的讀者仔細閱讀Radosavovic等人,以了解有關如何為不同計算量設計特定網絡的更多詳細信息。(2020 年)。例如,一個有效的 32 層 RegNetX 變體由下式給出k=1(沒有瓶頸),g=16(組寬為 16),c1=32和c2=80第一階段和第二階段的渠道,分別被選為d1=4和d2=6塊深。該設計的驚人見解是它適用,即使在調查更大規模的網絡時也是如此。更好的是,它甚至適用于具有全局通道激活的擠壓和激勵 (SE) 網絡設計 (RegNetY)(Hu等人,2018 年)。
classRegNetX32(AnyNet):def__init__(self,lr=0.1,num_classes=10):stem_channels,groups,bot_mul=32,16,1depths,channels=(4,6),(32,80)super().__init__(((depths[0],channels[0],groups,bot_mul),(depths[1],channels[1],groups,bot_mul)),stem_channels,lr,num_classes)
classRegNetX32(AnyNet):def__init__(self,lr=0.1,num_classes=10):stem_channels,groups,bot_mul=32,16,1depths,channels=(4,6),(32,80)super().__init__(((depths[0],channels[0],groups,bot_mul),(depths[1],channels[1],groups,bot_mul)),stem_channels,lr,num_classes)
classRegNetX32(AnyNet):lr:float=0.1num_classes:int=10stem_channels:int=32arch:tuple=((4,32,16,1),(6,80,16,1))
classRegNetX32(AnyNet):def__init__(self,lr=0.1,num_classes=10):stem_channels,groups,bot_mul=32,16,1depths,channels=(4,6),(32,80)super().__init__(((depths[0],channels[0],groups,bot_mul),(depths[1],channels[1],groups,bot_mul)),stem_channels,lr,num_classes)
我們可以看到,每個 RegNetX 階段都逐漸降低分辨率并增加輸出通道。
RegNetX32().layer_summary((1,1,96,96))
Sequentialoutputshape:torch.Size([1,32,48,48])Sequentialoutputshape:torch.Size([1,32,24,24])Sequentialoutputshape:torch.Size([1,80,12,12])Sequentialoutputshape:torch.Size([1,10])
RegNetX32().layer_summary((1,1,96,96))
Sequentialoutputshape:(1,32,48,48)Sequentialoutputshape:(1,32,24,24)Sequentialoutputshape:(1,80,12,12)GlobalAvgPool2Doutputshape:(1,80,1,1)Denseoutputshape:(1,10)
RegNetX32(training=False).layer_summary((1,96,96,1))
Sequentialoutputshape:(1,48,48,32)Sequentialoutputshape:(1,24,24,32)Sequentialoutputshape:(1,12,12,80)Sequentialoutputshape:(1,10)
RegNetX32().layer_summary((1,96,96,1))
Sequentialoutputshape:(1,48,48,32)Sequentialoutputshape:(1,24,24,32)Sequentialoutputshape:(1,12,12,80)Sequentialoutputshape:(1,10)
8.8.4.訓練
在 Fashion-MNIST 數據集上訓練 32 層 RegNetX 就像以前一樣。
model=RegNetX32(lr=0.05)trainer=d2l.Trainer(max_epochs=10,num_gpus=1)data=d2l.FashionMNIST(batch_size=128,resize=(96,96))trainer.fit(model,data)
model=RegNetX32(lr=0.05)trainer=d2l.Trainer(max_epochs=10,num_gpus=1)data=d2l.FashionMNIST(batch_size=128,resize=(96,96))trainer.fit(model,data)
model=RegNetX32(lr=0.05)trainer=d2l.Trainer(max_epochs=10,num_gpus=1)data=d2l.FashionMNIST(batch_size=128,resize=(96,96))trainer.fit(model,data)
trainer=d2l.Trainer(max_epochs=10)data=d2l.FashionMNIST(batch_size=128,resize=(96,96))withd2l.try_gpu():model=RegNetX32(lr=0.01)trainer.fit(model,data)
8.8.5。討論
具有理想的歸納偏差(假設或偏好),如視覺的局部和平移不變性(第 7.1 節),CNN 一直是該領域的主導架構。自 LeNet 以來一直如此,直到最近 Transformers(第 11.7 節)(Dosovitskiy等人,2021 年,Touvron等人,2021 年)開始在準確性方面超越 CNN。雖然最近在視覺 Transformer 方面取得的大部分進展都可以反向移植到 CNN 中(Liu等人,2022 年),只有在更高的計算成本下才有可能。同樣重要的是,最近的硬件優化(NVIDIA Ampere 和 Hopper)只是擴大了支持變形金剛的差距。
值得注意的是,與 CNN 相比,Transformer 對局部性和平移不變性的歸納偏差程度要低得多。這并不是最不重要的,因為大型圖像集的可用性,例如 LAION-400m 和 LAION-5B(Schuhmann等人,2022 年),其中有多達 50 億張學習結構的圖像。令人驚訝的是,在這方面的一些更相關的工作甚至包括 MLP(Tolstikhin等人,2021 年)。
總之,視覺 Transformers(第 11.8 節)目前在大規模圖像分類中的最先進性能方面處于領先地位,表明可擴展性勝過歸納偏差(Dosovitskiy等人,2021 年)。這包括使用多頭自注意力(第 11.5節)預訓練大型 Transformers(第11.9節)。我們邀請讀者深入研究這些章節以進行更詳細的討論。
8.8.6.練習
將階段數增加到 4。你能設計一個性能更好的更深的 RegNetX 嗎?
通過用 ResNet 塊替換 ResNeXt 塊來去除 ResNeXt-ify RegNets。你的新模型表現如何?
通過違反RegNetX 的設計原則來實現“VioNet”系列的多個實例。他們的表現如何?哪一個(di,ci,gi,bi) 是最重要的因素?
您的目標是設計“完美”的 MLP。你能用上面介紹的設計原則找到好的架構嗎?是否可以從小型網絡推斷到大型網絡?
-
pytorch
+關注
關注
2文章
808瀏覽量
13202 -
卷積網絡
+關注
關注
0文章
42瀏覽量
2163
發布評論請先 登錄
相關推薦
評論