4. 實驗: 手刻 NLP

實驗一: RNN

前往下載ai-lab程式碼,將其貼上google colab進行實驗!

https://github.com/Chen11111112/ai-lab.git

實驗1-1 – 隱藏狀態

在做什麼

  • 用一段中文短文訓練一個迷你 RNN,學習「給前 3 個字,猜第 4 個字」。
  • 訓練完成後,給開頭「老王」,讓 AI 自動續寫 20 個字。

核心概念

  • Embedding:把字轉成向量
  • nn.RNN:依序處理時間步,累積隱藏狀態
  • BPTTloss.backward()):沿時間反向傳播,更新權重 W
  • optimizer.step():根據梯度調整參數

觀察重點

  • 續寫內容是否出現訓練文本中的片語(如「暗巷」「打火機」「微積分」)?
  • 模型能否區分兩條故事線(暗巷 vs 教室)?
  • 序列長度僅 3,模型記憶很短,容易「忘記」更早的上下文。

多執行幾次會發現傳統 RNN 的隱藏狀態(Hidden State)無法在長距離的文本中,精準分配『現在該把注意力放在暗巷還是教室』的權重,這也成了它撞上技術高牆的實證,進而推動了後續 LSTM 門控機制Transformer 注意力機制 的誕生。


實驗1-2 – 梯度消失

在做什麼

  • 與 1-1 相同的 RNN 架構,但將 seq_length3 拉到 30
  • 其餘設定相同,方便對照。

觀察重點

  • 對比 1-1 的續寫品質:長序列下,RNN 是否更難學到有效模式?
  • 生成結果是否更混亂、重複,或無法延續合理語意?
  • 梯度消失(Vanishing Gradient) 的直觀展示:序列越長,早期時間步的梯度在反向傳播中逐漸衰減,模型難以學習遠距離依賴。

對照實驗建議:先跑 1-1,再跑 1-2,比較兩次「AI 續寫」的差異。

因為我們用的是 nn.RNN。當時間軸拉到 30 個字這麼長時,反向傳播的誤差訊號傳到第 5 個字就已經因為「連乘效應」萎縮到變成 0(梯度消失)了。前面的字根本練不到權重,AI 直接放棄思考。


實驗1-3 – LSTM

在做什麼

  • 將 RNN 換成 nn.LSTM,引入細胞狀態(Cell State)與閘門機制。
  • 生成時保留並傳遞 hidden statestate 變數),讓模型跨步驟累積記憶。
  • 開頭線索改為「老王在」(長度 3,符合 seq_length)。

觀察重點

  • 續寫 40 字時,是否能維持較一致的語意走向?
  • 對比 1-1 同樣短序列的 RNN:LSTM 在需要跨多步推理時是否更穩定?
  • 注意生成迴圈中 state 有沒有被正確傳入下一輪——這是 LSTM 與普通 RNN 推理時的關鍵差異。

這如果是原本的普通 RNN,它只要一進入「教室裡」,就再也回不來「暗巷裡」了。但這台迷你 LSTM 它寫完教室的劇情之後,Cell State(記憶高速公路)成功的把「老王還有另一個暗巷劇本」的記憶一路傳了下來,所以它能在後半句成功切換時空,開始寫「老王在暗巷裡點燃了…」。


了了了了了?

那為什麼它會開始像跳針一樣「了了了了了他」?這在 NLP(自然語言處理)裡叫 「過度自信與貪婪解法(Greedy Search)的缺陷」

在我們的程式碼中,有這關鍵的一行:

next_char_id = torch.argmax(pred).item() # 永遠只拿機率最高的那個字
Code language: PHP (php)

因為我們的句子裡出現了非常多次的「了」(點燃、照亮、拿起、難倒)。對 AI 來說,在機率統計上,「了」後面再度接「了」的期望值被嚴重放大了

這也是為什麼現代的 LLM 在生成文字時,會加入一個叫 Temperature(溫度/隨機性) 的參數。


實驗二 – Attention Mechanism

在做什麼

  • 以極小規模的英翻中資料(3 句)訓練 Encoder-Decoder 模型。
  • Decoder 端加入 Attention,讓解碼時能「回頭看」Encoder 各位置的輸出。
  • 訓練過程中,在第 1、5、10、20、40、60、80、100 輪印出翻譯結果,觀察漸進式學習。

核心概念

  • Encoder (GRU):把英文句子壓成隱藏表示
  • Decoder (GRU):逐字產生中文
  • Attentionsoftmax(Q·K^T) 計算權重,加權合併 Encoder 輸出作為 Context
  • <SOS> / <EOS>:標記生成起點與終點

觀察重點

訓練輪次預期現象
第 1 輪輸出接近亂碼或重複字元
第 5–20 輪開始出現正確字詞片段(如「你」「名」「字」)
第 40–100 輪逐漸接近完整譯句,Loss 持續下降
  • 測試句 "What is your name?" 是否最終翻成「你叫什麼名字?」?
  • 觀察 Loss 數值與翻譯品質的對應關係。
  • 可嘗試修改 LEARNING_RATEEPOCHS,觀察收斂速度變化。

【第 1 輪】翻譯 ──> 王壞王累壞館的的的他王王肉育麼肉拍肉拍肉

這時候模型剛初始化,所有的矩陣參數(Linear 層、Embedding 層)裡全是隨機亂數。

  1. 胡亂抓取高頻字: 此時模型還沒有任何「文法」或「對焦」概念。它只注意到數據集裡第 2、3 句有大量關於老王去體育館、累壞了、打球的詞彙。
  2. 長句壓制短句: 因為第 2、3 句的中文長度(15、11 個字)遠大於第 1 句(7 個字),在隨機狀態下,長句子的 Token 產生的梯度影響力在矩陣裡佔了上風。於是模型在解碼時,大腦裡全是第 2、3 句的隨機雜訊,最後拼湊出一串克蘇魯式的胡言亂語。

