【個人上課筆記】2026 TAICA 自然語言處理 - 第六周

Hello Guys I’m LukeTseng. 本篇筆記主要紀錄跟統整上課內容,性質屬於個人學習用途,敬請斟酌參考,謝謝。

清大 MOOCS 2026 TAICA 自然語言處理課程網址:https://mooc.nthu.edu.tw/course/info/470

機器翻譯的重要性

機器翻譯在自然語言處理(Natural Language Processing,NLP)的發展中扮演重要角色,許多 NLP 模型與技術,一開始都是為了解決翻譯問題而發展,例如:

  • 如何理解一句話的完整意思?
  • 如何處理不同語言之間的語序差異?
  • 如何根據上下文選擇正確翻譯?
  • 如何產生長度不同的輸出句子?

以 “HOW DARE YOU!”(你怎麼敢!)作為例子,這個例子看起來簡單,但已經反映出機器翻譯的基本特性:翻譯不是單純將每個英文單字分別換成中文。

例如,若逐字翻譯:

  • How:如何
  • dare:敢
  • you:你

直接組合可能變成「如何敢你」,顯然不符合中文語法,正確翻譯必須先理解整句的語氣和結構,再產生自然的中文句子。

因此,翻譯比較接近以下流程:理解完整輸入句子 → 取得句子的語意表示 → 產生目標語言句子。

即時翻譯與句子翻譯

一般句子翻譯可以先取得完整句子,再開始翻譯;即時翻譯則可能在說話者尚未講完整句子時,就必須開始輸出結果。

例如,只聽到 “I saw the man with…”,此時還不知道後面是:

  • with a telescope:我用望遠鏡看到那名男子
  • with a red hat:我看到那名戴紅帽子的男子

在句子尚未完成前,模型難以判斷前面文字的正確關係。

但在課程中沒有處理如此困難的即時翻譯,而是先將問題設定為:輸入一個完整句子,再產生另一個完整句子。

機器翻譯的主要挑戰:輸入與輸出長度不同

機器翻譯與一般分類任務最大的不同之一,是輸入與輸出的長度不一定相同。

【個人上課筆記】2026 TAICA 自然語言處理 - 第六周 - 機器翻譯的主要挑戰:輸入與輸出長度不同

Image Source:TAICA MOOCs | 自然語言處理 | 高宏宇老師 | 6-1 上課投影片

這三句話的意思大致相同,但經過不同語言的切分後,序列長度完全不同,這代表機器翻譯面對的是 TinputToutputT_\text{input} \neq T_\text{output} 這樣的情形。

  • TinputT_{\text{input}}:輸入序列長度
  • ToutputT_{\text{output}}:輸出序列長度

例如,輸入可能有 3 個 Token,輸出卻可能有 4 個、8 個或 12 個 Token。

Token 數量不是固定的語言特性

在投影片中的 Token 數量主要是為了說明長度差異,實際 Token 數量會受到分詞方法(Tokenization)影響。

例如中文的「請看那邊」可以切成:

  • 字元層級:請/看/那/邊
  • 詞彙層級:請/看/那邊
  • 子詞層級:依模型詞彙表決定。

因此,重點不是某個句子一定有幾個 Token,而是相同意思在不同語言中,通常不會具有完全相同的序列長度。

為什麼不能直接使用一般前饋神經網路?

We cannot just add an FFN at the end.

一般前饋神經網路的輸入與輸出維度通常是事先固定的,例如 xR100FFNyR10\mathbf{x} \in \mathbb{R}^{100} \rightarrow \text{FFN} \rightarrow \mathbf{y} \in \mathbb{R}^{10} 代表模型固定接收 100 個數值,固定輸出 10 個數值。

這種設計適合分類問題,例如輸入一張圖片,輸出 10 個類別的機率(圖片→[貓,狗,鳥,…]),輸出的類別數量固定,因此可以事先決定最後一層需要多少個神經元,但翻譯的輸出不是固定類別,而是一串長度不固定的文字。

像是:

  • 「謝謝」可能只需要產生很短的輸出。
  • 一個長句可能需要產生十幾個 Token。
  • 某些語言可能用較短的句子表達。
  • 某些語言可能需要補充敬語、助詞或語法成分。

所以不能只設計一個固定輸出 10 個位置的網路,並假設所有翻譯結果都是 10 個 Token。

真正需要的是「先理解,再生成」

The hidden state should be utilized to encode the original sequence and it should be passed to the generation process.

意思是模型不能直接把輸入句子的每個字機械式地映射到輸出,而應該分成兩個概念階段:

  1. 讀取並編碼原始句子。
  2. 根據編碼結果產生目標句子。

可以想成真人翻譯的過程,假設有人說:“I went to the bank to deposit some money.”

翻譯者不會聽到 bank 就立刻翻成某個固定中文,而是會先根據後面的 deposit some money,判斷這裡的 bank 是「銀行」,不是「河岸」。

因此模型需要保留目前讀過內容的資訊,這個資訊在 RNN 中主要由隱藏狀態(Hidden State)表示。

概念上可寫成如下公式:$$h_t = f(x_t, h_{t-1})$$

  • xtx_t:第 tt 個輸入 Token
  • ht1h_{t-1}:讀取前一個 Token 後的隱藏狀態
  • hth_t:加入目前 Token 資訊後的新隱藏狀態

如果輸入 “Look over there”,則模型依序處理:$$\begin{aligned} &\text{Look} \rightarrow h_1 \ &(\text{over}, h_1) \rightarrow h_2 \ &(\text{there}, h_2) \rightarrow h_3 \end{aligned}$$

最後的 h3h_3 可以被視為模型讀完整句子後,所形成的壓縮語意表示,但須注意,隱藏狀態不是把原句完整地存放在某個記憶體中,而是將讀過的資訊轉換成一個向量表示。

序列到序列(Seq2Seq)模型的概念

variable-length input sequences to variable-length output sequences.
將可變長度的輸入序列,映射成可變長度的輸出序列。

什麼意思?像是:

  • 機器翻譯:英文句子 → 中文句子
  • 文字摘要:長文章 → 短摘要
  • 對話生成:使用者輸入 → 系統回答

這三種任務的共同點是,輸入與輸出都是序列,而且長度不固定。

Seq2Seq 與 Autoencoder 的關係

一般對於 Autoencoder 的概念是:$$\text{原始輸入} \rightarrow \text{壓縮表示} \rightarrow \text{重建輸出}$$

例如,一個高維度向量經過編碼後,變成較短的表示:xzx^\mathbf{x} \rightarrow \mathbf{z} \rightarrow \mathbf{\hat{x}}

  • x\mathbf{x}:原始輸入。
  • z\mathbf{z}:壓縮後的表示。
  • x^\hat{\mathbf{x}}:重建結果。

而 Seq2Seq 的精神與此類似:$$\text{來源句子} \rightarrow \text{語意表示} \rightarrow \text{目標句子}$$

差異在於,傳統 Autoencoder 的輸入與輸出經常是相同或維度固定的向量;Seq2Seq 處理的是長度可能不同的序列。

例如:[x1,x2,x3,x4]h[y1,y2,y3,y4,y5,y6][x_1, x_2, x_3, x_4] \rightarrow \mathbf{h} \rightarrow [y_1, y_2, y_3, y_4, y_5, y_6] 輸入有 4 個步驟,輸出可以有 6 個步驟,中間的 h\mathbf{h} 則負責承接輸入序列的資訊,這種架構後來通常稱為編碼器—解碼器(Encoder-Decoder)架構:$$\text{Encoder} \rightarrow \text{Representation} \rightarrow \text{Decoder}$$

Seq2Seq 的三種主要應用

1. 機器翻譯

機器翻譯將某種語言的句子轉成另一種語言,模型必須處理這些情形:

  • 語序不同。
  • 詞彙意義不同。
  • 上下文影響。
  • 輸入與輸出長度不同。

因此,機器翻譯是 Seq2Seq 最具代表性的任務。

2. 文字摘要

文字摘要(Text Summarization)將長篇內容濃縮成較短內容,它不是單純刪掉部分句子,而是要理解文章的重要資訊,再用較短文字重新表達。

其序列長度通常要滿足 Toutput<TinputT_\text{output} < T_\text{input},這也顯示 Seq2Seq 不要求輸入與輸出的長度相同。

3. 對話生成

對話生成(Dialogue Generation)會根據一段輸入產生適當回應,使用者問題與系統回答的長度通常不同,所以也適合使用 Seq2Seq。

對話生成的輸入與輸出未必屬於不同語言,但仍是「一段序列轉換成另一段序列」。

序列模型:RNN 與 LSTM

序列模型是專門處理有順序資料的類神經網路架構,例如:

  • RNN(Recurrent Neural Network,循環神經網路)
  • LSTM(Long Short-Term Memory,長短期記憶網路)

文字資料具有明確順序,例如:「狗咬人」與「人咬狗」,即使使用相同的三個字,因為順序不同,意思也完全不同。

