Skip to content

對比學習與語意檢索:沒有標籤,怎麼學出能比相似度的向量|Stanford CS229 第 13 堂

2026年10月12日1 分鐘
TL;DR這堂課把 SimCLR 損失拆成一張 B×B 相似度矩陣:每一列做一次 softmax 分類,答案是對角線那一格。對角線被拉高、其餘被壓低,模型不需要標籤就學到「相似的東西向量相近」。後半堂用同一組向量做語意檢索,再接到 RAG。

🌏 English version

影片狀態:已附影片。 這篇依 Stanford Online 公開的 Spring 2026 第 13 堂錄影的英文字幕寫成,公式記號對照 2026 主講義第 16 章(Representation Learning);我沒有看到板書畫面,板書上的式子是從口述與講義還原的。

這堂是 Stanford CS229 導讀裡表徵學習的收尾。上一堂講到預訓練模型可以拿來微調或接 linear probe,這堂回答兩個接下來的問題:那個「好用的向量」要用什麼損失函數訓練出來,以及在大型語言模型的年代它還能拿來做什麼。講者是 Tengyu Ma:春季班由他與 Chris Ré 分堂授課,Chris Ré 在第 10 堂結尾說那是他這學期的最後一堂。先備是 softmax 與交叉熵損失(cross-entropy loss),以及向量內積。讀完之後,你應該能自己寫出 SimCLR 損失、對著一個小批次手算,並說明語意檢索與 RAG 各自把這組向量用在哪一步。

先交代一件事:這支影片在 YouTube 上的標題是「LLMs, Next-Word Prediction Loss」,但整堂 61 分鐘沒有進到語言模型的訓練目標。大型語言模型只在最後八分鐘以 RAG 的使用者身分出現,下一個字的預測損失留到下一堂。這堂實際的內容是講義第 16 章。

YouTube:Stanford CS229 Machine Learning | Spring 2026 | Lecture 13: LLMs, Next-Word Prediction Loss

我們要的向量長什麼樣子

(錄影 00:00–03:12)

表徵學習要的是一個函數 ϕθ\phi_\theta,參數是 θ\theta,把原始輸入 xx(一張圖、一段文字、一段聲音或影片)對應到一個 mm 維向量 ϕθ(x)∈Rm\phi_\theta(x)\in\mathbb{R}^m。這個向量有好幾個名字:representation、feature、embedding。講者說 representation 這個字太泛,現在最常聽到的是 embedding,這堂就用它。

好的 embedding 只有一個要求:相似的輸入要落在相近的位置,不相似的要離得遠。兩張不同的貓的照片,向量應該彼此接近;一架飛機的向量應該在別的地方。滿足這個性質,就能做相似度搜尋:手上有一張貓的圖,想找其他貓的圖,只要去看哪些圖的向量離它最近。

上一堂提過 embedding 的另一個用途,是在上面加一層線性分類器(linear probe)。講者在這裡直接說,這個用法現在少了,因為整個模型可以端到端訓練,不必拆成兩段。所以他認為 embedding 現在最重要的用途是相似度搜尋,後半堂會回到這件事。

監督式預訓練:拿分類器的倒數第二層

(錄影 03:12–12:21)

第一種訓練法很好描述,講者說它現在已經不常用,簡單帶過。

拿一個有標籤的大資料集,例如有一千個類別的 ImageNet,訓練一個普通的分類網路。網路有很多層,倒數第二層的輸出是一個向量,後面接一個矩陣 WW 變成 logits,再過 softmax。訓練完之後,把 WW 和 softmax 丟掉,宣告倒數第二層的輸出就是 ϕθ(x)\phi_\theta(x)。

為什麼這樣得到的向量會好用?講者說沒有證明,只有一個直覺:最後一層只是線性分類器,如果倒數第二層的向量沒有把相似的圖放在一起,線性分類器根本分不出類別。網路能把一千類分對,表示它在前面那些層已經把語意整理好了。

這個方法的限制有兩個。第一,標籤要夠多樣。如果標籤只有 0 到 9 十個數字,網路只需要認得跟數字有關的筆畫;更極端的例子是標籤只有「黑白照」與「彩色照」兩種,網路不需要任何複雜的表徵就能分對,學到的向量對別的任務沒用。第二,要再擴大規模就得標更多資料,很貴。第二點就是轉向非監督方法的理由。

