- 參考:
- 李宏毅2021/2022春機器學習課程
- 王樹森 RNN & Transformer 教程
文章目錄
- 0. 背景:序列資料及相關任務
- 1. 早期序列模型
- 1.1 循環神經網絡 RNN
- 1.2 長短期記憶網絡 LSTM
- 1.3 改善 RNN/LSTM 的三個技巧
- 1.3.1 通過堆疊擴充為深度模型
- 1.3.2 使用雙向模型避免遺忘
- 1.3.3 使用預訓練模型
- 1.4 傳統序列模型任務
- 1.4.1 自動文本生成(Autoregress 結構)
- 1.4.2 機器翻譯(Encoder-Decoder 結構)
- 2. 注意力機制
- 2.1 注意力機制 (Attention)
- 2.2 自注意力機制 (Self-Attention)
- 3. Attention + Encoder-Decoder
- 3.1 Transformer
- 3.2 GPT
- 3.3 BERT
0. 背景:序列資料及相關任務
-
是由一組互相關聯的樣本組成的資料,其中任意樣本對應的标記是由其自身和其他樣本共同決定的;序列資料
是輸入或輸出為序列資料的機器學習任務,用傳統機器學習模型處理他們是困難的,比如 序列模型(1)—— 難處理的序列資料 中第 3 節的例子序列資料任務
- 傳統方法的局限性在于其問題模組化,這些模型不是針對可變長度的輸入輸出設計的,無法展現序列資料的特點,具體而言
- 傳統的 MLP、CNN 這類模型都是
模型,即一個輸入一個輸出。這種模型會把序列資料作為一個整體來考慮,其輸入輸出的尺寸必須是固定的,比如輸入一張固定尺寸的圖像輸出其類别,或者輸入一段固定長度的句子預測下一個詞one-to-one
Note:借助一些手段,可以強行用傳統模型處理變長的輸入輸出問題,如 各種監督學習範式(強監督、半監督、多标記、偏标記、多示例、多示例多标記、标記分布…) 中的多示例多标記問題,但是這些模型本質上還是要将輸入輸出處理成固定的長度(比如用固定長度的 0/1 向量選出不同數量的樣本作為輸入輸出)。至于樣本間的關系,雖然可以通過網絡自己學出來,但由于缺乏考慮這種關系的顯式結構,是以很難學得好(可以參考 從模型容量的視角看監督學習)
- 一個良好的序列模型應該是
模型,即支援可變長度的輸入輸出,并且最好能對序列樣本間的關系進行顯式模組化,注意這樣的模型也可以直接用來處理 many-to-many
,one-to-many
甚至 many-to-one
問題。語音識别、本文情感分析、序列預測等等序列任務都能被這種模型更好地處理one-to-one
RNN 就是一種良好的序列模型,下圖給出了各類輸入輸出情況下的 RNN 結構
1. 早期序列模型
-
RNN 和 LSTM 等早期序列模型模仿人類處理序列資料的過程,人類閱讀文本時每次看一個詞,逐漸在大腦中積累文本資訊,這些模型也是如此,其内部有一個隐狀态代表目前積累的資訊,每次讀入一個序列樣本就将其更新,如下圖所示
注意這裡的每一列都是不同時刻下的同一個模型,可見,模型的每個隐狀态都表示 “目前位置之前的 ‘已見序列片段’ 的特征”,具體應用時
- 對于 “文本情感分析” 這類
(many-to-one),常将序列模型作為 “特征提取器”,隻使用最後一個隐狀态 作為整個序列的特征向量,用它接一個分類頭或回歸頭作為整個模型的輸出。在訓練時,隻要像圖像分類等普通監督學習任務一樣訓練即可傳統監督學習任務
- 對于 “文本生成” 這類
(one-to-many),即不斷根據之前序列樣本預測下一個樣本值的任務,通常會如下圖所示做 Autoregress,這時我們會增加一個分類頭或回歸頭将隐狀态 變換為輸出 ,推斷時不斷地将上一步模型輸出合并到下一步模型的輸入中。在訓練時,會構造很多以連續的 n 個樣本作為輸入,緊接着第 n+1 個樣本作為标簽的自監督樣例,詳見下文 1.4.1 節标準語言模型任務
- 對于 “文本翻譯” 這類
(many-to-many),通常使用 Encoder-Decoder 結構。這時 Encoder 就是類似 1 中的 many-to-one 序列特征提取器,Decoder 就是類似 2 中的 one-to-many 序列生成器,Encoder 提取的特征作為 Decoder 的初始 seed,二者結合就能做 many-to-many 了。訓練時通常用 teacher-forcing 形式,詳見下文 1.4.2 節Seq2Seq 任務
1.1 循環神經網絡 RNN
-
RNN 是實作第 1 節中 “資訊積累” 概念的最簡單模型,其結構圖如下所示
具體而言,設隐藏層激活函數為 , 時刻輸入批量大小為 樣本次元為 的小批量樣本 ,設隐藏變量次元為 ,前一個時刻的小批量隐層變量為 。引入模型的權重參數 和偏置參數 ,則該步的隐藏變量 可以如下計算
再設輸出次元為 ,用于輸出的 FC 層參數為 ,該步的輸出變量 可以如下計算
注意在不同的時間步使用的都是相同的模型參數 ,是以 RNN 的參數開銷不會随着時間步的增加而增加
1.2 長短期記憶網絡 LSTM
- LSTM 是對 RNN 模型的改進,可以有效緩解 RNN 的梯度消失(見下文 1.3.2 節)問題,其結構如下所示
- 這個結構看上去很複雜,不過我們可以從先從宏觀角度來了解:相比 RNN,LSTM 針對序列資料性質增加了對資料處理過程的限制,進而減少了模型的彈性/容量,使它能在使得相同樣本量下更好地提取序列資訊,這和 CNN 比 MLP 能更好地處理圖像資料是一個道理
- 現在來仔細看一下這個結構,LSTM 的設計靈感來自于計算機的邏輯門,它的輸出和 RNN 一樣仍然是隐狀态 ,隻是生成過程更複雜。相比 RNN,LSTM 引入了
,它和隐狀态 具有相同的形狀,用于記錄附加的資訊并産生輸出 ,其他的所有門都是為了控制這個記憶單元服務的。具體而言,設隐藏層次元為 ,batch size 為 ,樣本次元為 ,則批量輸入為 ,前一時刻隐狀态為記憶單元cell
- 候選記憶 和 RNN 中的隐狀态 ,隻是指定了使用激活函數 ,将值壓倒
- 遺忘門 用來控制要保留記憶單元 ,用 sigmoid 激活的 FC 層壓倒 (0,1)
- 輸入門 用來控制候選記憶 ,用 sigmoid 激活的 FC 層壓倒 (0,1)
- 輸出門 用來控制記憶單元 中作為輸出 ,用 sigmoid 激活的 FC 層壓倒 (0,1)
列一下公式,有
這裡的 都是要學習的參數,相比 RNN 多了三組。另外,所有資訊選取都是通過對應位置乘以 (0,1) 間小數的方式進行的,如圖可見有 “遺忘門 去除部分老記憶 ”、“輸入門 合并部分候選記憶 ” 和 “輸出門 保留部分新記憶 ” 三處,公式表示為
- 直覺地看這個結構,其實就是每次 batch 輸入先産生和 RNN 中 完全一樣的 作為 “目前步記憶”,然後用遺忘和輸入門控制它和 “曆史序列記憶” 混合得到 “最新序列記憶” ,最後使用輸出門将 衰減後輸出為
- 如果遺忘門始終為 1 且輸入門始終為 0, 則過去的記憶元
- 隻要輸出門接近 1,我們就能夠有效地将所有記憶資訊傳遞給預測部分, 而對于輸出門接近 0,我們隻保留記憶元内的所有資訊,而不需要更新隐狀态
- 最後再回到宏觀來看,LTSM 通過學習四組系數 & 偏置參數,要求模型按照上述邏輯構造記憶并從中提取輸出,相比 RNN 直接一個 FC 加激活函數,它通過模型結構引導其做出更符合序列性質的行為,進而緩解了 RNN 的梯度消失和梯度爆炸問題,大幅提升了 RNN 的性能
1.3 改善 RNN/LSTM 的三個技巧
1.3.1 通過堆疊擴充為深度模型
- 如果模型隻有一層,其容量是有限的,無法表示太複雜的映射。但是注意到第 1 節這種序列模型對每一個輸入都可以有一個輸出代表對目前時刻之前的序列内容的聚合,而所有這些輸出可以組合成一個新的序列,這樣一來我們其實可以再把這個新序列輸入參數相同的模型,這樣每個輸出将會是對目前時刻之前的序列内容的進一步聚合,如此反複堆疊就得到了
,其容量更大,能表示的映射關系也更加複雜,如下圖所示Stacked RNN/Stacked LSTM
- 需要注意的是,對于更大容量的模型,需要提供更多的訓練樣本以避免過拟合
1.3.2 使用雙向模型避免遺忘
- 無論 RNN 還是 LSTM,模型都隻能利用隐藏狀态間接地擷取之前序列的資訊,由于隐藏狀态的次元一定遠遠小于之前的變長序列所有樣本的連接配接次元,這種做法無可避免地會損失一些資訊,展現在微觀層面上就是兩個經典問題
-
:參數更新梯度被近期樣本主導(和 MLP 裡的梯度消失不太一樣)梯度消失
-
:參數更新梯度梯度趨近梯度爆炸
這個 RNN 的任務是根據先前序列預測下一個單詞,在 處輸入了 “China”,但由于梯度消失這個資訊幾乎沒法被記住,模型難以在相隔較遠的 處給出 “Chinese” 的預測
這裡模型大概能學到應該輸出一個語言,但是具體是什麼語言會被近期序列的傾向所主導 -
- LSTM 通過引入 “記憶單元” 緩解了這些問題,但依然無法完全解決。一個簡單粗暴的優化方案是直接同時從兩個方向訓練 RNN 或 LSTM,這樣得到的
結構如下Bidirectional RNN/Bidirectional LSTM
- 這樣一來,一個方向的早期樣本就成了另一個方向的近期樣本,可以緩解 RNN/LSTM 的遺忘問題。另外這裡輸出的
1.3.3 使用預訓練模型
- 處理序列資料時往往要做一步 embedding,使得序列樣本變得可以處理。比如常見的文本模型,你沒法直接向網絡輸入一個單詞,這時有幾個做法
- 直接給每個單詞編一個數字,但是這種簡單做法存在問題,比如 “car” 編碼為 1,“cat” 編碼為 2,“red” 編碼為 3,那麼你會發現 “car” + “cat” = “red”,這是不合理的
- 更合理一點的做法是把單詞編碼成 one-hot 向量,但是這樣一來如果單詞數量多的話,輸入的次元就會特别大,導緻處理困難
- 目前通常的做法是先編碼成 one-hot,然後接一個 FC 層把它降維,而且我們希望降維之後的詞向量能夠展現語義上的遠近關系,比如 “汽車” 和 “跑車” 應該在嵌入空間接近;“汽車” 和 “蘋果” 應該在嵌入空間遠離
- 一個問題是,FC 嵌入層往往很大,有時甚至比模型參數還多不少,如果直接把它接在 RNN 或 LSTM 的輸入之前一起訓練,很容易導緻嵌入層過拟合,影響模型性能。這時我們可以先用一個别的任務專門訓練這個 FC 嵌入層,這就是所謂的
過程,幾個注意點是預訓練pre-train
- 預訓練任務必須要有對相同對象做嵌入的嵌入層,而且最好有大資料集
- 預訓練任務可以是不同的任務,但是和目标任務越相關越好,因為學到的詞向量會受到任務性質影響,越相關的任務,後期的調優過程越好做
- 預訓練任務可以使用不同的模型,隻要有 FC 嵌入層就行
過程微調fine-turn
- 所有涉及預處理的部分都可以用類似思路進行預訓練,可以有效提高模型性能
1.4 傳統序列模型任務
- 雖然 RNN 和 LSTM 都能對每個輸入給出一個輸出,但通常還是會用最後一個樣本對應的輸出作為整個序列的特征用于具體任務,是以這些傳統模型自身是傾向
形式的,但是通過巧妙的應用,其能處理的問題涵蓋了 many-to-one
,one-to-many
和 many-to-one
所有情況,下面簡單舉兩個例子進行說明one-to-one
1.4.1 自動文本生成(Autoregress 結構)
- 這個任務隻需要一大段文本作為材料,就能生成出類似風格的句子
- 訓練階段,使用自監督方式構造樣本,每個樣本都是以一段固定長度文本作為輸入序列,其後的詞或字元作為預測标記,直接做多分類監督學習即可。樣本的具體構造方式可以是随機截取,也可以是用一個滑動視窗以一定的偏移量掃過輸入文本材料。注意到這樣的輸入輸出符合
的形式,另外由于輸入長度必須固定,某種程度上也能看做 many-to-one
形式one-to-one
- 預測階段,使用 Autoregress 的方式反複生成單詞或字元,如下圖所示
- 随便給定一個種子向量代表前驅序列特征,再給一個起始 token,模型就能輸出後繼 token 和新的隐狀态,再将後繼 token 作為輸入就能繼續生成,不斷重複下去就能生成無限制長度的序列,這就是所謂的 “Autoregress”。總體上看屬于
形式one-to-many
換個角度看,此模型也可了解為先用序列模型提取前驅序列的特征(RNN/LSTM 視角下就是隐變量),再用這個特征做多分類來選擇生成下一個 token,直接把這兩件事放在一起進行 end-to-end 的訓練
to-one
任務的模型完成 to-many
任務。監督學習模型基本都是 to-one 的,是以借助這個結構,我們甚至可以用 MLP 等八竿子打不着的模型來做文本生成,但由于這些網絡缺乏對于序列任務的歸納偏置,提取序列特征的能力差,效果通常很差。可以參考 序列模型(1)—— 難處理的序列資料 1.4.2 機器翻譯(Encoder-Decoder 結構)
- 機器翻譯的輸入和輸出都是變長度序列,是典型的
問題,機器學習中也稱這種任務為 “Seq2Seq” 任務。為了讓 RNN/LSTM 有能力處理這類問題,模型要設計成特殊的 “編碼器-解碼器架構”。正如其名,這種架構含有兩個元件many-to-many
-
:接受一個長度可變的序列作為輸入, 并将其轉換為具有固定形狀的編碼狀态,即從輸入序列中提取一個特征向量。原始的 RNN/LSTM 可以完成這種 編碼器Encoder
的任務,但特征向量中不可避免地會損失長跨度樣本的資訊,這也是後面出現注意力機制的重要原因many-to-one
-
:将固定形狀的編碼狀态映射到長度可變的序列。這恰好是上面 1.4.1 節那種 解碼器Decoder
任務,是以 Decoder 也可以看做使用 Encoder 編碼特征作為初始種子特征向量的自動文本生成任務。為了能自動控制輸出長度,通常要增加 “起始” 和 “終止” 兩個特殊 tokenone-to-many
-
- 機器翻譯模型設計得很巧妙,通過連接配接
和 many-to-one
的兩個元件真正實作了 one-to-many
任務many-to-many
- 訓練階段,Encoder 和 Decoder 連在一起作為一個 many-to-many 模型訓練。以文本翻譯任務為例,對于資料庫中一個形如
- Encoder 輸入完整的樣本序列,将 “起始” token 輸入 Decoder,預測目标序列第1個 token ,它和标簽序列第1個真實 token 計算損失
- Encoder 輸入完整的樣本序列,将 “起始” token 和标簽序列第1個 token 輸入 Decoder,預測目标序列第2個 token ,它和标簽序列第2個真實 token
- 重複直到标簽序列最後一個 token
- 測試階段,Encoder 輸入待翻譯的句子,Decoder 輸入 “起始” token,讓它如 1.4.1 節一樣自回歸地生成序列,直到輸出 “結束” token 未知
- 訓練階段,Encoder 和 Decoder 連在一起作為一個 many-to-many 模型訓練。以文本翻譯任務為例,對于資料庫中一個形如
- 本節介紹的 “編碼器-解碼器結構” 是序列學習領域的一個重要模型,後面的 transformer 其實也是基于這個架構設計的
2. 注意力機制
2.1 注意力機制 (Attention)
- 正如上文 1.3.2 節的讨論,RNN 和 LSTM 這種傳統序列模型對長跨度序列樣本的特征提取能力有限,很容易出現梯度消失等問題,這個問題即使用雙向模型也無法完全解決。Attention 是針對這個遺忘問題設計的,以機器翻譯任務為例
- 可見當句子長度超過 20 詞時,傳統序列模型翻譯的 BLEU 評分會迅速下降,增加 attention 機制後問題解決
- Attention 機制最早提出于 ICLR 2015 的文章 Neural machine translation by jointly learning to align and translate,這篇文章基于 Encoder-Decoder 結構做機器翻譯任務,它的思想很簡單,Decoder 輸出每個詞時不要再隻依賴一個隐狀态特征向量 ,而是去看完整地看一遍要翻譯的原句,從原句的所有樣本中提取資訊彙聚成上下文向量
Note: 可以作為附加資訊和 一起作為解碼器輸入;也可以完全不用 隻依靠 和輸入 token 進行解碼,因為
- 将這樣的 attention 機制加入 RNN/LSTM Encoder-Decoder 結構,如下
- 相比原始 RNN/LSTM Encoder-Decoder,差别僅在于多了一個上下文向量 作為解碼的附加資訊(下圖顯示了注意力彙聚過程和附加輸入結構,忽略了注意力計算過程)
- 下面不嚴謹地歇寫一下上下文向量的計算過程
- 利用
,計算輸入 Decoder 的隐狀态 和 Encoder 的所有樣本輸出 的注意力評分函數
,這個得分展現的是 和 Encoder 各個輸出 的相關性注意力得分
- 對注意力得分進行 softemax,轉為權重形式
- 利用權重對 Encoder 各個輸出進行彙聚,得到解碼目前 token 所需的
,直接将其作為解碼 時輸入 RNN Decoder 的附加資訊即可上下文向量
- 使用矩陣 對 的次元進行變換,得到
查詢向量query
- 使用矩陣 對 的次元進行變換,得到
鍵向量key
- 使用矩陣 對 的次元進行變換,得到
值向量key
- 利用
- 這裡的 矩陣都是自己學出來的,在彙聚上下文向量 時,通過 計算相關性,對 ,如下圖所示
- 注意力評分函數可以有多種設計
- 原始論文中使用的是
加性注意力additive attention
,它允許 和 是不同長度的向量(事實上原始論文中的 和 就是 和 本身),給定 ,注意力得分為
其中可學習的參數包括
- 現在主流用的是 transformer 使用的
,它的計算效率更高,但要求 和 是相同長度的向量。假設查詢和鍵的所有元素都是獨立的 維随機變量, 且都是均值0方差1,那麼兩個向量的點積的均值為0方差為,将點積除以 使其方差為1,注意力得分為縮放點積注意力scaled dot-product attention
- 原始論文中使用的是
- 引入 attention 機制,要求模型顯式地考慮輸出 token 和輸入句子各個 token 間的相關性,可以解決傳統 Seq2Seq 模型的遺忘問題。通過可視化訓練後的 attention 向量,發現機器确實能夠學到輸入句子和輸出句子各個 token 間合理的相關性
- 但這種能力是有代價的,設輸入序列長 ,輸出序列長 ,對傳統模型引入 attention 機制會使計算複雜複從 大幅上升到
- 值得注意的是,這種和傳統序列模型結合的 attention 在 transformer 語境中被稱為 cross attention,用來強調 attention 計算發生在 Decoder 和 Encoder 之間
2.2 自注意力機制 (Self-Attention)
- Self-Attention 機制提出于 2016 年的論文 Long Short-Term Memory-Networks for Machine Reading,原始的 attention 機制僅能用于 Seq2Seq 模型,而 self-attention 解除了這個限制,可以用于所有的 RNN/LSTM 結構的模型上
- Self-Attention 的思想也很簡單,其實和 2.1 節的在計算上沒有任何差別,隻是原始 attention 機制計算的是輸出句子各個 token 和輸入句子各個 token 之間的 attention;Self-Attention 隻考慮一個句子,它計算自己的各個 token 和該 token 位置之前的其他 token 的 attention,然後基于這個從之前的所有 token 中彙聚上下文 ,直接代替傳統 RNN 中的隐狀态 ,或者作為附加資訊和 拼在一起使用,Self-Attention 和 SimpleRNN 結合的一個簡單示例如下
- 這裡 代表隐狀态,而且在更新隐狀态計算時直接用彙聚向量 代替
- 直覺地看,Self-Attention 就是在更新 RNN/LSTM 隐狀态時顯式地要求模型重新看一遍之前的所有曆史序列樣本,進而避免遺忘,思想本質和 Attention 機制沒有差別,不同之處在于 Self-Attention 的主要目的是用文本中的其它 token 來增強目标 token 的語義表示。從 attention 向量的可視化結果看,Self-Attention 可以避免遺忘,還能幫助傳統 RNN/LSTM 更加關注有相關性的 token
- 值得注意的是,這種和傳統序列模型結合的 self-attention 在 transformer 語境中被稱為 masked self-attention,它其實隻關注了句子中該 token 之前的部分
3. Attention + Encoder-Decoder
- 累了,這塊待續…
3.1 Transformer
- 完全利用 Self-attention、Masked Self-attention、Cross-attention 和 MLP 搭建的 Encoder-Decoder 結構
3.2 GPT
- Transformer 的 Decoder 部分,Autoregress 預訓練,主要用來做文本生成任務,有特征提取能力但是不如 BERT
3.3 BERT
- Transformer 的 Encoder 部分,完形填空+判斷上下句預訓練,主要用來學特征(動态詞嵌入),利用這個特征可以做很多下遊任務