模型也不能只知道句子裡有哪些詞,還必須知道它們出現的先後順序。

RNN 如何處理順序?

RNN 會逐步讀入資料:x1,x2,x3,,xTx_1, x_2, x_3, \dots, x_T,每一個步驟都會參考前一步的隱藏狀態 ht=f(xt,ht1)h_t = f(x_t, h_{t-1}),因此,hth_t 不只包含目前輸入 xtx_t 的資訊,也受到先前內容影響。

如何理解 Hidden State?

Hidden State 可以想成模型「讀到目前為止所累積的資訊」,但它不是一般人可以直接閱讀的句子,也不是像資料庫一樣逐字保存內容。

假設隱藏狀態是一個向量:$$h_t = \begin{bmatrix} 0.42 \ -0.18 \ 0.73 \ \vdots \end{bmatrix}$$

我們無法直接說:

  • 第一個數值代表主詞。
  • 第二個數值代表時態。
  • 第三個數值代表語氣。

這些資訊通常是分散式地編碼在整個向量中,因此稱為分散式表示(Distributed Representation)。

較合理的理解是:Hidden State 是模型根據目前已讀序列,計算出的內部語意表示。

它會影響下一個步驟的處理與輸出,但每個維度通常沒有明確、可直接解釋的人類語意。

為什麼每個輸出都依賴前一步?

Each output of a sequential model depends on the hidden state variable provided by the previous step.

以文字生成為例,模型產生後面的詞時,必須知道前面已經產生了什麼,例如已經產生「我今天要去」這句,而下一個詞可能是:

  • 學校、
  • 圖書館、
  • 上課、
  • 買東西等等。

但如果句子換成「我今天已經」,則下一個詞的合理選擇就會不同,所以序列生成不是每個位置獨立預測,而是 yt=g(ht)y_t = g(h_t),而 hth_t 又受到前一個狀態影響(即 ht=f(xt,ht1)h_t = f(x_t, h_{t - 1})),所以目前輸出會間接依賴前面的輸入與狀態。

這種逐步依賴關係,正是 RNN 能夠處理文字順序的原因。

RNN 如何進行序列生成

【個人上課筆記】2026 TAICA 自然語言處理 - 第六周 - RNN 如何進行序列生成

Image Source:https://web.stanford.edu/~jurafsky/slp3/

RNN 序列生成分為四個步驟。

1. Input Encoding:將輸入轉成模型能處理的表示

文字不能直接送入 RNN,必須先經過以下步驟:$$\text{原始文字} \rightarrow \text{Tokenization} \rightarrow \text{Embedding} \rightarrow \text{RNN}$$

例如句子 So long and ...,先切成 Token:[So,long,and],每一個 Token 再轉成 Embedding 向量,例如:esoRd\mathbf{e}_{so} \in \mathbb{R}^d

Embedding 可以是模型自行訓練,也可以用預先訓練好的詞嵌入(Pre-trained Embedding),之後這些向量才會逐一輸入 RNN。

2. Hidden State Update:每一步更新 Hidden State

RNN 在第 tt 個時間步,會同時接收:

  • 目前輸入的 Embedding et\mathbf{e}_t
  • 前一步的 Hidden State ht1h_{t-1}

接著計算新的 Hidden State ht=f(et,ht1)h_t = f(\mathbf{e}_t, h_{t-1}),RNN 處理下一個 Token 時,不只是看到目前的字,也會接收到前面累積的資訊。

這裡的「遞迴」不是模型重新從頭執行,而是把前一步的狀態傳給下一步,如:h1h2h3h_1 \rightarrow h_2 \rightarrow h_3 \rightarrow \dots

3. Output Generation:根據 Hidden State 預測下一個 Token

每個時間步的 Hidden State 會經過輸出層與 Softmax,得到整個詞彙表上的機率分布:$$P(y_t \vert{} h_t) = \text{Softmax}(Wh_t + b)$$

假設詞彙表為 V={So,long,and,goodbye,}V = \{\text{So}, \text{long}, \text{and}, \text{goodbye}, \dots\},則模型可能得到這些機率分布:$$\begin{aligned} &P(y_t = \text{long}) = 0.72 \ &P(y_t = \text{and}) = 0.11 \ &P(y_t = \text{goodbye}) = 0.05 \end{aligned}$$

模型再從機率分布中選擇一個 Token,作為目前輸出。

在投影片使用 Sampled Word,表示模型可以從分布中取樣;實際上也可能直接選擇機率最高的字,但在此節的重點是:RNN 每次只產生一個 Token,而不是一次產生完整句子。

4. Iteration:把輸出送回去,繼續產生下一個 Token

產生一個 Token 後,會將它作為下一個時間步的輸入,再繼續生成,例如:$$\langle s \rangle \rightarrow \text{So} \rightarrow \text{long} \rightarrow \text{and} \rightarrow \dots$$

其中 s\langle s\rangle 是特殊的句子開始 Token(Start-of-Sequence Token)。

具體流程如下:

  1. s預測 So\langle s \rangle \rightarrow \text{預測 So}
  2. 接著將 So 輸入下一步:So預測 long\text{So} \rightarrow \text{預測 long}
  3. 再將 long 輸入下一步:long預測 and\text{long} \rightarrow \text{預測 and}

這是一種逐步依賴先前輸出的生成方式。模型會一直重複,直到達到預先設定的最大長度,或產生句子結束 Token,例如 /s\langle/ s\rangle

一般 RNN 輸出方式為什麼不適合直接翻譯?

【個人上課筆記】2026 TAICA 自然語言處理 - 第六周 - 一般 RNN 輸出方式為什麼不適合直接翻譯?

Image Source:https://web.stanford.edu/~jurafsky/slp3/

RNN 的做法是一個輸入進來,就產生一個對應輸出,這種形式適合命名實體辨識(Named Entity Recognition,NER):每個輸入 Token 都有一個對應標籤,因此可以在讀取該 Token 時立刻輸出。

但機器翻譯不能這樣處理。

假設這句 “The green witch arrived.”,如果模型讀到第一個單字 The 就必須產生第一個西班牙文單字,它尚未看到後面的 green、witch、arrived,模型甚至還不知道句子的完整結構。

更重要的是,不同語言的語序可能不同,投影片中的英文與西班牙文為:

  • the green witch arrived
  • llegó la bruja verde

英文由 S+V 結構形成,而西班牙文譯句則將 arrived 對應的 llegó 放在前面。

因此,第一個目標語言 Token 可能對應來源句子最後面的資訊,模型若邊讀邊翻,就尚未取得產生第一個輸出所需的完整內容。

所以機器翻譯採用的策略是:「先將來源句子全部讀完,再開始產生目標句子。」

Encoder 階段雖然仍可能在每個時間步產生輸出,但這些輸出可以直接忽略,真正需要的是 Hidden State 持續累積來源句子的資訊。

Encoder:先讀完整個來源句子

在上方投影片的左半部,Encoder(即 Source Text 括起來的地方) 依序讀入:$$\begin{aligned} x_1 &= \text{the} \ x_2 &= \text{green} \ x_3 &= \text{witch} \ x_4 &= \text{arrived} \end{aligned}$$

Hidden State 的更新流程為:

h1=f(x1,h0)h2=f(x2,h1)h3=f(x3,h2)h4=f(x4,h3)\begin{aligned} h_1 &= f(x_1, h_0) \\ h_2 &= f(x_2, h_1) \\ h_3 &= f(x_3, h_2) \\ h_4 &= f(x_4, h_3) \end{aligned}

在這個最基本的 Encoder–Decoder RNN 中,最後一個 Hidden State h4h_4 被當作來源句子的整體表示,一般化後可寫成:$$h_n = \text{Encoder}(x_1, x_2, \dots, x_n)$$

hnh_n 也稱為 Context Vector。

Context Vector:Encoder 與 Decoder 的中介資訊

Context Vector(上下文向量)是整個架構最重要的中介物,它負責把來源句子的資訊從 Encoder 傳給 Decoder:$$x_{1:n} \rightarrow \text{Encoder} \rightarrow c \rightarrow \text{Decoder} \rightarrow y_{1:m}$$

在這邊 c=hnc = h_n,也就是直接使用 Encoder 最後一個 Hidden State 作為 Context Vector。

例如這句 “the green witch arrived” 經過 Encoder 後,被壓縮成一個向量:$$c = \begin{bmatrix} c_1 \ c_2 \ \vdots \ c_d \end{bmatrix}$$

Decoder 不直接重新讀取英文原句,而是根據這個向量產生西班牙文譯句,投影片說 Context Vector 包含輸入的所有資訊,更準確地說,它的設計目標是:將 Decoder 需要的來源句子資訊,濃縮在一個固定維度的表示中。

所以它類似 Autoencoder 中間的壓縮表示:$$\text{來源句子} \rightarrow \text{Context Vector} \rightarrow \text{目標句子}$$

Decoder:根據 Context Vector 逐字生成翻譯

