← LING5006 講義 Lectures Lecture 04 · ELI5

Week 4 · ELI5 · Sep 30, 2026 · Shu-Kai

梯度是什麼?
從蒙眼下山到邏輯迴歸

循序漸進指南 · 對照 Jurafsky 的投影片

梯度只是一張清單:每個旋鈕該往哪個方向轉、轉多少。邏輯迴歸的梯度推到最後只剩一句話:預測錯多少,就乘上輸入,往反方向修一點。

導讀

初學者卡關,通常不是因為哪一步特別難,而是微積分符號、向量、連鎖律三件新東西同時出現。這份文件把它們拆成十站,每站只多一件新東西,並配一個生活比喻。

站這站多學的一件事比喻
1梯度下降的想法蒙眼下山
2導數推一下,變多少
3偏導數與梯度調音台上的一排旋鈕
4學習率步伐大小
5損失函數要下的是哪座山
6連鎖律傳話接力
7推導三小段相乘
8手算一次走完第一步
9Python 驗證用電腦對答案
10SGD 與 mini-batch從一句話到一疊句子

先備知識只有兩樣:國中的「\(\text{斜率}=\text{高度變化}\div\text{水平變化}\)」,以及看得懂簡單的 Python。不需要先修微積分,每個公式都在用到的那一站才出現。

第一站:蒙眼下山

梯度下降就是:看不見整座山,只能用腳感覺腳下往哪邊斜,往下坡跨一小步,重複到再也踩不出更低的地方為止。

講義第 52 頁用河谷地圖來說明:環顧四周,找最陡的下坡方向,往那裡走。把這個比喻對應到模型上:

登山的你模型裡的東西
你站的位置目前的權重,例如 \(w=0\)
腳下的海拔損失(loss),模型現在錯得多離譜
山谷最低點最好的權重,錯得最少
腳底感覺到的傾斜梯度
跨出去的一步一次參數更新

為什麼一定要「蒙眼」?因為真實模型的權重成千上萬,損失地形是高維空間裡的曲面,沒有人能把整座山畫出來看。我們唯一能做的,是在目前這一點量出坡度。梯度就是這個量測結果。

邏輯迴歸有個好消息:它的損失地形是凸的(convex),像一只碗,只有一個最低點(講義第 53 頁)。不管從哪裡出發,一路往下坡走終究會到碗底。神經網路的地形崎嶇得多,有許多小窪地,那是之後的故事。

第二站:斜率,就是「推一下,變多少」

導數(derivative)回答一個很樸素的問題:把 \(w\) 往右推一點點,損失會跟著變多少?

先拿一座最簡單的山來練習,這只碗的底在 \(w=3\):

\[ L(w) = (w - 3)^2 \]

假設你現在站在 \(w=1\),親手「推一下」:

  1. 此刻的損失:\(L(1)=(1-3)^{2}=4\)。
  2. 往右推 0.01,走到 \(w=1.01\):\(L(1.01)=(-1.99)^{2}=3.9601\)。
  3. 損失變化 \(= 3.9601-4=-0.0399\)。
  4. 每推一單位的變化 \(= -0.0399\div 0.01\approx -4\)。

這個 \(-4\) 就是 \(w=1\) 這一點的斜率。推的距離越小,比值就越接近真正的導數:

\[ \frac{dL}{dw} = \lim_{h \to 0} \frac{L(w+h) - L(w)}{h} \]

對 \((w-3)^{2}\) 而言,微積分給的捷徑公式是 \(\mathrm{d}L/\mathrm{d}w=2(w-3)\),代入 \(w=1\) 正好是 \(-4\)。捷徑只是省去一再試推的麻煩,意思完全一樣。

斜率的正負號就是方向指示:

斜率意思該怎麼走
負,例如 \(-4\)往右推,損失下降\(w\) 變大
正往右推,損失上升\(w\) 變小
0平地,推了沒變化到了,停下

三種情況濃縮成一條規則:永遠朝斜率的反方向走。寫成更新公式(講義第 58 頁):

\[ w^{t+1} = w^{t} - \eta \, \frac{dL}{dw} \]