【第 5 輪】翻譯 ──> 老?壞館館的他

【第 10 輪】翻譯 ──> 老

【第 20 輪】翻譯 ──> 體育館

這是最精彩的階段!隨著 Loss 從 3.65 降到 2.55,神經網路開始發現「只要亂講話,Loss 就會被重罰(CrossEntropyLoss 很高)」。

  1. 第 5-10 輪的「擺爛(過度保守)」: 模型發現多說多錯,但它還沒學會正確翻譯,於是乾脆「少說少錯」。它吐出一個 之後,後面的機率直接撞上 <PAD>(0)或 <EOS>,選擇提早閉嘴。
  2. 第 20 輪的「局部對焦錯誤」: 這行 體育館 是整份日誌的黃金教材!為什麼輸入 What is your name?,AI 會翻成 體育館
    • 來看第 2 句的英文:"He", "played", "badminton", "in", "the", "gym"
    • 來看第 3 句的英文:"the", "gym", "tired", "him."
    • 你發現了嗎?這兩句都有 "the", "gym"。而此時第 1 句是 "What", "is", "your", "name?"
    • 模型的 Attention 搞錯了: 隨機權重在修正時,不小心把第 1 句後段 Padding 的 0(空白)和第 2、3 句的結尾特徵搞混了。此時的 Attention 權重($Q \times K$)錯誤地對焦到了 "gym" 這個字上!既然眼睛盯著 "gym",Decoder 理所當然地吐出了 "體育館"

【第 40 輪】翻譯 ──> 你叫什麼名字?

當 Loss 跌破 1.5 的臨界點(臨界點通常代表模型終於理清了不同句子間的邊界),Adam 優化器終於把 Attention 矩陣(Q, K, V)校準到了正確的空間位置。

  1. 精準語序翻轉(倒裝句成功):此時模型的 Q(目前我想吐出什麼中文)與 K(英文單字特徵)正確碰撞。當解碼器(Decoder)帶有 <SOS> 啟動時,它的 Attention Weights 算出來,最高分不再是隨機亂數,也不是 "gym",而是死死盯著 "your"。因此成功吐出 。接著視線大挪移,瞬間移回句首盯著 "What",吐出
  2. 學會自律閉嘴(<EOS> 發揮作用):更厲害的是,當吐出 之後,因為你在訓練集加了 "<EOS>",模型大腦計算出「這時候該結束了」的機率高達 99%。於是它在第 8 步吐出了 zh_to_int["<EOS>"]。程式碼中的 if next_id == zh_to_int["<EOS>"]: break 瞬間被觸發,迴圈完美中斷。

後面的 60、80、100 輪,雖然輸出的文字一模一樣,但 Loss 從 1.25 一路暴跌到 0.10。這代表 AI 對於「這個字後面該接哪個字、眼睛該看哪裡」的把握度(機率分佈的波峰)變得極度自信與尖銳


實驗三 – Transformer

在做什麼

  • 從維基百科網頁即時爬取前三段文字。
  • 透過 NVIDIA NIM 雲端 API,呼叫 Llama-3.1-8B 進行 2–3 句英文摘要。
  • 體驗工業級 Transformer 的實際應用,與前面本地小模型的差異。

使用前設定

  1. NVIDIA Build 申請 API Key。
  2. 開啟 3-Transformer/1-Transformer.py,將第 16 行的 NVIDIA_API_KEY 替換為你的金鑰:
NVIDIA_API_KEY = "nvapi-你的真實金鑰"Code language: JavaScript (javascript)
  1. 執行後可輸入任意英文維基百科 URL,或直接按 Enter 使用預設的 AI 條目。

觀察重點

  • 爬蟲是否成功抓取段落(注意終端機印出的字元數)。
  • 摘要是否抓住文章核心定義與因果關係(而非逐句複述)?
  • 對比實驗 1、2 的「從零訓練」:雲端大模型展現的是預訓練 + 指令微調後的語意理解能力。
  • 可嘗試不同維基頁面(如 Machine_learningNeural_network),觀察摘要品質變化。

請求與指令傳輸階段(The Instruction Prompt)

在 3-Transformer/1-Transformer.py 中最聰明的一環,是在 client.chat.completions.create 裡面:

  • role: system:我們在這裡賦予了 AI 一個「人格」。我們告訴它:「你是一位傑出的教授,請用 2-3 句話精煉文章,聚焦在因果關係。」這是在設定 Transformer 內部神經元激活的方向(Bias)。
  • role: user:這是「注入食材」。我們將剛才爬蟲抓下來的一大坨文字(input_text)跟指令合併在一起。

矩陣運算與推論階段(The Transformer Reasoning)

當數據送達 NVIDIA 的雲端後,真正的 Transformer 模型就接手了。這發生了什麼事?

  • 向量化(Embedding):模型將這些文字轉換成數千維空間中的「數字向量」。
  • 多頭注意力機制(Multi-Head Attention):這是核心!模型同時計算這 1000 多個字中,每一個字與其他字之間的「權重」。它會發現「人工智慧」、「定義」、「發展」這些詞出現頻率最高,且因果關係最緊密。
  • 概率分佈預測(Probability Distribution):模型不是在「複製」文章,它是在根據剛剛計算出的向量權重,預測下一個最應該出現的字是什麼。它在腦中重新構思了一段新的文字,來回應我們的指令。

到最後一個實驗,其實我們已經做了一個簡單版的 RAG (Retrieval-Augmented Generation,檢索增強生成) !

整個篇章到這裡,相信你也已經是個小白AI工程師了!!!