Decoder(即上方投影片的 Target Text)接收 Encoder 提供的 Context Vector,接著從特殊 Token s\langle s\rangle 開始生成。

以投影片為例,目標句子為 “llegó la bruja verde”,生成流程如下:

  • 第一步:Decoder 接收 Context Vector cc、開始 Token s\langle s\rangle,預測 y1=llegoˊy_1 = \text{llegó}
  • 第二步:Decoder 接收前一個目標 Token llegó,預測 y2=lay_2 = \text{la}
  • 第三步:輸入 la\text{la} 預測 y3=brujay_3 = \text{bruja},接著依序產生 verde\text{verde},最後產生 /s\langle /s\rangle

完整流程:$$\langle s \rangle \rightarrow \text{llegó} \rightarrow \text{la} \rightarrow \text{bruja} \rightarrow \text{verde} \rightarrow \langle /s \rangle$$

Decoder 可以自行決定輸出長度,只要尚未產生結束 Token 就繼續生成。

訓練 Decoder 時的 Teacher Forcing

Decoder 需要什麼輸入?

Decoder 在第 tt 步要產生 yty_t,通常需要前一個 Token yt1y_{t-1} 作為輸入,但問題是:訓練期間應該輸入哪一個版本?

假設正確目標句子是 llegó la bruja verde,模型在第一步應該預測 llegó,但它可能錯誤預測成其他單字。

此時有兩種選擇:

  1. 把模型自己預測錯的字送入下一步。
  2. 把正確答案 llegó 送入下一步。

Teacher Forcing 採用第二種方式。

也就是在訓練期間,Decoder 的下一個輸入使用真實答案(Ground Truth),而不是模型前一步的預測。

Teacher Forcing 的訓練流程

假設目標序列為 y1,y2,y3,y4y_1, y_2, y_3, y_4,則 Decoder 的輸入與預測關係是:

時間步Decoder 輸入預測目標
1s\langle s\rangley1y_1
2正確的 y1y_1y2y_2
3正確的 y2y_2y3y_3
4正確的 y3y_3y4y_4
5正確的 y4y_4/s\langle/ s\rangle

例如:

時間步輸入正確輸出
1s\langle s\ranglellegó
2llególa
3labruja
4brujaverde
5verde/s\langle/ s\rangle

模型每一步仍會產生預測,並與 Ground Truth 計算 Loss:$$L_t = - \log P(y_t \vert{} y_{<t}, c)$$

整句的 Loss 則是各時間步的總和:$$L = \sum_{t=1}^{m} L_t$$

Teacher Forcing 可比喻成每做完一步,老師就立刻提供正確答案,讓你使用正確步驟繼續往下做。

這能避免模型在訓練初期因第一步犯錯,導致後面所有步驟都接收到錯誤輸入,因此通常會比較容易訓練。

訓練與測試階段的差異

Teacher Forcing 雖然方便訓練,但會造成一個明顯問題:「訓練時有正確答案可以輸入,測試時沒有。」

  • 訓練階段:Decoder 接收正確的前一個 Token yt1Decodery^ty_{t-1} \rightarrow \text{Decoder} \rightarrow \hat{y}_t,即使模型前一步預測錯誤,下一步仍然使用 Ground Truth。
    • 例如模型錯誤預測 y^1y1\hat{y}_1 \neq y_1,但第二步仍輸入正確的 y1y_1
  • 測試階段:實際翻譯新句子時,根本不存在目標答案,因此只能把模型自己產生的 Token 放回去 y^t1Decodery^t\hat{y}_{t-1} \rightarrow \text{Decoder} \rightarrow \hat{y}_t,流程變成:sy^1y^2y^3\langle s \rangle \rightarrow \hat{y}_1 \rightarrow \hat{y}_2 \rightarrow \hat{y}_3 \rightarrow \dots。如果模型第一步預測錯誤,第二步就會接收到錯誤輸入。第二步又可能因此繼續犯錯,使錯誤逐步累積。
    • 例如正確句子為「我今天去學校」,但模型第一步錯誤產生「他」,下一步模型看到的是「他」,後續可能產生「他今天沒有回來」這樣的句子,整個句子便逐漸偏離原本翻譯。
    • 「前面稍微做錯一點,後面就回不來」,指的就是這種序列錯誤累積現象。

Encoder–Decoder RNN 的三個組成部分

1. Encoder

Encoder 接收來源序列 x1:n=(x1,x2,,xn)x_{1:n} = (x_1, x_2, \dots, x_n),並產生一系列 Hidden States h1:n=(h1,h2,,hn)h_{1:n} = (h_1, h_2, \dots, h_n),每一個 hth_t 都是讀到第 tt 個位置後的上下文化表示(Contextualized Representation),也就是 ht=f(xt,ht1)h_t = f(x_t, h_{t-1})

2. Context Vector

Context Vector 是由 Encoder 的 Hidden States 所形成的輸入摘要 c=g(h1,h2,,hn)c = g(h_1, h_2, \dots, h_n),在本段最基本的做法中 c=hnc = h_n,它負責將輸入的核心資訊傳遞給 Decoder。

3. Decoder

Decoder 接收 Context Vector,產生自己的 Hidden States s1,s2,,sms_1, s_2, \dots, s_m,再從每個 Decoder Hidden State 產生輸出 y1,y2,,ymy_1, y_2, \dots, y_m,可表示為:$$\begin{aligned} s_t &= f_{\text{dec}}(s_{t-1}, y_{t-1}, c) \ P(y_t) &= \text{Softmax}(W s_t + b) \end{aligned}$$

其中 mm 不必等於來源序列長度 nnmnm \neq n)。

這就是 Encoder–Decoder 架構能夠處理不同輸入、輸出長度的原因。

Encoder 與 Decoder 不一定只能使用 RNN

Encoder–Decoder 是一種架構思想,不是只能綁定某一種神經網路,但只要某個模型能完成以下任務,就可以替換:

  • Encoder:將輸入轉換成有效的表示。
  • Decoder:根據表示產生輸出序列。

因此,對於 Encoder–Decoder 的抽象流程是:$$\text{Input} \rightarrow \text{Encoder Model} \rightarrow \text{Representation} \rightarrow \text{Decoder Model} \rightarrow \text{Output}$$

另外在課堂上也提到:

  • BERT 主要採用 Encoder 部分。
  • GPT 主要採用 Decoder 類型的生成架構。
  • Transformer 也可以具有完整 Encoder–Decoder 結構。

RNN 的梯度消失與梯度爆炸

為什麼梯度需要連續相乘?

RNN 的訓練使用時間反向傳播(Backpropagation Through Time,BPTT)。

假設最後的 Loss 是 LL,要知道早期 Hidden State 對 Loss 的影響,就必須使用連鎖律(Chain Rule)逐步向前追溯。

例如,hTh_T 對較早的 hth_t 的梯度可以寫成:$$\frac{\partial h_T}{\partial h_t} = \frac{\partial h_T}{\partial h_{T-1}} \frac{\partial h_{T-1}}{\partial h_{T-2}} \dots \frac{\partial h_{t+1}}{\partial h_t}$$

距離目前時間越遠的資訊,需要經過越多次矩陣或導數相乘。

The further back in time an input is, the more factors it needs to be multiplied by.

也就是說,越早期的輸入,在反向傳播時必須穿過越長的計算鏈。

可以把它想成傳話遊戲:

  • 後面的人要把錯誤訊號一路傳回第一個人。
  • 每經過一個人,訊號都可能被縮小或放大。
  • 經過的人越多,最後收到的訊號越可能嚴重失真。

梯度消失是怎麼發生的?

假設每一步的梯度因子大約都是 0.50.5

  • 經過一次時是 0.5
  • 經過兩次是 0.52=0.0250.5^2 = 0.025
  • 經過十次為 0.5100.000980.5^{10} \approx 0.00098
  • 經過二十次:0.5200.000000950.5^{20} \approx 0.00000095

可以看到,雖然每一個因子都不是零,但連續相乘後,梯度會快速趨近於零,這就是梯度消失(Gradient Vanishing)。

一般化來看,若連乘項的大小長期小於 11:$$\left\vert{} \frac{\partial h_k}{\partial h_{k-1}} \right\vert{} < 1$$

則經過許多時間步後:$$\prod_{k=t+1}^{T} \frac{\partial h_k}{\partial h_{k-1}} \rightarrow 0$$

結果是早期時間步幾乎收不到有效梯度。

梯度爆炸是怎麼發生的?

反之,假設每一步的梯度因子大約是 1.51.5

  • 經過十次:1.51057.671.5^{10} \approx 57.67
  • 經過二十次:1.5203325.261.5^{20} \approx 3325.26

因此,只要連乘項的大小長期大於 11,梯度就可能快速膨脹,這就是梯度爆炸(Gradient Exploding)。

一般化表示為:$$\left\vert{} \frac{\partial h_k}{\partial h_{k-1}} \right\vert{} > 1$$

則:$$\prod_{k=t+1}^{T} \frac{\partial h_k}{\partial h_{k-1}}$$