減號就是「反方向」,\(\eta\)(讀作 eta)是步伐大小,第四站再談。以 \(\eta=0.1\) 試走一步:\(w=1-0.1\times(-4)=1.4\),比原本更靠近谷底的 \(3\)。

虛線是目前這一點的切線,傾斜程度就是斜率。往谷底走,切線越來越平,到谷底時斜率變成 0。

第三站:一整排旋鈕,偏導數與梯度

模型不只一個 \(w\),而是一整排權重,外加一個偏差 \(b\)。梯度就是「每個旋鈕各自的斜率」排成的一張清單。

想像錄音室的調音台:一排旋鈕共同決定聲音好不好聽(損失)。要知道某一顆旋鈕的影響,做法是其他旋鈕全部按住不動,只轉這一顆一點點,聽聲音變好還是變差。這個「只動一顆」的斜率叫做偏導數(partial derivative),符號從 \(\mathrm{d}\) 換成 \(\partial\)。

用兩顆旋鈕的碗試算,谷底在 \((w_1,w_2)=(3,-1)\):

\[ L(w_1, w_2) = (w_1 - 3)^2 + 2\,(w_2 + 1)^2 \]

現在站在 \((1,\,0)\):

  1. 按住 \(w_2\),只動 \(w_1\):\(\partial L/\partial w_1=2(w_1-3)=-4\),往右轉會變好。
  2. 按住 \(w_1\),只動 \(w_2\):\(\partial L/\partial w_2=4(w_2+1)=4\),往右轉會變差。
  3. 兩個數字排在一起就是梯度:\([-4,\,4]\)。
  4. 以 \(\eta=0.1\) 反方向走一步:\(w_1=1+0.4=1.4\),\(w_2=0-0.4=-0.4\)。兩顆旋鈕都更靠近谷底。

一般化之後就是講義第 62 頁的兩個式子。\(\theta\)(theta)是「全部參數」的統稱,也就是所有 \(w\) 加上 \(b\):

\[ \nabla_{\theta} L = \begin{bmatrix} \dfrac{\partial L}{\partial w_1} \\[6pt] \dfrac{\partial L}{\partial w_2} \\ \vdots \\ \dfrac{\partial L}{\partial w_n} \end{bmatrix} \qquad \theta^{t+1} = \theta^{t} - \eta \, \nabla_{\theta} L \]

講義第 57 頁說梯度「指向函數上升最快的方向」。直覺是:每個分量說明該方向有多陡,按比例合起來,就是最陡的上坡。我們要下山,所以取負號。

符號念法白話
\(\mathrm{d}L/\mathrm{d}w\)導數只有一顆旋鈕時的斜率
\(\partial L/\partial w_j\)偏導數按住其他旋鈕,只動第 \(j\) 顆的斜率
\(\nabla L\)梯度(\(\nabla L\))所有偏導數排成的清單(向量)
\(\theta\)theta全部參數:\(w_1\ldots w_n\) 與 \(b\)
\(\eta\)eta,學習率每一步跨多大

第四站:步伐多大,學習率

學習率 \(\eta\) 決定每一步跨多遠:太小走到天黑,太大會跳過谷底,甚至越跳越高(講義第 65 頁)。

回到第二站的碗 \(L(w)=(w-3)^{2}\),從 \(w=1\) 出發,每步都用同一條規則 \(w\leftarrow w-\eta\times 2(w-3)\),只換 \(\eta\):

  • \(\eta=0.01\):每步只縮短 2% 的距離,十步後還在 1.37 附近,小碎步走到天黑。
  • \(\eta=0.1\):每步縮短 20%,十步後到 2.79,穩穩靠近 3。
  • \(\eta=0.9\):每一步都跨過谷底到對面山坡,左右橫跳,但擺幅逐漸變小。
  • \(\eta=1.1\):跨得太過頭,每跳一次離谷底更遠,永遠下不了山。

依第二站的更新規則計算。四種學習率,各 10 步。\(\eta=0.1\) 穩穩靠近;太小的步伐幾乎沒動;\(1.1\) 則越走越遠。

