LLM 學算術

上週一位前同事貼了篇文章:

看一個YouTube 影片測試Gemini and Chatgpt, 一個簡單的邏輯問題,一瓶汽水一元,兩個空瓶可以換一瓶新的汽水,請問20元可以喝到幾瓶汽水,兩個都答錯,

原本以為寫程式是個邏輯能力的展現,但是GenAI 邏輯不好為何可以寫程式呢?

然後臉友們試了各個 LLM, 沒有一個可以正確解答這題. 我試的 copilot 一開始答錯, 算出 30 瓶也就罷了. 提示它這是個遞迴的問題, 它竟然回答:

你說得對,這確實是一個遞迴的問題。讓我們再次計算一下,…

總結來說,你可以喝到無限多瓶汽水,只要你不停地用兩個空瓶換取新的汽水。

希望這個解答對你有幫助!

那麼正解是什麼呢? 簡單的解法是使用 chain of thought (COT), 很像是建構式數學. 我們要按照人類的思路一步一步地教它. 雖然我下面的描述提到一點 Action, 而不是純 Reaseon, 但 RecAct [3] 才是終極版結合 reason 和 action 的提示方式.

  1. 首先 20 元可以買 20 瓶汽水.
  2. 喝完 20 瓶汽水得到 20 個空瓶, 可以換 10 瓶新的汽水.
  3. 喝完 10 瓶汽水又得到 10 個空瓶, 可以換 5 瓶汽水.
  4. 喝完 5 瓶汽水又得到 5 個空瓶, 可以換 2 瓶汽水, 奇數的汽水產生一個空瓶還沒有兌換.
  5. 喝完 2 瓶汽水又得到 2 個空瓶, 可以換 1 瓶汽水.
  6. 喝完 1 瓶汽水又得到 1 個空瓶, 加上還沒兌換的 1 個空瓶, 又可以換一瓶汽水.
  7. 喝完 1 瓶汽水又得到 1 個空瓶 沒有還沒兌換的空瓶, 不能再喝了.

However, Copilot 無法學會. 繼續堅持 30 瓶的答案. 這招對它無效. 但是 ChatGPT 學得會 20 元的例子. 但改問它 10 元, 30 元可以喝幾瓶就沒辦法了.

根據我的理解, LLM 不是 encoder only, decoder only, 就是 encoder + decoder. 無論哪一類模型, 都要把文字轉向量. 在向量空間中, 類似的詞都有類似的向量. 所以狗, 犬, 㹴的向量都彼此靠近, 和貓距離比較遠. 以 encoder 類 LLM 來說, training 時隨機遮掉句子中的一個詞, 讓 LLM 學習填空. 學得再不完美, 關鍵字也會是同義字.

但是數字 1,2,3,4… 就沒有相關性了. 它本身沒意義, 很多個 1 也比 1 個 9 大 (11111…111 > 9). 因此看到數學很容易胡說八道, 但背公式可以.

想要徹底解決 LLM 數學不好的問題, 是採用 program-aid Language (PAL) model [1]. 也就是說, 我們叫 LLM 寫個小程式, 把原理寫成註解, 計算寫成數學式. 把這整個當作 prompt, LLM 應該就能學會了.

我不打算暗示 LLM 怎麼寫出 Python script, 於是我試著這樣問:

一瓶汽水一元,兩個空瓶可以換一瓶新的汽水,請問20元可以喝到幾瓶汽水?

# 一瓶汽水一元

Buy_bottle = money / 1

# 喝汽水

Used_bottle = buy_bottle

#  假如 used_bottle != 0, 兩個空瓶可以換一瓶新的汽水

While  used_bottle >= 2, do the following calculation

If used_bottle %2 = 0, buy_bottle += use_bottle / 2, used_bottle = 0

Else if used_bottle %2 = 1,  buy_bottle += use_bottle / 2,  used_bottle = 1

Answer = buy_bottle

結果 ChatGPT 算出 45 瓶, 因為它換過的汽水瓶又拿去換. 反倒是 Copilot 比較好一點. 雖然功敗垂成, 但是好歹它有聽進去我的話了~~~ 當然我的提示也有容易誤解的地方, 當我想寫得更詳細, 它卻答得更差. 所以我只能保留這一版.