可能迅速變成非常大的值。

梯度消失與梯度爆炸的差別

問題梯度狀態訓練結果
梯度消失梯度趨近於零參數幾乎不更新
梯度爆炸梯度變得極大參數更新過度,訓練震盪

對於梯度消失而言,更新規則如下:$$\theta \leftarrow \theta - \eta \frac{\partial L}{\partial \theta}$$

如果 Lθ0\frac{\partial L}{\partial \theta} \approx 0,那麼參數幾乎不會改變,也就是 θnewθold\theta_{\text{new}} \approx \theta_{\text{old}}

梯度爆炸的話,如果 $$\left\vert{} \frac{\partial L}{\partial \theta} \right\vert{} \gg 1$$

則一次參數更新可能跨得太遠。

模型就有可能出現以下的這些情形:

  1. Loss 劇烈震盪。
  2. 訓練無法收斂。
  3. 參數數值極端增大。
  4. 輸出結果不穩定。

也就是模型訓練時「跳來跳去」(如果看梯度圖的話),難以找到穩定的方向。

為什麼 Seq2Seq 更容易遇到這些問題?

在傳統 RNN 任務中,序列可能只需要處理幾個或十幾個時間步,但在基本 Seq2Seq 中,模型需要經過 整個 Encoder 序列+整個 Decoder 序列

例如:

  • Encoder 讀入 30 個 Token。
  • Decoder 產生 25 個 Token。

整條計算鏈可能橫跨許多時間步。

而基本 Encoder–Decoder 又依賴 Encoder 最後一個 Hidden State 保存來源句子的資訊,若來源序列很長,早期輸入的資訊需要通過很多次 Hidden State 更新,才能影響最後的 Context Vector。

因此,前面詞彙的影響容易逐漸減弱。

例如:「小明在十年前搬到台北,後來經歷許多事情,目前他仍然住在那裡。」這句,模型要理解最後的「那裡」指的是「台北」,必須保留句子前面較遠的資訊。

若早期資訊的梯度已經消失,模型就不容易學會這種長距離依賴(Long-term Dependency)。

梯度消失造成的三種後果

1. Information Loss:資訊遺失

序列越長,較早時間步的資訊越容易被後續運算沖淡,所以模型可能只記得最近的幾個 Token,卻忘記句子開頭的重要內容。

例如:“The book that I bought from the old bookstore yesterday was very interesting.”

模型需要知道 was 的主詞是前面的 The book,而不是距離較近的 bookstore 或 yesterday。

如果模型無法保存較遠資訊,就可能無法掌握這種跨越多個 Token 的關係。

句子讀得太長之後,前面的資訊已經模糊,不知道原本是什麼。

2. Training Instability:訓練不穩定

若早期時間步的梯度消失,與早期輸入相關的參數就很難得到有效更新,結果可能是:

  • Loss 下降非常慢。
  • 訓練停滯。
  • 模型偏向依賴最近資訊。
  • 增加訓練時間也未必有效。

梯度消失使模型訓練不動;梯度爆炸則使模型難以收斂,也就是兩種梯度問題都可能造成訓練品質不佳,但表現形式不同。

3. Difficulty in Generating Long Sequences:難以生成長序列

短句可能只需要保存少量近期資訊,因此基本 RNN 有時還能處理,但在長句生成中,模型必須持續保持:

  • 主題。
  • 主詞。
  • 時態。
  • 語意脈絡。
  • 前後一致性。

若較早資訊逐漸消失,生成結果就可能會有:

  • 前後矛盾。
  • 偏離原本主題。
  • 重複相同內容。
  • 後半段失去意義。

短序列可能還可以,但序列一長,RNN 就較難產生正確且連貫的結果。

LSTM 為什麼被提出?

【個人上課筆記】2026 TAICA 自然語言處理 - 第六周 - LSTM 為什麼被提出?

Image Source:TAICA MOOCs | 自然語言處理 | 高宏宇老師 | 6-3 投影片

長短期記憶網路(Long Short-Term Memory,LSTM)的主要目的,是改善傳統 RNN 的長期資訊保存能力與梯度消失問題。

LSTM 不只是維護一個 Hidden State,而是額外維護一條 Cell State(ctc_t),LSTM 的狀態主要有:

  • Hidden State:hth_t
  • Cell State:ctc_t

可以簡單理解為:

  • ctc_t:較長期、較穩定的記憶通道。
  • hth_t:目前時間步要提供給外部或下一步的表示。

LSTM 再利用數個閘門(Gate),控制哪些資訊要保留、加入或輸出,這些閘門不是人工規定哪個字重要,而是透過訓練自動學習其權重。

LSTM 的核心觀念:資訊不是全部保留

一般 RNN 的資訊傳遞主要是 ht1hth_{t-1} \rightarrow h_t 這樣在傳,而 LSTM 會更進一步判斷:

  • 哪些舊資訊應繼續保存?
  • 哪些舊資訊應該忘記?
  • 哪些新資訊值得寫入?
  • Cell State 中的哪些內容要對外輸出?

可以把它想成學生整理筆記:

  • 不重要的內容刪除。
  • 新的重要內容加入。
  • 真正需要回答問題時,再從筆記中取出相關資訊。

這就是 LSTM 所謂「選擇性保留或捨棄資訊」。

Forget Gate:遺忘閘門

【個人上課筆記】2026 TAICA 自然語言處理 - 第六周 - Forget Gate:遺忘閘門

Image Source:TAICA MOOCs | 自然語言處理 | 高宏宇老師 | 6-3 投影片

投影片使用 RtR_t 表示 Forget Gate:$$R_t = \sigma_r(W_r x_t + U_r h_{t-1} + b_r)$$

  • xtx_t:目前輸入。
  • ht1h_{t-1}:前一個 Hidden State。
  • Wr,Ur,brW_r,U_r,b_r:可訓練參數。
  • σ\sigma:Sigmoid 函數。

Sigmoid 的輸出範圍為 0<σ(z)<10 < \sigma(z) < 1 ,所以 Forget Gate 的每一個維度都會得到介於 0011 之間的值。

Forget Gate 的含義

如果某個維度接近 00,即 Rt(i)0R_t^{(i)} \approx 0,代表該部分舊資訊應被大幅丟棄。

如果接近 11,即 Rt(i)1R_t^{(i)} \approx 1,代表該部分舊資訊應繼續保留。

Forget Gate 會乘上前一個 Cell State Rtct1R_t \odot c_{t-1},其中 \odot 表示逐元素相乘。

例如:$$\begin{aligned} R_t = \begin{bmatrix} 0.9 \ 0.1 \ 0.8 \end{bmatrix} , \qquad c_{t-1} = \begin{bmatrix} 10 \ 10 \ 10 \end{bmatrix} \ R_t \odot c_{t-1} = \begin{bmatrix} 9 \ 1 \ 8 \end{bmatrix} \end{aligned}$$

第一與第三部分大致保留,第二部分則大幅削弱。

以中文斷詞說明 Forget Gate

例如「清華大學」,看到「清」之後,後面的「華」與它形成詞彙「清華」的可能性很高,但在其他句子中,「清」不一定與「華」組合。

因此,模型需要根據目前文字與前文,判斷:

  • 前面的資訊是否仍有助於辨認目前詞彙。
  • 某個位置是否可能是詞彙邊界。
  • 哪些過去資訊不應繼續影響後面的判斷。

Forget Gate 的精神就是讓模型學習:到目前位置時,哪些舊資訊應繼續傳遞,哪些應停止傳遞。

不過,實際訓練後的 Gate 不一定像人類想像得那麼清楚,模型可能有效,但人類未必能直接解釋每個 Gate 為何在某個位置開啟或關閉。

Input Gate:輸入閘門

【個人上課筆記】2026 TAICA 自然語言處理 - 第六周 - Input Gate:輸入閘門

Image Source:TAICA MOOCs | 自然語言處理 | 高宏宇老師 | 6-3 投影片

投影片使用 KtK_t 表示 Input Gate:$$K_t = \sigma_r(W_k x_t + U_k h_{t-1} + b_k)$$

Input Gate 負責決定目前新產生的候選資訊,有多少應該寫入 Cell State。

【個人上課筆記】2026 TAICA 自然語言處理 - 第六周 - Input Gate:輸入閘門

Image Source:TAICA MOOCs | 自然語言處理 | 高宏宇老師 | 6-3 投影片

除了 Gate 外,模型還會計算目前時間步的候選記憶:$$\tilde{c}t = \sigma_c(W_c x_t + U_c h{t-1} + b_c)$$

在常見寫法中,候選記憶通常使用 tanh\tanh:$$\tilde{c}t = \tanh(W_c x_t + U_c h{t-1} + b_c)$$

投影片將 Cell State 更新寫為:$$c_t = R_t \cdot c_{t-1} + K_t \cdot \sigma_c(W_c x_t + U_c h_{t-1} + b_c)$$