這一段有三個學生提問,都是自學時容易卡住的地方:

  • 「正確的 embedding 從哪裡來?」 沒有這種東西。講者的回答是「There's no so-called ground truth representation, by the way.」(約 08:19)。訓練時的標籤是離散的類別(鬥牛犬、波斯貓、消防車),不是向量。向量是訓練的副產品,好不好只看它有沒有滿足前面說的性質。
  • 「直接把像素接成一個向量不行嗎?」 不行。同一隻波斯貓出現在畫面的不同位置,逐像素比對會差非常多,但人一看就知道是同一隻貓。embedding 要抽出的是語意,原始像素做不到。
  • 「這跟微調有什麼不同?」 這裡是從頭訓練一個分類網路,訓練完把最後的線性層拿掉,交付的成果就是倒數第二層,沒有後續的調整步驟。

對比學習:一股拉力,一股推力

(錄影 12:21–25:42)

現在手上只有一堆沒有標籤的圖。目標沒變:相似的圖要有相近的向量。

第一步,製造「相似的一對」。 對同一張圖 xx 做兩次隨機的資料增強(augmentation),得到 x^\hat{x} 與 x~\tilde{x}。增強的手法有隨機裁切、翻轉、模糊、加雜訊、改顏色或轉黑白;講者認為裁切大概是最重要的一種,而具體用哪幾種影響不大,只要夠多樣。兩次增強用不同的亂數種子,可能一張是原圖的右上角,另一張是左下角。它們來自同一張圖,所以我們要求 ϕθ(x^)\phi_\theta(\hat{x}) 與 ϕθ(x~)\phi_\theta(\tilde{x}) 靠近。這件事不需要標籤。

第二步,發現只有拉力會壞掉。 如果損失函數只要求這兩個向量靠近,最省事的解是把所有圖都對應到同一個點,每一對的距離都是零。講者的說法是,沒有相對的比較,「近」沒有意義;一定要同時指定什麼東西該遠。

第三步,加上推力。 再從資料集隨機拿一張別的圖 zz,也做增強得到 z^\hat{z}(講者說對 zz 做增強只是為了讓它跟 x^\hat{x} 落在同一種輸入空間裡)。然後要求 ϕθ(x^)\phi_\theta(\hat{x}) 與 ϕθ(z^)\phi_\theta(\hat{z}) 離得遠。整個目標就是這兩條:同一張圖的增強要近,不同圖的增強要遠。

隨機抽到的另一張圖剛好也是貓,怎麼辦

講者問大家這個設計有什麼問題,學生馬上答出來:xx 和 zz 可能本來就相似。兩張都是波斯貓、或兩架很像的飛機,這時候把它們推開是在幫倒忙。

講者承認這是真的,然後說沒關係。從一大堆圖裡隨機抽兩張,絕大多數時候是一架飛機配一張椅子、一隻貓配一棟建築,真的相似的機率很小。他的原話是「With some small chance, they are similar, but you just say those are collateral damage.」(約 16:23)。推力帶來的好處蓋過這些誤傷。

接著有學生追問了好幾輪,問法各不相同,講者最後給的總結是把它想成幾股互相平衡的力:「you should think of this as multiple different forces that balance each other.」(約 25:42)。就算損失函數偶爾想把兩隻貓推開,其他項(這兩隻貓各自的正配對、以及它們跟飛機與椅子之間的推力)會把它們留在同一區,所以那股錯的推力不會成功。

這段問答裡還有兩個值得記下來的澄清:

  • 不要求「貓離狗比離飛機近」。 講者說這不是目前的目標。貓、狗、飛機就當成三種不同的東西,彼此都遠。他還補充,實際去看資料,一隻貓最近的鄰居往往只有同品種的貓,不同品種的貓之間已經相當遠。階層式的結構最後多少會出現,但那是次要的效應。
  • 需要的是多樣的圖,不需要標籤。 如果資料集全是貓,「隨機抽兩張多半不相似」的假設就不成立。講者同意,然後指出多樣的無標籤圖很容易取得,從網路下載就有;難的一直是標籤。

SimCLR 損失:把目標寫成一張矩陣

(錄影 25:42–38:47)

接下來要把「這些配對要近、那些要遠」寫成可以最佳化的式子。講者用的是 SimCLR 的做法,並且先打了預防針:這個式子不是表達上述想法最簡潔的方式,寫成這樣是為了計算效率。

建立相似度矩陣

抽一個批次,BB 張圖 x(1),…,x(B)x^{(1)},\dots,x^{(B)}。每張圖做兩次增強,得到 2B2B 個輸入:

