第 5 講・互動版

文字接龍:手刻一個會寫字的語言模型

一言以蔽之:ChatGPT 其實就是文字接龍而已。

好啊,我們來開始上課吧。

今天這句話我要放在最前面講,因為它就是整堂課的核心:ChatGPT 這麼神奇,它到底在幹嘛?它其實就是在玩文字接龍而已。

你現在大概覺得我在唬爛。一個可以幫你寫程式、幫你改論文、跟你討論人生的東西,你跟我說它只是在接字?

對。今天這堂課我們就要來證明這句話是真的。而且不是用嘴巴證明——我們要在這個網頁裡,真的 train 一個語言模型出來。沒有預錄、沒有假資料,下面每一段中文都是你的瀏覽器現場算出來的。

今天的 Roadmap

這堂課我們分成六段:

  1. 先把「產生文字」翻譯成一個機器學習問題(你會發現它是分類問題)
  2. 最笨的作法做一次——直接數數看。然後看著它撞牆
  3. 想辦法多看幾個字,然後撞上第二面牆(組合爆炸)
  4. 神經網路登場。手刻一個語言模型,按下按鈕真的把它 train 起來
  5. 生成策略——greedy、temperature、top-k。這段你今天就用得上
  6. 最後拿尺來量:為什麼 loss 低,還不代表答案可信?

最後我們會回答一個問題:它為什麼會胡說八道?

這一版是互動的。每一個滑桿你都可以拉、每一個按鈕你都可以按,文字會馬上重新生成。請務必動手——這堂課有一大半的東西是動手才會有感覺的。

先不要講定義,先看兩個你天天遇到的現象

你每天都在用這種聊天機器人。所以我不從「什麼是語言模型」開始講,那個太無聊了。我從你已經有感覺的兩件事開始。

現象一:它為什麼可以一直講下去?你問它一句話,它可以講一大段;你叫它繼續,它還能繼續。它好像永遠不會「沒話講」。這件事本身就很奇怪對不對?

現象二:它為什麼會一本正經地胡說八道?你問一個很冷門的問題,它給你一個聽起來超級專業、有頭有尾、連參考文獻都幫你列好的答案。結果你去查,那篇論文不存在。

欸這就很妙了。它不是「我不知道」,它是很有自信地講了一個假的東西

你可能會想說:這兩件事應該沒什麼關係吧?一個是優點,一個是缺點。

它們是同一件事。今天講完你就會知道為什麼。

把「產生文字」變成一個機器學習問題

好,那我們從最基本的地方開始。假設現在螢幕上已經有一段文字:早上小明一個人在教。請問下一個字是什麼?

你腦中大概會冒出「室」。你怎麼知道的?因為你看過很多中文,你知道「在教」後面接「室」很合理,接「鳳梨」很怪。

好,這就是全部了。語言模型在做的事情就是這件事:輸入是目前為止的所有文字,輸出是下一個字是什麼。產生一整段文字,就是把這件事重複做很多次——接一個字 → 把它加到後面 → 再拿去問下一個字 → 再接一個字 → ⋯⋯

這就是「文字接龍」。現象一解決了:它為什麼可以一直講下去?因為它每次只負責接一個字啊。只要有前文,它就永遠有得接。它根本沒有「講完」這個概念。

注意,這其實是一個「分類問題」

這邊有一個轉折,你要看清楚。

「下一個字是什麼」——所有可能的字有幾個?假設中文常用字有五千個,那答案就是從這五千個裡面挑一個。從固定的幾個選項裡面挑一個,這叫什麼?這叫分類(classification)。

所以語言模型不是什麼神秘的東西。它就是一個五千選一的分類器而已,只是這個分類題它一秒鐘要做好幾百次。

那我們把第 1 講的三步驟套進去看看:

步驟
這堂課的版本
1. Model
輸入前文,輸出「每個字是下一個字的機率」
2. Loss
正確答案的那個字,機率有沒有夠高?
3. Optimization
Adam(第 3 講講過了)

你看,還是同一件事。第 1 講講的三步驟,到今天完全沒有變,變的只有第一步的 model 長什麼樣子。我第 1 講就跟你講過了對不對。

先準備資料

做機器學習永遠的第一步:先看資料。我們今天不下載任何東西,訓練文字是用五個句型 + 五組可替換的詞,在你的瀏覽器裡現場生出來的。