其概念可以拆成兩部分:$$c_t = \underbrace{R_t \odot c_{t-1}}_{\text{保留的舊記憶}} + \underbrace{K_t \odot \tilde{c}t}{\text{寫入的新記憶}}$$

所以目前 Cell State 為經過篩選的舊資訊,加上經過篩選的新資訊。

Output Gate:輸出閘門

【個人上課筆記】2026 TAICA 自然語言處理 - 第六周 - Output Gate:輸出閘門

Image Source:TAICA MOOCs | 自然語言處理 | 高宏宇老師 | 6-3 投影片

投影片使用 VtV_t 表示 Output Gate:$$V_t = \sigma_r(W_v x_t + U_v h_{t-1} + b_v)$$

新的 Hidden State 為:$$h_t = V_t \sigma_h(c_t)$$

可理解成:$$h_t = V_t \odot \tanh(c_t)$$

Output Gate 決定 Cell State 中哪些資訊應該形成目前的 Hidden State,傳給下一個時間步或輸出層。

Cell State 可能保存許多長期資訊,但不是每個時間步都需要把全部資訊對外輸出,因此:

  • Cell State 負責儲存。
  • Output Gate 負責控制目前要使用哪些儲存內容。
  • Hidden State 是目前實際對外傳遞的結果。

三種 Gate 的分工

Gate主要問題功能
Forget Gate舊資訊要留多少?選擇性丟棄過去記憶
Input Gate新資訊要寫多少?控制新內容加入 Cell State
Output Gate哪些記憶要輸出?控制 Cell State 到 Hidden State 的資訊流

可以用筆記本來做比喻:

  • Forget Gate:把已經沒有用的舊筆記擦掉。
  • Input Gate:決定哪些新內容值得抄進筆記。
  • Output Gate:回答問題時,決定要把筆記中的哪些內容拿出來。

LSTM 為什麼能改善梯度消失?

LSTM 的重要差異是 Cell State 的更新含有加法:$$c_t = R_t \odot c_{t-1} + K_t \odot \tilde{c}_t$$

相比一般 RNN 每一步反覆進行非線性矩陣轉換,Cell State 提供一條較直接的資訊傳遞路徑。

如果 Forget Gate 接近 11(即 Rt1R_t \approx 1),則:$$c_t \approx c_{t-1} + K_t \odot \tilde{c}_t$$

舊記憶便能較完整地傳到後面。

從直觀上看,資訊不需要在每一步都被徹底重新計算,而可以沿著 Cell State 持續流動,這使得:

  • 梯度較容易往較早時間步傳遞。
  • 模型較能記住長期資訊。
  • 長距離依賴較容易學習。
  • 訓練通常比簡單 RNN 穩定。

LSTM 只能「改善」,不能完全解決

LSTM can partially avoid the vanishing gradient problem.

重點是 partially,也就是「部分改善」,LSTM 並不保證完全沒有:

  • 梯度消失。
  • 梯度爆炸。
  • 長序列資訊遺失。
  • 訓練不穩定。

原因是 LSTM 仍然需要經過許多時間步,用到多個矩陣運算,也用非線性函數,以及依賴上一個時間步的狀態等等。

如果 Gate 長期處在不利的數值範圍,梯度問題仍可能出現,因此 LSTM 只能緩解梯度問題,但沒有徹底消除梯度問題。

另外,LSTM 不一定在所有任務中都比 RNN 好,只是它具有較好的長期資訊控制能力。

LSTM 為什麼更難訓練?

LSTM 比簡單 RNN 多了許多參數,簡單 RNN 主要計算:$$h_t = f(Ux_t + Wh_{t-1})$$

LSTM 則要分別計算:

  • Forget Gate。
  • Input Gate。
  • Output Gate。
  • Candidate Memory。
  • Cell State。
  • Hidden State。

每一個 Gate 都有自己的權重矩陣與偏差,再加上候選記憶的參數,所以 LSTM:

  • 參數量更多。
  • 每個時間步的計算更複雜。
  • 訓練成本高於簡單 RNN。
  • Gate 的實際行為不一定容易解釋。

LSTM 本質上就是增加一些參數,學習如何控制資訊傳遞的開與關。

RNN 與 LSTM 最大的共同問題:無法有效平行化

無論是 RNN 還是 LSTM,都有相同的時間依賴:hth_t 依賴 ht1h_{t-1} ,而對 LSTM 而言,還有 ctc_t 依賴 ct1c_{t-1},因此,模型不能同時直接計算所有時間步。

必須按照順序:$$h_1 \rightarrow h_2 \rightarrow h_3 \rightarrow \cdots \rightarrow h_t$$

因為在 ht1h_{t-1} 尚未計算完成前,hth_t 就缺少必要輸入。

什麼叫做不能平行化?

假設一句話有四個 Token:x1,x2,x3,x4x_1, x_2, x_3, x_4,RNN 必須按照 x1h1x_1 \rightarrow h_1 的方式去做,完成後才能計算 (x2,h1)h2(x_2, h_1) \rightarrow h_2,然後這個完成後才能再去做 (x3,h2)h3(x_3, h_2) \rightarrow h_3,以此類推。

總之就是他這過程就像是 queue 這種資料型態的特性一樣,只有前面做完了後面才能做。

不能直接把四個時間步全部同時送入四個處理單元,因為後面的計算需要等待前面的狀態。

不能平行化造成的實際問題

1. 訓練速度受到序列長度限制

序列越長,需要等待的時間步越多,即使 GPU 有很多核心,也無法完全同時處理所有時間位置。

2. 硬體利用率不佳

GPU 擅長大量矩陣平行計算,但 RNN 的時間相依性會使部分運算必須等待前一步完成,造成硬體資源無法充分利用。

即使在等待,系統時脈與功耗仍在運作,因此整體訓練效率不佳。

3. 大規模訓練受到限制

當資料集很大、模型很大、序列很長時,這種逐步等待會嚴重拖慢訓練。

因此 RNN 和 LSTM 在小型資料上仍可能有效,但在大規模語言模型訓練中會遇到結構上的瓶頸。

LSTM 與 RNN 的比較

特性簡單 RNNLSTM
狀態主要使用 hth_t使用 hth_tctc_t
資訊控制無明確 GateForget、Input、Output Gate
長期依賴較弱較強
梯度消失較嚴重可部分緩解
參數量較少較多
計算成本較低較高
可平行化困難同樣困難
是否完全解決梯度問題仍然沒有完全解決

為什麼要引入 Attention?

傳統 Encoder–Decoder RNN 將整個輸入句子壓縮到最後一個 Hidden State c=hnc = h_n,接著 Decoder 在所有生成步驟都依賴同一個 cc

這種設計存在明顯問題:不論輸入句子有多長,都必須被塞入一個固定維度的向量,當句子很長時,前面資訊容易被壓縮或遺失。

Attention 的想法則是:不必要求最後一個 Hidden State 獨自記住全部內容;原本各個輸入位置的 Hidden State 都保留下來,Decoder 需要時再回頭查看。

也就是不要每次只靠上一個 State 傳過來的資訊,而是直接查看所有輸入字,並學習每個字應該具有多少權重。

因此,對第 ii 個輸出而言,不再固定使用 c=hnc = h_n,而是產生一個專屬於目前輸出位置的 Context Vector cic_i,不同輸出位置的 cic_i 通常不同。

Attention 的核心精神:不同輸出應注意不同輸入

以機器翻譯為例子:

nmt-model-fast

Image Source:https://research.google/blog/a-neural-network-for-machine-translation-at-production-scale/

  • 當 Decoder 產生 Knowledge 時,應該較重視來源句子的:「知、識」
  • 產生 is 時,應該較重視:「就、是」
  • 產生 power 時,應該較重視:「力、量」

因此,每一個目標詞對來源詞的關注程度不同,在概念上可表示成下表:

Decoder 目前輸出可能較重視的來源 Token
Knowledge知、識
is就、是
power力、量

這些對應關係不是人工指定,而是模型根據平行翻譯資料(Parallel Data)訓練出來的。翻譯某個詞時,需要參考哪些來源詞,以及每個詞的比重,都是透過監督式資料學習得到。

Attention 與傳統固定 Context Vector 的差異

傳統 Seq2Seq

Encoder 產生 h1,h2,,hTxh_1, h_2, \dots, h_{T_x},但最後只取 c=hTxc = h_{T_x},所有 Decoder 步驟都依賴相同的 cc:$$c \rightarrow y_1, y_2, \dots, y_{T_y}$$

這像是先把整本課本壓縮成一張摘要,再要求你只依靠這張摘要回答所有問題。

加入 Attention 後

上課投影片

Image Source:TAICA MOOCs | 自然語言處理 | 高宏宇老師 | 6-4 上課投影片

Encoder 仍然產生 h1,h2,,hTxh_1, h_2, \dots, h_{T_x},但不再只使用最後一個 hTxh_{T_x}

Decoder 在產生第 ii 個輸出時,會對所有 Encoder Hidden States 計算權重:$$\alpha_{i1}, \alpha_{i2}, \dots, \alpha_{iT_x}$$

