<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      探秘Transformer系列之(7)--- embedding

      探秘Transformer系列之(7)--- embedding

      0x00 概要

      在Transformer中,把每個 Token(對應離散的輸入數據,如單詞或符號)映射到高維稠密向量空間的工作是由嵌入層(Embedding Layer)來實現的。輸入嵌入層是Transformer框架中不可或缺的一部分,它的作用如下:

      • 將輸入數據轉換為模型可以處理的形式。例如對于”新年大吉“這四個字,假設高維空間是512維,則嵌入層會生成一個 4 x 512 維的嵌入矩陣(Embedding Matrix)。每個token對應矩陣中的一個行,即一個向量。
      • 為模型提供了必要的語義。有研究發現,在Transformer類模型中,概念的形成過程始于輸入嵌入層。這與人類早期認知發展相似。
      • 通過位置編碼為模型提供序列中每個單詞的位置信息,使得模型能夠處理序列數據并保留位置信息。

      這些工作為后續模塊(如自注意力機制和前饋網絡)的處理和任務執行奠定了基礎。

      0x01 演進思路

      1.1 概念

      首先,我們要看看嵌入(Embedding)和向量化(Vectorization)這兩個和嵌入層密切相關的概念。有的文章中并沒有區分它們。而有的文章做了精細區分,認為雖然它們都與將數據表示為向量有關,但在概念、應用和實現上有顯著的區別。

      簡單來說,向量化就是把其它格式的數據轉換為向量形式,這里的其它格式包括我們常見的一切格式的數據,文本,圖像,視頻,音頻等等。因此,可以直接把向量化理解為一種數據格式轉換的技術。

      在大模型技術體系中,向量化主要存在以下兩種情況:

      • 數據向量化。
      • embedding。

      雖然兩種方式都是向量化,但還是有本質區別的。數據向量化是一種數值轉化的過程,是機械式的;而embedding則是更高層次的智能化的向量化。比如,對于“我愛中國”這四個字,數據向量化的邏輯中,它們可能只是4個函數,生成4個獨立的向量。而在Embedding中,它蘊含著更多的信息,包括主謂賓的語義結構、主語賓語的位置、情感、動作等等,這些信息被統一稱為語言特征。數據向量化和embedding兩者關系如下表所示。

      維度 嵌入(Embedding) 向量化(Vectorization)
      目的 學習低維稠密語義表示。經過嵌入之后的數據是具有語義關系的,而不是毫無關系的離散向量 將數據轉換為數值向量,可能稀疏,也可能稠密。更注重數據表示的直接性,指的是數值表示形式本身
      是否需要學習 需要(通常通過神經網絡或優化算法學習) 不需要(可以基于規則或統計方法生成)
      語義表示能力 強調以有意義和結構化的方式表示數據的概念,需要維護數據之間的深層語義關系和相似性 可能不保留語義,僅是特征的機械化表示
      典型方法 word2vec、GloVe、BERT、node2vec 詞袋模型(BoW)、TF-IDF、獨熱編碼(One-hot Encoding)
      結果向量維度 通常低維且稠密。此”低“是和向量化相比,實際上是高維且稠密 通常是高維且稀疏

      以文本翻譯為例,Transformer的輸入層首先要把輸入文本的每個詞(或者字)轉換到高維稠密的向量空間,得到文本語義含義的信息密集表示,這就是Token Embedding,也就是深度學習中常說的詞嵌入(Word Embedding)。Transformer實際上是把人類的語義通過向量化“編碼”成自己的語言(Embedding)。Word Embedding可以為后續的計算提供更加豐富和表達能力的輸入特征。同時,這些Embedding也是可以訓練的。在訓練過程中,模型會依據從數據中學習到的知識來調整這些Embedding的數字以提高其任務執行能力。

      一旦得到Embedding,Transformer就將其傳遞給自注意力層,自注意力會分析輸入序列中token之間的關系,從Embedding中提取各種豐富的知識和結構,然后進行關聯、并加權積累生成新的embedding。最后會把新的embedding“編碼”回人類的語言。本質上看,Transformer其實是構建了一個高維的語言體系,可以將自然語言,程序語言,視覺聽覺語言,映射(或者叫編碼)到這個高維的語言空間中。

      我們看看如何從文本晉級到embedding的演化思路,即如何把一個詞編碼成embedding的過程。

      1.2 需求

      為何要把輸入數據(通常是文本數據中的單詞或字符)從離散的符號形式轉換為連續的數值向量形式?這是因為計算機無法直接處理非數值性計算,所有的計算都需要將計算對象轉換成數值才行。但是數值計算的方式有很多,為什么會選擇向量作為載體,而非其它形式?這就需要我們深入分析,看看計算對象轉換的發展歷程。

      1.3 文本

      詞是人類語言的抽象總結,是NLP領域的主要處理對象。但是如何在計算機中表示一個單詞?ASCII是一種有效的方式,其滿足唯一性和可區分性。但是ASCII只能告訴我們這個詞是什么,卻無法傳遞詞匯的深層含義。在文本時代,搜索是基于關鍵詞的搜索,即最原始的字符匹配。

      但是我們所期望的是語義搜索,即找出包含最相似含義的文本。語義搜索能夠讓計算機更好的理解人類的語言和需求;它能夠根據你的語義理解你想做什么;而不是你必須給出明確的指令,它才能明白你想做什么。

      雖然語義搜索對普通人來說似乎很容易,但對計算機卻是困難的,因為語言是相當復雜的,想做到語義搜索就要理解語義。語義理解是自然語言的基礎,也是實現人工智能必不可少的一環。另外,我們不僅僅希望只做到語義搜索,還希望做到相似性搜索,即應用到圖像搜索,混合搜索,智能推薦等多種應用場景。

      所以我們繼續探尋。

      1.4 數字

      計算的基礎是數,所以計算機把數字視為語言,卻無法直接理解文字。因此很容易想到的做法是讓文字轉換為數字,這樣便于計算機進行處理和理解。

      解決方案很容易,我們用整個文本庫中出現的詞匯構建詞典,詞典是無重復且有序的,每個詞在詞典都有一個下標索引。于是我們以詞匯在詞典中的索引來表示詞匯。這其實是一種索引表示。即為每個單詞分配一個基于模型已知的所有單詞列表的唯一數字。為行文方便,我們將這個轉換過程叫做索引化。

      整數的好處是連續、有序并且非常容易索引。比如詞典中有4個單詞:新年快樂,我們給這四個字分配如下索引:1,2,3,4。再比如,我們以前面生成的詞典對象為例,vocab對象有8185個單詞,每個單詞可以使用一個index來進行唯一標識,這樣計算機才可以識別。

      vocab = {Vocab: 8185} <torchtext._torchtext.Vocab object at 0x0000021A26983DF0>
        0000 = {str} '<s>'
        0001 = {str} '</s>'
        0002 = {str} '<blank>'
        0003 = {str} '<unk>'
        0004 = {str} '.'
        0005 = {str} 'Ein'
        0006 = {str} 'einem'
        ......
      

      這個方案有一個問題:數字攜帶的信息量太少,無法描述語義,導致數字化后的數值與詞義之間缺乏聯系。比如在英文字典中,abeyance(緩辦,中止),abide(遵守),ability(能力)這三個單詞會被賦予臨近的數值,但是其語義相差甚遠。而a和an這兩個同質的詞卻隔得非常遠。這樣不利于計算機理解共性、區分特性。因而模型需要更多的參數才能描述同等的信息量,學習的過程顯然困難也會更大。

      因為單一的數字缺少表達能力,當用一個標量來表示一個詞時,詞和詞之間的關系只能基于兩個標量間的差值得到,這顯然無法滿足需求。因此需要多個數字一起來表達概念,即把多個數字分散嵌入到一個數學空間中。當需要一個多維的數字形態時,我們很自然會想到使用向量 ——— 對于每一個詞,我們可以表達為一組數,而非一個數;我們可以利用這些數字來做些事情,比如計算距離和角度。這樣一來,就可以在不同的維度上定義遠近,詞與詞之間復雜的關系便能在這一高維的空間中得到表達。

      因此我們來到了第三種方案:向量。

      1.5 向量

      在數學中,向量(也稱為歐幾里得向量、幾何向量)指具有大小(magnitude)和方向的量。它可以形象化地表示為帶箭頭的線段。箭頭所指代表向量的方向。線段長度代表向量的大小。從通俗意義來講,向量是標量值(即數字)的有序列表。例如,二維坐標(4,3)是一個二維向量,可以表示一個二維位置。接下來我們講講把文本變成向量的方法,即向量化。

      向量化是一種將數據轉換為向量形式的過程,通常用于將非數值數據轉化為數值形式,便于機器學習模型處理。 向量化主要是將原始數據表示為可以直接輸入模型的數值向量。向量化可以是簡單的規則轉換,而不需要通過訓練得到。 向量化的結果不一定是稠密向量,也可能是稀疏向量。

      為什么要向量化(Vectorization)? 原因就在于向量的幾個優秀特性:

      • 便于計算機進行處理。
      • 能夠表示文本,圖像等之間的語義關系。
      • 可以使用矩陣來表示多個向量,這樣計算效率更高。

      因此,在神經網絡中唯一能夠處理的數據格式就只有一種——向量,向量就是是大模型的底層數據結構。任何需要輸入到模型的數據都需要向量化,不僅僅是實體需要向量化,記錄語義關系的數據也都需要向量化。

      獨熱編碼

      獨熱編碼(One-Hot Encoding)大概是最常見的向量,這是一種常見且眾所周知的表示分類數據的方法。其本質上是用一個“只含一個 1、其他都是 0”的二進制向量來唯一表示某個單詞。獨熱編碼的編碼過程具體如下:

      • 把語料中所有詞的集合構建成一個詞典,假設這個詞典的大小為\(|V|\),每個詞在詞典都有一個下標索引。
      • 為每個詞構造一個唯一的長度為\(|V|\)的向量,該向量中,只有該單詞對應的索引位置是1,其余位置是0,向量的維度就等于詞表的大小。具體向量如下圖所示。

      比如:假如詞表中有“新、年、快、樂”這四個字,獨熱編碼就是給這四個字分別用0-1來編碼:每個單詞都由一個向量表示,該向量在與該單詞對應的位置上為“1”,其他地方為“0”。

      獨熱編碼有以下顯著的缺點:

      • 單維向量,無法承載更多信息。獨熱編碼的每個向量只有一個有信息量的維度。而且該維度上只是1,能表達的信息太少,導致效率低下。
      • 高維稀疏。我們總希望模型可以處理更多的詞,這意味著詞典長度\(|V|\)越大越好,結果就是詞表存儲過大。而且,詞典中有多少字,向量就有多少維,這樣向量維度容易過大,變得非常稀疏,會導致詞特征的維度過高,從而內存消耗高。
      • 缺少語義信息。在獨熱編碼中,每個單詞與其他單詞的距離相等,這樣基本上將所有單詞視為相互獨立的實體,沒有相似性或上下文的概念。或者說,在獨熱編碼中,單詞之間都是正交的,沒有任何聯系,故無法捕捉單詞之間的任何語義關系。
      • 硬編碼,無法調整或者訓練。

      改進訴求

      目前為止,我們已經找到了可以用于表達詞義的數字化形式 —— 向量。也發現獨熱編碼這個常見向量的眾多問題。我們希望在獨熱編碼基礎上進行改進。于是針對one-hot的缺點,我們需要:

      • 增維,從單維變成多維,進而承載更多信息。此處的增維指的是用更多的維度來表示語義信息。
      • 降維,從稀疏性變成稠密性。此處降維指的是降低向量的總體維度。獨熱編碼這種稀疏性不是我們想要的,它會帶來計算和存儲的雙重負擔。我們更希望要稠密的向量。因此需要采用某些方法來降維,把每一個單詞的向量維度從詞典大小降維到較小的維度。
      • 語義相似性。增加關系表達能力,最好可以表示文本,圖像等之間的語義關系(語義相似性)。因為隨著互聯網技術的發展,非結構化數據變得越來越多,比如圖像,音視頻,圖文混合等等多種類型的數據格式。我們需要一種可以處理非結構化數據的方法。
      • 不要硬編碼,可以學習。獨熱編碼是硬編碼,我們希望可以通過訓練進行調整。

      我們來分析下這幾個改進的必要性。

      增維

      獨熱編碼是單維向量,我們需要拓展為用多維向量來表示語義信息。為什么要把一個詞用多維向量表示?因為人類的概念就是復雜多樣,無法用單一維度表達的。假定一個桌子,我們需要多方面特征才能完整的表示出來其特點,比如:長、寬、高、重量、材質、款式等,每一個方面都是一個維度。單詞也具備多維度的信息。比如instant這個單詞就有多種詞性,而每種詞性可能又有多種含義。

      • 作為名詞,其意義是瞬息,頃刻,剎那。
      • 作為形容詞,其意義是立即的,即刻的;速溶的;速食的;緊急的,急迫的;現時的,目下的,此刻的;堅持的,不屈不撓的;瞬間產生(或發生)的;事先未準備(或考慮)的,即興的,當場完成任務的;
      • 作為副詞,其意義是立即,立刻。

      多維的意義在于:概念信息分布在整個向量上,而不是局限于任何單個維度的局部。

      降維

      此處有兩層含義或者說思考的維度。

      • 從稀疏性變成稠密性,即把每一個單詞的向量維度從詞典大小降維到較小的維度。因為embedding的長度實質上代表特征的維度數目,而如果是ont-hot編碼,則特征維度的數目需要和字典中的token數量相同。
      • 前面提及了多維向量,但是究竟多少維度適合?如果維度過高,雖然可以表達豐富的語義,但是可能依然會出現類似獨熱編碼的問題。

      第一點可以理解,這樣可以減少計算和存儲的雙重負擔。比如,降維可以減少模型的參數數量,從而減輕過擬合的風險,并提高模型的訓練效率。

      第二點是如何確定新的多維向量的維度。我們仔細分析下。依據Johnson–Lindenstrauss 引理,任何高維數據集均可以被隨機投影到一個較低維度的歐氏空間,同時可以控制pairwise距離的失真。比如一個一百萬維空間里的隨便一萬個點,一定可以大致被裝進一個幾十維的子空間里。具體來說,即假設有N個向量,不管這N個向量原來是多少維的,我們都可以將它們降到O(log?N),并將相對距離的誤差控制在一定范圍內。即,只需要O(log?N)維空間就可以塞下N個向量,使得原本高維空間中的檢索問題可以降低到O(log?N)維空間中。這其實對應了另外一種說法:語言模型就是一個壓縮器。而降維是一種壓縮方法:提取共性,保留個性,過濾噪聲。

      語義相似性

      我們希望向量可以表達單詞之間的語義相似性(semantic similarity),這樣我們可以把我們見過的事物和從未見過的事物聯系起來。語義相似性是基于分布假說的, 這是一個基本的語言學假設,即出現在相似語境中的單詞在語義上是相互關聯的。比如從下面句子中我們可以知道,數學家和物理學家之間語義關聯很大。

      • 數學家跑向商店。
      • 物理學家跑向商店。
      • 數學家喜歡喝咖啡。
      • 物理學家喜歡和咖啡。
      • 數學家解決了這個懸而未決的問題。
      • 物理學家解決了這個懸而未決的問題。

      因為one-hot編碼不具備語義,所以無法表達數學家和物理學家之間的密切相關性。而稠密向量恰好可以彌補這個缺點。為了進一步說明詞與詞之間的關系,我們還可以將單詞對應的向量在平面上繪制出來。例如,在下圖中,物理學家與數學家的含義相近,它們就聚在一起。籃球運動員、足球運動員和橄欖球運動員的語義差異比數學家大,所以他們距離物理學家就相對較遠。

      另外,我們也希望用數學方法來比較向量的語義相似性和差異性,因為向量把自然語言轉化為一串數字,從此自然語言可以計算,所以我們希望可以通過向量來表達:郭靖 - 黃蓉 = man - women

      可訓練

      我們期望向量是可以被訓練的。隨著訓練的進行,模型可以通過反向傳播來調整嵌入向量,使得相似詞的向量更加接近,不相似詞的向量則更遠離。

      小結

      我們現在知道具備語義性的稠密多維向量的重要性,或者說,語義分析是人工智能實現的基礎,而語義分析實現的基礎卻是稠密多維向量。RGB(三原色)就是非常好的具備語義性的稠密向量示例,其特點如下:

      • 任何顏色都可以用一個 RGB 向量來表示。
      • 這三個維度都有明確的物理含義(語義),而且解釋性很強。
      • 每一維度都是事先規定好的。
      • 物理含義(語義)相近的向量在顏色空間內位置相近。可以通過計算高維向量之間的距離來表示兩個單詞含義的相近程度。

      1.5 embedding

      其實,具備語義性的稠密多維向量就是embedding。我們先看看權威機構對embedding的定義。

      • Pytorch網站上給出的定義是:Word embeddings are dense vectors of real numbers, one per word in your vocabulary。

      • Tensorflow 社區給出的定義是:An embedding is a mapping from discrete objects, such as words, to vectors of real numbers.

      • OpenAI 官方文檔中是這樣解釋的:

        Embeddings are numerical representations of concepts converted to number sequences, which make it easy for computers to understand the relationships between those concepts.

      我們再給出自己的定義:詞嵌入是從高維稀疏向量轉化而來的稠密向量,向量的每個維度可以代表相應對象的某個特征,這樣就可以把將一個對象從復雜的空間投射到相對簡單的空間,也可以在向量中編碼語義相似性,這使得計算機能夠輕松理解這些概念之間的關系。

      我們接下來看看詞嵌入的兩個關鍵點:表征概念,表達關系(以相似性進行舉例)。

      表征概念

      我們知道,目前LLM所作的是根據之前的單詞來預測下一個單詞。因此我們要看看人類如何預判,或者說對于人類的預判來說,什么是最關鍵的。事實上,人類先進的預見能力必須依賴于同樣先進的表征能力。從廣義上講, 這些表征能力被稱為抽象能力或抽象思維。 即,人類將具象物理的世界抽象到主觀認識中,形成了概念。

      NLP處理的是語言。語言作為一個抽象符號,人是可以理解每個語言單詞(概念)的意義的,但是計算機并沒有辦法直接的從感知抽象出每個語言符號的意義。這是因為成功的語言交流依賴于對世界的共同體驗。正是這種共同的經歷使話語變得有意義。比如 man 是一個很抽象的詞語概念,其含義包含了視覺、聽覺、交互、關系及更抽象的責任等等。人類的概念之所以如此復雜且有效,是因為其具有如下特點:

      • 它們可以被投影(可能是非線性的)到多種用途中。
      • 在概念空間里,通過相關性,不同事物被廣泛的聯系在一起。
      • 概念不僅相互關聯,而且支持特定形式的邏輯推斷和復雜的計算。

      如果我們希望某一個途徑或者方法可以精確的表征人類概念,則此方法不但要能夠表達出各種屬性,而且還應該能夠處理心理學中的關鍵現象,包括相似性的計算、建立關系、類比推理,以及把某些概念重新組合成新的結構化思維等。也可以這樣認為:語言破壞了空間關系,卻形成了一個概念空間。在此概念空概念內,幾何和微積分不再適用。它需要新的數學或者新的語言。這個統一結構和語言, 連接幾何空間和概念空間的就是embedding。以Sora為例,具象的物理空間和語言都被表達為embedding 進行聯合訓練,物理和語言空間被統一表示為embedding 和embedding 的連接構建關系。

      上面這段話也許過于學術性,我們用通俗的語言來看看一個良好的embedding方法應該具備何種特性,才可以精確的表征人類概念。

      • 唯一性。詞和embedding必須一一對應,不能重復。因為如果重復則會增加計算機理解語言的難度,類似多音字或者多義字給人們帶來的麻煩。

      • 可區分性。從數學表示來看,詞義相近詞需要有"相近"的量化值(在空間中相鄰);詞義不相近的詞量化值需要盡量“遠離”。

      • 可計算性。不論兩個“詞”如何構成,它們都應該能在同一個空間內做計算,且計算結果也在同一個空間內。比如,詞的表示能通過簡單的數學計算“連接”起來,表示一個新“詞”。

      • 蘊含深層語義信息。ELMo的作者認為在理想情況下,一個好的embedding應該對以下兩個方面進行建模:

        • 單詞使用的復雜特征(如句法和語義)。
        • 這些用法在語言語境中的變化(即對多義詞進行建模)。

        即,在正確的架構中,該方法可以像單詞一樣發揮作用。

      而認知心理學及相關領域最近的理論和計算進展表明,向量可能滿足表達人類概念所需的所有特性。這部分原因是因為向量是作用在向量空間之上。向量空間是向量的集合,是一個線性代數概念,它描述了向量的集合在一定維度和基下的分布和操作。向量空間具有標準的數學運算,例如,機器學習中的高維數據可以看作是向量空間中的點,不同的距離度量(如歐幾里得距離、余弦相似度)可以用于計算點之間的關系。也可以通過相加兩個向量或用一個數縮放一個向量來創建新的向量。因為向量空間的存在,任何特定向量的意義都不能孤立地確定,而是來自于這些向量在更大計算過程中所起的作用。在最基本的層面上,這種作用包括向量之間的幾何關系,包括距離和角度,但也包括向量上的動態計算。

      樣例

      比如,我們可以把數學家和物理學家都投射成向量,每個屬性(比如是否可以跑,是否喜歡咖啡等等)都是向量的一個維度。我們看到數學家和物理學家都可以跑步,都喜歡喝咖啡,所以我們可以給這些維度一個較高的分數,這樣就將詞語或短語從詞匯表映射到向量的實數空間中。使得數據對象的某些屬性被編碼到其向量表示的幾何屬性中,從而可以表達相似性。

      請注意,word embedding可能無法解釋。也就是說,盡管通過我們上面手工制作的向量,我們可以看到數學家和物理學家都喜歡咖啡,但這些向量中的數字意味著什么尚不清楚。我們只是知道它們在某些潛在的語義維度上是相似的。

      相似性

      我們接下來看看相似性。

      從心理學角度看,向量就是把概念映射到某個空間(通常是二維或三維)中的點,使向量空間的幾何結構與心理量的實證測量(如項目之間的相似性、距離等)保持一致。雖然得到的向量坐標不能單獨解釋,但向量之間的關系承載著關于心理測量的意義。兩個相似的概念在向量空間中會彼此靠近,而這個空間中的距離會與人們在這兩個概念之間進行泛化的意愿相一致。我們接下來仔細分析。

      首先,我們從數值的角度來看。詞嵌入就是將單詞與一系列數字(向量)關聯的一種方式,類似的單詞會被關聯到接近的數字,而不相似的單詞則被關聯到相距較遠的數字。相似性是通過為相似的詞分配大的數字,為不同的詞分配小的數字,來度量兩個詞(或句子)的相似程度的一種方式。我們相信,在一個好的詞嵌入中,“bank”和“the”這樣的詞之間的相似性幾乎為零,因為它們無關。因此,模型將知道忽略“the,轉而專注于那些可能與"bank"有較高相似度的詞。

      其次,概念在高維向量空間中得到了充分表征,其背后邏輯是概念可以通過向量之間的相互關系和計算動態得出的。這其實對應了另外一個邏輯。即通過丘奇編碼目標系統來動態模擬任何其他計算系統。丘奇編碼是數學邏輯中的一個思想,其中一個系統的動態可以反映出另一個系統的動態。

      相似度計算

      詞嵌入本質上是向量,因此,如果想了解兩個單詞或句子在語義上的接近程度,我們可以根據不同向量之間的關系,如距離,長度,方向等去計算不同格式數據之間的相關性。比如我們可以用向量之間的距離來度量。距離越小,其語義意義上越接近,借以實現語義搜索。有幾種不同的度量方法可以用來衡量兩個向量之間的距離:

      • 歐幾里得距離。
      • 曼哈頓距離。
      • 余弦距離。
      • 點積。

      歐幾里得距離(L2)

      歐幾里得距離是定義兩點(或向量)之間距離的最標準方法,也是日常生活中最常用的度量方法,即連接兩個點的線段的長度。歐幾里得距離缺點是:盡管這是一種常用的距離度量,但歐式距離并不是尺度不變的,這意味著所計算的距離可能會根據特征的單位發生傾斜。因此,通常在使用歐式距離度量之前,需要對數據進行歸一化處理。此外,隨著數據維數的增加,歐氏距離的作用也會變小。這與維數災難(curse of dimensionality)有關。

      曼哈頓距離(L1)

      \[distance = \sum _{i=1}^n|x_i - y_i|distance = \sum _{i=1}^n|x_i - y_i| \]

      曼哈頓距離(也稱為L1范數)通常稱為出租車距離或城市街區距離,用來計算實值向量之間的距離。這個名稱來源于紐約的曼哈頓,因為這個島有一個街道網格布局,而在曼哈頓兩點之間的最短路線將是L1距離。

      曼哈頓距離在計算距離時不涉及對角線移動。想象一下均勻網格棋盤上的物體,它們只能移動直角,因為你需要遵循街道網格。當數據集具有離散或二進制屬性時,曼哈頓距離似乎工作得很好,因為它考慮了在這些屬性的值中實際可以采用的路徑。對比以下,歐式距離會在兩個向量之間形成一條直線,但實際上這是不可能的。

      缺點:盡管曼哈頓距離在高維數據中似乎可以工作,但它比歐式距離直觀性差,尤其是在高維數據中使用時。此外,由于它可能不是最短路徑,有可能會比歐氏距離給出一個更高的距離值。

      余弦相似度

      \[Similarity = \frac{\vec x \cdot \vec y} {||\vec x||\times ||\vec y||} \]

      余弦相似度通過測量兩個向量的夾角的余弦值來度量它們之間的相似性,這就像是測量兩個向量之間的夾角,夾角越小,相似度越高。余弦相似度衡量的是兩個向量之間的角度差異,而非它們的長度,因為這是在方向上的度量。余弦相似度的值范圍是從-1到1。值越接近1,表示兩個向量的方向越相似;值為0,則表示它們是正交的;而值為-1,則表示它們方向完全相反。

      余弦相似度經常被用作抵消高維歐式距離問題。通過計算向量之間的余弦相似度,我們可以更精確地評估它們在語義上的相似度,而不僅僅是比較它們的數值大小或直線距離。

      缺點:沒有考慮向量的大小,而只考慮它們的方向。以推薦系統為例,余弦相似度就沒有考慮到不同用戶之間評分尺度的差異。

      點積

      點積或標量積的公式如下,即我們將一對數字相乘,然后相加。

      \[\vec x \cdot \vec y = \sum _{i=1}^n x_i \times y_i \]

      點積運算的幾何意義是兩個向量的夾角,表征一個向量在另一個向量上的投影,兩個向量越相似,他們的點積就越大,反而就越小。如果兩個向量的夾角為90度,則這兩個向量線性無關,完全沒有相關性。基于這個內積,我們可以建立距離、長度、角度等概念。事實上,Transformer就是用點積來表征兩個向量之間的相似度。

      另外,點積的結果很大程度上依賴于向量的大小;例如,讓我們計算兩對向量之間的點積:

      • 對于向量A=(1,1)和B=(1,1),它們的點積計算結果是1?1+1?1=2。
      • 當我們考慮另一對向量C=(1,1)和D=(10,10)時,點積為1?10+1?10=20。

      盡管這兩對向量在方向上保持一致,但由于它們的大小存在差異,導致它們的點積有很大的不同。這就解釋了為什么在進行向量比較時,點積并非總是最合適的選擇。

      進展

      在機器學習和數據科學領域,余弦相似度長期以來一直是衡量高維對象之間語義相似度的首選指標。余弦相似度已廣泛應用于從推薦系統到自然語言處理的各種應用中。它的流行源于人們相信它捕獲了嵌入向量之間的方向對齊,提供了比簡單點積更有意義的相似性度量。

      然而,論文”Is Cosine-Similarity of Embeddings Really About Similarity?“Netflix 和康奈爾大學的一項研究挑戰了我們對這種流行方法的理解:余弦相似度給出的答案經常與實際情況不符。

      對于從正則化線性模型派生的ebmedding,論文結論是:對于某些線性模型來說,相似度甚至不是唯一的,而對于其他模型來說,它們是由正則化隱式控制的。除了線性模型,類似的問題在更復雜的場景中也存在:深度學習模型通常會同時使用多種不同的正則化技術,這可能會對最終嵌入的余弦相似度產生意想不到的影響。在通過點積優化來學習嵌入時,如果直接使用余弦相似度,可能會得到難以解釋且沒有實際意義的結果。

      基于這些見解,研究團隊得出結論:不要盲目使用余弦相似度,并概述了替代方案。

      • 直接針對余弦相似度訓練模型。
      • 完全避免在嵌入空間中工作。相反,在應用余弦相似度之前,先將嵌入投影回原始空間。
      • 在學習過程中或之前應用歸一化或減少流行度偏差,而不是像余弦相似度那樣僅在學習后進行歸一化。

      在論文的基礎上,博客作者 Amarpreet Kaur 歸納了一些可以替換余弦相似度的備選項:

      • 歐幾里得距離:雖然由于對向量大小敏感而在文本數據中不太流行,但在嵌入經過適當歸一化時可以發揮作用。

      • 點積:在某些應用中,嵌入向量之間的非歸一化點積被發現優于余弦相似度,特別是在密集段落檢索和問答任務中。

      • 軟余弦相似度:這種方法除了考慮向量表示外,還考慮了單個詞之間的相似度,可能提供更細致的比較。

      • 語義文本相似度(STS)預測:專門為語義相似度任務訓練的微調模型 (如 STSScore) 有望提供更穩健和和更可解釋的相似度度量。

      • 歸一化嵌入與余弦相似度:在使用余弦相似度之前,應用層歸一化等歸一化技術能有效提升相似度計算的準確性。

      在選擇替代方案時,必須考慮任務的具體要求、數據的性質以及所使用的模型架構。通常需要在特定領域的數據集上進行實證評估,以確定最適合特定應用的相似度。

      1.6 小結

      對比

      我們首先看看embedding和獨熱編碼的對比,具體可以從如下幾個角度來思考:

      • 可以把獨熱向量看作是word embedding的特例,其中每個單詞基本上都有相似性0。而word embedding向量是密集的,也就是說它們的維度上的數值(通常)是非零的。
      • 直觀上看,embedding 相當于是對 獨熱向量 做了平滑,而獨熱向量相當于是對 embedding 做了 max pooling。
      • 引用蘇神的話:"Embedding層就是以one hot為輸入、中間層節點為字向量維數的全連接層!而這個全連接層的參數,就是一個“字向量表”!字向量就是one hot的全連接層的參數!"。
      • 獨熱向量編碼會產生一個高維稀疏向量,而嵌入向量是低維的稠密向量,更加節省存儲空間,另外獨熱向量 編碼無法捕捉類別之間的關系,而嵌入向量通過訓練可以學習到類別之間的語義相似性。

      一種經典建模方法,就是從字符的整數索引為基礎,查表得到一個向量,這樣就很容易使用神經網絡把一串字符向量處理成任意級別的向量。而one hot型的矩陣和其它矩陣相乘,就像是相當使用獨熱編碼來查表,即把one hot型的矩陣運算簡化為了查表操作。當查表操作得到了這個全連接層的參數之后,我們可以直接用這個全連接層的參數作為特征傳給Transformer后續的模塊,或者說,用這個全連接層的參數作為字、詞的表示,從而得到了字、詞的向量。

      \[\begin{pmatrix}1 & 0 & 0 & 0 \\0 & 1 & 0 & 0 \end{pmatrix}\begin{pmatrix}w11 & w12 & w13 & w14 \\w21 & w22 & w23 & w24 \\w31 & w32 & w33 & w34 \\w41 & w42 & w43 & w44 \\ \end{pmatrix} = \begin{pmatrix} w11 & w12 &w13 & w14 \\ w21 & w23&w23 & w24 \end{pmatrix} \]

      實際上,詞表就是這兩個矩陣之間的映射。

      流轉過程

      其次,下圖展示了從文本到embedding的流轉過程,從中能夠看到兩個空間之間的轉換。

      優勢

      再次,我們看看embedding的優點。

      • 語義理解:基于 Embedding 的檢索方法通過詞向量來表示文本,能夠根據上下文為詞語賦予不同的向量表示。這使得模型能夠捕捉到詞匯之間的語義聯關系,在處理一詞多義的情況時更具優勢。相比之下,基于關鍵詞的檢索往往關注字面匹配,可能忽略了詞語之間的語義聯系,可能無法很好地區分同一個詞在不同語境下的含義。

      • 容錯性:由于基于 Embedding 的方法能夠理解詞匯之間的關系,所以在處理拼寫錯誤、同義詞、近義詞等情況時更具優勢。而基于關鍵詞的檢索方法對這些情況的處理相對較弱。

      • 多語言支持:許多 Embedding 方法可以支持多種語言,有助于實現跨語言的文本檢索。比如你可以用中文輸入來查詢英文文本內容,而基于關鍵詞的檢索方法很難做到這一點。

      • 可擴展性和靈活性:輸入嵌入層的設計允許模型輕松地適應不同的語言或領域,只需通過調整詞匯表和嵌入矩陣即可。這種靈活性使得Transformer模型可以廣泛應用于多種自然語言處理任務。

      • 高效:Embedding嵌入模型經過矩陣算法的優化,比傳統的向量化方式效率更高,效果更好。

      因此,業界有一種說法“萬物皆可嵌入”。Pinecone的創建者Edo Liberty在博客中這樣寫道: “機器學習將一切都表示為向量,包括文檔、視頻、用戶行為等等。這些表示使得不同事物可根據相似性或相關性,就能夠準確檢索、搜索、排名和分類。在很多場景中,如產品推薦、語義搜索、圖像搜索、異常檢測、欺詐檢測、人臉識別等都有應用”。

      0x02 Transformer嵌入層實現

      嵌入層的作用就是,將文本中詞匯的數字表示轉變為高維的向量表示,旨在高維空間捕捉詞匯間的關系。下面我們來詳細解析輸入嵌入層的關鍵作用和構建過程。

      2.1 流程&架構

      下圖在前面文章中已經有涉及,此處重新展示是為了讓讀者更好理解。

      假設模型的輸入是句子“Data visualization empowers users to”,因為這個句子并不能被模型理解,因此我們需要把自然語言進行編碼,也就是對文字進行向量化。具體分為以下幾個步驟:

      • 對輸入文字進行Tokenize(詞元化)。
      • 把每個token對應到一個input embedding。此處作用是把詞本身的信息投射到embedding空間,所以這操作是一個整數(0~V-1)到向量空間的映射。V代表詞表大小。
      • 給序列中的每個位置添加一個位置編碼。位置編碼的作用是告訴模型正確的語序,所以此操作也是一個整數(0~n-1)到向量空間的映射。n代表句子序列長度。
      • 把input embedding和位置編碼相加,得到最終的word embedding。因為Embedding和Position Encoding之間需要相加,所以兩者維度相同。

      上述流程的后面三步在Transformer的架構圖中對應藍色框部分。從下圖可以看到,輸入和輸出(其實也被轉換為輸入)都會先壓縮為Embedding,然后加上Positional Encoding之后,輸入到注意力層,即用圖上的概念來表示:

      \[\begin{aligned}&Multi Head\ Attention\ 輸入 =Input\ Embedding(Inputs) + Positional\ Encoding \\&Masked\ MultiHead\ Attention\ 輸入 = Output\ Embedding(Outputs) + Positional\ Encoding\end{aligned} \]

      思考

      在BERT、XLNet、RoBERTa中,詞表的embedding size(E)和transformer層的hidden size(H)是相等的。兩者一定要相等嘛?其實也有不同觀點。如果兩者相等,則有兩個缺點:

      • 從建模的角度來看,wordpiece的單詞表征向量學習到的是上下文獨立的表征。而transformer所學習到的表示應該是依賴內容的。因此理論上,存儲了上下文信息的H要遠大于E。讓H和E解耦有利于減少參數量( ?? >> ?? 時)。
      • 從實際的角度出發,NLP領域的詞匯表大小 ?? 往往比較大。如果讓 ??=?? ,當 ?? 增大時, ?? 也會跟著增大,那么詞嵌入矩陣 ????? = ????? 將會非常龐大。而且embedding在實際的訓練中更新地也比較稀疏。

      ALBERT的作者就認為沒有必要,因為預訓練模型主要的捕獲目標是H所代表的“上下文相關信息,而非E所代表的“上下文無關信息”。因此,ALBERT沒有直接用獨熱編碼去映射到大小為 ?? 的隱層空間,而是使用因式分解(Factorized),先映射到一個低維空間 ?? ,然后再映射到隱層空間 ??。通過這種分解的方式,參數量從 ??(?????) 變為 ??(?????+?????) 。當 ??>>?? 時,參數量大大減少。將龐大的詞嵌入矩陣轉換成兩個小的矩陣,分離了隱層大小和詞嵌入大小,使得增加隱層大小時而不用改變詞嵌入的大小。

      另外,詞向量是否越大越好?論文“massive exploration of neural machine translation architectures ”中就提到, 2048 維的詞向量相比512 維的詞向量相比,效果提升不大。道理很簡單:詞向量只要能把相似單詞聚在一起就完成了它的任務。即便詞向量只有 100 維,并且每個維度只能取 {0, 1} 兩個值,整個詞向量空間也可以編碼 \(2^{100}\)個不同的單詞,把幾萬個單詞嵌入到這個空間里綽綽有余。

      2.2 實現

      嵌入層的目標是把token序列轉換為詞嵌入。因此需要做兩步:

      • 通過梯度下降學習到更有意義的詞向量表示,這些詞向量構成嵌入矩陣。
      • 從嵌入矩陣nn.Embedding中查找token對應的嵌入向量。即把token從原本的詞向量維度(d_vocab)投射到我們自定義的維度大小(d_model)。這里d_model大小也會決定模型最終的參數量。

      注:我們此處只給出Input Embedding對應的實現,會在后續章節進行分析位置編碼的實現。

      嵌入矩陣

      在大語言模型中,嵌入層本質上是一個嵌入矩陣,被用來把輸入和輸出的token ID轉換為\(d_{model}\)維的向量。嵌入矩陣本質就是一個由詞表衍生出來的查找表,這是一個保存“所有”單詞的表。里面存儲了模型詞匯表中每個 Token(詞或子詞)的向量表示。嵌入矩陣的維度可以看作一個大小為 V x D 的二維矩陣,其中:

      • 行數:V 是模型詞匯表(vocabulary)的大小,即模型所能識別的所有詞或子詞的數量。詞表有多少詞,嵌入矩陣就有多少行。嵌入矩陣的每一行對應一個詞表中的詞 ID,每一行存儲的向量就是這個詞 ID 對應的高維向量表示。

      • 列數:D 是每個詞向量的維度,通常是一個較大的數字(比如 300 維、512 維、1024 維等),這取決于具體模型的設計。模型的維度是多少,就有多少列。

      舉例:假設詞匯表大小為 V = 10000,詞向量維度 D = 300,那么嵌入矩陣的大小就是 10000 x 300,每一個詞表中的詞都有一個 300 維的向量表示。因此,嵌入矩陣的大小由詞匯表的大小和詞向量的維度決定。

      在訓練或推理過程中,當輸入經過 Token 化并轉化為詞表 ID 序列后,嵌入層會利用這些 ID 在嵌入矩陣中查找相應的詞向量。這是通過查找(lookup)機制完成的。使用Embedding來編碼就是使用token index(詞表ID)來在矩陣中查詢相應的向量。每個單詞會定位這個表中的某一行,而這一行就是這個單詞學習到的在嵌入空間的語義。這樣就完成了從離散的詞 ID 到連續向量空間的轉換。

      下圖中用不同顏色標明了兩個embedding分別的計算過程。對于第三個單詞'Hello',就是直接使用token序號為2來進行查找,取出embedding矩陣的第3行(index為2)。同理,對于單詞'World',相當于取出embedding矩陣的第四行(index為3)。嵌入層輸出的就是這些對應的向量,然后模型會將這些向量作為輸入,送入神經網絡的后續層進行處理。

      從另一個角度講,token序號可以認為是獨熱編碼,比如2就是 [0,0,1,0,0,0] ,依據token 序號來查找就相當于用獨熱編碼乘以Embedding矩陣,embedding矩陣中有且僅有一行被激活。行間互不干擾。或者說,Embedding查表操作其實就是把 one-hot 向量轉換為高維向量了。

      下面是從 ALBERT 中摘取的代碼,可以看出來是如何處理獨熱編碼的。所以,實際上我們可以將one-hot 編碼理解為一種在MLP權重中查詢知識的過程,而輸出結果就是一個嵌入表示向量,而這個輸出結果實際上是可逆的(就是一個矩陣方程而已)。

      def embedding_lookup(input_ids,
                           vocab_size,
                           embedding_size=128,
                           initializer_range=0.02,
                           word_embedding_name="word_embeddings",
                           use_one_hot_embeddings=False):
        """Looks up words embeddings for id tensor.
      
        Args:
          input_ids: int32 Tensor of shape [batch_size, seq_length] containing word
            ids.
          vocab_size: int. Size of the embedding vocabulary.
          embedding_size: int. Width of the word embeddings.
          initializer_range: float. Embedding initialization range.
          word_embedding_name: string. Name of the embedding table.
          use_one_hot_embeddings: bool. If True, use one-hot method for word
            embeddings. If False, use `tf.nn.embedding_lookup()`.
      
        Returns:
          float Tensor of shape [batch_size, seq_length, embedding_size].
        """
        # This function assumes that the input is of shape [batch_size, seq_length,
        # num_inputs].
        #
        # If the input is a 2D tensor of shape [batch_size, seq_length], we
        # reshape to [batch_size, seq_length, 1].
        if input_ids.shape.ndims == 2:
          input_ids = tf.expand_dims(input_ids, axis=[-1])
      
        embedding_table = tf.get_variable(
            name=word_embedding_name,
            shape=[vocab_size, embedding_size],
            initializer=create_initializer(initializer_range))
      
        if use_one_hot_embeddings:
          flat_input_ids = tf.reshape(input_ids, [-1])
          one_hot_input_ids = tf.one_hot(flat_input_ids, depth=vocab_size)
          output = tf.matmul(one_hot_input_ids, embedding_table)
        else:
          output = tf.nn.embedding_lookup(embedding_table, input_ids)
      
        input_shape = get_shape_list(input_ids)
      
        output = tf.reshape(output,
                            input_shape[0:-1] + [input_shape[-1] * embedding_size])
        return (output, embedding_table)
      

      哈佛代碼

      Embeddings類定義如下。

      # 繼承自PyTorch的nn.Module類
      class Embeddings(nn.Module):
          # 初始化函數
          def __init__(self, d_model, vocab):
              """
              輸入參數是:
              d_model:詞嵌入維度
              vocab:詞表大小
              """
              # 調用父類初始化方法
              super(Embeddings, self).__init__()
              # 創建一個nn.Embedding作為詞嵌入層,參數為詞表大小和詞嵌入維度
              self.lut = nn.Embedding(vocab, d_model)
              # 將詞嵌入維度保存為類屬性
              self.d_model = d_model
       
          # 前向傳播函數
          def forward(self, x):
              # 通過詞嵌入層將輸入x編碼為向量,并乘以詞嵌入維度的平方根進行縮放
              # x是token的索引
              return self.lut(x) * math.sqrt(self.d_model)
      

      上述代碼有一個細節:在獲取輸入詞向量之后需要對矩陣乘以embedding size的開方。這是因為在計算注意力分數時會涉及到一個點積操作,如果詞嵌入維度很大時會導致點積的結果也很大,進而導致在訓練過程中出現梯度消失或梯度爆炸。而將詞嵌入矩陣乘以嵌入維度(embedding dimension)的平方根可以使得點積的范圍更加合理,從而幫助增加訓練穩定性,使模型可以更有效地學習從輸入到輸出的映射。

      另外,乘以embedding size的開方也會使 Embedding 和 Position Encoding 的規模一致。具體的原因是:

      • 因為Position Encoding是通過三角函數算出來的,值域為[-1, 1]。

      • 而如果使用 Xavier 初始化,Embedding 的方差為 1/d_model,當d_model非常大時,矩陣中的每一個值都會減小。通過乘一個 d_model 的平方根可以將方差恢復到1。

      所以當加上 Position Encoding 時,需要放大 embedding 的數值,否則規模不一致相加后會丟失信息。

      簡而言之,這個操作的目的是為了數值穩定性和訓練穩定性,幫助模型更有效地學習從輸入到輸出的映射。

      PyTorch Embedding

      Embeddings類中最主要邏輯由nn.Embedding(vocab, d_model)完成,因此我們看看nn.Embedding(vocab, d_model)的細節。nn.Embedding 是 PyTorch 提供的一個非常重要的層,主要用于將離散的詞匯索引 (或其他類別索引) 映射到連續的稠密向量空間中。nn.Embedding的參數本身就是模型參數的一部分,會參與梯度下降計算,通過訓練可以學習到類別之間的語義相似性。

      與 torch.nn.Linear 對比,nn.Embedding 專門用于離散索引到向量的映射,而 nn.Linear 是一個全連接層,用于任意形狀的輸入到輸出的線性變換。另外 nn.Embedding 的每個索引都有獨立的嵌入向量,而 nn.Linear 的權重是一個共享的線性變換矩陣。

      Embedding類定義如下。

      class Embedding(Module):
          __constants__ = ['num_embeddings', 'embedding_dim', 'padding_idx', 'max_norm',
                           'norm_type', 'scale_grad_by_freq', 'sparse']
          num_embeddings: int # 詞典的大小,即不同類別的總數,例如在詞嵌入中,通常是詞匯表的大小
          embedding_dim: int # 每個嵌入向量的維度,即將單詞編碼成多少維的向量
          padding_idx: Optional[int] # 指定一個索引,用于填充 (padding)
          max_norm: Optional[float] # 如果設置了該值,每次嵌入向量更新后會將其范數裁剪到不超過 max_norm
          norm_type: float # 用于裁剪范數的類型,默認為 2,即 L2 范數
          scale_grad_by_freq: bool # 梯度將會被頻率 (即每個索引出現的次數) 縮放
          weight: Tensor # 用于初始化嵌入矩陣的權重
          sparse: bool # 使用稀疏梯度,這在處理大型詞匯表時可以節省內存
      
          def __init__(self, num_embeddings: int, embedding_dim: int, padding_idx: Optional[int] = None,
                       max_norm: Optional[float] = None, norm_type: float = 2., scale_grad_by_freq: bool = False,
                       sparse: bool = False, _weight: Optional[Tensor] = None,
                       device=None, dtype=None) -> None:
              factory_kwargs = {'device': device, 'dtype': dtype}
              super(Embedding, self).__init__()
              self.num_embeddings = num_embeddings
              self.embedding_dim = embedding_dim
              if padding_idx is not None:
                  if padding_idx > 0:
                      assert padding_idx < self.num_embeddings, 'Padding_idx must be within num_embeddings'
                  elif padding_idx < 0:
                      padding_idx = self.num_embeddings + padding_idx
              self.padding_idx = padding_idx
              self.max_norm = max_norm
              self.norm_type = norm_type
              self.scale_grad_by_freq = scale_grad_by_freq
              if _weight is None:
                  self.weight = Parameter(torch.empty((num_embeddings, embedding_dim), **factory_kwargs))
                  self.reset_parameters()
              else:
                  self.weight = Parameter(_weight)
              self.sparse = sparse
              
          def forward(self, input: Tensor) -> Tensor:
              return F.embedding(
                  input, self.weight, self.padding_idx, self.max_norm,
                  self.norm_type, self.scale_grad_by_freq, self.sparse)
      

      nn.Embedding的輸入是 Token ID,輸出則是對應的嵌入向量。當給定一個 Token 時,嵌入層會根據 Token ID,直接從嵌入矩陣中取出相應的嵌入向量。nn.Embedding的使用代碼示例如下。比如下面是把[0,2,0,5] 這四個單詞的index進行編碼的結果。可以看到,nn.Embedding 類似于一個查找表,它將每個離散的輸入索引 (通常是整數) 映射到一個固定大小的向量 (即嵌入向量)。這種映射允許模型將離散的類別信息轉化為連續的向量表示,從而能夠更好地捕捉類別之間的語義關系。

      # 10個向量,每個嵌入向量的維度為 3
      embedding = nn.Embedding(10, 3, padding_idx=0)
      # 輸入的索引
      input = torch.LongTensor([[0,2,0,5]])
      # 獲取嵌入向量
      embedding(input)
      # 輸出
      tensor([[[ 0.0000,  0.0000,  0.0000],
               [ 0.1535, -2.0309,  0.9315],
               [ 0.0000,  0.0000,  0.0000],
               [-0.1655,  0.9897,  0.0635]]])
      

      調用

      在哈佛代碼中,構建模型時候會用到Embeddings類生成EncoderDecoder類的實例,進而在EncoderDecoder類的forward()函數中會利用Embedding類來對輸入和輸出序列進行編碼。

      model = make_model(len(vocab_src), len(vocab_tgt), N=6)
      
      def make_model(
          src_vocab, tgt_vocab, N=6, d_model=512, d_ff=2048, h=8, dropout=0.1
      ):
          c = copy.deepcopy
          attn = MultiHeadedAttention(h, d_model)
          ff = PositionwiseFeedForward(d_model, d_ff, dropout)
          position = PositionalEncoding(d_model, dropout)
          
          # src_vocab是詞典大小
          model = EncoderDecoder(
              Encoder(EncoderLayer(d_model, c(attn), c(ff), dropout), N),
              Decoder(DecoderLayer(d_model, c(attn), c(attn), c(ff), dropout), N),
              nn.Sequential(Embeddings(d_model, src_vocab), c(position)),
              nn.Sequential(Embeddings(d_model, tgt_vocab), c(position)),
              Generator(d_model, tgt_vocab),
          )
          
      class EncoderDecoder(nn.Module):
          """
          A standard Encoder-Decoder architecture. Base for this and many
          other models.
          """
          def __init__(self, encoder, decoder, src_embed, tgt_embed, generator):
              super(EncoderDecoder, self).__init__()
              self.encoder = encoder
              self.decoder = decoder
              # 對應nn.Sequential(Embeddings(d_model, src_vocab), c(position))
              self.src_embed = src_embed 
              # 對應nn.Sequential(Embeddings(d_model, tgt_vocab), c(position))
              self.tgt_embed = tgt_embed
              self.generator = generator    
      
          def forward(self, src, tgt, src_mask, tgt_mask):
              "Take in and process masked src and target sequences."
              return self.decode(self.encode(src, src_mask), src_mask, tgt, tgt_mask)
      
          def encode(self, src, src_mask):
              return self.encoder(self.src_embed(src), src_mask) # 這里會調用到
      
          def decode(self, memory, src_mask, tgt, tgt_mask):
              return self.decoder(self.tgt_embed(tgt), memory, src_mask, tgt_mask) # 這里會調用到
      

      輸入輸出

      假設我們正在做機器翻譯,有三個句子,一句話最大長度是10個字,則Embedding的輸入輸出如下。

      • Input Embedding的輸入是token index序列,形狀是 torch.Size([3, 10]),即[batch_size, seq_len]。對應圖上的標簽1。
      • Input Embedding的輸出是一個3x10x512的張量,每個句子對應的字向量是一個10 X 512的二維矩陣(每一行的512維向量代表一個字)。對應圖上的標簽2。
      • Positional Encoding的輸入是句子長度,對應圖上的標簽3。Positional Encoding的輸出是位置向量,這也是一個10 X 512的二維矩陣(每一行代表對應字的位置信息)。對應圖上的標簽4。這里要注意的是,在哈佛代碼中,PositionalEncoding其實是做了相加操作,所以PositionalEncoding類的輸入是Embedding的輸出,其形狀是 torch.Size([3, 10, 512]),即[batch_size, seq_len, d_model]。
      • 每個句子對應的兩個矩陣相加得的新二維矩陣能更好更全面的表達語義,這就是Word Embedding。對應圖上的標簽5。

      2.3 訓練

      Embedding的來源

      單詞的 Embedding 通常有兩種選擇:

      • 直接使用預訓練好的詞向量且對詞向量不做改變。例如可以采用 Word2Vec、Glove 等算法預訓練得到。因為在使用模型時(推理階段),這些嵌入向量會作為輸入送入模型的后續層進行處理,不會發生變化,因此在這種情況下,嵌入矩陣實際就是一個查找表。如果監督數據較少,我們可以固定Embedding,只讓模型學習其它的參數。這也可以看成一種 Transfer Learning。
      • 使用動態詞向量。所謂動態詞向量就是在訓練一個特定任務的同時,同時也基于語料來訓練詞向量了,Embedding矩陣就是模型訓練后的產出。具體來說就是Transformer 中訓練得到一個embedding層。每個token在Embedding層中都有一個對應的高維表示,這種映射關系在模型訓練的過程中一直在更新,不斷進行改進。

      為何要再訓練?

      為何要再進行一次訓練?因為預訓練的embedding是固化的。而對于有效的自然語言模型來說,需要能夠以理解每個單詞、短語、句子或段落在不同語境下可能的含義。因此我們有必要在實際上下文中繼續訓練。而且,一般來說,靜態embedding 是神經網絡倒數第一層的參數權重,只具有整體意義和相對意義,不具備局部意義和絕對含義,這與 embedding 的產生過程有關。

      讓模型自己設計

      機器學習模型不使用非結構化數據。為了使模型能夠理解文本或圖像,我們必須將它們轉換為數字表示。在機器學習之前,這樣的表示通常是通過Feature Engineering“手工”創建的。但隨著特征數目的增加,向量維度越來越大,這些新的向量是一個很大的痛苦:你可以想到某個物體有數千個不同的語義屬性,你到底會如何設置不同屬性的值?

      隨著深度學習的出現,復雜數據中的非線性特征交互是由模型學習而不是人工設計的。因為深度學習的核心思想是:讓神經網絡自己學習特征的表示,而不是要求程序員自己設計它們。因此,研究人員就提出讓詞嵌入成為模型中的參數,然后在訓練過程中進行更新。

      訓練流程

      在訓練中基本是如下流程。

      • 初始化。首先我們要初始化字向量為[vocab size, embedding dimension], vocab size為總共的字庫數量,embedding dimension為字向量的維度,也是每個字的數學表達。假設選用了3000個常用漢字,每個字對應一個512維的隨機向量,則整個字向量就是一個3000 X 512 的二維矩陣,每一行代表一個字。嵌入矩陣的初始化有兩種方式:

        • 隨機初始化:大多數模型在一開始會用隨機數來初始化嵌入矩陣的每個向量。為了保持向量的可區分性,向量通常被設置為正交的。實現正交性的一個簡單方法是隨機性:在高維空間中,兩個隨機生成的向量近似正交,因此符號通常被初始化為隨機的高維向量,這樣可以讓生成的各個向量間盡可能的獨立,即沒有相關性。比如就像“你、我、他、拿、和、中”指代的具體意思在最初定義時是可以隨機互換的,之間也無關系,這些詞之間的相關性/關系是在語境中根據語義、語法、文化等因素形成的。
        • 預訓練嵌入:在某些情況下,模型可能使用預訓練的詞向量來初始化嵌入矩陣。例如,可以使用 Word2Vec、GloVe 或 FastText 等模型生成的詞向量作為嵌入層的初始值。這些預訓練的詞向量已經通過大量語料訓練,包含了豐富的詞匯語義關系,為后續的Transformer層(如自注意力層和前饋網絡層)提供了良好的處理基礎。。然后在訓練過程中,預訓練的嵌入向量會結合模型的損失函數和訓練任務(如掩碼語言模型或下一個詞預測)再進一步微調更新,從而學習到具體的語義表示。
      • 更新。嵌入矩陣的向量在模型的訓練過程中通過訓練數據進行學習。transformer的目標就是在訓練中逐步調整這些嵌入,讓這些向量可以互相交流,通過互相傳遞信息來更新自己的值。

        • 學習過程的目標是讓共享上下文的單詞也共享相似的向量。我們可以通過反向傳播來調整單詞的向量來做到這一點。一方面在數值空間中將相近的向量推得更近。使其更像其鄰居的向量。另一方面,訓練將向量推離不共享其上下文的隨機單詞的向量。因此這些向量不僅僅編碼一個單獨的詞,而是融入了更加豐富的上下文含義。
        • 隨著優化算法,不斷迭代更新。這個過程不是一次性的事情。它更像是一個循環。你瀏覽語料庫中的每個單詞,根據其鄰居更新其向量。有時,你還會讓它與隨機的、不相關的單詞略有不同。隨著你不斷重復這個過程,迭代一次又一次,向量開始穩定下來。它們會找到一種平衡點,在這個點上的調整會變得越來越小,這樣,具有相似模式的詞語就會把這些相似更新累積到可觀的程度,這意味著相似的單詞(如同義詞)在嵌入空間中會有相似的向量表示。比如,在注意力機制中,會讓周圍的嵌入把自己的信息傳入給“故”對應的嵌入,無論位置遠近。見下圖。
      • 固化。最后網絡收斂停止迭代的時候,網絡各個層的參數就相對固化,得到隱層權重表(此時就相當于得到了我們想要的 embedding)。這就是收斂,向量現在不僅代表隨機分配,還代表基于單詞出現的上下文的有意義的關系。這個過程重復足夠多次后,就會揭示出你的語料庫中隱藏的結構,即從頭開始構建的單詞關系圖。

      • 然后在通過查表可以單獨查看每個元素的 embedding。embedding過程也是查表過程,因為它們之間也是一個一一映射的關系。每個token都有一個對應的高維表示,這種映射關系在模型訓練的過程中一直在更新。

      整個流程如下圖所示。

      0x03 文本嵌入

      Text embedding就是將文本轉成一組固定維度的向量表示。word embedding是以token為基本單位,而text embedding則是以文本為基本單位的。理想的text embedding應該盡可能保留文本的語義信息,相同語義但不同表述方式的文本可以被映射到同一個位置,而不同語義的文本在向量空間應該保持對應的距離。實際上,text embedding和word embedding很可能就是使用同樣的模型。

      3.1 歷史

      文本嵌入模型旨在將自然語言文本的語義內容編碼為向量表示,然后促進各種自然語言處理(NLP)任務,如語義文本相似性、信息檢索和聚類。論文”When Text Embedding Meets Large Language Model: A Comprehensive Survey“給出了文本Embedding的歷史。

      • 統計機器學習時代。在早期,研究人員主要依靠手動設計的特征來表示文本。該方法的發展伴隨著輸出向量形式的轉換,即從詞袋模型(獨熱向量)到TF-IDF(稀疏向量),然后到詞嵌入(密集向量)。這些方法需要領域專家手動選擇和設計特征,而特征的質量和數量限制了它們的有效性。隨著機器學習技術的進步,研究人員開始探索使用統計方法學習文本嵌入的方法,如潛在語義分析(Latent Semantic Analysis,LSA)、潛在狄利克雷分配(Latent Dirichlet Allocation,LDA)等。雖然這些方法可以自動學習文本嵌入,但在捕獲復雜的語義和句法結構方面仍然存在局限性。
      • 深度學習時代:隨著深度學習技術的出現,詞嵌入已經成為文本嵌入學習的一個重大突破。Word2Vec、GloVe和FastText等模型可以將單詞映射到低維連續向量空間,以捕捉單詞之間的語義和句法關系。在過去的幾年里,首先提出了具有數百萬個參數的預訓練語言模型(PLM),例如BERT和RoBERTa。“預訓練-微調”范式在各種下游任務中表現良好,并在實際應用中發揮了重要作用。然而,這些PLM的嵌入空間被證明是各向異性的(anisotropic),導致為任何兩個文本計算體現出驚人的高相似性。
      • LLM時代:LLM本身的表征直接用于Embedding,比如用于檢索/聚類/STS等任務,效果其實不太好。因此才需要將Embedding模型和大模型區分開來。然而,使用LLM來生成Embedding是近來的趨勢。該論文將模型參數數量超過1B的語言模型稱為LLM,這樣就排除了基于Transformer編碼器的預訓練語言模型(如BERT和RoBERTa)和通常具有較小參數計數的神經網絡(如LSTM和GRU)。論文給出的生成Embedding的LLM如下圖所示。

      各大模型服務商,以及開源社區都發布了大量的Embedding模型來提供給用戶使用。而Embedding就是一種經過專門訓練的用來向量化數據的神經網絡模型。只不過Embedding嵌入模型經過矩陣算法的優化,比傳統的向量化方式效率更高,效果更好。

      我們接下來看看嵌入模型以及embedding在演化進程的幾個典型案例。

      3.2 Word2Vec

      一個比較容易想到的方法是,令詞義的不同維度和向量不同維度進行關聯。例如,對詞義的維度進行全面的拆分:名詞性、動詞性、形容詞性、數量特征、人物、主動、被動、情感色彩、情感強度、空間上下、空間前后、空間內外、顏色特征,只要維度的數量足夠多,一定是可以把詞義所包含的信息全都囊括在內;一旦我們給出每一個維度的定義,就可以給出每個詞在相應維度上的數值,從而完成詞的向量化,并且完美地符合以上給出的兩點性質。但這個看似可行的設計,并不具備可實現性。

      首先,需要極高的維度才能夠囊括所有詞義的不同維度,而對詞義進行這么精細的切分是非常困難的。其次,即使切分出來極高的維度,要將每個詞不同維度的意義賦予有效的數值,哪怕是再資深的語言學家恐怕也會難以感到棘手。

      既然純構建的方式不可行,于是人們想出了一個大力出奇跡的方法,就是由Google在2013年提出的Word2Vec:通過訓練來讓模型自己學習如何關聯。

      思路

      Word2Vec的總體思路是:一個詞的意義可以被它所出現的上下文定義,因此我們可以通過上下文來理解某個單詞的意義,使用該詞的上下文來表示這個單詞的特征。這句話換一種說法又可以表述為:上下文相似的詞在詞義上也一定存在相似性。這個觀點就是語言學家 Zellig Harris 在1954 年提出的“Distribution Hypothesis”。

      比如我們不熟悉"Puma"這個單詞,但是下面有一些該詞的描述句子。

      • 外觀上沒有花紋且頭骨較小。雄性大于雌性。雄性頭體長1.02-1.54米,雌性頭體長860-1310毫米。
      • 軀體均勻,四肢中長,趾行性。視、聽、嗅覺均很發達。犬齒及裂齒極發達。
      • 常相居在山谷叢林中,善于游泳和爬樹,尤其喜歡在樹上活動,也善于奔跑,每小時能跑53-64千米。跳躍能力極強,能從12-13米高的樹上或懸崖上跳下,輕輕一躍能達8-9米多遠,也能躍過3米以上至6米的高度或5米以上至13米的距離,所以對20米以內的獵物,只要奮力跳躍兩次就可以捕到。
      • 棲息于森林、叢林、丘陵、草原、半沙漠和高山等多種生境,其中包括山地針葉林、低地熱帶森林、灌叢、沼澤以及任何有足夠遮蓋物和獵物的地區。
      • 在拉普拉塔,主要獵食麋鹿、鴕鳥、絨鼠和其他四足小動物;在智利,獵食小馬和牛等。

      從這樣的一些描述里面,我們可以比較放心地猜測"Puma"是類似于金錢豹之類的動物。因為上面的這些描述里面,如果我們把"Puma"替換成"金錢豹"我們會發現這就是經常對"金錢豹"的一些描述。也就是說,因為"Puma"和"金錢豹"有很多相似的上下文,所以我們認為它們本身也是相似的。

      從這一點出發,我們不再去統計一個單詞在一個大文檔里面出現的次數,而是統計一個單詞有哪些單詞以多高的頻率出現在它的上下文(一般來說,我們以當前單詞為中心,把它前后N個單詞叫做它的上下文)。有了上下文的定義之后,我們就去構造一個word-context的矩陣,進而獲取單詞對應的向量。

      Word2Vec的第一篇論文是Efficient Estimation of Word Representations in Vector Space。論文題目提到了表示論(Representation Theory),這屬于數學中抽象代數的一個分支。表示論將抽象代數結構中的元素“表示”成向量空間上的線性變換,并研究這些代數結構上的模,藉以研究結構的性質。表示論的妙用在于能將抽象代數問題轉為較容易解決的線性代數問題。例如Word2Vec論文中"Additive Compositionality" 有一些論述,模型中學到的詞和短語表現出一種線性結構,這使得使用簡單的向量運算進行精確的類比推理成為可能,同時一些簡單的元素級別的向量表示加法可以有意義地組合單詞。比如:“apple”與“apples”之間的幾何向量關系大致與“car”與“cars”之間的關系相同,這意味著我們可以將“apples”計算為“apple + car - cars”。這實際上就是構建了這樣的二元運算群到線性空間的映射,使得它們在線性空間的運算和群的操作同構。

      架構

      Word2Vec有兩種主要的架構:CBOW(Continuous Bag of Words)和Skip-Gram。

      • CBOW架構會基于詞的上下文來預測目標單詞,其本質上就是“輸入一個中心詞的上下文后得到的就是中心詞”。
      • Skip-Gram正好相反,它基于目標單詞預測其上下文。Skip-Gram和CBOW非常類似,只不過是“輸入中心詞,預測上下文的詞“。

      兩者的模型結構如下圖。以CBOW為例,輸入層初始化的時候直接為每個詞隨機生成一個n維的向量,并且把這個n維向量作為模型參數學習,最終得到該詞向量。生成詞向量的過程是一個參數更新的過程。

      我們再仔細分析上下文或者說窗口機制。在用語言模型進行無監督訓練時會使用窗口機制,即通過一個字附近n個字來預測這個字,這n個字就是這是個單詞的直接鄰域,是它在句子世界中的社交圈。比如一個句子"我要去后海玩",如果我們使用大小為2的窗口,那么的上下文就是[我,要,后,海]四個字。CBOW就是使用[我,要,后,海]去預測“去”這個字。本質上就是「輸入一個中心詞的contex后得到的就是中心詞」。

      在訓練中,同一個窗口內的詞語會有相似的更新,而具有相似模式的詞語就會把這些相似更新累積到可觀的程度。“相似的模式”指的是在特定的語言任務中,它們是可替換的。比如"我特別喜歡吃蘋果”中的“蘋果”被“香蕉”替換之后,該句子依然成立。因此“蘋果”和“香蕉”在特定的語言任務中必然具有相似的詞向量。或者說“麒麟”這兩個字,幾乎是連在一起用的,更新“麒”的同時,幾乎也會更新“麟”,因此它們的更新幾乎都是相同的,這樣“麒”、“麟”的字向量必然幾乎是一樣的。

      本質上來說,經過Word Embedding之后,在word-context矩陣里面一個單詞所在的那一行就是該單詞的一個向量表示。各個word就組合成了一個相對低維空間上的一組向量,這些向量之間的遠近關系則由他們之間的語義關系決定。這些表達有以下的特征:

      • 把一個單詞嵌入到一個高維歐式空間中,成為歐式空間的一個向量。
      • 相似的文字在歐式空間中相鄰。

      問題

      無論使用哪種架構,Word2Vec的核心都是一個“靜態”的詞嵌入:每個單詞都被映射到固定的,預先訓練好的向量上。這意味著無論該詞出現在何種上下文中,它的向量表示都是相同的。

      CBOW模型在訓練期間確實通過滑動窗口考慮了詞的局部上下文,但是Word2Vec只是利用和挖掘了“Distribution Hypothesis”的淺層結構。并沒有嘗試去理解句子內的語義。所以一旦模型訓練完成,每個詞的嵌入就被固定下來,不會隨著不同上下文場景的變化而改變。因此,這種嵌入被認為是靜態的缺失語境信息的。

      而對于語言模型來說,一個巨大的挑戰是一個詞在不同的情境中可以被使用。在語境中,孤立的單詞大多沒有意義,我們需要從共享的知識和經驗中汲取,才能理解它們。因此,有效的自然語言模型需要可以理解每個單詞、短語、句子或段落在不同語境下可能的含義。

      在這點上,固化的或者說靜態詞嵌入有一個巨大的軟肋:擁有多個定義的詞。比如“故”這個字,在每句話中依據上下文有不同的意思。

      • 公問其故(原因,緣故)。
      • 溫故(舊的知識)而知新。

      靜態embedding本質上是從查找表中讀取出來,無論在哪種上下文中,”故“這個詞都有一個與之對應的固定向量。都是只編碼了該特定詞的含義,沒有上下文參照,這限制了它在理解和區分不同意義時的能力。即,Word2Vec雖然在訓練時考慮到了局部上下文,但一旦訓練完成,每個單詞的嵌入就固定了,不再變化。

      因為靜態向量表示無法解決多義詞問題,人們就提出了動態向量。在動態向量表示中,模型不再是簡單的向量對應查表關系,而是通過一個訓練好的模型即時計算出來。在對詞進行向量表示時,模型根據當前語境對多義詞進行理解,推斷出每個詞對應的意思,從而得到該文本的詞向量。如果上下文不同,則計算出的向量會有所改變。動態詞向量相較于靜態詞向量,更加充分利用了上下文信息,所以可以解決一詞多義的問題。

      3.3 ELMO

      針對word embedding無法解決多義詞的問題,ELMO( Embedding from Language Models )提出了一個簡潔有效的解決方案。

      思路

      ELMo不是對每個單詞使用固定的嵌入,而是在為每個單詞分配嵌入之前查看整個句子。ELMo的本質思想是:根據當前上下文對Word Embedding動態調整。我們從ELMO的論文題目“Deep contextualized word representation”中的”contextualized “一詞就可以看出來其中精髓。

      ELMo首先使用語言模型學好一個單詞的Word Embedding,此時word Embedding中已經有了一定的語義信息。當實際使用Word Embedding時,ELMo再依據單詞的上下文來調整單詞的Word Embedding表示。這樣經過調整后的Word Embedding更能表達在這個上下文中的具體含義,自然也就解決了多義詞的問題了。

      ELMo模型的雙向語言模型(雙向雙層LSTM)由前向語言模型和后向語言模型組成。前向語言模型從左到右對文本進行編碼,而后向語言模型則從右到左進行編碼。然后將這兩部分信息進行整合,得到單詞的上下文信息。這種雙向設計使得ELMo不僅能看到本單詞前面的單詞,也能看到本單詞后續的單詞,從而同時捕捉到詞語在前后文中的信息,生成更加豐富的詞向量表示。

      在實際模型中,這樣的前向后向LSTM網絡可能是多層的。對于每一層,將每個token的正向和反向LSTM輸出拼接起來,得到這個token在該層的表示。

      訓練

      在ELMo之前的模型中,embedding模型很多是單獨訓練的,而ELMo之后則爆發了直接將embedding層和上面的語言模型層共同訓練的浪潮(ELMo的全名就是Embeddings from Language Model)。

      ELMo用了一個典型的兩階段過程來做預訓練。

      • 第一階段是利用語言模型進行預訓練。提取字符級別的特征,這就是對應單詞的最初詞表示(word embedding)。在預訓練過程中,ELMO不僅學會了單詞的word embedding,也學會了一個雙層雙向的LSTM網絡結構(這代表了每個詞語的前向和后向表示)。
      • 第二階段是做下游任務時,ELMo模型通過將這多層雙向LSTM的隱藏狀態和字符嵌入以某種方式組合在一起(連接后加權求和),并通過一個線性變換(比如用與任務相關的縮放系數進行加權求和)得到最終的上下文相關詞向量,作為下游任務模型的輸入。這個操作等于將單詞融合進了上下文的語義,可以更準確的表達單詞的真實含義。

      訓練階段具體如下圖所示。

      目前預訓練有兩種方法:

      1. Feature-based:將訓練出的表征作為feature用于下游任務,從詞向量、句向量、段向量、文本向量都是這樣的。新的ELMo也屬于這類,但遷移后需要重新計算出輸入的表征。
      2. Fine-tuning:這個主要借鑒于CV,就是在預訓練好的模型上加些針對任務的層,再對后幾層進行精調。新的ULMFit和OpenAI GPT屬于這一類。

      3.4 BERT

      BERT(Bidirectional Encoder Representations from Transformers)使用了Transformer架構,特別是通過自注意力機制(self-attention)能夠捕捉每個詞與句子中其他所有詞之間的關系。這意味著,BERT為詞生成的嵌入不僅僅依賴于該詞本身,還依賴于它所處的上下文。

      動機

      ELMo在拼接正反向LSTM輸出時,只是簡單地將前后向信息拼接,未考慮到左側上下文和右側上下文之間的交互。而GPT使用從左往右的解碼器,每個單詞只能獲得上文信息。這些結構對于句子級別的、需要雙向上下文信息的任務(比如NER、情感分析)是次優的。因此 BERT 決定真正地融合雙向的上下文信息。

      思路

      在模型的推理階段(即使用模型進行預測時),BERT會根據當前輸入的上下文動態調整每個詞的嵌入,這樣就可以為同一個詞在不同句子中生成不同的嵌入表示。而且,BERT的關鍵特點是其雙向性,模型在處理任何單詞時都會考慮到它的整個上下文(即,左邊和右邊的所有單詞)。這使得BERT能夠更精確地捕捉語言的復雜性和細微差別。

      下圖給出了預訓練模型架構的差異。BERT使用雙向Transformer。OpenAI GPT使用從左到右的Transformer。ELMo使用獨立訓練的從左到右和從右到左LSTM的連接來為下游任務生成特征。在這三者中,只有BERT表示在所有層中都受到左和右上下文的共同制約。除了架構差異外,BERT和OpenAI GPT是微調方法,而ELMo是一種基于特征的方法。

      對比OpenAI GPT(Generative pre-trained transformer),BERT是雙向的Transformer block連接;就像單向RNN和雙向RNN的區別,直覺上來講效果會好一些。

      對比ELMo,雖然都是“雙向”,但目標函數其實是不同的。ELMo是分別以??(????|??1,...?????1) 和 ??(????|????+1,...????) 作為目標函數,獨立訓練處兩個representation然后拼接,而BERT則是以 ??(????|??1,...,?????1,????+1,...,????) 作為目標函數訓練LM。

      BERT 提出了兩個學習任務:1)Masked語言建模(MLM),首先Mask文本中的部分單詞,然后通過上下文信息對Mask的單詞進行預測;2)Next-sentence-prediction(NSP),即預測兩個句子的前后關系。和GPT類似,BERT也使用特殊token:每個輸入最前面加[CLS],句子間隔和結束用[SEP]。

      embedding

      BERT的輸入的編碼向量是3種Embedding特征element-wise和,如下圖所示。這三種Embedding特征分別是:

      • Token Embeddings是單詞的詞向量,第一個單詞是CLS標志,可以用于之后的分類任務。
      • Segment Embeddings是兩個句子的區分標識,因為預訓練不光做LM還要做以兩個句子為輸入的分類任務。如B是否是A的下文(對話場景,問答場景等)。對于句子對,第一個句子的特征值是0,第二個句子的特征值是1。例如 “[CLS] My name is xx [SEP] How are you [SEP]” 那么對應的 SegmentEmbeddings 就是 0000001111,粗體0后1表示對應的單詞
      • Position Embeddings:將單詞的位置信息編碼成特征向量,Position embedding能有效將單詞的位置關系引入到模型中,提升模型對句子理解能力。此處不是三角函數而是學習出來的。

      為什么 Bert 的三個 Embedding 可以進行相加?以下是一些深刻的解釋。

      • 這三個embedding對應三種不同的頻率,從信號處理的角度來看,疊加或者是加法是一個比較常規的操作。
      • Embedding的數學本質是以one hot為輸入的單層全連接,因此三種embedding先用one-hot編碼,然后concat到一起,最后過一個MLP,和直接的embedding相加是等價的。
      • 三種embedding的相加,是把符號空間、符號屬性空間和位置空間通過線性映射到一個統一的、同質的特征空間上去,然后再以求和的方式做坐標綜合。

      代碼

      具體代碼如下。這部分主要通過生成三個Embedding層,再通過截斷正態分布賦予權重。

      
      class BertEmbeddings(nn.Module):
          """Construct the embeddings from word, position and token_type embeddings."""
      
          def __init__(self, config):
              super().__init__()
              self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id)
              self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size)
              self.token_type_embeddings = nn.Embedding(config.type_vocab_size, config.hidden_size)
      
              # self.LayerNorm is not snake-cased to stick with TensorFlow model variable name and be able to load
              # any TensorFlow checkpoint file
              self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
              self.dropout = nn.Dropout(config.hidden_dropout_prob)
              # position_ids (1, len position emb) is contiguous in memory and exported when serialized
              self.position_embedding_type = getattr(config, "position_embedding_type", "absolute")
              self.register_buffer(
                  "position_ids", torch.arange(config.max_position_embeddings).expand((1, -1)), persistent=False
              )
              self.register_buffer(
                  "token_type_ids", torch.zeros(self.position_ids.size(), dtype=torch.long), persistent=False
              )
      
          def forward(
              self,
              input_ids: Optional[torch.LongTensor] = None,
              token_type_ids: Optional[torch.LongTensor] = None,
              position_ids: Optional[torch.LongTensor] = None,
              inputs_embeds: Optional[torch.FloatTensor] = None,
              past_key_values_length: int = 0,
          ) -> torch.Tensor:
              if input_ids is not None:
                  input_shape = input_ids.size()
              else:
                  input_shape = inputs_embeds.size()[:-1]
      
              seq_length = input_shape[1]
      
              if position_ids is None:
                  position_ids = self.position_ids[:, past_key_values_length : seq_length + past_key_values_length]
      
              if token_type_ids is None:
                  if hasattr(self, "token_type_ids"):
                      buffered_token_type_ids = self.token_type_ids[:, :seq_length]
                      buffered_token_type_ids_expanded = buffered_token_type_ids.expand(input_shape[0], seq_length)
                      token_type_ids = buffered_token_type_ids_expanded
                  else:
                      token_type_ids = torch.zeros(input_shape, dtype=torch.long, device=self.position_ids.device)
      
              if inputs_embeds is None:
                  inputs_embeds = self.word_embeddings(input_ids)
              token_type_embeddings = self.token_type_embeddings(token_type_ids)
      
              embeddings = inputs_embeds + token_type_embeddings
              if self.position_embedding_type == "absolute":
                  position_embeddings = self.position_embeddings(position_ids)
                  embeddings += position_embeddings
              embeddings = self.LayerNorm(embeddings)
              embeddings = self.dropout(embeddings)
              return embeddings
      

      3.5 BGE

      在中文世界,智源研究院在論文"C-Pack: Packaged Resources To Advance General Chinese Embedding"中提出的BGE是比較有名的開源embedding model。BGE希望做中文世界的通用embedding模型,這就意味著BGE需要支持所有的embedding使用場景,包括但不限于:retrieval、re-rank、clustering、classification、pair-classification等任務。我們接下來看看論文作者們都做了哪些努力。

      數據集

      論文作者為通用中文嵌入訓練策劃了最大的數據集C-MTP,兼顧scalediversityquality這三個維度,確保了文本嵌入的通用性,這是通用embedding模型能訓練出來的前提。

      C-MTP從兩個來源來收集數據:大部分數據基于海量未標記數據的管理,即C-MTP(未標記),它包含了1億對文本。一小部分來自高質量標記數據的全面整合,即C-MTP(標記),這包括了大約100萬對文本。

      訓練

      論文使用3階段訓練策略,從pre-traininggeneral-purpose fine-tuning 再到 task-specific fine-tuning;前兩個階段是保證通用性的基石,最后一個階段則在保持通用的基礎上,進一步精進下游任務的效果。

      pre-training階段在Wudao Corpora上進行,此階段未在任何文本對數據上訓練,其目標是訓練出更適合embedding任務的pre-trained model。此處的核心技術是RetroMAE訓練策略。

      general-purpose fine-tuning階段在C-MTP(unlabeled)上進行,該階段在100M的文本對上訓練,可以視作一種大規模的弱監督學習過程,其目標是初步學習出通用embedding model;這一階段的核心技術是對比學習,采用in-batch negative sample方法和大batch size來進行訓練。而且重點優化了retrieval任務。

      最后的task-specific fine-tuning階段,在C-MTP(labeled)上進行,通過在少而精的下游任務labeled data上微調,在保證通用性的同時,強化模型在具體任務上的表現。這一階段的難點在于:在任務間存在差異的情況下,如何更好地multi-task learning。論文采取了兩個關鍵技術來解決技術難點:instruction-based fine-tuning和hard negative sampling。

      RetroMAE

      pre-training階段的目標是為了學習出更適合embedding的pre-trained model。

      目前主流的語言模型的預訓練任務都是token級別的,比如MLM或者Seq2Seq,但是這種訓練任務難以讓模型獲得一個高質量的基于句子級別的句向量,這限制了語言模型在檢索任務上的潛力。針對這個弊端,目前有兩者針對檢索模型的預訓練策略,第一種是self-contrastive learning,這種方式往往受限于數據增強的質量,并且需要采用非常龐大數量的的負樣本。另一種基于anto-encoding,一種自重建方法,不受數據增強跟負樣本采樣策略的影響。決定基于這種方法的模型性能好壞有兩個關鍵因素,其一是重建任務必須要對編碼質量有足夠的要求,其二是訓練數據需要被充分利用到。

      論文作者提出了RetraoMAE,它包括兩個模塊:

      • 首先使用類似于BERT的編碼器先對輸入進行隨機Mask,然后進行編碼。
      • 然后使用一個一層transformer的解碼器再進行重構。通過這一過程,強迫encoder學習到良好的embedding。

      Encoding

      給定一個句子輸入X,隨機mask掉其中一小部分token后得到 \(X_{enc}\) ,這里通常會采用中等的mask比例(15%~30%),從而能保留句子原本大部分的信息,然后利用一個編碼器 \(Φ_{enc(.)}\)\(X_{enc}\) 進行編碼,得到對應的的句向量 ? 。由于采用了類似BERT的編碼器,最終將[CLS]位置最后一層的隱狀態作為句子向量。

      Decoding

      給定一個句子輸入X,隨機mask掉其中一部分token得到 \(X_{dec}\) ,這里通常會采取比encoder部分更加激進的mask比例(50%~70%),利用mask后的文本和encoder生成的句向量對文本進行重建。

      Enhanced Decoding

      前面提及的解碼策略有一種缺陷,就是訓練信號只來源于被mask掉的token,而且每個mask掉的token都是基于同一個上下文重建的。于是研究人員提出了一種新的解碼方法,Enhanced Decoding,具體做法如下。

      • 首先生成兩個不同的輸入流H1(query)跟H2(context)

      • 通過注意力機制得到新的輸出A,第i個token所能看得到的其他token是通過抽樣的方式決定的(要確保看不到自身token,而且要看見第一個token,也就是encoder所產出CLS句向量的信息)

      • 最終利用A跟H1去重建原文本,這里重建的目標不僅僅是被mask掉的token,而是全部token。

      最終RetroMAE的損失由兩部分相加得到,其一是encoder部分的MLM損失,其二是deocder部分自重建的交叉熵損失。

      3.6 LLM-As-Embedding

      我們接下來看看使用LLM生成Embedding。下圖給出了目前常見模型的總體狀態。

      Backbone選擇

      多年來,構建文本嵌入模型的主導范式依賴于預訓練的雙向編碼器模型或僅編碼器模型,如BERT和T5。之前普遍認為僅解碼器模型不能用于嵌入提取。因為基于編碼器的模型可以通過雙向注意力捕獲語義。而僅解碼器模型通常使用因果注意力僅與之前的詞標記交互,因此,無法像編碼器 - 解碼器模型那樣捕獲豐富的語義,例如上下文化信息。直到最近,社區才開始采用僅解碼器的LLM來生成嵌入文本。從上圖可以看到,在Backbone選擇上,Encoder-Decoder架構中主要使用T5,Decoder-Only架構主要使用Mistral和LLaMA。

      架構改進

      深度學習模型在處理輸入數據之后得到的內部表示會具有語義和壓縮的數據信息,因此我們通常提取神經網絡的最后一層隱藏狀態作為嵌入。

      雖然Decoder-only的大模型在諸多NLP任務上表現出色,但是直接利用其生成文本表征的效果往往比較糟糕,這跟大模型本身的訓練任務跟模型架構有關。因此需要對架構進行改進。

      Pooling策略

      Pooling是指LLM的最后一層的hidden_states經過一個額外的映射器(區別于LLM原有的Linear映射器)再得到表征,主要分為以下5種策略

      • First Pooling:使用第一個token作為表征,比如對于BERT使用 [CLS] token,對于T5使用 [START] token。

      • (Weighted) Mean Pooling:使用每個token的(加權)平均。BERT和T5使用Mean Pooling效果就較好。GPT-Style LLM讓靠后的token權重更大、則效果更好。

      • (Special) Last Pooling:使用最后一個token。因為LLM的最后一個token一般和下一詞的token表征對齊,所以一般采用以下兩種方式進行轉換:

        • 使用提示詞讓模型在最后一個token來總結全文語義。代表工作為PromptEOL,模板是:“The sentence [X] means in a word”。
        • 引入特殊token,并進行相應微調。這一系列工作主要使用句尾的作為特殊token,ChatRetriever 在末尾添加了一個Embedding序列 [EMB1], … , [EMBt]。用這部分表征作為思維鏈會提升表征的質量。
      • Partial Pooling:Echo 提出了Prompt 如下:Rewrite the sentence: [x], rewritten sentence: [x] 。其中兩個[x]處都會填入相同的文本,從而讓第2個[x]的token都能看到句子內所有token。然后取第二個[x]內的token用于pooling。

      • Trainable Pooling:在常規的pooling前加上注意力機制。NV-Embeder將LLM的最后一層hidden_states H作為Q,將K和V作為可學習參數設計了 latent attention layer,從而在Pooling前和H進行更深的交互。LMORT使用對齊均勻性指標從各層挑出一些合適的H,再輸入一個多層注意力網絡。

      注意力架構

      Decoder-Only LLM使用的單向注意力(Casual Attention)確保語言建模只能引用前綴來預測下一個令牌,然而Casual Attention可能會降低下游任務性能。因此研究人員使用以下幾種方法來修改模型架構,從而適應Embedding任務。

      • 保留隨意注意力并使用其他技巧(例如,使用各種pooling策略)。

      • 轉換為雙向注意力并讓模型適應新結構。BELLM將LLM的最后幾層改為雙向注意力。NV-Embeder 進一步發現使用充足數據時,LLM無需額外操作即可全部轉換至雙向注意力。LLM2Vec 提出了結合MLM(masked language model)的MNTP任務(masked next token prediction任務),從而更好地利用更多數據。

      額外投影層(Additional Projector)

      投影層是文本嵌入中常用的策略,具體分為如下:

      • 低維表征映射。將表征映射到較低維度表征(比如4096=>1024)。
      • 稀疏表征映射。使用門控機制、topk-masking、正則項等方法將表征轉換為稀疏表征。
      • 其他用途映射。InBedder使用較短的問答對作為訓練數據,預測下一詞得到表征。
      • Parameter-Efficient Fune-Tuning Module:是否使用參數高效的微調技術主要取決于使用的數據量,而不是資源限制。一些研究引入了BitFit或者LoRA作為PEFT模塊,并使用單個數據集進行微調,這些小規模數據可以激發LLM本身所具備的語義泛化能力。其他工作使用基于數百個數據集的全參數調整和(多階段)微調,這足以允許對模型參數進行大幅更改并避免過擬合。

      我們接下來介紹幾個利用大模型生成embedding的方案。

      3.7 LLM2Vec

      因果注意力機制的特點是每個單詞只能關注之前的單詞,這限制了對整個句子意義的全面理解。而LLM2Vec正是為了解決這個問題。LLM2Vec是一種能將任何decoder-only模型改造成文本表征模型的無監督方法,該方法主要涉及了雙向注意力機制改造,masked next token prediction任務,以及無監督對比學習三個部分。這種方法能夠有效的將大模型改造成通用化的文本編碼器,而不需要其他適配任務或者數據。相比其他進一步訓練的方案,LLM2Vec不需要高昂成本的標注訓練數據就可以在不增加輸入長度的情況下,保證推理成本。

      LLM2Vec包括修改注意力機制以及兩個無監督訓練任務,具體如下。

      • 首先直接將單向注意力機制改成雙向注意力機制,使每個token都能訪問序列中的所有其他token。
      • 然后利用MNTP(Masked Next Token Prediction)進行訓練使得模型能適配雙向注意力機制,這個時候其實模型已經具備生成高質量的token級別表征的能力。
      • 最后再利用無監督對比學習SimCSE(Unsupervised Contrastive Learning)進一步訓練,使得模型可以生成高質量的句子級別的表征。

      LLM2Vec雖然涉及兩個訓練任務,但都是無監督學習,不需要高質量的標注數據。

      雙向注意力機制

      LLM2Vec的第一步是將decoder-only LLM的的單向注意力機制改成雙向注意力機制,使得每個位置的token都可以看到其他位置的信息。改造的原因在于研究人員認為decoder-only LLM看不到未來信息的機制可能會損害文本表征的質量。具體操作是將解碼器LLM的因果注意力掩碼(causal attention mask)替換為全一掩碼矩陣(all-ones attention mask),使每個token都能訪問序列中的所有其他token。下圖最左側展示了LLM2Vec如何通過改變注意力掩碼,將每個單詞的注意力范圍擴展到整個序列。這種方式允許每個單詞同時考慮上下文中的所有其他單詞,從而顯著提升模型對語境的理解能力。

      Masked next token prediction(MNTP)

      完成第一步改造后,需要讓模型適應新的雙向注意力機制,于是研究人員就設計了MNTP這個任務,跟BERT預訓練的MLM任務相似,在此步驟中,模型會隱藏句子中的某些單詞,然后通過查看其他所有單詞(包括隱藏單詞之前和之后的上下文)來預測這些單詞。具體參見下圖中間部分。這一步幫助模型習慣于同時關注前后文,從而更好地理解和表示文本。

      Unsupervised contrastive learning

      前兩步改造使得LLM可以生成高質量的token級別的表征,但是還不能生成高質量的句子級別的表征,所以研究人員直接搬用了SimCSE的無監督對比學習,對LLM做進一步的訓練。具體而言是將每個位置的隱狀態通過mean pooling作為句向量,通過最大化同一序列的兩個不同表示之間的相似性,同時最小化與批次中其他序列表示的相似性,來提升模型性能。即通過對比學習的方式去拉進相似文本的距離,疏遠不相似文本之間的距離。這樣能夠讓模型更好地區分相似和不同的句子,從而提高句子表示的質量。具體參見下圖右側部分。

      以上三步讓LLM2Vec能夠將任何大型語言模型轉化為一個在各種NLP任務中都非常實用的文本理解和表示工具。

      3.8 NV Embedding

      截止2025.01,MTEB榜單里NV-Embed-v2 平均72.31分數奪得榜首,其改進思路值得借鑒。NV-Embed-v2來自論文"NV-Embed: Improved Techniques for Training LLMs as Generalist Embedding Models"。論文中基于 Mistral-7B 的預訓練模型進行改造,通過增加潛在注意力層來提高表征能力。

      上圖給出了由僅解碼器LLM和潛在注意力層組成的架構設計:一個潛在注意力層層后面接上一個MLP層。

      雙向注意力

      論文沒有使用LLM2VEC和GRIT的技巧,而是直接在對比學習過程中去除了decoder-only LLM的因果注意掩碼(causal attention mask),結果發現它的效果非常好。

      潛在注意力層

      有兩種流行的方法可以獲得一系列令牌的嵌入:i)mean pooling(均值池),ii)the last token embedding(最后一個令牌嵌入)。

      以前的雙向嵌入模型通常使用均值池mean pooling,而the last token embedding在僅解碼器的LLM嵌入模型中更受歡迎。然而,這兩種方法都有一定的局限性。mean pooling只是取令牌嵌入的平均值,可能會稀釋關鍵短語中的重要信息,同時the last token embedding可能會受到近因偏差的影響,嚴重依賴于最后一個標記的輸出嵌入。

      NV-Embed提出了一個潛在注意力層(latent attention layer),該層可以在通用嵌入任務中實現更具表現力的序列池。具體來說,潛在注意力層作為一種交叉注意力的形式,把decoder-only LLM最后一層的隱狀態作為query,把可訓練的潛在數組(latent array)作為key和values。藍色虛線表示QKV注意力機制中涉及的兩個矩陣乘法。

      MLP層

      MLP就是常規MLP,它由兩個線性變換組成,中間有GELU激活。

      Embedding 聚合

      論文在MLP層之后,可以采用兩種方式來聚合輸出向量,獲得整個序列的嵌入。

      • 采用文本的結尾的EOS token對應的輸出作為整句的embedding。
      • Mean-Pool: 每個token在Embeddings做平均,保留每個token的特征信息。

      NV-Embed采用Mean-Pool 的方式。

      3.9 通過提示工程的方法

      論文“Simple Techniques for Enhancing Sentence Embeddings in Generative Language Models”通過提示工程的方法,來增加大模型的直接生成文本表征能力。

      如何更好地將生成模型預測下一個Token的方式,與生成一個向量的偏差更小呢?在此論文之前,PromptEOL 是比較常見的方式(PromptEOL由一篇發表于2023年的論文"Scaling sentence embeddings with large language models"提出,是一種使用特定提示模板讓LLM生成句子embedding的方法)。此論文在PromptEOL 基礎上,提出了PromptSTH和PromptSUM。這三種方式具體如下:

      • PromptEOL :將句子用一個“word”表示,即 This sentence : "[X]" means in one word:"。
      • PromptSTH:將句子用“something”表示,即 This sentence : "[X]" means something。
      • PromptSUM:本句子可以總結成什么內容,即 This sentence : "[X]" can be summarized as。

      從結果上來看,在直接生成向量時,PromptEOL方式效果更好,說明與與大模型本身預訓練階段更加吻合。但相較于BERT模型直接生成向量來說,還是存在差距。

      那是不是大模型直接生成向量的效果就是不理想呢?論文參考COT和ICL方式,提出Pretended Chain of Thought和Knowledge Enhancement方法,具體如下:

      從結果上來看,通過提示詞工程,不僅可以激發大模型的推理能力,大模型的向量表征能力同樣可以被激發出來。

      3.10 使用MoE進行Embedding

      論文“YOUR MIXTURE-OF-EXPERTS LLM IS SECRETLY AN EMBEDDING MODEL FOR FREE”提出了基于MoE來生成embedding,即MoE嵌入(MOEE),用于解決LLMs作為嵌入模型的問題。

      研究背景

      盡管LLMs在生成任務上表現出色,但其解碼器架構限制了其在沒有進一步表示微調的情況下作為嵌入模型的潛力。研究難點在于:LLMs的最終或中間隱藏狀態(HS)可能無法捕捉輸入令牌的關鍵特征和所有信息,尤其是當涉及到細微語義差異時。此外,現有的嵌入方法通常依賴于靜態架構,可能忽略了輸入的可變性。

      動機

      作者們發現 MoE 中的路由權重可以為解碼器嵌入提供補充信息。MoE模型通過動態路由機制將輸入分配給不同的專家。每個專家專注于輸入的特定特征,每一層的路由權重反映了對輸入標記的推理選擇,或者說,路由權重表示每個專家對最終輸出的貢獻。因此路由權重包含了隱藏狀態嵌入可能丟失的輸入的語義信息。通過連接所有層的路由權重,可以形成基于路由的嵌入\(e_{RW}\)。像下圖標號1那樣描述。其中 g 是 門控函數,H 表示隱藏狀態。論文將所有 MoE 層的路由權重連接起來,以避免丟失模型的推理選擇。

      論文又通過聚類分析和相關性分析,研究了RW和HS嵌入的不同之處。發現RW和HS嵌入在聚類行為和主題上有所不同,且它們之間的相關性較低,這表明它們具有互補性。具體來說,RW和HS嵌入的聚類結果顯示出中等的重疊(AMI和NMI在0.29左右),但它們的Jaccard相似度和精確匹配率較低(分別為0.06和45.54%)。這表明RW和HS嵌入在結構化和主題上捕獲了不同的信息。進一步的分析顯示,RW嵌入強調輸入的不同主題,而HS嵌入則更好地捕捉到句子的整體結構和意義。

      因此,基于RW和HS嵌入的互補性,為了充分利用路由權重和解碼器嵌入,作者們提出了一種稱為 MoE 嵌入(MoEE)的方法,以形成更全面的嵌入表示。MoEE 有兩種類型。

      • 串聯組合:如上圖標號2所示。這種方法很簡單,我們只需將路由權重和解碼器嵌入直接連接起來。作者們稱這種方法為 MoEE(concat)。它可以保留每個路由權重捕獲的不同信息,同時允許下游任務利用組合表示。該方案的優點是:簡單直觀,保留了HS和RW嵌入的獨立信息,允許下游任務靈活地利用這兩種表示。該方案的缺點是:可能會引入冗余信息,因為兩種嵌入的類型和結構不同,簡單的串聯可能導致某些信息的重復或抵消。
      • 加權求和集成:如上圖標號3所示。分別計算HS和RW嵌入的相似度得分,然后進行加權求和,表示為 MoEE (sum)。?? 是一個超參數,用于控制路由權重的貢獻。該方案的優點是:通過加權求和可以平衡RW和HS嵌入的貢獻,避免直接融合帶來的復雜性。這種方法允許根據不同任務的需求調整RW和HS的權重,優化性能。該方案的缺點是:需要設置超參數α來控制RW的貢獻,這可能需要額外的實驗和調整。此外,加權求和可能會掩蓋某些嵌入的特定優勢。

      此外,作者們利用 PromptEOL 技術 來增強 MoEE。PromptEOL的使用顯著增強了MOEE方法的穩定性和性能,使其在不確定性較高的提示條件下也能保持較高的嵌入質量。

      0xFF 參考

      ALBERT: A Lite BERT for Self-supervised Learning of Language Representations[C]// International Conference on Learning Representations
      Distributed Representations of Words and Phrases and their Compositionality
      Echo embedding: 把文本重復兩次,自回歸模型就能生成更高質量的embedding
      Efficient Estimation of Word Representations in Vector Space
      Embeddings等技術解析J
      https://arxiv.org/pdf/2307.16645.pdf
      Is Cosine-Similarity of Embeddings Really About Similarity?
      LLM2Vec: Large Language Models Are Secretly Powerful Text Encoders
      LLM2Vec: 改造Decoder-only LLM以生成高質量text embedding 澤龍
      RetroMAE v2: Duplex Masked Auto-Encoder For Pre-Training Retrieval-Oriented Language Models
      RetroMAE: Pre-Training Retrieval-oriented Language Models Via Masked Auto-Encoder
      RetroMAE: Pre-Training Retrieval-oriented Language Models Via Masked Auto-Encoder閱讀筆記 陌路
      SimCSE: Simple Contrastive Learning of Sentence Embeddings
      Simple Techniques for Enhancing Sentence Embeddings in Generative Language Models
      When Text Embedding Meets Large Language Model: A Comprehensive Survey
      Word Embeddings: Encoding Lexical Semantics
      Your Mixture-of-Experts LLM is Secretly an Embedding Model for Free
      YOUR MIXTURE-OF-EXPERTS LLM IS SECRETLY AN EMBEDDING MODEL FOR FREE
      【LLM論文日更】 | 你的專家組合LLM是秘密的免費嵌入模型 OptimaAI
      【手撕LLM_Nv Embed】英偉達的LLM-as-Embedding ICLR高分, RAG檢索有救了! 小冬瓜AIGC [手撕LLM]
      為什么 Bert 的三個 Embedding 可以進行相加?
      從數學到神經網絡(一)結構篇:從幾何、語言到TOKEN 大象Alpha [大象Alpha]
      余弦相似度可能沒用?對于某些線性模型,相似度甚至不唯一 [機器之心]
      關于維度公式“n > 8.33 log N”的可用性分析 蘇劍林
      啥是Embedding(嵌入技術)?【UNDONE】 番子xiwa
      大模型之嵌入與向量化的區別是什么? DFires [AI探索時代]
      如何快速提高大模型的向量表征效果能力? 劉聰NLP
      如何用MoE進行Embedding的獲取 Alex [算法狗]
      如何看待瘦身成功版BERT——ALBERT?
      微軟E5-mistral-7b-instruct: 站在LLM肩膀上的text embedding
      最小熵原理(六):詞向量的維度應該怎么選擇? 蘇劍林
      殘差網絡解決了什么,為什么有效?
      深入解析 Transformers 框架(五):嵌入(Embedding)機制和 Word2Vec 詞嵌入模型實戰 [老牛同學]
      深度學習——Bert深度解析
      綜述分享-北航&阿里-當LLM遇上Embedding BrownSearch
      論文分享|Arxiv2024'麥吉爾大學|LLM2Vec—將LLM轉換為文本編碼器 BrownSearch
      詞向量與Embedding究竟是怎么回事? 蘇劍林
      詞向量嵌入發展綜述 安德安德魯
      詳解Albert:A LITE BERT FOR SELF-SUPERVISED LEARNING OF LANGUAGE REPRESENTATIONS boom
      語言模型之Text embedding(思考篇) 澤龍
      語言模型輸出端共享Embedding的重新探索 蘇劍林
      https://colala.berkeley.edu/papers/piantadosi2024why.pdf
      https://pytorch.org/tutorials/beginner/nlp/word_embeddings_tutorial.html

      posted @ 2025-02-27 20:32  羅西的思考  閱讀(3983)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 伊人av超碰伊人久久久| 黄浦区| 国产稚嫩高中生呻吟激情在线视频 | 欧美成人影院亚洲综合图| 粉嫩国产av一区二区三区| 国产仑乱无码内谢| 人妻 日韩精品 中文字幕| 激情综合网激情国产av| 国产超碰无码最新上传| 午夜欧美精品久久久久久久 | 丰原市| 欧美xxxxhd高清| 波多野无码中文字幕av专区| 亚洲色成人一区二区三区人人澡人人妻人人爽人人蜜桃麻豆 | 日韩国产成人精品视频| 毛片无遮挡高清免费| 国产一区二区日韩在线| 日本黄韩国色三级三级三| 欧美日韩精品一区二区三区在线 | 亚洲国产av剧一区二区三区| 久久亚洲中文字幕伊人久久大| 尤物yw193无码点击进入| 亚洲国产色播AV在线| A级毛片100部免费看| 亚洲国产长腿丝袜av天堂| 国产精品免费第一区二区| 国产欧美综合在线观看第十页| 插插无码视频大全不卡网站| 撩起胸让我的?蹭来蹭去| 狠狠躁日日躁夜夜躁欧美老妇| 亚洲av激情五月性综合| 久久精品国产99久久久古代| 欧美成人片在线观看| 91孕妇精品一区二区三区| 欧美性猛交xxxx乱大交丰满| 久久视频这里只精品| 超碰成人精品一区二区三| 女人喷水高潮时的视频网站| 亚洲顶级裸体av片| 国产中文三级全黄| 2021国产成人精品久久|