x^(1),…,x^(B),x~(1),…,x~(B)\hat{x}^{(1)},\dots,\hat{x}^{(B)},\qquad \tilde{x}^{(1)},\dots,\tilde{x}^{(B)}

兩個輸入的相似度定義成 embedding 的內積。實務上通常會先把 embedding 正規化成單位長度,這時內積就等於餘弦相似度(cosine similarity)。把所有「帽子版」對「波浪版」的相似度排成一個 B×BB\times B 矩陣:

Sij=ϕθ(x^(i))⊤ϕθ(x~(j))S_{ij}=\phi_\theta(\hat{x}^{(i)})^\top\phi_\theta(\tilde{x}^{(j)})

對角線 SiiS_{ii} 是同一張圖的兩個增強,我們要它大。非對角線 SijS_{ij}(j≠ij\neq i)是不同圖的增強,例如一隻貓對一架飛機,我們要它小。講者說到這裡為止沒有做任何深的事情,只是換了一種語言描述同一個目標。還沒回答的是:多大算大,多小算小?

逐列寫出損失

答案是一列一列看。第 ii 列的損失是

ℓi=−log⁡exp⁡(Sii)exp⁡(Sii)+∑j≠iexp⁡(Sij)\ell_i=-\log\frac{\exp(S_{ii})}{\exp(S_{ii})+\sum_{j\neq i}\exp(S_{ij})}

整個批次的損失把每一列加起來:

Lpre(θ)=∑i=1BℓiL_{\text{pre}}(\theta)=\sum_{i=1}^{B}\ell_i

這跟講義第 16.2 節的式子相同:固定 x^(i)\hat{x}^{(i)}、對 x~(j)\tilde{x}^{(j)} 求和,照上面 SijS_{ij} 的定義就是沿第 ii 列加總。看影片時會聽到講者先說「每一列」、中途改口說「每一欄」,他自己說這只是記號問題(約 31:06);本文一律照講義寫成列。

確認它真的在做我們要的事

把分子記成 A=exp⁡(Sii)A=\exp(S_{ii}),分母多出來的那一項記成 Bneg=∑j≠iexp⁡(Sij)B_{\text{neg}}=\sum_{j\neq i}\exp(S_{ij})(課堂上直接叫它 B,這裡加下標是為了不跟批次大小混淆)。兩者都是正數,損失是

ℓi=−log⁡AA+Bneg\ell_i=-\log\frac{A}{A+B_{\text{neg}}}

對 BnegB_{\text{neg}} 的單調性。 BnegB_{\text{neg}} 變大,分母變大,分數 AA+Bneg\frac{A}{A+B_{\text{neg}}} 變小;log⁡\log 是遞增函數,所以取 log⁡\log 之後也變小;前面有負號,所以 ℓi\ell_i 變大。結論是 ℓi\ell_i 對 BnegB_{\text{neg}} 遞增,要讓損失小就要讓 BnegB_{\text{neg}} 小,也就是每個 SijS_{ij}(j≠ij\neq i)都要小。

對 AA 的單調性。 這次分子分母都有 AA,不能直接看。講者的做法是改寫:

AA+Bneg=1−BnegA+Bneg\frac{A}{A+B_{\text{neg}}}=1-\frac{B_{\text{neg}}}{A+B_{\text{neg}}}

AA 變大時,BnegA+Bneg\frac{B_{\text{neg}}}{A+B_{\text{neg}}} 變小,一減去它就變大,取 log⁡\log 仍變大,加上負號變小。所以 ℓi\ell_i 對 AA 遞減,要讓損失小就要讓 SiiS_{ii} 大。

兩個方向都對上了:最小化 ℓi\ell_i 會拉高對角線、壓低同一列的其他格子。至於「多大算大」,答案藏在式子的形狀裡:損失只在乎 SiiS_{ii} 相對於同一列其他格子有多突出,絕對數值不重要。

換個角度:這就是多類別分類

講者接著指出,這個式子其實是大家熟悉的東西。把 x^(i)\hat{x}^{(i)} 交給你,問你 x~(1),…,x~(B)\tilde{x}^{(1)},\dots,\tilde{x}^{(B)} 裡哪一個是它的同伴。這是一個 BB 類的分類問題,正確答案是第 ii 類,logits 是第 ii 列的相似度 Si1,…,SiBS_{i1},\dots,S_{iB}。對 logits 做 softmax,再對正確類別的機率取負 log,就是交叉熵損失,也就是上面的 ℓi\ell_i。講者說「This is just literally a softmax.」(約 35:30),並說最初提出這個目標的人大概就是這樣想到的。