接著得到該時間步專屬的 Context Vector:$$c_i = \sum_{j=1}^{T_x} \alpha_{ij} h_j$$

因此 c1c2c3c_1 \neq c_2 \neq c_3

通常每一個輸出位置都會取得不同的資訊組合。

這就像回答不同題目時,可以重新翻閱整本課本,並針對目前題目查看最相關的段落,而不是永遠只看同一張摘要。

Attention 的三個主要計算步驟

Attention 可以拆成三個步驟:

  1. 計算對齊分數(Alignment Score)。
  2. 使用 Softmax 轉換成 Attention Weight。
  3. 對所有 Encoder Hidden States 做加權總和。

第一步:計算 Alignment Score

對 Decoder 的第 ii 個生成位置,以及 Encoder 的第 jj 個輸入位置,先計算:$$e_{ij} = a(s_{i-1}, h_j)$$

  • si1s_{i-1}:Decoder 前一個時間步的 Hidden State。
  • hjh_j:Encoder 第 jj 個輸入位置的 Hidden State。
  • a()a(\cdot):Alignment Model。
  • eije_{ij}:未正規化的相關程度分數。

eije_{ij} 的意思

eije_{ij} 衡量 Decoder 現在準備產生第 ii 個輸出時,Encoder 第 jj 個位置的資訊有多重要。

分數越高,代表 hjh_j 與目前 Decoder 的生成需求越相關。

例如 Decoder 準備產生 power:

epower,力e_{\text{power,力}}epower,量e_{\text{power,量}} 可能較高,而 epower,知e_{\text{power,知}} 可能較低。

Alignment Model 是什麼?

Alignment Model 是一個可訓練的函數,用來比較:

  • Decoder 目前的狀態。
  • 某個 Encoder 位置的表示。

它不是由人直接寫死的對照表,而是模型的一部分,其參數會在訓練過程中更新。

如果已經理解 Query、Key、Value(QKV),這裡可以粗略想成在比較 Query 與 Key 的相關程度。

但在這邊仍是 RNN Attention,不需要進一步展開 Transformer 的 QKV 計算,只是指出兩者在「比較目前需求與輸入資訊相關性」這一精神上相似。

第二步:將分數轉成 Attention Weight

原始 Alignment Score eije_{ij} 可能是任意實數,因此要經過 Softmax:$$\alpha_{ij} = \frac{\exp(e_{ij})}{\sum_{k=1}^{T_x} \exp(e_{ik})}$$

其中 αij\alpha_{ij} 就是 Attention Weight。

對固定的輸出位置 ii 而言:$$\sum_{j=1}^{T_x} \alpha_{ij} = 1$$

0<αij<10 < \alpha_{ij} < 1 。因此,這些權重可以被理解成目前輸出位置分配給各個輸入位置的注意力比例。

例如:$$\begin{aligned} \alpha_{\text{power,知}} &= 0.02 \ \alpha_{\text{power,識}} &= 0.03 \ \alpha_{\text{power,就}} &= 0.04 \ \alpha_{\text{power,是}} &= 0.06 \ \alpha_{\text{power,力}} &= 0.43 \ \alpha_{\text{power,量}} &= 0.42 \end{aligned}$$

這代表模型產生 power 時,主要關注「力」與「量」,另外這些數值只是示意,並非投影片中的實際模型結果。

第三步:加權總和形成動態 Context Vector

取得 Attention Weight 後,將所有 Encoder Hidden States 加權相加:$$c_i = \sum_{j=1}^{T_x} \alpha_{ij} h_j$$

這稱為加權摘要(Weighted Summarization),假設只有三個 Encoder Hidden States:h1,h2,h3h_1, h_2, h_3

Attention Weight 為:$$\alpha_{i1} = 0.1, \quad \alpha_{i2} = 0.7, \quad \alpha_{i3} = 0.2$$

那麼:$$c_i = 0.1 h_1 + 0.7 h_2 + 0.2 h_3$$

這表示目前生成步驟主要使用 h2h_2 的資訊,但也不是完全忽略其他位置,因此,Attention 不是只選一個輸入位置,而是對全部位置進行不同程度的加權。

完整 Attention 計算流程

在 Decoder 要產生第 ii 個 Token 時:

1. 對每一個 Encoder Hidden State 計算分數

eij=a(si1,hj)e_{ij} = a(s_{i-1}, h_j)

其中 j=1,2,,Txj = 1, 2, \dots, T_x

2. 對所有分數做 Softmax

αij=exp(eij)k=1Txexp(eik)\alpha_{ij} = \frac{\exp(e_{ij})}{\sum_{k=1}^{T_x} \exp(e_{ik})}

3. 產生目前的 Context Vector

ci=j=1Txαijhjc_i = \sum_{j=1}^{T_x} \alpha_{ij} h_j

4. Decoder 根據 Context Vector 產生目前狀態或輸出

si=Decoder(si1,yi1,ci)s_i = \text{Decoder}(s_{i-1}, y_{i-1}, c_i)

接著由 sis_i 預測 yiy_i

為什麼每個 Decoder State 都連到全部 Encoder States?

【個人上課筆記】2026 TAICA 自然語言處理 - 第六周 - 為什麼每個 Decoder State 都連到全部 Encoder States?

Image Source:TAICA MOOCs | 自然語言處理 | 高宏宇老師 | 6-4 上課投影片

最後一張投影片中,每個 Decoder State 都以虛線連接到所有 Encoder Hidden States。這代表當 Decoder 計算某一步的新狀態時,會與所有輸入位置計算 Attention Score,而不是只看某一個固定位置。

例如 sts_t 會查看 h1,h2,,hTxh_1, h_2, \dots, h_{T_x},下一個 Decoder State st+1s_{t+1} 也會再次查看 h1,h2,,hTxh_1, h_2, \dots, h_{T_x},但兩個 Decoder 步驟算出的權重通常不同:αtjαt+1,j\alpha_{tj} \neq \alpha_{t+1,j}

因為 sts_tst+1s_{t+1} 所代表的生成情境不同。

這種機制可概括成:每次要輸出一個詞時,都重新判斷目前應該注意來源句子的哪些詞。

Encoder 為什麼使用雙向 RNN?

Encoder Hidden States 來自雙向 RNN(Bidirectional RNN)。

雙向 RNN 在每個位置保留:

  • 從左到右讀取的資訊。
  • 從右到左讀取的資訊。

因此,第 jj 個位置的表示可寫成:hj=[hj;hj]h_j = [\overrightarrow{h_j}; \overleftarrow{h_j}],這表示 hjh_j 不只知道前面的詞,也包含後面詞的資訊。

例如來源句子:「他去銀行提款」,「銀行」的表示可以同時參考:

  • 前面的「他去」。
  • 後面的「提款」。

如此一來,Decoder 在對某個來源位置計算 Attention 時,使用的是較完整的上下文化表示。

Attention 如何改善固定資訊瓶頸?

傳統架構要求資訊必須經過這樣的流程:$$x_1 \rightarrow h_1 \rightarrow h_2 \rightarrow \dots \rightarrow h_{T_x} \rightarrow \text{Decoder}$$

來源句子開頭的資訊若要影響 Decoder,必須沿著很多時間步傳遞到最後的 hTxh_{T_x}

而 Attention 則建立了較直接的路徑:$$h_j \rightarrow c_i \rightarrow s_i$$

因此,即使 hjh_j 位於句子前面,Decoder 仍可以直接對它給予較高權重,而不必完全依賴最後一個 Hidden State 是否成功保留該資訊。

這能減輕這些問題:

  • 長句資訊被過度壓縮。
  • 早期資訊難以影響輸出。
  • 所有輸出共用同一個 Context Vector 的限制。

Attention Weight 圖如何閱讀?

上課投影片

Image Source:TAICA MOOCs | 自然語言處理 | 高宏宇老師 | 6-4 上課投影片

翻譯系統常會將 Attention Weight 視覺化,看到圖中:

  • 一側是來源句子。
  • 另一側是目標句子。
  • 連線代表兩個位置之間的 Attention。
  • 線條越粗或顏色越深,代表權重越大。

例如:Knowledge知、識\text{Knowledge} ↔ \text{知}、\text{識} 連線可能較明顯。

這種圖不是人事先畫出的翻譯規則,而是將模型學到的 αij\alpha_{ij} 視覺化。

圖中的線條粗細,實際上對應 αij\alpha_{ij} 的大小。程式可根據 Attention Weight 自動畫出不同粗細的連線,說明這些權重能被視覺化觀察。

Attention 並不代表完全忽略其他詞

「注意某些詞」容易被誤解為模型只選取一個詞,但公式是:$$c_i = \sum_{j=1}^{T_x} \alpha_{ij} h_j$$

所以通常所有輸入位置都可能參與,只是比例不同。

