NLP - Seq2Seq 與 Attention

NLP - Seq2Seq 與 Attention

傳統的 RNN 結構在處理長文本時容易產生記憶喪失,Bahdanau Attention 的誕生成功突破了這個瓶頸。本篇將深入 Seq2Seq 模型架構,探討 Attention 如何賦予 AI 「重點聚焦」的能力,並以 PyTorch 實作英中翻譯與注意力熱力圖視覺化!


在前一章節中,我們認識了如何使用 Word2Vec 將冷冰冰的文字轉換為豐富的語意向量。有了向量之後,我們該如何讓 AI 學會「翻譯語言」、「生成文章」或是「寫出對話」呢?

在 2017 年 Transformer 統治世界之前,自然語言處理 (NLP) 經歷了一場史詩級的架構革命。這場革命的起點,就是為了解決如何讓 AI 進行「序列到序列 (Sequence-to-Sequence)」的變長度轉換。

今天,我們就來解密推倒這堵技術高牆的功臣:Seq2SeqAttention (注意力機制)


一、 Seq2Seq 的瓶頸:把一整本書塞進一個小皮箱

Seq2Seq (Sequence-to-Sequence) 模型最早在 2014 年被提出,它的核心任務是輸入一個長度為 NN 的序列,並輸出另一個長度為 MM 的變長度序列(例如:將一個包含 5 個英文單字的句子,翻譯成包含 3 個中文詞彙的句子)。

在最初期(還沒有引入注意力機制之前),傳統的 Seq2Seq 採用了非常優雅但簡單的 Encoder-Decoder (編碼器-解碼器) 架構:

Loading Diagram...
  • Encoder (編碼器):負責將輸入的源語言句子(例如英文)讀進去,通過 RNN (或 GRU / LSTM),最後將整句話的所有資訊壓縮成一個固定維度的向量,我們稱為 Context Vector (語意上下文向量)
  • Decoder (解碼器):負責拿著這個 Context Vector 作為初始狀態,一個字一個字地吐出目標語言句子(例如中文)。

🚨 傳統架構的致命傷:資訊瓶頸 (Information Bottleneck)

在這個最原始的流程中,你看不到任何「注意力機制」。因為不論句子有多長,Encoder 最後都只丟給 Decoder 一個被強行壓縮的 Context Vector。 想像一下,如果我們要 AI 翻譯一個長達 50 個字的複雜長句。 Encoder 必須強行把這 50 個字的資訊壓縮成一個尺寸固定的向量(例如 128 維)。這就像是要求你把一整本厚厚的小說內容,精簡塞進一個小小的手提皮箱裡,然後丟給 Decoder 要求它還原整本小說的細節。

這會導致嚴重的資訊丟失 (Information Loss),隨句子變長,翻譯品質會呈現斷崖式下跌。


二、 Attention 機制:拯救長句子的「重點聚焦」

為了解決資訊瓶頸,Bahdanau 等人於 2015 年提出了 Attention Mechanism (注意力機制)

Attention 的思想非常符合人類的直覺:我們在看著一句話進行翻譯時,絕對不是先把它背下來,再閉著眼睛默寫出中文。我們是一邊看著英文單字,一邊翻譯對應的中文。

在加入 Attention 之後,Seq2Seq 發生了本質上的改變:

Loading Diagram...

流程圖細節拆解:為什麼翻譯「嗎」的時候,眼睛是盯著「you」?

如果你仔細看上面這張流程圖,可能會產生一個很有趣的疑問:「在英文中,you 代表『你』,不是應該對齊到中文開頭的『你』嗎?為什麼在翻譯句尾的『嗎』時,Decoder 反而把 80% 的注意力投射在 you 上?」

這正是 Attention 最神奇的非字對字死板翻譯(Semantic Alignment)超能力:

  • 英文的問句是靠句首的 "how" 來觸發;而中文的問句則是靠句尾的 "嗎" 來收尾。
  • 當 Decoder 準備吐出句尾的 「嗎」 時,它必須在英文原句中尋找「這句話是在問誰?」的線索。
  • 透過將 80% 的注意力投射在 "you" 上,AI 確認了這是一個「針對你」的問句,從而信心滿滿地決定在句尾輸出疑問助詞 「嗎 ?」