為什麼用句型而不是隨便抓一篇文章?因為我們的模型很小、資料很少。用有規律的文字,你才看得出來它到底有沒有學到東西。等一下你就知道我的用意了。

我們手上的全部家當: 個字,字表大小
前三句長這樣:

然後電腦不認識「早」這個字,它只認識數字,所以第一件事是編號。這裡我們做的是 character-level(字元級)——一個字就是一個單位。真正的大模型用的是介於「字」跟「詞」中間的東西,叫做 token(可以想成詞的碎片),但道理完全一樣:把文字切成一個一個單位,然後編號。就這樣而已。

好,資料準備好了。接下來我們來做一件很笨的事。

第一招(最笨的):直接數數看

假設你完全不會機器學習,但我叫你做文字接龍。你會怎麼做?

你八成會說:這簡單啊,我去數嘛。我把整篇文章拿來,數「早」這個字後面接過哪些字、各接了幾次。下次看到「早」,我就照這個比例抽一個出來。

這個想法非常合理,而且它有名字,叫做 bigram(二元組)。「bi」就是二,意思是一次只看兩個字:前一個字,跟後一個字。所謂的 bigram model,其實就是一張表而已:

分子是「i 後面接過幾次 j」,分母是「i 後面總共接過幾個字」。數數字然後除一除,沒有別的了。下面那張表就是它本人——點任何一個字,看它後面都接了什麼。

動手玩 ①・bigram 就是一張表
整張表()。亮=機率高,黑=那格是空的。紅線是你選的那一列。
你選的字後面,各個字的機率
點一個字,看它後面最常接什麼:
這張表寫出來的文章 (真的照機率一個字一個字抽出來的)

先講公道話:它不是完全不會。loss 從亂猜的 掉到 ,而且它寫出來的東西看起來是有中文字的形狀的,詞也大致是對的。

但你仔細讀上面那段。找找看有沒有這幾種東西:「助教」後面接「教室」,黏成「助教室」開頭是「如果」,結尾卻是問號;一句話講到一半換了句型,前半句還在「小明問老師:」,後半句已經滑到「⋯⋯可是老師不同意。」去了。按幾次「換一段文章」,錯的地方每次都不一樣,但毛病永遠是同一種。看出問題了嗎?

它每接一個字都接得很順,但整句話是壞的。

它從一個句型滑到另一個句型,自己完全不知道。

為什麼呢?因為它每接一個字,就把前面全部忘光了。它看到「教」的時候,它不知道這個「教」是「助教」的教,還是「教室」的教。它只看得到一個字,它沒有記憶

這就是 bigram 的天花板。那怎麼辦呢?

第二招:那我多看幾個字不就好了

這個想法太自然了。既然看一個字不夠,那我看前面 N 個字嘛。看前 2 個字叫 trigram,看前 N 個字就叫 n-gram。作法完全一樣,只是那張表從「一個字 → 下一個字」,變成「前 N 個字的組合 → 下一個字」。

聽起來完全沒問題對不對?那我們來算一下這張表要多大。字表有 個字,看前 個字,前文的組合有 種。我們的 你自己心裡先猜一下, 的時候是多少。猜完再拉下面的滑桿。

動手玩 ②・把表格拉大,看它怎麼爆炸
左邊是「理論上要填幾格」跟「你手上有幾個字」的對比(log 尺度,一格=十倍)。
1

這就是「稀疏」,而且它是絕症

你把滑桿拉到 8 看看。前文有 種組合,而我們手上只有 個字。你有八百兆個格子要填,但你只有五千筆資料。就算你把全世界的中文都拿來,也填不滿。

而且那個讀數還有兩欄要看。第一欄是「真的出現過的前文」: 的時候是 。你把這個數字跟八百兆放在一起看——你有一張八百兆格的表,總共只填到了兩千多格,其他全是空的。空格代表什麼?代表你遇到沒看過的前文時,連猜都沒得猜——分母是 0,這個模型直接當機。

第二欄更狠: 越大,「只出現過一次」的前文比例越高。到了 的前文在整份資料裡只出現過一次。只出現過一次你要怎麼估機率?你只能說「這個前文後面 100% 接這個字」,然後就徹底 overfit 了。

而且注意喔,我們這份資料是用五個句型生出來的,重複性高到不像話。換成真實世界的文章,這兩個數字只會更絕望。

