Learning Objectives
- 說明為什麼要把文件切塊,以及切太大、切太小各有什麼問題
- 用固定長度與重疊切文字,並處理邊界情況
- 依文件結構切塊,比較不同切法的真實檢索結果
- 用自己的問題集決定切法,不依賴通用的數字
長文件不能整份拿去算一個向量
上一課的手冊已經切成一節一節。實際拿到的常常是一份長長的 PDF 或網頁,要自己切。為什麼不把整份算成一個向量就好?
- 向量會糊掉: 一個向量要代表整份文件在談的所有事,和任何一個具體問題的相似度都不會突出。
- 送不進去或太貴: 就算找到了,整份文件送進模型,費用高,也可能超過上下文長度(第 2 課)。
- 引用不精確: 使用者想知道「哪一條」寫的,你只能回答「在這份文件裡」。
所以要切塊(chunking),每一塊各算一個向量。但切法會直接影響找不找得到答案。
這一課的教材是為課程編的虛構文件《優學大學學生校外實習辦法》,共十條、930 字,配上 12 個問題。它不是任何學校的規定。 每個問題都標出答案的原文,只有原文完整出現在取回的切塊裡才算命中。
逐步看:固定長度與重疊
請注意 start 怎麼移動,以及這條規定被切在哪裡。第一組沒有重疊,第二組每塊和前一塊重疊 8 個字。
逐步看:切壞了,回答就出問題
同一個問題「實習成績怎麼算?」,三種切法各取回最相似的一塊,交給模型回答。模型的回答是真實錄下來的。
從這個例子看到的事
| 切法 | 取回的那一塊 | 模型的回答 |
|---|---|---|
| 每塊 100 字、不重疊 | 結尾停在「實習機構評量佔」 | 說明資料不足,比例未提供 |
| 每塊 100 字、重疊 30 字 | 包含三項比例 | 答出 40%、30%、30% |
| 依條文切 | 完整的第七條 | 答出 40%、30%、30%,還補上逾期的扣分規定 |
我們在提示詞裡要求資料不足時要明說,這次它照做了;這只是一次錄製的結果,不保證每次都如此。檢索看起來成功了(相似度最高的確實是講成績的那一塊),問題要到看回答才會發現。這就是為什麼切塊要用問題集評估,不能只看切出來的樣子。
逐步看:用問題集比較切法
每種切法的每一塊都事先呼叫 API 算好向量。這裡看三種切法:每塊 100 字不重疊、每塊 400 字不重疊、依條文切。除了 recall,也要看取回的內容有多長。
這份文件的完整結果
我們多算了幾組。「答案完整」是答案原文完整落在某一塊裡的題數,「前 3 塊字數」是取回前 3 塊的平均字數:
| 切法 | 塊數 | 答案完整 | recall@1 | recall@3 | 前 3 塊字數 |
|---|---|---|---|---|---|
| 60 字、不重疊 | 16 | 7 / 12 | 5 | 7 | 175 |
| 60 字、重疊 20 | 23 | 11 / 12 | 3 | 8 | 179 |
| 100 字、不重疊 | 10 | 11 / 12 | 6 | 9 | 294 |
| 100 字、重疊 30 | 13 | 12 / 12 | 7 | 12 | 298 |
| 200 字、不重疊 | 5 | 11 / 12 | 6 | 9 | 565 |
| 200 字、重疊 50 | 6 | 12 / 12 | 5 | 10 | 587 |
| 400 字、不重疊 | 3 | 12 / 12 | 8 | 12 | 930 |
| 400 字、重疊 100 | 3 | 12 / 12 | 6 | 12 | 1130 |
| 依條文切 | 10 | 12 / 12 | 10 | 12 | 275 |
幾件值得注意的事,有些和直覺不同:
- 依條文切最好:一塊就是完整的一條,字數最少、recall@1 最高。這份文件結構整齊,換成沒有標題的逐字稿就無法這樣切。
- 重疊有幫助,但不是一定:100 字加重疊後 recall@3 從 9 升到 12;200 字加重疊只從 9 升到 10,recall@1 還從 6 掉到 5。
- 切太小不一定比較準:60 字的切塊,答案常常直接被切斷,加了重疊 recall@1 反而是最低的 3。
- 400 字的 12 / 12 要打折看:這份文件太短,3 塊全部取回就是整份。指標好看,但檢索什麼也沒做。文件比較長時結果會不同。
沒有萬用的切塊大小。 網路上看到的建議數字只是一個起點,適不適合要看你的文件和問題。依文件結構切、加上用自己的問題集量一次,比背一個數字可靠。
另外兩個常見做法,有需要時可以試:長條文再切小塊時,在每一塊前面附上標題(例如「第七條 成績評定:」),讓向量保留脈絡;或用小塊來檢索、回傳時附上前後文。
實作:chunk_text
完成 chunk_text(text, size, overlap):回傳切好的字串串列。要處理的邊界情況:
- 文字是空字串時回傳空串列。
- 最後一塊已經到結尾就停止,不要多切出一塊只有重疊內容的。
overlap大於或等於size時,用raise ValueError("overlap 要小於 size")拒絕,否則迴圈永遠走不完。
這題不呼叫模型。
小測驗:文件切塊
下面的程式會印出什麼?
text = "abcdefg"
size, overlap = 3, 1
start = 0
out = []
while start < len(text):
out.append(text[start:start + size])
if start + size >= len(text):
break
start += size - overlap
print(out)
把切塊長度調得很小(例如 60 字),在本課的實驗裡發生什麼事?
重疊(overlap)的主要目的與代價是什麼?
同學說:「我查到切塊的最佳大小是 500 字,照用就好。」你會怎麼回應?
重點整理
- 切塊讓向量聚焦、讓送進模型的內容變少、讓引用可以指到具體段落。
- 固定長度最簡單,但會切在句子中間;重疊降低這個風險,也帶來重複的內容。
- 文件有結構時優先依結構切。本課的依條文切字數最少、recall@1 最高。
- 評估要同時看 recall 和取回的份量;切太大時 recall 好看,只是因為把整份文件都取回了。
- 沒有萬用的切塊大小,要用自己的問題集試。
下一課: 把檢索到的內容組進提示詞,從頭寫一個 RAG,讓模型依據資料回答並附上引用。