用機率的寫法更好記。令

pij=exp⁡(Sij)∑k=1Bexp⁡(Sik)p_{ij}=\frac{\exp(S_{ij})}{\sum_{k=1}^{B}\exp(S_{ik})}

則 ℓi=−log⁡pii\ell_i=-\log p_{ii}。每一列是一題選擇題,piip_{ii} 是模型選對同伴的機率。

手算一個批次

以下是本文自擬的例子,不是課堂內容。

取 B=3B=3,embedding 是二維的單位向量,用它與 x 軸的夾角表示,所以兩個向量的內積就是夾角差的餘弦。假設某個時刻模型給出:

圖內容x^\hat{x} 的角度x~\tilde{x} 的角度
1貓0°20°
2飛機90°110°
3椅子200°230°

算相似度矩陣。 Sij=cos⁡(x^(i) 的角度−x~(j) 的角度)S_{ij}=\cos(\hat{x}^{(i)}\text{ 的角度}-\tilde{x}^{(j)}\text{ 的角度}):

S=(0.940−0.342−0.6430.3420.940−0.766−1.0000.0000.866)S=\begin{pmatrix}0.940 & -0.342 & -0.643\\ 0.342 & 0.940 & -0.766\\ -1.000 & 0.000 & 0.866\end{pmatrix}

例如 S11=cos⁡20°=0.940S_{11}=\cos 20°=0.940,S12=cos⁡110°=−0.342S_{12}=\cos 110°=-0.342,S13=cos⁡230°=−0.643S_{13}=\cos 230°=-0.643。

算第 1 列的損失。 先取指數:e0.940=2.559e^{0.940}=2.559、e−0.342=0.710e^{-0.342}=0.710、e−0.643=0.526e^{-0.643}=0.526,總和 3.7953.795。所以

p11=2.5593.795=0.674,ℓ1=−log⁡0.674=0.394p_{11}=\frac{2.559}{3.795}=0.674,\qquad \ell_1=-\log 0.674=0.394

同樣的算法得到 p22=0.577p_{22}=0.577、ℓ2=0.549\ell_2=0.549,以及 p33=0.635p_{33}=0.635、ℓ3=0.454\ell_3=0.454。整個批次的損失是三列相加,約 1.3981.398(用未四捨五入的值相加;直接加上面三個三位小數會得到 1.3971.397)。

這個數字代表什麼,要有對照才看得出來。 考慮前面說的退化解:所有圖都對應到同一個點。這時 SS 的每一格都是 1,每一列的 softmax 是均勻的 1/31/3,每列損失是 log⁡3=1.099\log 3=1.099,整批是 3.2963.296。只有拉力的時候,退化解的損失是零;加上分母那一串之後,退化解的損失變成 Blog⁡BB\log B,比上面排得還算整齊的 1.3981.398 高得多。它不再是損失最低的地方,最佳化沒有理由停在那裡。推力就是這樣寫進式子裡的。

誤傷長什麼樣子。 把第 3 張圖換成另一隻貓,角度是 x^(3)=10°\hat{x}^{(3)}=10°、x~(3)=30°\tilde{x}^{(3)}=30°,跟第 1 張圖的貓擠在同一區。第 1 列變成 S11=0.940S_{11}=0.940、S12=−0.342S_{12}=-0.342、S13=cos⁡30°=0.866S_{13}=\cos 30°=0.866,於是

p11=2.5592.559+0.710+2.377=0.453,ℓ1=0.791p_{11}=\frac{2.559}{2.559+0.710+2.377}=0.453,\qquad \ell_1=0.791

損失從 0.3940.394 升到 0.7910.791,而升高的原因是模型把兩隻貓放得近,這正是我們要的行為。這就是講者說的 collateral damage:損失函數在這一格懲罰了正確的事。

每一格被推或拉的力道有多大

這一小節是本文補充的推導,課堂上沒有寫,但它能解釋下一節為什麼要找難負例。把 ℓi\ell_i 展開:

ℓi=−Sii+log⁡∑k=1Bexp⁡(Sik)\ell_i=-S_{ii}+\log\sum_{k=1}^{B}\exp(S_{ik})

對各個 SS 微分。第二項對 SijS_{ij} 的微分是 exp⁡(Sij)∑kexp⁡(Sik)=pij\frac{\exp(S_{ij})}{\sum_k\exp(S_{ik})}=p_{ij},所以