這證明了 Attention 不是簡單的電子字典,而是能理解文法、跨越語序與結構,在語意層面進行對齊的強大機制!

  1. 不再丟棄中間狀態:Encoder 在讀取單字時,會保留每一個時間點的隱藏狀態 (Hidden States hth_t),而不是只保留最後一個。
  2. 動態計算注意力權重:Decoder 在預測下一個詞時,會拿自己當下的狀態去和 Encoder 的每一個 hth_t 進行比對,計算出一個「相似度分數」。
  3. 加權求和:通過 Softmax 將分數轉換為權重,將所有 Encoder 狀態加權求和,得到一個動態的 Context Vector
  4. 精準解碼:此時的 Context Vector 包含了最精準的聚焦資訊,幫助 Decoder 做出最正確的翻譯。

所以 Attention 的重點到底是什麼?

讀到這裡,你也許會問:「AI 把『人工智慧』對齊到『AI』,這不是很理所當然的事嗎?為什麼這會被稱為 NLP 領域的『偉大革命』?」

答案在於,這項機制讓 AI 從「閉卷考試」進化成了「開卷考試」!

1. 舊架構 (沒有 Attention):閉卷考試

  • 做法:Encoder 讀完一句 100 個單字的英文長句後,把書關起來,憑記憶在小紙條上寫下一句「極度簡短的大概大綱」(Context Vector)傳給 Decoder。Decoder 必須完全只看這張大綱紙條,默寫還原出 100 個字的中文翻譯。
  • 結果:句子只要稍微長一點,Decoder 根本想不起第 50 個英文單字細節,只能瞎猜。這就是致命的健忘症(資訊瓶頸)

2. 新架構 (有 Attention):開卷考試

  • 做法:Encoder 讀完英文後,把書攤開放在桌上(保留了每一個字的隱藏狀態)。
    • 當 Decoder 準備吐出 「人工智慧」 時,它抬頭看了一下桌上的英文書,眼睛精準地鎖定了 "ai"
    • 當 Decoder 準備吐出 「強大」 時,它又精準地盯著 "powerful"
  • 結果:因為是「看著原文寫答案」,所以不論句子多長,AI 都不會健忘,翻譯準確度呈現暴發式成長!

這項革命帶來了三個終極優勢:

  1. 長句子的健忘症被徹底治好:AI 翻譯第 100 個字時,可以直接「回頭看」英文原句的第 100 個字。
  2. 跨越語言的「語序倒裝」:不同的語言語法不同。有了 Attention,AI 的眼睛可以「跳著看」(先跳到句子最後面去看動詞,再跳回來看主詞)。注意力地圖上的紫色深淺,就是 AI 眼睛跳動的軌跡。
  3. 它是 ChatGPT (Transformer) 的靈魂核心:這項「眼睛該看哪裡」的機制,就是當今所有大語言模型最核心的底層超能力。

三、 PyTorch 程式實作

我們使用 PyTorch 從頭實作一個具備 Attention (Bahdanau-style) 機制 的 Seq2Seq 翻譯模型。

為了讓程式能夠在本地 CPU 瞬間完成訓練並完美展示效果,我們使用了一個小型英中平行語料庫,讓模型快速收斂。

import torch
import torch.nn as nn
import torch.nn.functional as F