這個問題叫做 sparsity(稀疏)。白話文就是:格子太多,資料太少,大部分格子永遠是空的。

而且這不是「資料再多一點就好」的問題。 是指數成長,你資料加十倍,我 N 加 1 就吃掉了。這條路是死的。

那怎麼辦呢?

關鍵轉折:不要背表格,去學一個函式

好,那神奇的地方來了。

我們卡住的原因是什麼?是因為我們一直想把答案存起來。存成一張表,每個前文對應一個答案。表太大了,所以不行。

那換個想法:我們不要存答案,我們去學一個函式。

這句話有沒有很耳熟?對,這就是第 1 講的第一句話:機器學習就是找一個函式。

函式的好處是什麼?函式不用把每個輸入都存起來。你給它一個沒看過的輸入,它一樣算得出一個輸出。比如說我們的資料裡有「在教室寫程式」也有「在宿舍寫程式」,表格法會把這兩個當成完全無關的兩格;但函式可以學到「教室跟宿舍是同一類東西」,於是即使它沒看過「在頂樓寫程式」,它也猜得出來後面該接什麼。

這就是為什麼要用神經網路。不是因為它比較潮,是因為表格法真的走不下去了。

我們的 model 長這樣

先把架構講清楚,四層而已:

第幾步
在幹嘛
① Embedding
查表:前 8 個字的編號,各換成一個長度 的向量,攤平成 個數字
② Hidden layer
乘一個矩陣、加 bias、過 tanh → 個數字
③ 線性層
再乘一個矩陣 → 個分數(叫 logits)
④ Softmax
個機率,加起來等於 1 ← 這就是「下一個字」的機率分布

我們固定只看前 8 個字。這個 8 有個名字,叫 context window(上下文長度)

你可能會想說:那不是跟 n-gram 一樣只看 8 個字嗎?對,看的範圍一樣,但存法完全不同——n-gram 要存 格,我們這個模型只有 個參數。表格法要存的東西是我們模型參數的 。差別就在這裡。

Embedding 其實就是查表而已

這是今天第一個要拆光環的詞。Embedding 聽起來很玄,它其實就是一張表。

我們有 個字,我準備一張 列的表,每一列是一個長度 的向量。看到第 5 個字,我就把第 5 列抓出來。就結束了。

那這張表裡的數字是誰決定的?沒有人決定,是 train 出來的。這張表就是模型參數的一部分,跟 一樣會被 gradient descent 更新。我們沒有告訴機器任何一個字的意思,我們只叫它做文字接龍——為了把接龍做好,它自己會去調整這張表。

那為什麼要多這一層?直接用編號當輸入不行嗎?不行。因為編號是我隨便排的,編號 5 跟編號 6 的字一點關係都沒有。你直接把編號丟進去,等於在告訴模型「5 號字比 4 號字大一點」,那是完全沒有意義的資訊。換成向量以後,字跟字的關係就變成可以學的東西了。

Loss:為什麼是 cross-entropy,不是 MSE?

模型最後吐出 個機率,正確答案是其中一個字。那要怎麼打分數?第 1 講我們用 MSE,這裡我們要換成 cross-entropy(交叉熵)

就這樣。模型給正確答案的機率,取 log,加負號。沒有更複雜的東西。

那你一定會問:為什麼不能用 MSE?假設正確答案是「室」,模型 A 給它 0.01 的機率,模型 B 給 0.001——B 錯得離譜十倍,該不該被罵得更慘?當然要吧。我們來看兩種 loss 各自怎麼罰:

給正確答案的機率
MSE 的懲罰
cross-entropy 的懲罰
0.100
0.8100
2.303
0.010
0.9801
4.605
0.001
0.9980
6.908

你會發現 MSE 到後面就罵不動了。機率從 0.01 掉到 0.001,MSE 的懲罰從 0.980 變成 0.998,幾乎沒差,它已經懶得罵你了。懲罰沒變代表什麼?代表梯度接近 0;梯度接近 0 代表什麼?第 1 講講過——參數不會動。模型錯得離譜卻收到一個「還好啦」的訊號,它就卡在那邊 train 不動了。

cross-entropy 不會。你越是打死不信正確答案,它就罵你罵得越兇。分類問題用 cross-entropy,理由就是這個。