有個有趣的巧合:\(\eta=0.1\) 和 \(\eta=0.9\) 在偶數步落在同一點,但前者一路平穩走過去,後者是左右橫跳過去的。

注意實際的步長是 \(\eta\) 乘上斜率,不是 \(\eta\) 本身。坡陡時斜率大,自動跨大步;接近谷底時斜率趨近 \(0\),步伐自然縮小。所以即使 \(\eta\) 固定,走法也會「先快後慢」。

\(\eta\) 不是模型從資料學來的,而是設計者事先挑好的,這類參數叫做超參數(hyperparameter)。實務上常從 0.1、0.01、0.001 這幾個數量級試起。

第五站:邏輯迴歸要下的是哪座山

邏輯迴歸的山叫做交叉熵損失:模型對正確答案越沒把握,海拔越高。先看模型怎麼算出「把握」,分兩步:

\[ z = w \cdot x + b = w_1 x_1 + w_2 x_2 + \dots + b \qquad \hat{y} = \sigma(z) = \frac{1}{1 + e^{-z}} \]
  • \(z\) 是加權總分:每個特徵 \(x_j\) 乘上它的權重 \(w_j\) 再加總,像評審把各項分數加權。\(z\) 可以是任何實數。
  • \(\sigma\)(sigmoid)是換算表:把總分壓進 0 到 1,變成「這篇是正面評論」的機率 \(\hat y\)。\(\sigma(0)=0.5\),\(\sigma(2)\approx 0.88\),\(\sigma(-2)\approx 0.12\)。

有了 \(\hat y\),再用講義第 63 頁的交叉熵衡量它錯得多離譜:

\[ L_{CE}(\hat{y}, y) = -\big[\, y \log \hat{y} + (1 - y) \log (1 - \hat{y}) \,\big] \]

公式看起來嚇人,其實 \(y\) 只能是 \(1\) 或 \(0\),它像一個開關,每次只留下一半:

正確答案公式剩下白話
\(y=1\)(正面)\(-\log\hat y\)看你給「正面」幾分把握
\(y=0\)(負面)\(-\log(1-\hat y)\)看你給「負面」幾分把握

\(-\log\) 的效果是把「把握」換成「驚訝」。以 \(y=1\) 為例:

模型給正確答案的機率損失 \(-\log p\)感覺
0.990.01完全不意外
0.90.11還好
0.50.69瞎猜
0.12.30很驚訝
0.014.61大吃一驚,重罰

最後是初學者最常混淆的一點:訓練時,資料 \(x\)、\(y\) 是固定的地形,權重 \(w\)、\(b\) 才是你的位置。我們要找的是讓損失最低的 \(w\) 和 \(b\),所以求導數時,是對 \(w\) 和 \(b\) 求,而不是對 \(x\) 求。

第六站:齒輪傳動,連鎖律

\(w\) 並不直接碰到損失,而是先經過 \(z\),再經過 \(\hat y\),才傳到 \(L\)。連鎖律說:沿途每一段的「放大倍率」相乘,就是 \(w\) 對 \(L\) 的總影響。

先看齒輪:A 轉 1 圈帶動 B 轉 2 圈,B 轉 1 圈帶動 C 轉 3 圈。A 轉 1 圈時 C 轉幾圈?2 × 3 = 6 圈。不必重新觀察 A 和 C,只要把兩段的倍率相乘。

邏輯迴歸的傳動路線有三段,每段各問一個「推一下,變多少」:

A B A 轉 1 圈 → B 轉 2 圈 → C 轉 6 圈 1 × 2 × 3 = 6 不必重看 A 和 C,兩段倍率相乘即可 前向 · 模型怎麼算 反向 wⱼ 權重 z 加權總分 ŷ 機率 L 損失 xⱼ 進來 y 進來 σ × ∂L/∂ŷ × ŷ(1−ŷ) × xⱼ ∂L/∂wⱼ = 第三段 × 第二段 × 第一段

上排從左往右是模型的計算。下排從損失往回走,每經過一段就乘上那一段的倍率,回到 \(w_j\) 就是梯度。\(x_j\) 在第一段進場,正確答案 \(y\) 在最後一段進場。