例如 αi1=0.7,αi2=0.2,αi3=0.1\alpha_{i1} = 0.7, \quad \alpha_{i2} = 0.2, \quad \alpha_{i3} = 0.1 代表主要注意第一個位置,但第二與第三個位置仍有影響。

因此,Attention 更接近「對所有輸入資訊重新分配重要程度」,而非硬性挑出唯一一個輸入詞。

Attention with RNN 仍不能完全平行化

Attention 提供可平行化的語言模型解法,但老師特別補充:

最早期的 Attention 仍然建立在 RNN 上,所以還是必須先得到各個 RNN Hidden State,並沒有真正解決時間序列的平行化問題。

Encoder 仍必須依序計算 h1h2hTxh_1 \rightarrow h_2 \rightarrow \dots \rightarrow h_{T_x},Decoder 也仍必須依序計算 s1s2sTys_1 \rightarrow s_2 \rightarrow \dots \rightarrow s_{T_y},Attention 所做的是在每個 Decoder 步驟中,查看所有 Encoder Hidden States:$$s_i \longleftrightarrow {h_1, h_2, \dots, h_{T_x}}$$

但這些 hjh_j 本身仍然需要先由 RNN 依序計算完成。

所以應區分:

  • Attention with RNN
    • 改善固定 Context Vector 的資訊瓶頸。
    • 每次輸出可查看所有 Encoder States。
    • 仍依賴 RNN 的循序計算。
    • 尚未真正達到完整平行化。
  • Attention without RNN
    • 不再依賴 RNN 逐步傳遞 Hidden State。
    • 才進一步提供較強的平行運算能力。
    • 屬於後續課程範圍。

早期 Attention 雖然把 RNN 的精神推到極致,讓 Decoder 查看所有 Hidden States,但仍必須等待那些 Hidden States 完成,因此尚未真正平行化。

Attention 的時間與空間取捨

傳統 RNN 每一步主要依賴前一步狀態,因此不必同時比較所有位置,Attention 則需要:

  • 保存所有 Encoder Hidden States。
  • 對每個 Decoder 步驟計算全部輸入位置的分數。
  • 保存或處理相應的 Attention Weight。

假設來源序列長度為 TxT_x,目標序列長度為 TyT_y,就會形成 Ty×TxT_y \times T_x 組注意力關係。

例如 Tx=100,Ty=80T_x = 100, \qquad T_y = 80,需要計算約 1000×80=80001000 \times 80 = 8000 個對齊分數。因此,Attention 用較高的記憶體與計算空間,換取直接查看全部輸入位置的能力。

Attention Weight 的矩陣視覺化

螢幕擷取畫面 2026-07-23 221924

Image Source(Neural Machine Translation by Jointly Learning to Align and Translate):https://arxiv.org/abs/1409.0473

第一張投影片展示兩個機器翻譯的 Attention 矩陣,矩陣的兩個方向分別放置:

  1. 來源語言(Source Text)的 Token。
  2. 目標語言(Target Text)的 Token。

矩陣中的每一個格子代表某一組來源詞與目標詞之間的 Attention Weight。

假設 αij\alpha_{ij} 表示生成第 ii 個目標詞時,模型對第 jj 個來源詞配置的注意力權重,那麼矩陣中第 ii 列、第 jj 欄的亮度就對應 αij\alpha_{ij} 的大小。

可以簡單理解成:

  • 顏色越亮:模型越重視這組詞的對應關係。
  • 顏色越暗:這個來源位置對目前輸出的影響較小。

這些圖就是把模型內部學到的 Attention Weight 印出來,因此可以直接觀察模型在翻譯某個詞時,主要參考來源句子的哪些位置。

為什麼矩陣通常呈現對角線?

投影片中的兩組翻譯大致呈現一條由左上往右下延伸的亮色對角線,這表示翻譯關係大致遵循:$$\begin{align*}
x_1 &\leftrightarrow y_1 \
x_2 &\leftrightarrow y_2 \
x_3 &\leftrightarrow y_3
\end{align*}$$

也就是來源句子前面的詞,通常對應目標句子前面的詞;來源句子後面的詞,也通常對應目標句子後面的詞。

投影片中的語言屬於較接近逐詞翻譯的拼音語系,因此語序相對接近,Attention Weight 才容易形成對角線。

例如英文:“The agreement on the European Economic Area was signed in August 1992.”,與對應的法文句子在多數詞彙上的順序相近,所以亮點會大致沿著對角線排列,但這不代表每個目標詞只能對應一個來源詞。

某個目標詞可能同時參考附近數個來源位置,因此在主要亮點旁邊,仍可能出現較淡的亮色區塊。

若語言語序不同,矩陣不一定是對角線

如果來源語言與目標語言的語序差異較大,例如翻譯成中文,Attention 矩陣未必會形成如此整齊的對角線。

原因是翻譯並不一定依照原句順序逐詞輸出。

例如來源句子的某個後段詞彙,可能必須在目標語言中提前產生,此時較高的 Attention Weight 就可能出現在偏離對角線的位置。

因此,Attention 矩陣同時可以呈現:

  • 詞彙對齊。
  • 片語對齊。
  • 語序交換。
  • 一對多或多對一的翻譯關係。

這裡的重點不是矩陣一定要有對角線,而是亮色位置反映模型在每個輸出步驟實際重視的輸入位置。

Attention Weight 與模型可解釋性

在傳統 RNN 中,資訊不斷被壓縮進 Hidden State,即使模型得到正確結果,也很難直接判斷某個輸入詞對輸出的影響有多大。

加入 Attention 後,模型會產生明確的權重 αij\alpha_{ij},因此,研究者可以將這些權重畫成:

  • 熱度圖(Heatmap)。
  • 連線圖。
  • 權重矩陣。
  • 不同粗細的詞彙對應線。

這使 Attention 在論文分析、模型結果展示與模型可解釋性方面相當有用,若模型翻譯正確,就可以進一步查看它是否確實關注了合理的來源詞。

不過,在這段是主要把 Attention Weight 當作觀察模型內部運算的方法,沒有進一步討論其他可解釋性技術。

為什麼要提出 Attention without RNN?

早期 Attention 仍然依附在 RNN Encoder–Decoder 上。

即使 Decoder 可以查看全部 Encoder Hidden States,這些 Hidden States 仍必須由 RNN 依序計算 h1h2hnh_1 \rightarrow h_2 \rightarrow \cdots \rightarrow h_n,因此仍需等待前一個狀態 ht=f(xt,ht1)h_t = f(x_t, h_{t-1})

也就是說,Attention with RNN 雖然改善了資訊存取方式,卻沒有移除 RNN 的循序依賴。只是多看了所有詞、額外計算了權重,但仍然要等待 RNN 的 Hidden State,所以還沒有真正平行化。

因此,後續研究開始思考:既然 Attention 已經能直接計算各個位置之間的相關程度,是否可以完全不要 RNN?這就是 Attention without RNN 的出發點。

RNN 所做的事情能否用其他方法完成?

RNN 的主要功能是處理序列資訊,它透過 ht1hth_{t-1} \rightarrow h_t 將前面的內容傳給後面,使模型知道詞彙順序與上下文。

但這種方式有兩個問題:

  • 第一,必須逐步等待,無法同時計算不同時間位置。
  • 第二,長序列需要經過很多次狀態傳遞,增加計算與訓練困難。

Attention without RNN 的想法是,不再依靠前一個 Hidden State 傳遞資訊,而是讓每個位置直接與其他位置計算關聯。

所以也就不再需要 ht1hth_{t-1} \rightarrow h_t 做傳遞,而是直接計算「目前位置⟷所有相關位置」,這樣就能避免時間步之間必須依序等待的結構。

Q、K、V 的基本來源

公式:$$\begin{align*}
Q &= W_Q y \
K &= W_K x \
V &= W_V x
\end{align*}$$

  • xx:輸入端表示。
  • yy:目前輸出端或查詢端的表示。
  • WQW_Q:產生 Query 的可學習矩陣。
  • WKW_K:產生 Key 的可學習矩陣。
  • WVW_V:產生 Value 的可學習矩陣。

這三個矩陣都是模型參數,會隨訓練資料更新。

輸出端使用 Query,去檢索輸入端的 Key 與 Value。

可寫成這樣的表示方式:$$\begin{align*}
\text{Output representation} &\rightarrow Q \
\text{Input representation} &\rightarrow K, V
\end{align*}$$

與前一段 RNN Attention 對照:原本是 Decoder Hidden State 與 Encoder Hidden State 計算 Alignment Score;移除 RNN 後,則改由可學習矩陣直接產生 QQKKVV

Query:目前想尋找什麼?

Query 可以理解為:目前輸出位置想從輸入中找什麼資訊。

例如模型目前準備產生翻譯中的某個詞,Query 就代表目前生成階段的資訊需求。

在資訊檢索的比喻中,Query 就像使用者輸入的搜尋條件。例如使用者搜尋:「清華大學研究所招生」這串查詢表達的是使用者現在想找到的內容。

同樣地,在 Attention 中,QQ 表達目前輸出位置想要尋找哪一類資訊。