還有一個地方順便講掉:softmax 配 cross-entropy 的時候,梯度會化簡成一個漂亮到不像話的形式:

「模型的機率」減掉「正確答案」。就這樣。意思很直白:你給錯的字多少機率,就往下壓多少;正確答案你給了 ,那就往上推 你越有自信答錯,被推得越用力。

好,硬 train 一發

下面這個東西你一定要按。它是真的在你的瀏覽器裡跑 forward、backward、Adam,沒有預錄的影片,也沒有事先算好的答案。

而且我們會在四個時間點各叫它寫一段文字:還沒 train 的時候、第 步、第 步、train 完。四段用的是同一組隨機種子、同一個生成函式,差別只在參數被 train 過了多少

動手玩 ③・訓練現場
完整的訓練過程(灰線=亂猜,橘線=bigram 用數的)
放大看後半段——它什麼時候超車 bigram 的?
【訓練前】參數還是亂數
【第 步】剛學會標點跟換行
【第 步】句型出來了,還有錯字
【train 完】⟵ 今天的重點畫面
正在檢查手刻的 backprop⋯⋯

從一團亂碼,變成通順的句子。而且它學到的不只是「哪些字常一起出現」

而我們沒有教它任何一條規則。我們只叫它做一件事:把下一個字接對。資料倒進去,硬 train 一發,這些東西它自己就長出來了。

等一下,它怎麼知道什麼時候要停?

你看它生成出來的東西:它會自己在該結束的地方畫句號,然後換行。我們從來沒有寫過任何一行「如果句子夠長就停下來」的程式。那它是怎麼知道的?

答案很反高潮:「結束」對它來說,也只是一個字而已。

句號是一個字,換行也是一個字,它們跟「教」「室」在模型眼中沒有任何差別,都只是字表裡的一個編號。所以「什麼時候該結束」根本不用特別教——它在學文字接龍的時候就順便學會了。證據在這裡(這是剛剛 train 完的模型現場算的):

三步接得非常果斷。真正的大模型也是這樣做的,只是它們會另外準備一個特別的 token 叫 EOS(end of sequence)。模型接出 EOS 的那一刻,程式就知道「好,它講完了」,迴圈就停下來。所以「它什麼時候停」的答案是:它想停的時候就停,而「想停」也是 train 出來的。

不過這邊我要誠實跟你講一件事

不然就變成賣藥的了。我另外用同一組句型生了一份模型沒看過的文字,拿去量它的 loss:

你會發現一件很有意思的事:訓練 loss 比理論下限還低。

「理論下限」是什麼?我生資料的時候,每句話都是隨機選一個句型再填五個空格,這六個選擇是我用亂數決定的,神仙也猜不到。所以就算是一個完美的模型,loss 也不可能低於那個數字。

那我們怎麼會低於它?除非——它在背答案。

對,我們的模型 overfit 了。一萬多個參數配五千個字,它有足夠的容量把訓練資料整段背起來。所以你換一份它沒看過的文字,loss 立刻彈高一大截。它背得很熟,但沒有你以為的那麼會舉一反三。

這就是第 3 講講的 overfitting,在這裡活生生出現一次。我為什麼要特別講這個?因為這件事本身就是 hallucination 的伏筆。等一下你就知道了。

生成策略:同一個模型,可以判若兩人

好,接下來這一段最實用,你今天回去馬上用得到。

我們的模型現在會吐出 個機率。問題來了:拿到機率之後,到底要怎麼挑那個字?

最直覺的作法:挑機率最高的那個啊。這個叫 greedy decoding(貪婪解碼)。聽起來完全沒毛病對不對?每一步都選最好的,結果一定最好嘛。我們來跑跑看:

greedy:每一步都挑機率最高的

你看,它寫著寫著就開始一字不差地重複前面寫過的句子,然後永遠出不來。

為什麼呢?想想看,greedy 是完全確定性的——同樣的前 8 個字,一定得到同樣的下一個字。所以只要前 8 個字曾經重複出現過一次,後面就一定完全重複。它掉進去就出不來了。

這不是我們模型的 bug 喔。這是 greedy decoding 本身的性質。你用最大的模型,開 greedy,一樣會鬼打牆。

而且 greedy 還有第二個問題:它每次都挑最有把握的字,所以寫出來的東西一定是最平庸、最無聊的那種。你叫它寫詩,它會寫出全世界最安全的詩。