# 1. 定義 Encoder (編碼器)
class Encoder(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(Encoder, self).__init__()
        self.hidden_size = hidden_size
        self.embedding = nn.Embedding(input_size, hidden_size)
        self.gru = nn.GRU(hidden_size, hidden_size, batch_first=True)

    def forward(self, input_seq, hidden):
        embedded = self.embedding(input_seq)
        output, hidden = self.gru(embedded, hidden)
        return output, hidden

# 2. 定義帶有 Attention 的 Decoder (解碼器)
class AttnDecoder(nn.Module):
    def __init__(self, hidden_size, output_size, max_length=15):
        super(AttnDecoder, self).__init__()
        self.hidden_size = hidden_size
        self.output_size = output_size
        self.max_length = max_length

        self.embedding = nn.Embedding(output_size, hidden_size)
        self.attn = nn.Linear(hidden_size * 2, max_length)
        self.attn_combine = nn.Linear(hidden_size * 2, hidden_size)
        self.gru = nn.GRU(hidden_size, hidden_size, batch_first=True)
        self.out = nn.Linear(hidden_size, output_size)

    def forward(self, input_step, hidden, encoder_outputs):
        embedded = self.embedding(input_step) # (1, 1, hidden_size)
        
        # 合併嵌入向量與隱藏狀態,計算注意力權重 (Bahdanau 簡化版)
        concat_features = torch.cat((embedded[0], hidden[0]), 1) # (1, hidden_size * 2)
        attn_weights = F.softmax(self.attn(concat_features), dim=1) # (1, max_length)
        
        # 根據實際輸入句長度截斷
        attn_weights = attn_weights[:, :encoder_outputs.size(1)]
        attn_weights = F.softmax(attn_weights, dim=1)
        
        # 將注意力權重乘以 Encoder 所有狀態,得到動態 Context Vector
        context = torch.bmm(attn_weights.unsqueeze(1), encoder_outputs) # (1, 1, hidden_size)
        
        # 結合嵌入特徵與 Context Vector 送入 GRU
        output = torch.cat((embedded, context), 2) # (1, 1, hidden_size * 2)
        output = self.attn_combine(output)
        output = F.relu(output)
        
        output, hidden = self.gru(output, hidden)
        output = F.log_softmax(self.out(output[:, 0]), dim=1)
        return output, hidden, attn_weights

四、 實作結果與注意力熱力圖視覺化

當我們將英文句子 "ai is powerful ." 送入訓練好的模型進行翻譯時,模型不僅能正確輸出中文 "人工智慧 很 強大 。",還能為我們導出一個注意力矩陣

我們將這個矩陣繪製成熱力圖,可以清晰直觀地看見 AI 翻譯時的「目光焦點」:

Attention Map

(Seq2Seq + Attention 的注意力對齊熱力圖)

真實熱力圖深度解析:

  • 橫軸:輸入的英文序列(Encoder 讀取到的資訊)。
  • 縱軸:輸出的中文序列(Decoder 當下生成的字詞)。
  • 顏色深淺:代表注意力權重的大小(顏色越深,代表當下關聯度越高,最深約 0.40)。
  • 這張真實訓練圖告訴我們的故事
    1. 「人工智慧」完美鎖定 "ai":翻譯第一個詞時,最深紫色的格子極度精準地落在了 ai 的位置,這是一次教科書般的完美語意對齊!
    2. 「很」也盯著 "ai":因為英文 "ai is powerful" 裡並沒有直接對應「很」的單字(這是中文在修飾形容詞時的語法習慣),所以 AI 在翻譯「很」的時候,目光依然鎖定在主詞 ai 上以保持句意連貫。
    3. 「強大」的注意力為什麼分散、甚至飄到了 <EOS>?(超有趣的小模型特點!):這正是這張真實熱力圖最有趣的地方!因為這是一個僅有 8 句話的超迷你玩具模型。AI 雖然很快學會了主詞對齊,但在翻譯句尾的 強大 時,因為 powerful 是最後一個實詞,加上資料量極小,模型產生了輕微的過擬合(Overfitting),AI 已經「迫不及待」地想要結束句子了,因此它的目光提早飄向了句尾結束符號 <EOS>
    4. 「。」精準對齊 <EOS>:當最後要吐出句點時,深紫色的方塊再次精準地落在了 <EOS> 上,完成了句子終止信號的對齊。

這張圖真實地向我們展示了:即使是極度簡單的玩具模型,AI 也已經展現出了強大的跨語言語意對齊能力;同時,熱力圖也是我們 Debug 模型、觀察模型是否過擬合的強大視覺化利器!


總結

Seq2Seq + Attention 的誕生,是 NLP 領域中極具里程碑意義的突破:

  1. 打破空間局限:成功解決了 RNN 長期以來 Context Vector 資訊量不足以應付長句子的致命傷。
  2. 可解釋性大增:注意力熱力圖讓黑盒子模型有了極佳的可解釋性,我們能一眼看出 AI 到底看懂了哪些單字。

然而,Seq2Seq 機制依然有一個巨大的先天缺陷:它是基於 RNN 架構的。 由於 RNN 的計算是「順序性」的(必須算完第一個字才能算第二個字),這導致模型無法進行平行運算,在大數據時代的訓練速度極慢。

為了解決 RNN 無法平行運算的問題,2017 年,一篇名為《Attention Is All You Need》的論文震驚了全世界——他們大膽地將 RNN 徹底丟棄,只保留 Attention 機制,這項劃時代的發明就是「Transformer」。

下一關,我們將正式進入 Transformer 的奇點世界,探討 Self-Attention (自注意力) 的魔力!