Key:用來比對是否相關的索引

Key 可以理解為:每一個輸入位置提供給 Query 比對的特徵。

用「檢索」來說明 Key。每個輸入 Token 都會透過 WKW_K 產生自己的 Key:ki=WKxik_i = W_K x_i,Query 會與所有 Key 比較:qk1,qk2,,qkNq^\top k_1, \quad q^\top k_2, \quad \ldots, \quad q^\top k_N

哪個 Key 與 Query 越相似,表示對應輸入位置越符合目前需求。

因此,Key 不一定是最後真正傳給輸出的內容,而是用來判斷哪個位置值得被注意?

Value:真正要取出的內容

Value 是每個輸入位置真正提供給輸出的資訊,每個輸入 Token 透過另一個矩陣產生 vi=WVxiv_i = W_V x_i,當 Query 與 Key 比較後,模型得到 Attention Weight αi\alpha_i,接著用這些權重對 Value 做加權總和:$$y_{\text{output}} = \sum_{i=1}^{N} \alpha_i v_i$$

因此:

  • Key 用來判斷相關性。
  • Value 是最後被取用並整合的內容。

可將其比喻成資料庫:

  • Query:查詢條件。
  • Key:索引欄位。
  • Value:查詢命中後取出的實際資料。

為什麼 Key 與 Value 要分開?

同一個輸入 xix_i 會分別經過兩個矩陣:$$\begin{align*}
k_i &= W_K x_i \
v_i &= W_V x_i
\end{align*}$$

原因是「用來比較是否相關的特徵」與「真正要傳遞的內容」不一定相同。

例如一本書的檢索系統書名、關鍵字可用來搜尋,書籍全文才是實際要讀取的內容。

Key 類似可搜尋的索引;Value 類似命中後取出的實際內容。

所以模型可以分別學習:

  • WKW_K:哪些資訊適合用來判斷相關性。
  • WVW_V:哪些資訊應該真正提供給輸出。

這些矩陣在一開始未必具有合理功能,而是隨著監督式訓練逐漸學會適當的轉換方式。

Q 與 K 如何計算相似程度?

用內積:QKQ^\top K

如果只有一個 Query qq 與多個 Key:k1,k2,,kNk_1, k_2, \dots, k_N,則每個位置得到一個分數:qkiq^\top k_i,這個分數用來表示目前 Query 與第 ii 個 Key 的匹配程度,分數較高,代表該位置更可能與目前輸出需求相關。

將所有分數做 Softmax 後得到:$$\alpha_i = \text{Softmax}\left(\frac{q^\top k_i}{\sqrt{d}}\right)$$

所有 αi\alpha_i 形成目前 Query 對全部輸入位置的注意力分配。

Scaled Dot-Product Attention

Attention Score:$$\alpha_i = \text{Softmax}\left(\frac{Q^\top K}{\sqrt{d}}\right)$$

這個式子能拆成四個步驟:

第一步:Query 與 Key 做內積

QKQ^\top K 得到相關程度分數。

第二步:除以 d\sqrt d

QKd\frac{Q^\top K}{\sqrt{d}} 進行尺度調整。

第三步:使用 Softmax

αi=Softmax(QKd)\alpha_i = \text{Softmax}\left(\frac{Q^\top K}{\sqrt{d}}\right) 將分數轉成注意力權重。

第四步:對 Value 加權求和

youtput=i=1Nαiviy_{\text{output}} = \sum_{i=1}^{N} \alpha_i v_i

所以整體的流程如下:Q 與 K 比較 → 取得權重 → 使用權重整合 V

為什麼要除以 d\sqrt d

假設 Query 與 Key 都有 dd 個維度:$$Q = [q_1, q_2, \ldots, q_d]$$$$K = [k_1, k_2, \ldots, k_d]$$

它們的內積為:$$Q^\top K = \sum_{i=1}^{d} q_i k_i$$

假設各維度經過適當正規化,單一乘積項的變異數約為 11,當 dd 個項目相加時,總變異數會隨維度增加:$$\text{Var}(Q^\top K) = \text{Var}\left(\sum_{i=1}^{d} q_i k_i\right) = d$$

也就是 Var(QK)=d\text{Var}(Q^\top K) = d

因此,當向量維度越大,內積結果的數值尺度也會越大。

除以 d\sqrt d 後的變異數

若將內積除以 d\sqrt d:$$\frac{Q^\top K}{\sqrt{d}}$$

則其變異數為:$$\text{Var}\left(\frac{Q^\top K}{\sqrt{d}}\right) = \frac{\text{Var}(Q^\top K)}{(\sqrt{d})^2}$$

代入:$$\text{Var}(Q^\top K) = d$$

最後可得:$$\text{Var}\left(\frac{Q^\top K}{\sqrt{d}}\right) = \frac{d}{d} = 1$$

所以除以 d\sqrt d 的目的,是讓不同維度下的 Attention Score 維持較穩定的尺度。

此為正規化過程,希望在訓練時讓數值維持適當的平均值與變異數,而不是隨著維度 dd 增加而不斷放大。

Attention without RNN 如何平行化?

對每個輸入位置 x1,x2,,xNx_1, x_2, \dots, x_N 可以直接同時計算 $$\begin{align*}
k_i &= W_K x_i \
v_i &= W_V x_i
\end{align*}$$

這些計算不需要 hi1h_{i-1},也就是第 ii 個位置不必等待第 i1i-1 個位置完成。所有 Key 與 Value 可以透過矩陣運算一次計算:$$\begin{align*}
K &= XW_K \
V &= XW_V
\end{align*}$$

Query 也能以相同方式產生 Q=YWQQ = YW_Q,接著一次完成 QKQK^\top

「全部的 operation 一次算」,就是將各位置的運算整理成大型矩陣運算,而不是像 RNN 一樣逐步等待。

移除 RNN 不代表運算量很低

Attention without RNN 雖然可以平行處理不同位置,但並不表示它不需要大量運算,每一個 Query 都要與所有 Key 計算相關程度。

假設序列長度是 NN,則會形成約 N×NN \times N 組位置關係。

這表示序列越長,需要計算的 Attention Score 越多。

此外,模型還需要儲存這些資訊:

  • Query。
  • Key。
  • Value。
  • Attention Score。
  • Attention Weight。
  • 加權後的輸出。

所以 Attention 的優點不是「完全沒有成本」,而是將原本循序等待的計算,轉換成適合大量矩陣平行運算的形式。

現代模型加速研究經常會特別針對 Attention 運算進行最佳化,因為這一部分的計算量並不低。

用空間換取時間

Attention without RNN 可概括為電腦科學中常見的「用空間換取時間」的做法,有關這種做法,可以參考很知名的 DP 演算法,也是用空間換取時間的例子。

傳統 RNN 不必一次保存所有位置之間的關係,但必須依序等待 h1h2hNh_1 \rightarrow h_2 \rightarrow \dots \rightarrow h_N;Attention 則同時查看多個位置 QKQK^\top,因此需要較大的記憶體來保存矩陣與中間運算結果,但可以更充分利用 GPU 的矩陣平行運算能力。

兩者可以對照為:

架構計算方式主要代價
RNN沿時間步循序計算等待時間長、難以平行化
Attention without RNN同時計算位置間關係記憶體與矩陣運算量較大

QKV 如何透過訓練形成?

WQW_QWKW_KWVW_V 一開始通常只是尚未學會任務的參數,模型透過監督式資料進行訓練後,這三個矩陣逐漸學會不同角色。

  • WQW_Q 將目前輸出表示轉換成:現在需要尋找什麼資訊?
  • WKW_K 將輸入表示轉換成:哪些特徵適合用來判斷與 Query 是否相關?
  • WVW_V 將輸入表示轉換成:如果這個位置被判定重要,實際應該提供哪些資訊?

因此,QKV 並不是人工規定好的語意欄位,而是透過資料與 Loss 學習形成的表示。

若要真正理解這些矩陣與 Attention Weight 的變化,最好實際執行模型,將訓練過程中的中間值輸出觀察。

架構演進

RNN

以 Hidden State 逐步處理序列 ht=f(xt,ht1)h_t = f(x_t, h_{t-1}),主要限制是梯度問題與無法有效平行化。

LSTM

利用 Cell State 與 Gate 緩解傳統 RNN 的梯度問題,但仍然必須循序計算。

Attention with RNN

保留所有 Encoder Hidden States,讓 Decoder 在每個生成步驟重新分配注意力權重。

它改善固定 Context Vector 的資訊瓶頸,但底層仍依賴 RNN,因此尚未解決平行化問題。

Attention without RNN

不再透過上一個 Hidden State 傳遞資訊,而是直接以 QKV 計算不同位置的相關性:$$\text{Attention}(Q, K, V) = \text{Softmax}\left(\frac{QK^\top}{\sqrt{d}}\right)V$$

這使各位置的表示可以使用矩陣運算平行計算,同時也增加記憶體與 Attention 計算成本。