那怎麼辦呢?很簡單嘛——不要每次都挑第一名,照機率抽就好了。機率 0.5 的字就有一半機會被抽到,機率 0.01 的字就偶爾出現一次。這叫 sampling(取樣)

但這樣又有新問題:抽太保守,還是很無聊;抽太放,就開始亂講。我要一個旋鈕,可以調鬆緊。這個旋鈕就叫 temperature(溫度)

Temperature:一個除法而已

temperature 聽起來很厲害,它其實就是在 softmax 之前先除一個數字而已:

就這樣。一個除法,然後就結束了。那它為什麼有用? 很小,所有分數的差距被拉開,最高分那個會被推到接近 1 → 幾乎等於 greedy; 很大,差距被壓扁,大家機率都差不多 → 越來越接近亂抽。

與其用講的,你自己拉。左邊那張圖是 log 尺度——請盯著那條「尾巴」看。

動手玩 ④・temperature
前 15 名候選字的機率(log 尺度)。灰線是 的原始分布,當基準。
「第 5 名以後的字」加起來佔多少機率
1.00
用這個溫度寫一段 (模型參數完全沒變,只換了抽字的方式)

把溫度慢慢往上推,看那條尾巴怎麼被整條抬起來。抬多少?右邊那張圖才是重點——「第 5 名以後的字」加起來佔的機率,從幾乎是 0,一路漲到三成以上。

你想想看這代表什麼:溫度開到最高的時候,有將近三分之一的機率是分給那些「根本不該被選到」的字。也就是說你每接三個字,大概就有一個是從垃圾堆裡抽出來的。

而那個錯字接上去以後,它又變成後面的前文,整句話就跟著歪掉了。這就是為什麼高溫生成看起來會「發瘋」——不是因為它每個字都亂寫,是因為它偶爾寫一個超級離譜的字,然後錯誤會自己滾雪球

那怎麼辦呢?

Top-k:把離譜的選項直接砍掉

很粗暴但很有效的作法:只留機率最高的 k 個字,其他直接不准選。這就是 top-k sampling

這樣你就可以「又要溫度高、又不要它亂講」——溫度負責在合理的候選字裡面增加變化,top-k 負責把不合理的選項擋在門外。

下面兩根滑桿一起玩:先把溫度拉到 2.5 看它發瘋,然後把 k 從 73 一路往下拉,看文字怎麼一句一句長回來。

動手玩 ⑤・top-k:把尾巴砍掉
2.50
4
同樣的溫度,砍掉尾巴以後

你把 k 拉到 1 看看——那就是 greedy,鬼打牆又回來了。往上拉一點點,句子開始有變化;拉太高,錯字就跟著回來。這中間有一個甜蜜點,而它會隨著溫度移動。這就是為什麼實務上這兩個旋鈕要一起調。

這段的 punchline

你把上面所有的輸出擺在一起看:greedy 的、低溫的、高溫的、加了 top-k 的。它們看起來像是四個不同的模型寫的對不對?

可是它們是同一個模型。參數一個都沒有變,一根寒毛都沒動。我們從頭到尾只改了「拿到機率之後怎麼挑」而已。

這件事給你的實務教訓是:

當你覺得一個模型「很爛」的時候,先確認一下是不是 sampling 參數沒調好。

這是真的很常發生。有人抱怨模型講話很無聊、一直重複——結果是 temperature 設太低。有人抱怨模型在胡言亂語——結果是 temperature 開太高又沒設 top-k。模型沒問題,是旋鈕沒轉對。

那,它為什麼會胡說八道?

好,我們回到開場的現象二

我現在給模型一個開頭,然後問它下一個字。你想想看,這個問題有標準答案嗎?

動手玩 ⑥・同一個開頭,寫六次
同一個開頭,同一個模型,讓它寫六次

同一個問題問六次,它給了你好幾個不同的答案。而且每一次都講得斬釘截鐵,一點都不心虛。

為什麼會這樣?因為我在生訓練資料的時候,那個地點是我用亂數填的。老師半夜在哪裡,這件事在資料裡根本沒有答案

而且模型自己知道它不知道——你看上面那張機率分布,就是在猜嘛。但是。一旦我們抽了一個字出來接上去,這個「猜」的痕跡就消失了。讀者看到的是一句斬釘截鐵的話,沒有任何地方寫著「本句信心水準 」。