\[ \frac{\partial L}{\partial w_j} = \underbrace{\frac{\partial L}{\partial \hat{y}}}_{\text{第三段}} \times \underbrace{\frac{\partial \hat{y}}{\partial z}}_{\text{第二段}} \times \underbrace{\frac{\partial z}{\partial w_j}}_{\text{第一段}} \]

記憶口訣:右邊的 \(\partial\hat y\) 和 \(\partial z\) 上下各出現一次,看起來像能「約掉」,剩下的正是左邊的 \(\partial L/\partial w_j\)。嚴格來說這不是真的約分,但當作檢查工具很好用。

這個「從損失往回,一段一段乘回去」的算法,放到幾十層的神經網路上,就叫反向傳播(backpropagation)。大型語言模型的訓練,本質上就是把這一站重複做到極致。

前向 · 一層層往前算 輸入 h⁰ 第 1 層 局部倍率 ··· 最後一層 局部倍率 L 損失 反向 × 第 1 層 × … × 最後一層 從這裡開始 每個權重的梯度 = 從損失乘回它所在的那一層

每一層只做第六站那一件事:把自己的倍率乘上去,再交給下一層。層數變多,算法不變。幾十層的網路,就是這條回程走幾十次。

第七站:三段倍率相乘,推出梯度公式

講義第 63 頁直接給出答案 \(\partial L/\partial w_j=[\sigma(w\cdot x+b)-y]\,x_j\),並說推導請見課本。這一站把它補齊:三段各算一個小導數,相乘後化簡,就得到這個結果。

工具箱:只需要三條規則

規則內容白話
A\(\log u\) 對 \(u\) 的導數是 \(1/u\)\(u\) 越小,\(\log\) 越敏感
B\(\sigma(z)\) 的導數是 \(\sigma(z)(1-\sigma(z))\)用 \(\hat y\) 寫就是 \(\hat y(1-\hat y)\)
C按住不動的東西,導數是 0偏導數的定義

第一段:\(w\) 推一下,\(z\) 變多少

\(z=w_1 x_1+w_2 x_2+b\)。只動 \(w_j\) 時,其他項都按住不動(規則 C),剩下 \(w_j x_j\) 這一項,它的斜率就是 \(x_j\):

\[ \frac{\partial z}{\partial w_j} = x_j \qquad \frac{\partial z}{\partial b} = 1 \]

直覺:特徵值 \(x_j\) 越大,轉動 \(w_j\) 對總分的影響就越大。某個特徵在這筆資料裡是 \(0\),轉它的權重就完全沒作用。

第二段:\(z\) 推一下,\(\hat y\) 變多少

直接套規則 B:

\[ \frac{\partial \hat{y}}{\partial z} = \hat{y}\,(1 - \hat{y}) \]

直覺:\(\hat y=0.5\) 時是 \(0.25\),最敏感;\(\hat y\) 接近 \(0\) 或 \(1\) 時趨近 \(0\),曲線兩端已經壓平,推 \(z\) 幾乎沒反應。

第三段:\(\hat y\) 推一下,\(L\) 變多少

第五站說過 \(y\) 是開關,所以最省力的方法是分兩種情況各算一次。

情況\(L\) 剩下\(\partial L/\partial\hat y\)(規則 A)
\(y=1\)\(-\log\hat y\)\(-1/\hat y\)
\(y=0\)\(-\log(1-\hat y)\)\(1/(1-\hat y)\)

\(y=0\) 那一格的正號來自兩個負號相乘:\(\log\) 前面的負號,以及 \((1-\hat y)\) 對 \(\hat y\) 的斜率 \(-1\)。

三段相乘

情況第三段 × 第二段再乘第一段可以寫成
\(y=1\)\((-1/\hat y)\times\hat y(1-\hat y)=\hat y-1\)\((\hat y-1)x_j\)\((\hat y-y)x_j\)
\(y=0\)\(1/(1-\hat y)\times\hat y(1-\hat y)=\hat y\)\((\hat y-0)x_j\)\((\hat y-y)x_j\)

