SimCSE,丹琦女神把對比學習用到了NLP中了!

語言: CN / TW / HK

theme: Chinese-red

持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第16天,點擊查看活動詳情


本文共計2944字,閲讀大概需要花費6分鐘


論文簡介

論文鏈接:SimCSE: Simple Contrastive Learning of Sentence Embeddings

如果大家瞭解對比學習的話就好辦了,這篇文章就是將對比學習應用到了自然語言處理領域。起初對學習先是用在圖像領域的。如果你瞭解的話就可以繼續往下看,如果你不瞭解的話我建議是先了解一下對比學習。 另外推薦幾篇我寫的對比學習的文章。

無監督獲取句子向量: - 使用預訓練好的 Bert 直接獲得句子向量,可以是 CLS 位的向量,也可以是不同 token 向量的平均值。

有監督的方式主要是: - Sentence-Bert (SBERT):Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks,通過 Bert 的孿生網絡獲得兩個句子的向量,進行有監督學習,SBERT 的結構如下圖所示。


對於對比學習來説,最重要是如何構造正負樣本。

在圖像中有多種構造對比學習的樣本,比SimCLR中提到的:反轉、局部裁剪、局部顯出、裁剪翻轉、調整飽和度、調整顏色、使用各種濾波器比如最大值濾波器,最小值濾波器、鋭化濾波器。

image.png

在自然語言處理中也有很多的數據增廣方式,但是他們對句子的影響都特別大。會嚴重降低對比學習的效果。為了解決這個問題SimCSE模型提出了一種通過隨機採樣dropout mask的操作來構造正樣本的方法。模型使用的是BERT,每次出來的Dropout是不同的。隨機dropout masks機制存在於模型的fully-connected layers和attention probabilities上,因此相同的輸入,經過模型後會得到不同的結果。所以只需要將同一個句子兩次餵給模型就可以得到兩個不同的表示。使用這種方法產生出來的相似樣本對語義完全一致,只是生成的embedding不同而已,可以認為是數據增強的最小形式。比其他的數據增強方法都要好很多。

什麼是dropout

首先我們要了解什麼是dropout。dropout在深度學習中通常被用來防止模型過擬合。dropout最初由Hinton組於2014年提出。可以看一下我之前的文章:模型泛化 | 正則化 | 權重衰退 | dropout

模型為什麼會過擬合。因為我們的數據集相較於我們的模型來説太小了。而我們的模型相較於我們的數據集來説太複雜。因此為了防止模型過擬合,我們可以使用dropout,讓模型在訓練的時候忽略一些節點。就像上圖中那樣。這樣模型在訓練數據時每次都在訓練不同的網絡,模型不會太依賴某些局部的特徵,所以模型的泛化性更強,降低了過擬合發生的概率。

dropout和其他數據增強方法進行比較

通過dropout masks機制進行數據增強構造正例的方法。

作者在STS-B數據集上進行試驗。比較dropout與其他數據增強方法的差異。

裁剪,刪除和替換等數據增強方法,效果均不如dropout masks機制,即使刪除一個詞也會損害性能,詳細如下表所示:

image.png

dropout正例與原始樣本之間採用完全相同的句子,只有在向量表徵過程中的dropout mask有所不同。可以視為一種最小形式的數據擴充。

不同的dropout rate

image.png

為了驗證模型dropout rate對無監督SimCSE的影響,作者在STS-B數據集上進行了消融實驗。從上面表格中我們可以看出當dropout rate設置為0.1的時候,模型在STS-B測試集的效果最好。

上圖中$Fixed 0.1$表示對於同一個樣本使用相同的dropout mask,也就是説編碼兩次得到的向量是一樣的,可以看到這種情況下效果是最差的。

我個人感覺$Fixed 0.1$的時候能達到40%以上已經挺好的了。畢竟在我眼裏可能會造成模型坍塌。

我還看了一下別人復現這篇論文的文章,復現的人説嘗試了0.1 0.2 0.3,效果都差不多,最後還是選擇了論文中的0.1.不。

對比學習評價指標

alignment 和 uniformity 是對比學習中比較重要的兩種屬性,可用於衡量對比學習的效果。

  • alignment 計算所有正樣本對之間的距離,如果 alignment 越小,則正樣本的向量越接近,對比學習效果越好,計算公式如下: $$ \ell_{\text {align }} \triangleq \underset{\left(x, x^{+}\right) \sim p_{\text {pos }}}{\mathbb{E}}\left\|f(x)-f\left(x^{+}\right)\right\|^{2} $$
  • uniformity 表示所有句子向量分佈的均勻程度,越小表示向量分佈越均勻,對比學習效果越好,計算公式如下: $$ \ell_{\text {uniform }} \triangleq \log \quad \mathbb{E}{x, y \stackrel{i . i . d .}{\sim} p{\text {data }}} e^{-2\|f(x)-f(y)\|^{2}} $$

其中$p_{data}$表示數據分佈。這兩個指標與對比學習的目標是一致的:正例之間學到的特徵應該是相近的,而任意向量的語義特徵應該儘可能地分散在超球體上。