這就是 hallucination 的機制。你回頭看我們今天做的每一件事:

從頭到尾,沒有任何一項在檢查「這句話是不是真的」

它的訓練目標從頭到尾只有「下一個字接得像不像」,沒有一項是「講得對不對」。

「像」跟「對」是兩件事。它被訓練成很會「像」,所以它會產生讀起來非常像真的、但完全是編的東西。它不是在說謊——說謊要先知道真相。它只是在接字而已。

而且還記得剛剛的 overfitting 嗎?資料裡真的有的東西,它背得很熟;資料裡沒有的東西,它就用學到的句型幫你補一個,而且補得非常通順。這就是為什麼它可以幫你「列出」一篇不存在的論文——論文標題的格式它學得很好,至於那篇論文存不存在,從來就不在它的 loss 裡面。

等一下,loss 很低,不就是很厲害嗎?

這裡我們撞上最後一面牆。

剛剛 train 的時候,我們一直盯著 cross-entropy,它變小我們就很開心。可是你已經親眼看到:model 可以把句子接得很順,同時把根本不知道的地點講得斬釘截鐵

所以只拿一把尺來量生成式 AI,會發生什麼事?它會開始專門討好那把尺。

比如說,考試只看文句通不通順,model 就可能交出一篇非常順、事實全部亂寫的答案。只看答案有沒有命中某個關鍵字,它也可能學會塞關鍵字,根本沒有回答問題。

怎麼辦呢?不是發明一個號稱「萬能」的分數,而是把問題拆開:

你到底想量什麼
要問的問題
流暢度
讀起來像不像自然語言?
事實性
裡面的主張能不能被可靠來源支持?
任務完成度
使用者交代的事真的做完了嗎?
安全性
即使答案看起來有用,會不會造成不該有的傷害?

這四把尺有時候還會互相打架。拒絕所有問題也許很安全,但完全沒用;每個問題都講得又快又滿,也許看起來很能幹,但出錯時更危險。

評估不是替 model 頒一張總成績單,而是先說清楚你在乎哪一種「好」。

這也是為什麼看到 benchmark 上的一個數字時,不要立刻問「誰比較強」。先問:它量的是哪一種能力?沒量到的是什麼?數字沒有騙你,是你可能問錯了數字。

兩個大家常常搞錯的地方

誤解一:「模型是先在心裡想好一整句話,再講出來的。」

很多人會這樣覺得,但其實不是。它是一個字一個字接的,而且接每一個字的時候,它並不知道自己這句話最後要講什麼。你看我們的生成迴圈就知道了——裡面沒有任何「計畫」的步驟,只有:算機率、抽一個、接上去、再算一次。那為什麼講出來的話還是有頭有尾?因為「有頭有尾」這件事本身,也是從訓練資料裡學到的接字習慣而已

誤解二:「temperature 是在調模型的創造力。」

千萬不要這樣講。temperature 一根寒毛都沒有碰到模型。你剛剛在「動手玩 ④」拉滑桿的時候,模型算出來的那 個分數從頭到尾一模一樣,變的只是你怎麼從那些分數裡面抽。

這個差別很重要:模型的能力是 train 出來的,改 temperature 改不了它的能力,只能改它的發揮方式。你把溫度調高,它不會因此多知道一件事實——它只會更常去抽那些原本就排在後面的字。

那我們這個小東西,跟 ChatGPT 到底差多少?

我們要誠實。差蠻多的。但差在哪裡,你現在有能力聽懂了。

  1. 規模。我們一萬多個參數、五千個字。真正的大模型是幾千億個參數、幾兆個 token。這個差距大到會產生質變——資料夠多的時候,模型學到的就不只是句型,而是各種知識跟推理的模式。
  2. 架構。我們是「固定看前 8 個字,攤平,丟進一層 hidden layer」。這有兩個很硬的限制:只能看 8 個字,而且每個位置的重要性是寫死的。真正的模型用 attention(第 4 講那個)——它可以看幾千幾萬個字,而且會自己決定要注意哪幾個字
  3. 單位不是字元。我們一個字一個單位,真正的模型用 subword token,比較有效率。
  4. pretrain 完還沒結束。一個只做過文字接龍的模型,你問它「台灣的首都是哪裡?」,它可能回你「台灣的第二大城市是哪裡?」——因為在網路文章裡,問題後面接問題是很常見的接法啊,它接得很合理