∂ℓi∂Sii=pii−1,∂ℓi∂Sij=pij(j≠i)\frac{\partial\ell_i}{\partial S_{ii}}=p_{ii}-1,\qquad \frac{\partial\ell_i}{\partial S_{ij}}=p_{ij}\quad(j\neq i)

梯度下降往負梯度方向走。pii−1p_{ii}-1 是負的,所以 SiiS_{ii} 會被往上推,力道是 1−pii1-p_{ii};pijp_{ij} 是正的,所以 SijS_{ij} 會被往下壓,力道是 pijp_{ij}。

套回第一個例子的第 1 列:p11=0.674p_{11}=0.674、p12=0.187p_{12}=0.187、p13=0.139p_{13}=0.139。正配對被拉的力道是 0.3260.326,兩個負配對被推的力道分別是 0.1870.187 和 0.1390.139,加起來剛好也是 0.3260.326。這是通則,因為同一列的機率加起來是 1。

由此看得出兩件事。第一,一個負配對被推開的力道等於模型把它誤認成同伴的機率;已經離得很遠的負配對,pijp_{ij} 接近零,幾乎不提供訊號。第二,在誤傷的例子裡 p13=0.421p_{13}=0.421,那隻被當成負例的貓受到的推力是真正的負例(飛機,0.1260.126)的三倍多。假負例傷害最大的時候,正好是模型把它們放對位置的時候。

正負配對從哪裡來,以及怎麼讓題目變難

(錄影 38:47–49:03)

有學生問:這樣算下來負配對比正配對多很多,這個比例是你要的嗎?講者先補上名詞。同一張圖的兩個增強叫正配對(positive pair),其他的叫負配對(negative pair)。有人叫它隨機配對(random pair),因為它們不一定真的是「負」的,但講者觀察到大家逐漸統一用 negative pair。講義的註腳也說 random pair 可能是更準確的說法。

正配對不一定靠增強

正配對可以用別的方法製造。一張圖配一段描述它的文字,圖當 x^(i)\hat{x}^{(i)},文字當 x~(i)\tilde{x}^{(i)}。這時候所有帽子版都是圖,所有波浪版都是文字;索引相同就是正配對,索引不同就是負配對。講者說,圖與文字共用的表徵就是這樣訓練出來的。

純文字通常沒有像裁切這樣自然的增強。講者提到公開論文裡的做法之一:一份文件的標題與小標當一個版本,內文當另一個版本,兩者構成正配對;甲文件的標題配乙文件的內文就是負配對。

負配對多是合理的,但隨機的負配對太簡單

回到學生的問題。矩陣裡只有對角線是正配對,其餘 B2−BB^2-B 格都是負配對,數量確實懸殊。講者認為這很合理,因為負配對的訊號弱得多:兩張完全不同的圖本來就很好分,需要多一點才能跟正配對的訊號抗衡。上一節的梯度也是同一個意思,容易的負配對 pijp_{ij} 接近零,一個抵不了多少。

更進一步,實務上發現隨機的負配對不夠強,要去找難負例(hard negatives),這件事叫 negative mining。難負例是看起來有點像、但其實不該配在一起的配對。講者的例子:一張圖是貓在踢足球,一段文字在講世界盃足球賽但沒有貓。這段文字該不該配給那張圖?有點模稜兩可。把這種容易混淆的配對放進訓練資料,損失函數的要求就變高,模型不能只會分辨明顯不同的東西。

講者說這是實務上最有用的技巧之一:「this is actually one of the biggest trick in practice.」(約 42:54)。它的難處在於沒有明確的停損點。負例越找越像,到某個程度它其實應該算正配對,再篩下去只是把自己搞混,所以必須在某處停手。

有學生接著問:如果手上有很好的負配對,還需要用整個矩陣嗎?講者說不用,可以把那一列換成一個正配對加上一組難負例。

批次不是整個資料集,而且抽法有講究

講者補了一句前面沒說清楚的事:BB 不是整個資料集的大小,只是一個小批次,可能是 128、1,000 或 4,000。每次抽 BB 張圖、建一張矩陣、更新一次,再換下一批。

怎麼抽這 BB 筆也有影響。實務上資料來自很多個來源,例如一個全是建築物的資料集、一個從網頁抓來的資料集。同一個來源裡的東西彼此比較像,所以大家會讓一個批次全部來自同一個來源,等於免費得到比較難的負例。講者舉了自己參與過的一家新創公司的例子,那家公司把這套方法用在自然語言上,他拿程式碼來說明:如果一個批次裡的範例分別是 Java、C++ 等不同程式語言,一眼就能分開,損失自然很小,模型沒有東西可學。所以批次應該至少來自同一種程式語言,甚至同一個程式碼庫。