兩種情況殊途同歸,於是得到:

\[ \boxed{\;\frac{\partial L_{CE}}{\partial w_j} = (\hat{y} - y)\,x_j \qquad \frac{\partial L_{CE}}{\partial b} = \hat{y} - y\;} \]

其中 \(\hat y=\sigma(w\cdot x+b)\),與講義第 63 頁完全一致。

不分情況的一般推導(對照用)

不拆開 \(y\) 也可以,只是代數多兩行。關鍵在第三行:兩個 \(y\hat y\) 正負抵消。

\[ \begin{aligned} \frac{\partial L}{\partial w_j} &= \Big[-\frac{y}{\hat{y}} + \frac{1-y}{1-\hat{y}}\Big] \cdot \hat{y}(1-\hat{y}) \cdot x_j \\ &= \big[-y(1-\hat{y}) + (1-y)\,\hat{y}\big]\, x_j \\ &= \big[-y + y\hat{y} + \hat{y} - y\hat{y}\big]\, x_j \\ &= (\hat{y} - y)\, x_j \end{aligned} \]

這個抵消不是巧合。\(\log\) 的導數在分母放了 \(\hat y\) 和 \(1-\hat y\),sigmoid 的導數在分子剛好送來 \(\hat y(1-\hat y)\),兩者一碰就消掉。sigmoid 與交叉熵是一對天生配好的搭檔,這也是講義稱它為「優雅的導數」的原因。

選讀:規則 B 從哪裡來

把 \(\sigma(z)\) 寫成 \((1+e^{-z})^{-1}\),用一次連鎖律:

