盘点近期大热对比学习模型:MoCo/SimCLR/BYOL/SimSiam
?PaperWeekly 原創 ·?作者|上杉翔二
單位|悠閑會
研究方向|信息檢索
很多大佬認為,深度學習的本質就是做兩件事情:Representation Learning(表示學習)和 Inductive Bias Learning(歸納偏好學習)。在表示學習方面,如果直接對語義進行監督學習,雖然表現很好,但是它需要很多的樣本并且往往是需要對特定的任務進行設計,很難具有遷移性。所以難怪各位大佬們都紛紛為自監督學習站臺,自監督是未來!
自監督學習有大類方法,一個是生成方法一個對比方法,如上圖。生成方法往往會對像素級損失進行約束,關于這一類筆者已經在之前的文章中進行了整理,而對比學習在表示學習上做的事情就是:其實模型不必要知道關于特征的細節,只要學到的特征足以使其和其他樣本區別開來就行。
Contrastive loss
對比損失 Contrastive loss,簡單的解釋就是,利用對比正-負樣本來學習表示。學習的目的為:
這里 x+ 是與 x 相似或相等的數據點,稱為正樣本。x? 是與 x 不同的數據點,稱為負樣本。score 函數是一個度量兩個特征之間相似性的指標,直接算內積來表示:
然后嘗試優化以下期望,即讓正例樣本越相似,要負例樣本越遠就好。
其實這個叫法最初似乎出自 Yann LeCun “Dimensionality Reduction by Learning an Invariant Mapping”,本來是用于處理在降維空間中正樣本和負樣本之間的相似/不相似的遠近距離關系,式子為:
其中 ,代表兩個樣本特征的歐氏距離,y 為兩個樣本是否匹配的標簽,y=1 代表兩個樣本相似或者匹配,y=0 則代表不匹配,margin 為設定的閾值。
損失函數主要懲罰如果原本相似的樣本 y=1,但在特征空間的歐式距離較大,則說明當前的模型不好,損失變大。同樣的如果原本不相似 y=0,但其特征空間的歐式距離反而小的話,損失也會變大。
上圖是 loss 與樣本特征的歐式距離 d 之間的關系,其中紅色虛線表示的是相似樣本的損失值,藍色實線表示的不相似樣本的損失值。
def?contrastive_loss(self,?y,d,batch_size):tmp=?y?*tf.square(d)#tmp=?tf.mul(y,tf.square(d))tmp2?=?(1-y)?*tf.square(tf.maximum((1?-?d),0))return?tf.reduce_sum(tmp?+tmp2)/batch_size/2???而這種成對 loss 的思想在其他領域如搜索推薦會有其他的變體:
Pairwise Ranking Loss
Triplet Ranking Loss
而馬上要總結的 MoCo 使用的其實是 Contrastive loss 一種變體 InfoNCE:
一個正例 ,K 個負例 ,這樣可以使只有真正匹配(與 query q 算點積)的樣本更相似,并且同時不匹配的不相似時,loss 才低。最初出自 Contrastive Predictive Coding,據說使用 InfoNCE,可以同時優化 encoder 和自回歸模型。
如何選擇正-負例pair?
Easy negative example 比較容易識別,所以相對來說找一些較難的 pair 是有利于訓練的。一般可分為:
Offline mining:計算所有的數據的 embedding,然后計算所以 pair 之間的距離判斷其難易程度,主要選擇 hard 或者 semi-hard 的數據。
Online mining:為每一 batch 動態挖掘有用的數據,將一個 batch 輸入到神經網絡中,得到這個 batch 數據的 embedding,Batch all 的方式還是會計算所有的合理的,Batch hard 偏向于選擇距離最大的正樣本和距離最小的負樣本。
這里需要思考的問題是這種 pair 對究竟多少數量是合適的?
一般來說,對比方法在有更多的負樣本的情況下效果更好,因為假定更多的負樣本可以更有效地覆蓋底層分布,從而給出更好的訓練信號。
所以回到 MoCo 的圖了,既然樣本數量對于學習到的樣本質量有很大的影響,那么我們就擴展負樣本的數量就好!但是目前對于 batch size 是沒有很好的解決辦法的,實際上如下圖 a,loss 的梯度會流過編碼器的正樣本 q 和負樣本 k 的 Encoder。
這意味著樣本的數量被限制在 mini-batch 的尺寸上,即我們并不能采樣無窮多的樣本,GPU 負載能力有限。
對于查詢正樣本 ,要在一個 batch 中(dictionary size = mini-batch size)的所有 K 中區別開來,有上圖三種方法:
end-to-end:先編碼 encoder(可同可不同),然后內積算 loss 再梯度。但是這種方法由于 dictionary size 和 mini-batch 的強耦合性(負例樣本對也會為 loss 產生貢獻,也會回傳梯度),在 batch 大的時候優化難,而在 batch 小的時候,batch 之間的參數會不一樣,也就是 GPU 大小限制了模型的性能。
memory bank:把 dictionary size 從 mini-batch 中解耦出來,即先把所有樣本的特征保存下來 bank,然后每次隨機采樣,再梯度 query 的 encoder 的參數。但是這樣只有當所有 key 被 sample 完以后才會更新 memory bank,不同的 key 在和 query 是不一致的和滯后的,因為每一次 sample encoder 都會更新雖有 memory bank 后面也加入了 momentum,但是是針對 sample 來的,在更新 memory bank 時會保留一部分上一輪的特征值。
MoCo:是以上兩者的融合版本,將 dictionary 作為一個 queue 進行維護當前的negative candidates pool,且它是改成了 queue 的動態更新機制,每 sample 一個 batch key(所以一個 trick 就是會使用 Shuffling BN,打亂再 BN),進隊后相對于一些最早進入隊列的 mini-batch 對應的 key 進行出隊操作,這樣保證一些過時的、一致性較弱的 key 可以被清除掉。這樣就同樣是解耦,K 是隊列長度,K 可以設置很大,同時更新也不會有問題。
按照以上偽碼,可以簡單看看 MoCo 的三個比較重要的函數:
@torch.no_grad()def?_momentum_update_key_encoder(self):"""key?encoder的Momentum?update"""for?param_q,?param_k?in?zip(self.encoder_q.parameters(),?self.encoder_k.parameters()):param_k.data?=?param_k.data?*?self.m?+?param_q.data?*?(1.?-?self.m)@torch.no_grad()def?_dequeue_and_enqueue(self,?keys):"""完成對隊列的出隊和入隊更新"""#?在更新隊列前得到keyskeys?=?concat_all_gather(keys)#合并所有keysbatch_size?=?keys.shape[0]ptr?=?int(self.queue_ptr)assert?self.K?%?batch_size?==?0??#?for?simplicity#?出隊入隊完成隊列的更新self.queue[:,?ptr:ptr?+?batch_size]?=?keys.Tptr?=?(ptr?+?batch_size)?%?self.K??#?用來移動的指針self.queue_ptr[0]?=?ptrdef?forward(self,?im_q,?im_k):#?計算query?featuresq?=?self.encoder_q(im_q)??#?queries:?NxCq?=?nn.functional.normalize(q,?dim=1)#?計算key?featureswith?torch.no_grad():??#?對于keys是沒有梯度的反向的self._momentum_update_key_encoder()??#?用自己的來更新key?encoder#?執行shuffle?BNim_k,?idx_unshuffle?=?self._batch_shuffle_ddp(im_k)k?=?self.encoder_k(im_k)??#?keys:?NxCk?=?nn.functional.normalize(k,?dim=1)#?還原shufflek?=?self._batch_unshuffle_ddp(k,?idx_unshuffle)#?計算概率#?positive?logits:?Nx1l_pos?=?torch.einsum('nc,nc->n',?[q,?k]).unsqueeze(-1)?#用愛因斯坦求和來算sum#?negative?logits:?NxKl_neg?=?torch.einsum('nc,ck->nk',?[q,?self.queue.clone().detach()])#?logits:?Nx(1+K)logits?=?torch.cat([l_pos,?l_neg],?dim=1)#?平滑softmax的分布,T越大越平logits?/=?self.T#?labels是正例indexlabels?=?torch.zeros(logits.shape[0],?dtype=torch.long).cuda()#?出隊入隊更新self._dequeue_and_enqueue(k)return?logits,?labels論文鏈接:
https://arxiv.org/abs/1911.05722
代碼鏈接:
https://github.com/facebookresearch/moco
完整的中文源碼閱讀筆記:
https://github.com/nakaizura/Source-Code-Notebook/tree/master/MoCo
SimCLR
MoCo 強調 pair 對的樣本數量對對比學習很重要,SimCLR 認為構建負例的方式也很重要。先說結論:
多個數據增強方法組合對于對比預測任務產生有效表示非常重要。此外,與有監督學習相比,數據增強對于無監督學習更加有用;
在表示和對比損失之間引入一個可學習的非線性變換可以大幅提高模型學到的表示的質量;
與監督學習相比,對比學習得益于更大的批量和更多的訓練步驟。
模型過程如下:
先 sample 一些圖片(batch)
對 batch 里的 image 做不同的 data augmentation,如圖上的 和 ,將其視為正對;
一個基本的神經網絡編碼器 f(·),從增強數據中提取表示向量, 作者使用 ResNet-50;
一個小的神經網絡投射頭(projection head)g(·),將表示映射到對比損失的空間;
目標是希望同一張圖片、不同 augmentation 的結果相近,并互斥其他結果。
作者認為多種數據增強操作的組合是學習良好表示的關鍵,論文里面主要討論過的有如下:
推薦有一個 github 用于數據增強很好用,pip install imgaug:
https://github.com/aleju/imgaug
為什么要用非線性的projection head?
由圖可知在 representation 與 contrastive loss 間使用了可學習的 non-linear projection,這個其實是非常簡單的單層 MLP+ReLU 的架構。其優勢在于避免計算 similarity 的 loss function 在訓練時丟掉一些重要的 feature,可以改善之前的層的表示質量。
損失函數 NT-Xent(the normalized temperature-scaled cross entropy loss), 和 是從 Projection Head 獲得的輸出矢量,output∈{0,1} if k≠i,τ 表示溫度參數可以用來放縮概率。
值得注意的一個 trick 就是會算兩次(即公式中間的 2N,會把 i-j 的計算,用 j-i 成對的再算一次)
做完訓練后,特征表示可以拿去下游做微調,比如用于圖像分類等下游任務。整體的框架圖如下:
論文鏈接:
https://arxiv.org/abs/2002.05709
代碼鏈接:
https://github.com/google-research/simclr
注:他們用了 128 塊 GPU/TPU,來處理每個 minibatch 9000 個以上樣本(這是為了獲得足夠的負樣本對比,所以必須要比普通的 batch 要大),并完成 1000 輪的訓練。
MoCo v2
在 MoCo 的基礎上加入了 SimCLR 的 projection head 和多種數據增強手段如模糊等。ImageNet 任務提升了 6%。
SimCLR v2
結合無監督預訓練、半監督訓練、有監督的微調和未標記數據的蒸餾等等一系列的訓練手段。具體如下圖:
左邊,非監督的方法學習一個任務無關的通用的表征,這里直接用 SimCLR,不同點在于網絡變大和也借用了 MoCo 部分架構。
中間,用監督的方法進行 fine-turning
右邊,在 unlabeled 大數據集上進行蒸餾
這種架構顯然很適合在工業界落地。
BYOL
無需負樣本也能夠取得好的效果?!出自 DeepMind的 NIPS20’的Bootstrap Your Own Latent(BYOL),BYOL 認為之前的方法都基于 negative pairs,而它們很大程度上取決于圖像增強的選擇,所以為什么不直接從圖像增強視角出發呢?框架圖如下:
沒有 pair,但是 BYOL 使用兩個相互交互并相互學習的神經網絡,分別稱為在線網絡和目標網絡。架構如上:
上面的分支是 online network,包括了 embedding,projection 以及 prediction,其中嵌入的使我們最要想要的模塊。
下面的分支是 target network,包括 embedding 和 projection 。
online 網絡參數使用 L2 的梯度進行更新,而 target 網絡直接通過 online 的 momentum 得到,這里 target 的就充當了之前負樣本的功能。
即 target 可以隨機開始得到輸出比如一開始的結果為 1.4% 非常差,此時新開一個分支訓練 online 去預測同一圖像在不同增強視角下的 target 的表示(從一個分支直接預測了另一個分支的輸出,用滾動編碼方法更新),此時結果居然就可以到非常高的程度了。
也正是 BYOL 主打其 不需要進行 negative 樣本的 idea。所以因此它的性能對 batch size 的大小不是特別敏感,在同等參數量的情況下,BYOL 的效果也是非常好。
為什么BYOL有效?
最近有一篇論文對其做了細致的測試,其中最關鍵的結論就是:BYOL 移除 BN 之后的表現就和隨機瞎猜一樣了。由于 BN 的出現本來就是為了克服 domain 和 target 的差異問題,即預防 mode collapse,可以將正負樣本的距離拉開,所以 BYOL 可能也是做了這樣的事情,做了對圖片均值和方差的學習,然后重新分配結果和特征值。
BYOL和MoCo、SimCLR的區別
MoCo、SimCLR 更偏向于問這兩張圖片之間有何差異?
BYOL 可能是在問這張圖片與這些圖片的平均有什么差異?
論文鏈接:
https://arxiv.org/abs/2006.07733
SimSiam
孿生網絡已成為無監督表達學習領域的通用架構,現有方法通過最大化同一圖像的兩者增廣之后的相似性使其避免“崩潰解(collapsing solutions)”問題。即在訓練網絡的時候,網絡會很迅速找了一個退化解并達到了最小可能損失 -1。
但是在 kaiming 大神的這篇文章中,他們提出的 Simple Siamese(SimSiam)網絡不僅可以沒有 negative sample pairs;沒有 arge batch;甚至沒有 momentum encoders 就學到有意義的特征表達。
主要是提出 stop-grad 的概念,結構如下:
前面的部分基本相同,輸入兩個隨機變換的 和 ,通過相同的孿生網絡提取特征并變換到高維空間,然后可以看到左邊的分支有個 projection head h 得到 ,之后再與右邊得到的 ,兩者的結果進行匹配使 cosine 最小化:
而重點的 Stop-gradient,意思是在 loss 的第一項的時候, 不會從 接收梯度信息;同時在計算第二項,則會從 接收梯度信息,即 loss 變為:
#?Algorithm1?SimSiam?Pseudocode,?Pytorch-like # f: backbone + projection mlp。f是backbone+projection head部分組成 #?h:?prediction?mlp for?x?in?loader:?#?load?a?minibatch?x?with?n?samplesx1,?x2?=?aug(x),?aug(x)?#?random?augmentation,隨機增強后的x1和x2#分別做兩次投影操作z1,?z2?=?f(x1),?f(x2)?#?projections,?n-by-dp1,?p2?=?h(z1),?h(z2)?#?predictions,?n-by-d#計算不對稱的兩個D得到loss?LL?=?D(p1,?z2)/2?+?D(p2,?z1)/2?#?lossL.backward()?#?back-propagate,反向傳播update(f,?h)?#?SGD?update,梯度更新def?D(p,?z):?#?negative?cosine?similarityz?=?z.detach()?#?stop?gradient,在這里使用detach做stopgrad的操作p?=?normalize(p,?dim=1)?#?l2-normalizez?=?normalize(z,?dim=1)?#?l2-normalize return?-(p*z).sum(dim=1).mean()其實 stopgrad 的本質就是一個交替方案(固定一個,求解另一個)的近似求解。
論文鏈接:
https://arxiv.org/abs/2011.10566
最后再看個對比方便分清楚:
更多閱讀
#投 稿?通 道#
?讓你的論文被更多人看到?
如何才能讓更多的優質內容以更短路徑到達讀者群體,縮短讀者尋找優質內容的成本呢?答案就是:你不認識的人。
總有一些你不認識的人,知道你想知道的東西。PaperWeekly 或許可以成為一座橋梁,促使不同背景、不同方向的學者和學術靈感相互碰撞,迸發出更多的可能性。?
PaperWeekly 鼓勵高校實驗室或個人,在我們的平臺上分享各類優質內容,可以是最新論文解讀,也可以是學習心得或技術干貨。我們的目的只有一個,讓知識真正流動起來。
?????來稿標準:
? 稿件確系個人原創作品,來稿需注明作者個人信息(姓名+學校/工作單位+學歷/職位+研究方向)?
? 如果文章并非首發,請在投稿時提醒并附上所有已發布鏈接?
? PaperWeekly 默認每篇文章都是首發,均會添加“原創”標志
?????投稿郵箱:
? 投稿郵箱:hr@paperweekly.site?
? 所有文章配圖,請單獨在附件中發送?
? 請留下即時聯系方式(微信或手機),以便我們在編輯發布時和作者溝通
????
現在,在「知乎」也能找到我們了
進入知乎首頁搜索「PaperWeekly」
點擊「關注」訂閱我們的專欄吧
關于PaperWeekly
PaperWeekly 是一個推薦、解讀、討論、報道人工智能前沿論文成果的學術平臺。如果你研究或從事 AI 領域,歡迎在公眾號后臺點擊「交流群」,小助手將把你帶入 PaperWeekly 的交流群里。
總結
以上是生活随笔為你收集整理的盘点近期大热对比学习模型:MoCo/SimCLR/BYOL/SimSiam的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pe界面怎么备份 PE备份界面操作指南
- 下一篇: 效率飞起!BML CodeLab发布重磅