計算成本在哪裡

有學生問為什麼要算矩陣裡每一格,那是 B2B^2 個數。講者的回答是 B2B^2 不是問題。真正的成本是算 embedding:2B2B 個輸入各要做一次前向傳播,每一次都要經過一個有數十億參數的網路。他說那才是瓶頸(「that's the real computational bottleneck」,約 43:58)。有了 2B2B 個向量之後,B2B^2 個內積很便宜。

講者一開始說式子寫成這樣是為了計算效率,從這裡看得出效率在哪(這個連結是本文的解讀):2B2B 次前向傳播換來 BB 個正配對和 B2−BB^2-B 個負配對,負配對幾乎是附送的。以 B=4,000B=4{,}000 來說(以下是本文的算術),8,000 次前向傳播得到 4,000 個正配對與 15,996,000 個負配對。

至於資料增強要做幾種版本:每次經過資料時隨機抽一組增強就好,想要更多版本就多跑幾輪。講者補充,實際上很少把資料跑超過幾輪。

本文補充:為什麼原版 SimCLR 多一個溫度參數

講義註腳說課堂用的式子是原始損失的簡化版。原版 SimCLR 會把相似度除以一個溫度參數 τ\tau 再取指數,課堂與講義都省略了。以下的說明是本文的補充,不是課堂內容。

如果 embedding 正規化成單位長度,SijS_{ij} 只能落在 −1-1 到 11 之間。最好的情況是 Sii=1S_{ii}=1、其餘全部是 −1-1(B>2B>2 時這在幾何上做不到,所以是寬鬆的上界),這時

pii≤ee+(B−1)e−1p_{ii}\le\frac{e}{e+(B-1)e^{-1}}

B=3B=3 時上界是 0.7870.787;B=128B=128 時只剩 0.0550.055,對應每列損失至少 2.902.90。也就是說,沒有溫度時,批次一大,softmax 就算在最理想的排列下也沒辦法把機率集中在正配對上。除以一個小於 1 的 τ\tau 等於把 logits 的範圍放大,讓這題選擇題有可能答得很有把握。例如取 τ=0.1\tau=0.1(本文隨手取的數字),logits 的範圍變成 −10-10 到 1010,B=128B=128 時同一個上界變成 e10e10+127e−10≈0.9999997\frac{e^{10}}{e^{10}+127e^{-10}}\approx 0.9999997。

語意檢索:把搜尋變成找最近的向量

(錄影 49:03–52:09)

有了 embedding 模型,語意檢索的做法很直接。

設定是:有一批文件(或圖片)d1,…,dnd_1,\dots,d_n,來了一個查詢 qq,要找出最相關的那幾份。圖片的版本是給一張新圖找相似的圖;文字的版本是給一個問題找最能回答它的文件。

做法分兩個階段:

  1. 事先:把每份文件丟進模型,算出 ϕθ(d1),…,ϕθ(dn)\phi_\theta(d_1),\dots,\phi_\theta(d_n) 並存起來。
  2. 查詢時:算出 ϕθ(q)\phi_\theta(q),然後找
i⋆=arg⁡max⁡i ϕθ(q)⊤ϕθ(di)i^\star=\arg\max_{i}\ \phi_\theta(q)^\top\phi_\theta(d_i)

通常不只取第一名,取前十名之類的也可以。

最簡單的實作是暴力法:把查詢向量跟每一個存好的向量算內積,排序。這一步不需要再跑神經網路,文件的向量早就算好了,手上只是一堆向量。講義補了成本:nn 份文件、mm 維向量,每次查詢是 O(nm)O(nm)(講義把文件數記成大寫 NN)。

要更快就用 k 最近鄰(k-nearest neighbor)演算法,從一大群點裡找出離查詢最近的 kk 個。講者說這是純演算法問題,不在這門課的範圍內,實務上直接用向量資料庫(vector database),它就是為了存向量並快速找最大內積而設計的。

一個小例子(本文自擬):沿用二維單位向量,查詢在 25°,四份文件在 10°、100°、50°、250°。內積分別是 cos⁡15°=0.966\cos 15°=0.966、cos⁡75°=0.259\cos 75°=0.259、cos⁡25°=0.906\cos 25°=0.906、cos⁡225°=−0.707\cos 225°=-0.707。取前兩名會拿到第 1 份和第 3 份。整個過程沒有比對任何字詞,相關與否完全由訓練時學到的幾何決定,所以 embedding 訓練得好不好直接決定檢索品質。

RAG:把檢索結果交給語言模型

(錄影 52:09–1:00:33)

講者先說語意檢索本身就有用。除了一些歷史悠久的系統,現在自己做搜尋系統大概都該用這套方法。他提到 Google 多半也用了某種程度的語意檢索,但外人不知道細節,那是很多方法的融合。

接著是大型語言模型出現之後的另一個用途:檢索增強生成(retrieval-augmented generation,RAG)。

動機

現成的大型語言模型由少數幾家前沿實驗室訓練,沒看過各家企業的內部資料,這些資料也不能流進它們的訓練集。個人也一樣:想要一個私人助理,但有些資料不想送出去給別人訓練。所以得在使用模型的當下,用某種方式把這些專有資料餵給它。

方法之一是微調:從基礎模型出發,用自己的資料繼續訓練。講者說這在某些應用是好主意,但常常太貴。資料量可能很小,而你不會想為了它在自己的 GPU 上架一個參數量龐大的模型,或是付微調與代管的費用。

流程

RAG 是比較簡單的那條路:

  1. 手上有查詢,以及一個語料庫(筆電裡的檔案,或企業的內部文件)。
  2. 先做檢索,從語料庫找出相關的文件。這一步就是上一節的語意檢索。
  3. 把檢索到的文件(通常是五份、十份左右)連同查詢一起放進語言模型的 context。
  4. 語言模型同時看到查詢與文件,產生答案。

模型的參數完全沒動,只是在對話介面裡多給了它一些資訊。講義把這一步寫成條件生成:若 R^(q)\hat{R}(q) 是對查詢 qq 檢索到的前 kk 筆,語言模型產生 y∼pψ(y∣q,R^(q))y\sim p_\psi(y\mid q,\hat{R}(q))。

取捨

講者列了 RAG 相對於微調的幾個好處,都來自「檢索這一塊幾乎跟語言模型完全分開」:

  • 權限控管容易。 有些使用者不該看到某些文件,例如公司尚未公布的獲利預測。只要在檢索階段不讓他們搜到那些文件就好。
  • 刪除容易。 把文件從語料庫刪掉,它就再也不會被檢索到。反過來,如果把企業所有資料都微調進模型,要讓模型「忘掉」某一筆很難;講者說這仍是相當開放的問題。
  • 便宜。 檢索步驟的成本遠低於訓練。

檢索不一定要用 embedding

最後兩分鐘講者補了一段前沿現況,而且話說得很保留。他說 Anthropic 現在在這個階段用的是另一類搜尋:讓語言模型產生正規表示式(regular expression)去搜關鍵字,而且會產生很多條、涵蓋關鍵字的各種變形。他也說自己不確定細節,因為前沿模型實際怎麼做外人看不到,這只是一般的理解。

這個做法在程式碼上效果很好,原因是程式碼庫的結構很規整:有清楚的檔案樹,變數名與函式名都是嚴格的關鍵字,正規表示式能做的事很多。其他情境下,語意檢索目前仍然很有用,也可以兩種併用。他的結論是:RAG 這件事確定是需要的,但檢索那一步該怎麼做要看應用,「we don't know exactly which one is the best way yet.」(約 59:31)。

講者說剩下的十分鐘不夠開始下一個主題,這堂就在這裡結束。

自我檢查

1. 只保留「正配對要靠近」這一項,最佳解是什麼?SimCLR 損失如何排除它?請算出 B=4B=4 時退化解的整批損失。

答:最佳解是把所有輸入對應到同一個向量,距離全為零。在 SimCLR 損失裡,這個解讓 SS 的每一格相等,每列 softmax 是均勻的 1/B1/B,每列損失 log⁡B\log B。B=4B=4 時整批損失是 4log⁡4≈5.5454\log 4\approx 5.545,不是零,所以它不再是最佳解。

2. 某一列的相似度是 (Si1,Si2,Si3)=(2, 0, 0)(S_{i1},S_{i2},S_{i3})=(2,\,0,\,0),正配對在第 1 格。算 ℓi\ell_i,並說明把 Si2S_{i2} 從 0 降到 −5-5 對損失的幫助有多大。

答:ℓi=−log⁡e2e2+1+1=−log⁡7.3899.389=0.240\ell_i=-\log\frac{e^2}{e^2+1+1}=-\log\frac{7.389}{9.389}=0.240。把 Si2S_{i2} 降到 −5-5 之後,分母變成 7.389+0.007+1=8.3967.389+0.007+1=8.396,ℓi=−log⁡0.880=0.128\ell_i=-\log 0.880=0.128。損失只少了約 0.110.11;如果再把它從 −5-5 降到 −10-10,分母幾乎不變,損失也幾乎不變。判準:能說出「已經很遠的負配對再推遠沒有用」,並連到難負例的動機。

3. 一個學生說:「負配對裡混了假負例,所以對比學習學到的向量一定會把同類的東西推開。」這句話哪裡不對?

答:判準是答案要包含兩點。一,隨機抽到同類的機率小,前提是資料夠多樣。二,損失是很多項的總和,其他項會把同類留在一起,那股錯的推力不會成功。能進一步指出「難負例找過頭時這個問題會真的變嚴重,所以要停手」更好。

4. 批次大小從 BB 變成 2B2B,前向傳播次數與相似度矩陣的格數各變成幾倍?哪一個決定訓練成本?

答:前向傳播從 2B2B 變成 4B4B,是兩倍;矩陣格數從 B2B^2 變成 4B24B^2,是四倍。成本由前向傳播決定,因為每一次都要經過整個網路,而矩陣的每一格只是一次內積。

5. 一家公司要讓語言模型回答內部文件的問題,其中一部分文件只有財務部門能看,而且文件每週都會增刪。微調與 RAG 各自要怎麼處理這兩個需求?

答:RAG 在檢索階段依使用者權限過濾文件,增刪文件只需要更新語料庫與對應的向量。微調把內容寫進參數,無法依使用者區分誰能看到什麼,刪除某一份文件的影響也沒有乾淨的做法,每週更新還要重新訓練。判準:答案要指出兩種做法的差別來自「知識放在模型外面還是裡面」。

對應閱讀

主講義第 16 章 Representation Learning,第 196–201 頁:

  • 16.1 Supervised pretraining(第 196 頁):對應本文第二節。講義把最後一層記成 UU,課堂口頭說的是 WW。
  • 16.2 Contrastive learning(第 196–198 頁):對應本文的對比學習與 SimCLR 兩節。損失函數與課堂相同。講義用哈士奇與大象當例子,課堂用貓與飛機。單調性的檢查在講義只是一則註腳(−log⁡pp+q-\log\frac{p}{p+q} 對 pp 遞減、對 qq 遞增),課堂上因為學生提問而完整走了一遍。圖 16.1 是正負配對的示意圖。
  • 16.3 Semantic retrieval(第 198–200 頁):對應語意檢索一節。
  • 16.4 Retrieval-augmented generation(第 201 頁):對應 RAG 一節,講義只有一段。

課堂有、講義沒有的: 幾股力互相平衡的說法與假負例的整段問答;多分類的詮釋;難負例與 negative mining;圖文配對與「標題配內文」的正配對做法;批次從同一來源抽;2B2B 次前向傳播才是成本瓶頸;RAG 相對於微調在權限、刪除、成本上的取捨;以正規表示式搜尋取代語意檢索的做法。

講義有、課堂沒講的: 用類別標籤定義正配對的 supervised contrastive 做法;近似最近鄰索引的種類(HNSW 這類圖索引、量化、倒排索引、以群中心為主的索引);檢索的評估指標 Recall@k 與 NDCG@k(式 16.1–16.3)。如果你要自己評估一個檢索系統,第 199–200 頁這一段要另外讀。

標題與內容: 影片標題的「LLMs, Next-Word Prediction Loss」對應的是講義第 17 章,這堂沒有講到。

系列導覽:上一堂 第 12 堂:擴散模型的雜訊預測損失、基礎模型與 LoRA|下一堂 第 14 堂:大型語言模型的 next-token 損失與 attention|課程總覽

課程影片來源

原始影片:Stanford CS229 Machine Learning | Spring 2026 | Lecture 13: LLMs, Next-Word Prediction Loss

查核日期:2026-10-12。

內容核對:已依字幕核對(2026-10-12):整份英文字幕(約 61 分鐘)讀完,文中引用的英文原句與時間戳都來自字幕,時間戳是該句所在字幕段落的起點。沒有看板書畫面,損失函數的寫法依講義第 16.2 節還原;講者口頭在「列」與「欄」之間改口的地方已在文中註明。字幕中學生提問有數處收音不清,本文只寫講者回答得出明確內容的部分。

參考資料