\[ \sigma'(z) = \frac{e^{-z}}{(1 + e^{-z})^2} = \underbrace{\frac{1}{1 + e^{-z}}}_{\sigma(z)} \cdot \underbrace{\frac{e^{-z}}{1 + e^{-z}}}_{1 - \sigma(z)} \]

用第二站的「推一下」驗算:\(\sigma(0.01)-\sigma(0)\approx 0.0025\),除以 \(0.01\) 得 \(0.25\),正是 \(\sigma(0)(1-\sigma(0))=0.5\times 0.5\)。

第八站:手算一次,走完第一步

用講義第 68 至 77 頁的迷你情感分析走一步:權重從全 \(0\) 變成 \([0.15,\,0.10,\,0.05]\),損失從 \(0.69\) 降到 \(0.40\)。

題目設定:一篇評論的正確答案是正面(\(y=1\)),抽出兩個特徵:

  • \(x_1=3\):正面詞典中的詞出現 3 次
  • \(x_2=2\):負面詞典中的詞出現 2 次
  • 起點 \(w_1=w_2=b=0\),學習率 \(\eta=0.1\)
步驟算什麼算式結果
1加權總分 \(z\)\(0\times 3+0\times 2+0\)\(0\)
2機率 \(\hat y\)\(\sigma(0)\)\(0.5\)
3錯多少 \(\hat y-y\)\(0.5-1\)\(-0.5\)
4\(\partial L/\partial w_1\)\(-0.5\times 3\)\(-1.5\)
5\(\partial L/\partial w_2\)\(-0.5\times 2\)\(-1.0\)
6\(\partial L/\partial b\)\(-0.5\times 1\)\(-0.5\)
7新 \(w_1\)\(0-0.1\times(-1.5)\)\(0.15\)
8新 \(w_2\)\(0-0.1\times(-1.0)\)\(0.10\)
9新 \(b\)\(0-0.1\times(-0.5)\)\(0.05\)
\[ \nabla_{w,b} L = \begin{bmatrix} -1.5 \\ -1.0 \\ -0.5 \end{bmatrix} \qquad \theta^{1} = \begin{bmatrix} 0 \\ 0 \\ 0 \end{bmatrix} - 0.1 \begin{bmatrix} -1.5 \\ -1.0 \\ -0.5 \end{bmatrix} = \begin{bmatrix} 0.15 \\ 0.10 \\ 0.05 \end{bmatrix} \]

驗收:真的下山了嗎?用新權重重算,\(z=0.15\times 3+0.10\times 2+0.05=0.70\),\(\hat y=\sigma(0.70)\approx 0.668\)。損失從 \(-\log 0.5\approx 0.693\) 降到 \(-\log 0.668\approx 0.403\)。模型對「正面」的把握從五成升到六成七。

三個觀察:

  1. 梯度的三個分量是 3 : 2 : 1,正好是 \(x_1:x_2:1\) 的比例。所有權重共用同一個錯誤量 \(-0.5\),差別只在各自的特徵值。
  2. 誤差是負的(模型低估了正面),所以所有權重都往上調。
  3. 負面詞的權重 \(w_2\) 竟然也變大了。原因是這一筆資料是正面評論,卻含有 2 個負面詞;模型只看到「這些特徵出現了,而且答案是正面」,分不出是哪個詞的功勞。講義第 77 頁補充:看過足夠多負面例子之後,\(w_2\) 終究會轉為負值。

第九站:用 Python 對答案

推導對不對,讓電腦用最笨的方法檢查:每顆旋鈕各推一點點,量出斜率,再跟公式 \((\hat y-y)x_j\) 比對。兩者在隨機位置的差距只有約 10⁻¹¹,公式正確。

這種檢查叫梯度檢查(gradient checking)。它不需要任何微積分,只用到第二站的「推一下,變多少」,所以也是初學者最能信任的驗證方式。

import numpy as np

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

def loss(w, b, x, y):
    """一筆資料的交叉熵損失(第五站)"""
    y_hat = sigmoid(w @ x + b)
    return -(y * np.log(y_hat) + (1 - y) * np.log(1 - y_hat))

def analytic_grad(w, b, x, y):
    """第七站推出的公式:(ŷ − y)·x 與 (ŷ − y)"""
    err = sigmoid(w @ x + b) - y
    return err * x, err

def numeric_grad(w, b, x, y, h=1e-5):
    """第二、三站的「按住其他旋鈕,只推一顆」"""
    gw = np.zeros_like(w)
    for j in range(len(w)):
        e = np.zeros_like(w)
        e[j] = h
        gw[j] = (loss(w + e, b, x, y) - loss(w - e, b, x, y)) / (2 * h)
    gb = (loss(w, b + h, x, y) - loss(w, b - h, x, y)) / (2 * h)
    return gw, gb

x = np.array([3.0, 2.0])   # 正面詞 3 個、負面詞 2 個
y = 1                      # 正確答案:正面
w, b, eta = np.zeros(2), 0.0, 0.1

gw, gb = analytic_grad(w, b, x, y)
nw, nb = numeric_grad(w, b, x, y)
print("解析梯度:", gw, gb)
print("數值梯度:", nw.round(6), round(nb, 6))

w1, b1 = w - eta * gw, b - eta * gb
print("更新後:", w1, b1)
print(f"損失: {loss(w, b, x, y):.3f} -> {loss(w1, b1, x, y):.3f}")

rng = np.random.default_rng(0)
wr, br = rng.normal(size=2), rng.normal()
aw, ab = analytic_grad(wr, br, x, y)
nw, nb = numeric_grad(wr, br, x, y)
print("隨機點的最大差距:", max(np.abs(aw - nw).max(), abs(ab - nb)))

實際執行的輸出:

解析梯度: [-1.5 -1. ] -0.5
數值梯度: [-1.5 -1. ] -0.5
更新後: [0.15 0.1 ] 0.05
損失: 0.693 -> 0.403
隨機點的最大差距: 3.579958551824802e-11
  • numeric_grad 用的是左右各推一點(\(h\) 與 \(-h\))再相減,叫中央差分,比只往右推更準。
  • 解析公式一次算完所有旋鈕;數值法每顆旋鈕要多算兩次損失。模型有上億個參數時,只有解析公式跑得動,所以才需要推導。
  • 課堂練習:把 y = 1 改成 y = 0,先用第七站的表格預測梯度的正負號,再執行驗證。

第十站:從一句話到一疊句子

真實訓練有成千上萬筆資料。每一步該看幾筆再決定方向?看一筆叫隨機梯度下降(SGD),看全部叫 batch,看一小批叫 mini-batch(講義第 64、78 頁)。

做法每一步看幾筆比喻特性
SGD1 筆,隨機抽問一個路人就出發快,但方向忽左忽右
mini-batch\(m\) 筆,例如 512 或 1024問一小群人,取平均意見折衷,實務最常用
batch全部資料開全村大會投票方向最穩,但每步很慢

一批資料的梯度,就是每筆梯度 \((\hat y-y)x_j\) 的平均。公式沒有新東西,只是多了一個求平均:

\[ \frac{\partial\, \mathrm{Cost}}{\partial w_j} = \frac{1}{m} \sum_{i=1}^{m} \big(\hat{y}^{(i)} - y^{(i)}\big)\, x_j^{(i)} \]

講義第 64 頁的演算法可以直接翻成 Python。以下用六篇迷你評論訓練 20 輪,比較三種批次大小:

import numpy as np

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

X = np.array([[3, 2], [4, 0], [2, 1], [0, 3], [1, 4], [1, 2]], dtype=float)
Y = np.array([1, 1, 1, 0, 0, 0])

def mean_loss(w, b):
    p = sigmoid(X @ w + b)
    return -np.mean(Y * np.log(p) + (1 - Y) * np.log(1 - p))

def train(eta=0.1, epochs=20, batch_size=1, seed=0):
    rng = np.random.default_rng(seed)
    w, b, steps = np.zeros(2), 0.0, 0
    for epoch in range(epochs):
        order = rng.permutation(len(X))
        for start in range(0, len(X), batch_size):
            idx = order[start:start + batch_size]
            err = sigmoid(X[idx] @ w + b) - Y[idx]
            w -= eta * (err @ X[idx]) / len(idx)
            b -= eta * err.mean()
            steps += 1
    return w, b, steps

for bs in [1, 3, 6]:
    w, b, steps = train(batch_size=bs)
    print(f"batch={bs}  更新 {steps:3d} 次  w={w.round(2)}  b={b:.2f}  損失={mean_loss(w, b):.3f}")

跑出來的結果:

每批筆數20 輪共更新幾次\(w_1\)(正面詞)\(w_2\)(負面詞)平均損失
1(SGD)\(120\)\(1.69\)\(-1.36\)\(0.093\)
3(mini-batch)\(40\)\(1.01\)\(-0.83\)\(0.184\)
6(全部)\(20\)\(0.68\)\(-0.57\)\(0.270\)
  1. 第八站那個「怪怪的」\(w_2\),看過負面評論之後果然轉成負值,應驗了講義第 77 頁的預告。
  2. 同樣 20 輪,SGD 損失最低,是因為它更新了 120 次,不是因為它比較聰明。實務上 GPU 能同時算一整批,一批 512 筆的耗時接近一筆,所以 mini-batch 用相近的時間換來穩得多的方向。

語言學與認知旁欄:錯多少,學多少

\((\hat y-y)x_j\) 這條公式在認知科學裡並不陌生:它是「預測誤差驅動學習」的一個版本,而交叉熵正是心理語言學說的驚訝度(surprisal)。

本文出現的東西認知科學與語言學的對應可以跟學生點出的意義
更新量 \(=\eta(y-\hat y)x_j\)Widrow 與 Hoff(1960)的 delta rule;Rescorla 與 Wagner(1972)的聯想學習模型學習量與「預期落差」成正比;完全猜中就不學
損失 \(-\log p\)Hale(2001)、Levy(2008)的 surprisal 理論語言模型的訓練損失,就是對正確下一個詞的平均驚訝度
第八站的 \(w_2\) 也被調高跨情境詞彙學習(Smith & Yu 2008)單一情境無法判斷功勞歸誰,跨多筆資料的統計才能釐清

第一點:預測誤差。Rescorla–Wagner 模型的更新式與本文的梯度更新形式相同,差別在它的預測是線性加總,沒有經過 sigmoid。Ramscar 等人(2010)用這類模型解釋詞彙與類別學習中「線索先於標籤」的順序效應。Viviani 等人(2024)的重複實驗只部分重現這個效應,正好可以帶學生討論模型預測與實驗結果之間的距離。

第二點:驚訝度。第五站的表格可以直接改寫成心理語言學的語言:讀者讀到一個詞時的處理負擔,與 \(-\log P(\text{詞}\mid\text{上文})\) 相關。這也是為什麼本課程可以拿大型語言模型的損失去預測人類閱讀時間:兩者量的是同一種東西。

第三點:局部知識。梯度只提供「此地此刻」的坡度,模型從未看見整座山。學習因此是一連串微小修正的累積,這與用法本位(usage-based)理論把語法視為反覆使用所沉澱下來的結構,在精神上相通。這是類比,不是等同,值得在課堂上討論兩者的界線。

常見誤解與自我檢測

誤解正確說法回看
梯度指向下坡梯度指向上坡最陡處,所以更新時要減掉它第三站
要對 \(x\) 求導數\(x\)、\(y\) 是固定的資料,求導對象是 \(w\) 和 \(b\)第五站
學習率越大學越快太大會跳過谷底甚至發散,例如 \(\eta=1.1\)第四站
\((\hat y-y)x_j\) 是要背的魔法公式它是三段倍率相乘後,兩個 \(y\hat y\) 抵消的結果第七站
SGD 每一步都讓全部資料的損失下降單筆更新可能讓其他資料變差,看的是長期趨勢第十站

自我檢測

  1. 對 \(L(w)=(w-3)^{2}\) 而言,\(w=5\) 時的斜率是多少?該往哪邊走?\(\eta=0.1\) 時走到哪裡?
  2. 某筆資料的特徵 \(x_j=0\),這筆資料會改變 \(w_j\) 嗎?
  3. 如果第八站那篇評論其實是負面(\(y=0\)),梯度和新權重各是多少?
  4. 模型已經給正確答案 0.99 的機率,這筆資料造成的梯度大約多大?這合理嗎?
  5. \(\hat y=0.5\) 時,\(\partial\hat y/\partial z\) 是多少?\(\hat y=0.99\) 時呢?
參考答案
  1. 斜率 \(=2(5-3)=4\),為正,所以 \(w\) 要變小:\(5-0.1\times 4=4.6\)。
  2. 不會。梯度是 \((\hat y-y)\times 0=0\),沒出現的特徵不負任何責任。
  3. \(\hat y-y=0.5-0=0.5\),梯度 \(=[1.5,\,1.0,\,0.5]\),新權重 \(=[-0.15,\,-0.10,\,-0.05]\)。方向與原例正好相反。
  4. 若 \(y=1\),\(\hat y-y=-0.01\),梯度只有原例的五十分之一左右。已經猜對,幾乎不必改,很合理。
  5. \(0.5\times 0.5=0.25\);\(0.99\times 0.01\approx 0.0099\)。sigmoid 兩端越平,推 \(z\) 越沒反應。

參考文獻

  • Jurafsky 的邏輯迴歸投影片(配合 Jurafsky 與 Martin《Speech and Language Processing》第三版),本課程第四章講義,第 50 至 78 頁。
  • Hale, J. (2001). A probabilistic Earley parser as a psycholinguistic model. ACL Anthology
  • Levy, R. (2008). Expectation-based syntactic comprehension. Cognition, 106(3), 1126–1177. eScholarship
  • Ramscar, M., Yarlett, D., Dye, M., Denny, K., & Thorpe, K. (2010). The effects of feature-label-order and their implications for symbolic learning. Scholar Commons
  • Rescorla, R. A., & Wagner, A. R. (1972). A theory of Pavlovian conditioning. 書目紀錄
  • Smith, L., & Yu, C. (2008). Infants rapidly learn word-referent mappings via cross-situational statistics. PMC
  • Viviani, E., Ramscar, M., & Wonnacott, E. (2024). The effects of linear order in category learning. Oxford ORA
  • Widrow, B., & Hoff, M. E. (1960). Adaptive switching circuits. MIT Press 重印本