這個問題是一個經典的換瓶問題,可以通過迭代計算來解決。讓我們根據你提供的算法來計算一下:

  1. 首先,用20元可以買到20瓶汽水。
  2. 喝完這些汽水後,你會有20個空瓶。
  3. 根據算法,每兩個空瓶可以換一瓶新的汽水,所以你可以換10瓶新的汽水。
  4. 現在你又有10個空瓶,可以再換5瓶新的汽水。
  5. 接著你又有5個空瓶,可以換2瓶新的汽水,並且還剩下一個空瓶。
  6. 最後,你可以用剩下的3個空瓶(之前剩下的1個加上剛換的2瓶的空瓶)換1瓶新的汽水。

所以,總共可以喝到的汽水數量是:

20+10+5+2+1=38 瓶汽水。

因此,用20元可以喝到38瓶汽水。🥤

自己都說是經典了還這樣. 那麼還有別招嗎?

有的. 這個技術的總稱叫做 LangChain [2]. LangChain 是個模組化的架構, 讓 LLM 更容易和應用程式結合. 它可以有很多 agent, 每個 agent 有自己的 memory, tools, 和 prompt template. 可以針對複雜的 prompt 拆成不同的屬性, 各自用適合的 tool 去處理. 至於 tool 最後會做什麼事情, 不是預先設定好的, 而是根據 LLM 的輸出決定的.

既然 LangChain 有個 chain 字, 我就從網路上找個 agent 內部有 chain 的圖示意.

本圖取材自 https://www.langchain.cn/t/topic/35

再回溯 Chain of Thought (COT) 和另外一個 Tree of Thought (TOT), 前者也提到chain, 他們和 LangChain 又是甚麼關係呢? 它們都只是輔助 LLM 推理的方法, COT 就是 step by step, TOT 就是找不到方法是會換一條思路. 它們都只是強調推理 (reason) 的部分, 有 action 才會去執行指令, 做成可互動的應用程式 (ReAct) [3].

[REF]

  1. https://arxiv.org/abs/2211.10435
  2. https://www.langchain.com/
  3. https://react.dev/

24P 與 pull down

由於 HDMI 的 video 需要設定輸出格式, 在考慮畫質最佳化的時候, 就必須要處理 24P 的問題.

24P 的影片來自電影, 每秒播放 24 張 progressive 的畫面. 而傳統的隔行掃瞄電視的畫質不如電影, 多是 NTSC 或是 PAL 的 interleave 輸出, 因此需要使用 pull down 技術將 24P 塞進 29.997 frame/sec (約 60 field/sec) 或是 25 frame / sec (50 field/sec).

對於 NTSC 來說, 24P –> 60I (I = interleave) 相當於 2 frames 變成 5 個 fields. 也就是第一個 frame 變成 2 個 fields, 第二個 frame 變成 3 個 fields 即可. 此為 3:2 pull down (telecine pull down, 或是更能反映真實狀況的 2:3 pull down).

(24 / 2) * 2 + (24 / 2) * 3 = 60

對於 NTSC 來說, 24P –> 50I (I = interlaev) 相當於 2 frames 變成 4.167 個的 fields. 取其近似值, 可以將第一個 frame 變成 2 個 fields, 第二個 frame 變成 2 個 fields 即可. 此為 2:2 pull down.

(24 / 2) * 2 + (24 / 2) * 2 = 48

因為是採用近似值的關係, 平均播放速度增加為 50/48 = 104.167%, 速度會變快. 若是聲音的輸出頻率與 video 連動, 則聲音 pitch 也會變高一點點. 若聲音的輸出頻率沒有與 video 連動, 則 video 播完了, audio 還沒有播完. 因此, 另外一種可行方式就是 video 每 50 張 frame 再多補 2 張進去. 這樣就可以讓 audio、video 脫鉤.

對於新的逐行掃瞄電視而言, 畢竟 24P 和 60P/50P 也不是整數倍的關係. 所以 pull down 的過程仍然存在.

總結來說, 早期的電視技術並不發達, 所以制定的 interleave 規格以現在的眼光來看, 是比較落後的. 隨著電視進入 120 Hz 的境界, 電影的 24P 技術又顯得不夠看. 從藍光 DVD 播出的訊號, 仍然要經過 pull down 轉換到逐行掃描電視是一種額外損失. 因此, 透過 HDMI 介面的告知, 電視也直接支援 24P 的輸入, 的確是比較明智的做法.

當然電影拍攝技術也會往 48P 邁進, 大家都在進步. 身為 display 關鍵角色的 TV, 理當去支援各種 native (未經再處理的) 輸入才是王道.