至於這個“超球體”我認為是像InstDisc中右側這個圖一樣,將每個樣本的特徵表示映射到空間中。(個人觀點,如果理解有錯請各位指教。) image.png

無監督

image.png

無監督的目標函數是這樣的。看一下上邊,他圖中示例是把三個句子作為輸入傳給編碼器,然後編碼器會得到對應句子的embedding。輸入兩次會得到兩次不同的embedding。一個句子和它對應增強的句子是正樣本,其餘的句子作為負樣本。最終使用的損失函數如下: $$ \ell_{i}=-\log \frac{e^{\operatorname{sim}\left(\mathbf{h}{i}^{z{i}}, \mathbf{h}{i}^{z{i}^{\prime}}\right) / \tau}}{\sum_{j=1}^{N} e^{\operatorname{sim}\left(\mathbf{h}{i}^{z{i}}, \mathbf{h}{j}^{z{j}^{\prime}}\right) / \tau}} $$

有監督

image.png

使用有監督學習的一個難點,就是要找到適合構造正負樣本的數據集。最終作者的選擇如下:

image.png

那它的正負利是如何構造的呢。 以其中的NLI數據集為例,在這個數據集中打進一個前提。就是註釋者需要手動編寫一個絕對正確的句子及藴句子。一個可能正確的句子,中立句子。和一個絕對錯誤的句子矛盾句子。然後這篇論文就將這個數據集進行擴展,將原來的(句子,藴含句子)改變為(句子,藴含句子,矛盾句子)。在這個數據集中正樣本是這個句子及其包含藴含關係的句子。負樣本有兩種,是這個句子包含矛盾關係的句子以及其他的句子。 損失函數如下: $$ L_{i}=-\log \frac{e^{\operatorname{sim}\left(h_{i}, h_{i}^{+}\right) / \tau}}{\sum_{j=1}^{N}\left(e^{\operatorname{sim}\left(h_{i}, h_{j}^{+}\right) / \tau}+e^{\operatorname{sim}\left(h_{i}, h_{j}^{-}\right) / \tau}\right)} $$

結果

對7個語義文本相似度(STS)任務進行了實驗,將無監督和有監督的SimCSE與STS任務中的最先進的句子嵌入方法進行了比較,可以發現,無監督和有監督的SimCSE均取得了sota的效果,具體如下表所示:

image.png

因為SimCSE做的是一個句子表徵的任務,即獲得更好的句子的embedding,實驗效果如上圖。作者使用基於BERT和基於RoBERTa的SimCSE分別與Baseline進行比較,均取得較好的效果。

下邊是SimCSE使用不同版本的BERT及其變體做出的模型,對應的模型可以直接從hugging face上獲取.

|Model |Avg. STS| |---|----| princeton-nlp/unsup-simcse-bert-base-uncased |76.25 princeton-nlp/unsup-simcse-bert-large-uncased |78.41 princeton-nlp/unsup-simcse-roberta-base |76.57 princeton-nlp/unsup-simcse-roberta-large |78.90 princeton-nlp/sup-simcse-bert-base-uncased| 81.57 princeton-nlp/sup-simcse-bert-large-uncased |82.21 princeton-nlp/sup-simcse-roberta-base |82.52 princeton-nlp/sup-simcse-roberta-large |83.76

代碼實踐

既然它的效果這麼好,如何快捷的在電腦上使用SimCSE呢?

先安裝一下:

py pip install simcse

用這兩行代碼加載模型。我上面那個表格裏寫了不同的版本, SimCSE("在這裏填寫不同版本")

py from simcse import SimCSE model = SimCSE("princeton-nlp/sup-simcse-bert-base-uncased")

既然是用來做sentence embedding的,那先看一下他怎麼給句子編碼:

py embeddings = model.encode("A woman is reading.")

它反應比較慢,你需要等一下它才會出結果。不出意外的話,它應該會給出一個特別長的embedding編碼。

我輸出一下看一下,他應該是把一個句子編碼成768維度的向量。 image.png

計算兩組句子之間的‎‎餘弦相似性‎:

```python sentences_a = ['A woman is reading.', 'A man is playing a guitar.'] sentences_b = ['He plays guitar.', 'A woman is making a photo.'] similarities = model.similarity(sentences_a, sentences_b)

similarities1 = model.similarity('A woman is reading.', 'A man is playing a guitar.') similarities2 = model.similarity('He plays guitar.', 'A man is playing a guitar.') ``` 除了計算句子組之間,我還放了計算兩個句子的一些相似性。最後效果顯示如下: image.png

他還可以為那一組句子構建index,構建之後你再輸入一個句子,從其中進行查找。他會找到和哪個句子更為相似。並且輸出相似度是多少。

python sentences = ['A woman is reading.', 'A man is playing a guitar.'] model.build_index(sentences) results = model.search("He plays guitar.") 在上一段代碼中我已經計算過這兩個句子之間的相似度,我們可以看到跟上一段代碼中的結果是一樣的。

similarities2 = model.similarity('He plays guitar.', 'A man is playing a guitar.')的輸出結果是0.8934233..... 現在你查詢He plays guitar.它輸出最相似的句子為A man is playing a guitar.相似度為0.8934233.....

image.png