要讓它變成「會回答你」的助理,後面通常還有兩段路:

階段
它在學什麼
你可以怎麼想
Pretraining
從大量文字學會下一個 token
自己讀遍圖書館,累積實力
Instruction tuning
從「指令 → 好回答」的例子學會照指示做事
有老師示範題目要怎麼答
Preference alignment
從人類偏好或回饋學會哪種回答更合適
同一題有兩份答案,人類告訴它比較喜歡哪份

後兩段常被合稱為對齊訓練:讓 model 的行為更接近人類真正想要的樣子。但不要誤會喔——對齊不是把文字接龍引擎拆掉換一顆新的。核心仍然是預測下一個 token,差別是它又看了新的例子、接受了新的評分方式。

核心的機制,跟你剛剛在這個頁面上 train 出來的那個,是一樣的。輸入目前為止的文字,輸出下一個字的機率分布,抽一個,接回去,再算一次。就結束了。

隨堂小測驗

三題,選了馬上告訴你對不對。

換你動手:三個練習

光看是學不會的,你一定要自己動手。

  1. 練習 1(暖身,就在這個頁面上做):回到「動手玩 ④」,把 temperature 從 0.2 一路推到 3.0,找出開始出現錯字的那個溫度大概在哪裡。然後到「動手玩 ⑤」,同一個溫度加上 top-k = 4 再看一次——那個門檻往上跑了多少?
  2. 練習 2(核心,要寫程式):打開 notebooks/05_文字接龍_手刻一個會寫字的語言模型.ipynb,在 TEMPLATES 加一個新句型,或在 PLACES 加兩個新地點,然後重跑「準備資料 → 建字表 → train」。觀察兩件事:(a) 新句型 train 得起來嗎?(b) 如果新地點只出現過三、四次,模型學得會嗎?
  3. 練習 3(想一想,先不用寫程式):我們的 W1 是「context window × embedding 維度」乘上 hidden size。目前 W1 個參數。那如果我要看前 1000 個字呢?算算看 W1 會變成多大。

練習 3 你會算出一個很誇張的數字。這正是為什麼需要 attention——把 context window 用「攤平接一個大矩陣」的方式做,是撐不住的。(答案: 個參數,是現在的 125 倍。)

好,我們來複習一下今天講了什麼

  1. 語言模型就是一個分類器。輸入是目前為止的文字,輸出是「下一個字是誰」的機率。一直重複做,就變成一篇文章。這還是第 1 講的三步驟,一步都沒有多。
  2. 為什麼要用神經網路?因為數表格的方法會被 淹死。前文的組合是指數成長的,資料永遠不夠。所以我們不存答案,我們學一個函式
  3. 同一個模型,換一個 sampling 策略,可以判若兩人。greedy 會鬼打牆,溫度太高會發瘋,top-k 可以把離譜的選項擋掉。模型很爛的時候,先檢查旋鈕。

然後有一個觀念,我希望你今天下課只記得這一個:它的訓練目標從頭到尾只有「接得像不像」,沒有一項是「講得對不對」。這就是為什麼它會一本正經地胡說八道。這不是 bug,這是文字接龍這個目標本來就管不到的地方

還有一個配套的觀念:評估不是頒一張總成績單。你要先說清楚在乎哪一種「好」,因為那四把尺會互相打架。

最後,我們回到開場那句話。我一開始說:ChatGPT 其實就是文字接龍而已。你那時候大概覺得我在唬爛。

現在你已經在這個頁面上 train 過一個了。你看過那個迴圈:算機率、抽一個字、接回去、再算一次。裡面沒有魔法。那句話是真的。

那,留一個問題給你

文字有一個很棒的性質:它有先後順序。第一個字、第二個字、第三個字。所以我們可以一個一個接,接到天亮。

圖片呢?

一張圖有一百萬個 pixel。請問哪一個是「第一個」pixel?左上角嗎?那為什麼不是右下角?中間那個 pixel 的「下一個」是誰?

圖片沒有天生的順序。所以「一個一個接」這招,在圖片上直接失效。

那怎麼辦呢?下一堂課我們來看一個完全不同的想法——不要一個一個接,而是先把整張圖弄得亂七八糟,再學著把它一步一步還原回去。那個東西叫 Diffusion。

以上就是我今天想跟大家分享的內容。