1x1=1打一成語;0000打一成語正確答案 視頻
作者丨Onedroid@知乎(已授權)
來源丨https://zhuanlan.zhihu.com/p/632499470
編輯丨極市平臺
前幾天在知乎上看到華為提出的一個VanillaNet,其中的一個設計點和我一直想實現的功能非常類似,即訓練階段的時候模型是比較深的網絡,推理的時候會自動變成比較淺的網絡。起初我一直想緩漸地暴力地丟掉卷積層,結果不能如愿,模型性能會在訓練階段的最后幾個epoch一直退化,模型變成毫無用處的廢物。整整大半年一直也沒找到穩定性能的方案,這也把我后續所有的構想都否定了。本著拿來主義,本文直接采用VanillaNet的方案重新開始構建模型ReduceNet(reduce depth,還有就是張量的那個reduce)
ReduceNet和VanillaNet有以下幾個區別:
1. a) 引入殘差,提升模型性能。部署階段,殘差也可以作為特殊的卷積核融入卷積算子
b) 除了用一個VanillaNet中的"可消解的激活函數"外,不使用多余的非線性激活函數,從而使得不同模塊繼續融合。這也是reducenet關鍵設計,起源于某個偶然的實驗發現:resnet兩個3x3卷積構成的模塊,去掉第二個激活函數,性能會提升,順便提高了速度。所以reducenet模塊也只采用一個激活函數,而且是在訓練結束后會消失的激活函數
c) 不使用maxpooling,用卷積代替,不同stage可以繼續融合
2.模型只有在訓練階段存在激活函數,部署階段整個網絡沒有任何激活函數(最后的softmax還是需要的)
3.根據2,backbone所有的線性層理論上可以繼續融合為一個超大尺寸卷積核的卷積層,逼近一個全連接層,印證了那個傳說中的“單層網絡理論”(印象中是有的,貌似是單層大網絡學習容量足夠卻很難擬合之類的,我不記得出處了,有知道的可以告知一下)。
4. 利用bottleneck結構增加模塊網絡寬度,為訓練階段引入更多參數量,而不增加推理時候的開銷。理論上我們可以在訓練時讓模塊中間無限寬,推理時計算開銷只由模塊首尾的寬度決定。實際上應該會存在短板效應。我們大概只能在首尾寬度固定情況下不斷壓榨性能直到性能飽和。再繼續增加中間網絡寬度估計就變成冗余了。這時只能增加模塊首尾寬度繼續提升性能。用這種方式有點神經架構搜索的味道。也許神經神經架構搜索本身是很冗余的領域。漸進地調整寬高,壓榨性能,試探臨界才是較優解。
本文貢獻如下:
1.理論說明深度非線性網絡可以融合成單層網絡,實驗中模型也取得不錯的性能。本文提供了基本模塊的思路,目前的SOTA模型只要對應魔改,在不改架構的情況下,盡情利用本文模塊的特性榨取模型性能就可以,理論上100%漲點,不增加額外推理開銷。其實,本文的東西是提供了“填充”的功能,任何架構都可以通過本文模塊中“無限”和“縮并”的特性,理論上壓榨出該架構下最高性能。所以在數據充足的情況下,結合本文的模塊,最終的模型瓶頸還將會是架構設計。不過目前為止,我們還遠遠還沒摸到瓶頸。暫時只要用sota架構繼續壓榨就可以了。本人資源有限,沒卡,只要負責驗證方法的有效性就可以了。
上面說的是如何利用現有架構提升性能。另外我這里提供個模型設計方案去探索新的適合應用場景的sota架構設計方案。我們先定下模型的開銷約束,比如計算量,參數量,內存占用。在符合開銷約束情況下,掃描網絡深度和每層的寬度(這里是指部署的寬深,訓練階段,一層網絡是可以根據我們的辦法無限擴張寬深的,只要最后縮并回來),這樣就有不同的架構,讓它們一一被壓榨性能就可以了,因為寬深固定,性能極限就固定了。和SOTA比較,勝者為王。和神經架構搜索相比,我們可以做到專心壓榨性能,相當于搜索得更快了。因為神經架構搜索方法是沒法保證一定寬度深度各種組合下性能如何的,再加上搜索空間這么大,很容易三心二意顧此失彼。
總之,有一個證明,兩個pipeline方案
2.嘗試解釋了為什么單層網絡規模足夠大卻很難獲得學習能力,從優化的角度為多層非線性網絡和單層網絡建立了聯系,并指出多層非線性網絡本質上可以為"探索單層網絡的權重最優值”提供更大的探索自由度, 緩解優化算法和單層網絡的“不和”,從而具備更高的學習效率。
3.訓練階段,ReduceNet可以和現有的其他模型設計兼容,享有深度非線性網絡的性能收益。推理階段則最終可以變成單層網絡,保持高性能的同時,獲得激進的模型效率。當然,還有一個好處是,部署網絡的形態是具備靈活性的,你愛融合幾層就幾層,依照硬件的內存占用,延時條件調整融合的層數。比如,兩層3x3可以變成5x5卷積,在某個硬件上可能就能提速了。如果任務很容易,很小的模型就能滿足,那直接壓成單層,在硬件合適的情況下,推理速度會非???,提供了用深度網絡高效學習,用單層網絡快速推理的完美方案。
4.ReduceNet理論上可以在訓練階段無限深和無限寬(bottleneck中間表示無限寬),推理階段變成一個小網絡,有需要的話甚至直接變成單層網絡。
不排除我犯了很傻的代碼錯誤,一切只是我的妄想(真的碰到好幾次了),如果錯了就刪帖跑路
代碼鏈接
[https://github.com/ohmydroid/reducenet
模型壓縮的核心思想是先利用大網絡的學習能力,最后通過各種手段(知識蒸餾,剪枝,神經架構搜索,動態推理網絡等)將該能力轉移到較小的模型上,以減少資源開銷。(目前的分類習慣并不把神經架構搜索,動態推理網絡歸于模型壓縮,這僅僅是我個人習慣和理解)
ReduceNet的靈感來源有以下幾個:
1.RepVGG是比較特別的模型,訓練時有多個分支,推理時可以合并成單個分支,可以看作是網絡寬度這個角度的縮并。所以很容易聯想到把這種設計擴展到網絡深度方向,相關的工作有DepthShrinker,RMNet。嚴格來說,讀研期間設計的靜態condconv比這個更早,模塊化動態卷積又被condconv先發。橫向重參化的idea應該來源于condconv.
2.SkipNet, 一種動態推理網絡,在推理階段通過路由網絡為不同數據選擇性地跳過主網絡的一些網絡層,以減少計算.
3.Stochastic Depth,訓練階段以概率的方式激活或者不激活殘差塊中的卷積分支,可以提升性能,但是推理階段沒有減少網絡深度,按照概率融合卷積塊和殘差。
4.DepthShrinker, 比較接近我設想中的模型,但是條件很麻煩,需要學習mask參數,還要蒸餾,微調和融合。RMNet需要剪枝,微調,和融合。這些方式其實和華為的VanillaNet比較接近了。后者更加簡潔有效。我自己想的方案暫時又粗暴且無用,打不過就加入。
5.VanillaNet中Deep Training Strategy,里面的公式以及衰減策略和我設計的幾乎差不多,只是VanillaNet選擇了對激活函數下手,再后期融合兩個串聯的卷積算子。而我天真地以為idea結合了skipnet,Stochastic Depth,神經架構搜索等思想必定萬無一失(不排除以后會有正確穩定的方案),從而貪心地選擇最后丟棄整個卷積層。實驗結果一度讓我懷疑人生,模型性能會在最后階段徹徹底地崩潰。換成VanillaNet的Deep Training Strategy后模型結果非常理想。
6. 是我N年前構想的DetachNet,算是ReduceNet的雛形,希望有一個無限大的網絡可以為一個小網絡的每一層提供scale factor(scale的方式乘,加,卷積都可以)。這個和動態卷積非常類似了,只不過我希望大網絡在訓練的可以無限大,推理的時候可以摘除,將能力轉移給小網絡,完美的妄想的無限壓縮算法。最終敗于BP算法。也不是不可能。我一度相信,這種模型會讓AGI時代到來,完全沒想到AGI風暴是ChatGPT引起的。ReduceNet現在好像也可以在訓練階段無限大,推理階段非常小,所以DetachNet也算后繼有net。
總的來說,這種設計使得模型在訓練過程中借助多層卷積和非線性獲得更強的學習能力;推理階段,非線性消失則為減少網絡深度提供了可能。
順便一提,VanillaNet網絡雖然淺但是非常寬,計算量,參數量,內存占用都很大,唯一的優勢可能就是用更快的速度取得接近sota的準確率。然而,這速度也是有限制的。歸根結底這限制來自于寬網絡引發的一系列連鎖反應。龐大的通道數,會帶來更多的內存占用,進一步增加訪存。batch size等于1的時候vanillanet還有速度優勢,其遠超深網絡的計算開銷還能忽略。如果batch size變大呢,想必速度優勢不會有這么明顯,甚至會逐漸慢于普通的sota模型,那其優勢將蕩然無存。
VanillaNet的deep training strategy足以對深度網絡模型設計產生深遠的影響,可能在網絡架構上設計元素太多太雜,反而不能發揮整體優勢。
和VanillaNet不同,本文設計的基本模型由3x3 conv, 1x1 conv ,殘差連接構成。下采樣的話,也只要把3x3 conv的stride設置為2,并且去除收尾的殘差(激活函數的殘差保留)。值得注意的是整個模塊只有一個激活函數,訓練結束,激活函數也會消失。之所以只用一個激活函數是因為之前發現resnet的殘差塊中去掉一個激活函數效果會更好,速度還更快(查重發現被人發過了)。
數值,我是隨著iteration從1到0按照cosine的方式衰減,公式和VanillaNet的是反過來的。
訓練階段,模塊如左圖,推理階段,這個模塊會整合成一個純線性的3x3 conv。能夠融合的原因有以下幾點:
1.conv,bn可以融合成1個conv
2.一個3x3 conv和一個1x1 conv會整合成一個3x3 conv
3.殘差連接是可以看作一種特殊的卷積核,所以也可以參與線性融合
比起VanillaNet,ReduceNet的其中一個優勢在于能夠和現有的架構設計方式兼容。
此外,當訓練結束后,所有算子完成上述的融合后,ReduceNet的網絡backbone部分,只有連續的3x3 conv,這意味著這些純線性的3x3 conv可以進一步連續融合直至變成一個更大尺寸卷積核的單層卷積。最終,整個網絡會變成一個卷積層,pool, FC, 總共3層。pool是avgpool的話網絡應該可以直接變成1層。
按照resnet56 cifar的架構,reducenet56(訓練階段是56層) cifar10的準確率可以達到93%。resnet的模塊是兩個3x3 conv, 我的模塊是一個3x3 conv+一個1x1 Conv(可以融合成一個3x3 Conv)。
如果只融合模塊里的卷積不繼續融合的話,網絡深度應該是29. 為此構造了一個29層的ResNet進行比較(和原版resnet不同,每個模塊是一個非線性3x3卷積加一個殘差,因為要和我的模型推理階段的架構公平對比)準確率是91+%(手賤,不小心把tmux退出了)。具體架構和訓練代碼可以參考repo的代碼。雖然只是在cifar10上的實驗,但是效果已經足夠驚艷。代碼里還沒有加入任何融合操作,我就是確認純線性條件是不是成立,能跑不錯的結果就行,結果超出預期。
如果設計邏輯正確無誤,代碼也能確保線性條件成立的話,可以印證:
1.單層網絡的表達能力是足夠的,只是我們目前的優化算法并不能讓單層網絡找到合適解。不是模型容量的問題,是目前深度學習的優化算法碰上單層線性網絡的天然局限。
2.深度非線性網絡最終是可以轉換成單層網絡的,多層非線性網絡本質上可以看作是為單層網絡高效搜索權重的方法,彌補了當前單層網絡優化算法的缺陷。
這么多的非線性層也許只是為單個線性層找到更好的參數。最開始,參數只是隨機的,非線性的存在和BP算法機制,讓整體網絡的每個參數個體能夠像布朗運動一樣“更自由”地改變參數。更確切地說,是隨機初始化讓前向傳播有了一定隨機性,比如ReLU后的數值,隨機地為0。因果循環,這種隨機的失活,又讓反向傳播只能隨機地尋找路徑讓梯度流動(后面會提到,這是天然的子網采樣,每個子網都有機會接受數據洗禮)。
訓練階段,堆疊純非線性等價于一層線性層,即便堆疊再多,所有參數的更新軌跡都被“限定”了,訓練前的初始值給整個模型留下了深刻的烙印并且貫穿整個訓練過程。因為無論怎么更新,初始值已經決定了反向傳播梯度分配的比例,W這個整體權重完全散失了“隨機探索超流形的自由性”。
深度非線性網絡和巨大的參數量,或許只是為優化過程買更多的。參數越多,拓撲路徑組合的子網絡越多,W就能更加細致地,發揮群體力量更加高效地探索空間,最終只是為最后的單層網絡權重服務(這個過程當然也很有可能造成冗余。這種“冗余”某種程度上是有意義的,可以用計算開銷換取更快的學習速度,有粒子群優化的味道)改日再繼續想這個優化問題,因為是個很大的數學問題,而我只是憑借文字描述抓住一點鱗片,暫時無法更確切地描述。單單在多層非線性網絡和單層網絡之間建立辨證的數學聯系,這本身就是意義深遠的事情。假如深度非線性網絡的存在本身就是在自由隨機地為單個線性層(沒有考慮softmax)尋找"無數"可能性,網絡最終可以縮并成一層,那這實在是太具有暴力美學的數學哲思。應該可以顛覆現在優化領域的一些常識,也可能可以開個新的方向。
這并不是說模型可以無限壓縮,只有在深度非線性網絡學足以學到東西的時候,我們才會繼續轉換成單層網絡。單層網絡開銷有多大還是得由當前的深度非線性網絡所決定。所以,如何設計高效的深度網絡仍然是一個重要的課題,剪枝、蒸餾、神經架構搜索,動態推理網絡,這些方向或許本質上被某個共同的"規則"所左右,未來可能統一,神經架構搜索(NAS)可能會被廢棄(因為實在是太浪費了,NAS本質上是一種高度浪費的壓縮算法)。如果說這些算法實在壓縮,那么還有必然還一種對立的,是生長型的算法。小模型性能飽和之后,可以繼續成長成更大的網路從而繼續壓榨更高的性能。明明是對等的產物,不知道為什么少有人問津。
NAS設置的超網搜索空間實在是太大了,打個比方,就像把食物淺嘗幾口就丟掉。獲得超網也并不需要這么多并行的層。最簡單的VGG本身就是一個超網。我的意思是說,當訓練階段仔細探索子網,整個模型本身相對來說就是一個超網。與其嘗一口,嚼幾下就丟掉,還不如仔細咀嚼每一口。換言之:
1.需要充分地讓子網接受數據訓練的洗禮
2.用有限的計算資源在最簡單的網絡中創造更多數量的子網。當充分訓練,子網便會“自動集成”成高性能的模型。
當有了足夠的子網,便意味著有了足夠的學習容量。但這還不夠,每一個子網(它們之間也會共享部分,難分彼此)需要“相對獨立”地進行學習。每個子網地能力足夠強,整體網絡性能才會更高。我們并不需要額外的操作,像ReLU這種網絡就是天然的子網采樣器。訓練完畢,網絡本身就是所有子網的集成。
上面說過,當所有子網模型都可以獨當一面的時候,這就意味著整體網絡足夠健壯。那些剪枝算法只是回過頭來再選取合適的子網,使其成為更小的獨立模型。
所以有時候,我們大概不能片面地評估一個剪枝算法一定比另一個算法更好。因為當訓練不充分,某些剪枝算法也會因為運氣好(不好)(也是隨機)采樣到一些優秀(糟糕)的子網,這樣的比較并沒有太大的意義。除非它們自己能夠證明自己是“精準定位”到優秀子網的。舉個例子作類比,能不能打到好的獵物取決于山林資源條件如何,獵人身手如何。在貧瘠的山林里,再強的獵人也不見得收獲會比菜鳥獵人更豐。明顯只有資源足夠豐富的情況下,獵人的身手才能成為關鍵。當然也會存在一種極端情況,山林資源過于豐富,再怎么菜雞的獵人閉著眼睛亂射都能收獲滿滿。這也對應下文所說的,隨機剪枝完全足夠,不遜色任何算法。
原則上應該讓隨機剪枝大量采樣子網,證明網絡的健壯性。再讓其剪枝算法進行比較(比較采樣的良率,搜索時間,性能,效率等)。神經架構搜索(NAS)本身就可以看作是剪枝算法,但是大多數情況下NAS的超網太大了,不能也根本沒必要充分訓練。再回過頭來看,如果我們已經讓一個模型具備良好的健壯性了,花里胡哨的剪枝算法也就沒有什么太大的意義了(如果是那種剪枝,微調最后還不和相同規模的模型繼續比較那無異于掩耳盜鈴了。而且剪枝的網絡比大網絡更強也并不奇怪,畢竟隨機初始化,學習率主場都可能是關鍵。沒有統計意義的勝負也沒什么參考價值。)這個時候,隨機剪枝就足夠了,考慮到模型效率,必須是按照規則形狀的隨機剪枝。
VGG這種超網也可以變得強大。比如引進殘差,vgg的子網數量就變得更多了。比如隨機深度算法,讓一些更小的子網能夠獨立且充分地接受數據洗禮。個人認為上述兩條設計原則應該可以為輕量化模型設計提供一定程度的方向性指導。超網和子網的辨證關系想必對深度集成理論會有所幫助。
此外,如果模型能被壓縮(剪枝,蒸餾等),性能還不下降,說明大模型本身性能不飽和。也可以說是大模型存在結構冗余?,F實中,我們可以利用大模型快速收斂得到不錯的性能,再想方設法利用各種奇技淫巧壓縮模型,小心翼翼地防止精度損失。其實這和本文提出地bottleneck縮并技術很類似,都要經歷膨脹再收縮的過程。但是我覺得bottleneck(層積,模塊級,網絡級都可以)縮并技術更加簡潔,兩端網絡寬度固定,以滿足資源約束,中間盡可能大以獲得強大的學習能力,大到性能飽和為止,最后縮并成很窄的網絡即可,不需要花里胡哨的過程。這個貌似直接解決了幾年前所謂的信息瓶頸理論。(我不記得它說了什么,聽名字和bottleneck 縮并應該還是很相關的。應該不需要分析,直接做出來即可)。
做個實驗就很明白了。目前repo實驗代碼用的是layer級別的bottleneck設計,當然完全可以選擇block級,甚至整個網絡是一個bottleneck. 理論上net級容量是最大的,可以更快壓榨性能。
ReduceNet20, 調整expansion可以控制bottleneck中間的寬度,3個模型最終部署成本都是一樣的,簡直是免費的性能榨汁機。我還沒有探索expansion能有多大,有時間補上數據。20層只是訓練層數,之前說過了,部署的網絡形態是可以靈活改變的。比如,至少1x1卷積就可以消掉,網絡變成11層(10個3x3 conv, avgpool,FC),剩下3x3如果融合就要考慮計算量的增加自行trade-off了。硬件條件合適的話,計算量就能換更高速的模型推理。
完整訓練代碼:https://github.com/ohmydroid/reducenet
如果是我搞錯了,別打我。發這種半成品實在是因為已經浪費了好幾個月在研究貌似沒有什么用的東西。
模塊代碼如下:
可以看到,模塊中只有一個激活函數,即
self.scaler 就是一個不可訓練,人為控制數值的參數 \lambda\lambda 。在訓練過程中,隨著iteration(每個mini batch)按照cosine方式從1衰減至0。訓練結束后,這個激活函數就會退化成identity mapping(輸入等于輸出)。那么conv1,bn1,conv2,bn2四層可以融合成單層的3x3 conv。之后,把模塊首尾的殘差變成3x3卷積核的形狀,"對角線"元素填充為1(這比1x1 卷積核形狀的殘差稍微復雜一點點,不是正規的對角線,是3x3的中心點,每個通道沿著對角線,可以參考DiracNet),和3x3卷積繼續橫向融合。因為推理階段不存在任何非線性函數,所以所有的線性算子橫向和縱向都可以融合,這里不再贅述。
模塊中self.scaler可以由ReduceNet的Module的參數傳入。main.py文件控制self.scaler的數值,cosine衰減。代碼如下:
以后繼續優化:
1.和RepVGG等重參結合
2.3x3卷積可能不需要這么多,3x3卷積的數量關乎模型性能和效率,怎么尋找合適的數量值得研究
3.最大的愿望當然是能夠想出一步到位的方案,而不是用VanillaNet這種策略
4.還沒有寫融合算子和整個網絡的代碼
5. 嘗試在conv旁邊多加一條殘差
6.參考BagNet, MLP-mixer, weighted sum of patches is all you need
7.我比較好奇的是究竟能不能根據訓練結果的實時反映生成一個控制 \lambda\lambda 的信號,再讓這個新的數值再控制網絡形成控制閉環。
8.擁抱LORA。初聞LORA的時候我就意識到了,這東西和我從前想的多核卷積太像了,與我設想的無限可縮并的bottleneck不謀而合??偟膩碚f,這種類似“多重宇宙”,“影分身般”的特性無處不在。比如,谷歌某個集成方法,訓練多個模型,最后用加法融合每個模型的參數,最后微調整。不過這個方法也可以看作上文所說的net級別的bottleneck縮并技術的一個稀疏子集。
9.最難的恐怕是我該如何用一套數學語言去嚴謹地解釋多層非線性網絡是在尋找單層網絡的權重這一動態過程。
10.融合后可以繼續在現有網絡(訓練過的ReduceNet)基礎上生出新的活性網絡,可以是橫向的擴展寬度,縱向地插入新層,甚至每個模塊能成為bottleneck中間的小網絡,成為被縮并的對象,成就新的下一代模塊??偠灾?,繼承舊網絡,在此基礎上附上新網絡繼續擴展學習容量,壓縮融合,嫁接生長,循環反復。
寫得有些隨意,有空再整理。
代碼模塊其實和resnet沒什么兩樣,可以自行驗證相同架構網絡的效果。把conv1換成conv3,就可以和現有的resnet架構完全兼容。
最后解釋一下免得讓人誤解。ReduceNet在訓練中理論上可以無限深無限寬,但這并不意味著就模型最后一定具有無限的學習能力。部署時模型模型(都統一為單層網絡比較好了)的寬度等物理因素決定了它的能力上限,訓練階段無限的資源投入都只是讓最終的模型無限逼近固有的能力上限。這個規律同樣適用于現在的大模型。沒有免費的涌現。計算夠不夠久,數據夠不夠多,都是瓶頸,能量換信息所有過程都是為了逼近這個上限。突然對涌現這個詞語這么瘋魔,只是低估了這個上限了而已。