Bỏ qua để đến nội dung
Search & RAG

3.4b BM25: từ mô hình xác suất đến công thức bạn đang dùng

Công thức BM25 trông như ba mảnh dán vào nhau: một cái log, một phân số, một tỉ số độ dài. Ai cũng học được cách dùng nó (1.7), và bảng tinh chỉnh k1/b thì có ở 3.15. Trang này trả lời câu còn lại: ba mảnh đó từ đâu ra, và mỗi mảnh giả định điều gì về dữ liệu của bạn.

Đó không phải chuyện học thuật. Ba hệ quả rất thực tế chỉ đọc được từ nguồn gốc:

  • Vì sao phần tf phải có tiệm cận — chứ không phải log(1+tf) cũng “cong cong tương tự”.
  • Vì sao idf của BM25 có thể âm, và điều đó làm gì với hệ của bạn (đo được: 120 cặp truy vấn–tài liệu trên corpus của repo này).
  • Vì sao khi có nhiều field, cộng điểm BM25 của từng field là sai về nguyên tắc — và vì sao đo trên corpus nhỏ thì cái sai đó chưa lộ ra.

BM25 tên đầy đủ là Okapi BM25 (BM = best match), theo hệ thống Okapi ở City University London những năm 1980–1990. Khung lý thuyết đằng sau nó là Probabilistic Relevance Framework (PRF) của Robertson, Spärck Jones và cộng sự.


1. Bước 1 — ranking theo xác suất liên quan, và idf sinh ra từ đó

Phần tiêu đề “1. Bước 1 — ranking theo xác suất liên quan, và idf sinh ra từ đó”

Điểm khởi đầu của PRF là Probability Ranking Principle: xếp tài liệu theo P(liên quan | tài liệu, truy vấn) giảm dần là tối ưu. Để tính được, Binary Independence Model giả định: mỗi term chỉ được ghi nhận có mặt / không có mặt, và các term độc lập với nhau khi đã biết tính liên quan. Với hai giả định đó, trọng số của term i là log của một tỉ số odds:

wBIM(i)=logP(tirel)(1P(tirel))(1P(tirel))P(tirel)w_{\mathrm{BIM}}(i) = \log \frac{P(t_i \mid \mathrm{rel})\,\bigl(1 - P(t_i \mid \overline{\mathrm{rel}})\bigr)}{\bigl(1 - P(t_i \mid \mathrm{rel})\bigr)\,P(t_i \mid \overline{\mathrm{rel}})}

Ước lượng bốn xác suất đó bằng tỉ lệ mẫu thuần thì hàm log dễ ra ±∞, nên PRF thêm pseudo-count 0.5 vào mọi ô. Kết quả là trọng số Robertson/Spärck Jones (RSJ):

Ký hiệuNghĩa
Nsố tài liệu (hoặc cỡ mẫu đã được đánh giá)
n_isố tài liệu chứa term i — chính là df
Rsố tài liệu đã biết là liên quan
r_isố tài liệu liên quan có chứa term i
wRSJ(i)=log(ri+0.5)(NRni+ri+0.5)(niri+0.5)(Rri+0.5)w_{\mathrm{RSJ}}(i) = \log \frac{(r_i + 0.5)\,(N - R - n_i + r_i + 0.5)}{(n_i - r_i + 0.5)\,(R - r_i + 0.5)}

Đây là chỗ idf không phải một trực giác, mà là một trường hợp riêng. Khi không có bất kỳ phản hồi liên quan nào — tình huống thường gặp — ta đặt R = r_i = 0 (tương đương giả định P(t_i|rel) = 0.5), và công thức tự sập xuống:

wIDF(i)=logNni+0.5ni+0.5w_{\mathrm{IDF}}(i) = \log \frac{N - n_i + 0.5}{n_i + 0.5}

Ba điều đọc được ngay:

  1. idf của BM25 là log của một odds ratio, không phải log(N/df) của TF-IDF (3.4). Hai cái gần nhau về số nhưng khác nhau về xuất xứ — và khác nhau ở dấu (mục 5).
  2. Con số 0.5 không phải “làm mượt cho đẹp” — nó là pseudo-count của estimator, và nó có mặt ở cả tử lẫn mẫu.
  3. PRF có sẵn chỗ cho relevance feedback. Nếu bạn thật sự có nhãn (click, golden set), w_RSJ với R, r_i thật mới là bản đầy đủ; idf chỉ là bản “chưa biết gì”. Đây là lý do lý thuyết để reweight term theo log click, thay vì coi việc đó là hack.

2. Bước 2 — từ “có mặt” sang “xuất hiện mấy lần”: eliteness và 2-Poisson

Phần tiêu đề “2. Bước 2 — từ “có mặt” sang “xuất hiện mấy lần”: eliteness và 2-Poisson”

BIM chỉ biết có/không. Muốn dùng tf thì cần một mô hình nói vì sao tf liên quan đến tính liên quan. PRF đưa vào một biến ẩn: eliteness.

Với mỗi cặp (term, tài liệu) có một thuộc tính nhị phân ẩn E: tài liệu này có thật sự nói về term đó hay không. tf không liên quan trực tiếp tới relevance; nó chỉ là bằng chứng về E. Và E mới là thứ liên quan tới relevance.

Giả định phân phối: tf | elite ~ Poisson(λ_E)tf | không elite ~ Poisson(λ_Ē) với λ_E > λ_Ē. Trộn hai Poisson đó ra mô hình 2-Poisson. Nhét vào công thức BIM thì được một trọng số w(tf) phụ thuộc ba tham số (λ_E, λ_Ē, tỉ lệ trộn) mà trong thực tế không ai ước lượng được.

Robertson & Walker (1994) không cố ước lượng chúng. Họ làm việc khác: khảo sát hình dạng của w(tf), và chỉ ra nó luôn có bốn tính chất, bất kể tham số:

#Tính chấtNghĩa
1w(0) = 0không xuất hiện thì không cộng điểm
2tăng đơn điệu theo tfxuất hiện nhiều hơn thì không thể tệ hơn
3tiệm cận một giá trị hữu hạn khi tf → ∞có trần
4trần đó đúng bằng w_BIMbiết chắc tài liệu là elite là tất cả những gì có thể biết

Tính chất 4 là phần đẹp nhất và hay bị bỏ qua: trần của phần tf chính là trọng số mà eliteness sẽ có nếu ta quan sát được nó trực tiếp. Bao nhiêu lần xuất hiện đi nữa cũng chỉ để thuyết phục ta rằng tài liệu nói về term đó — và một khi đã chắc chắn thì không còn gì để biết thêm.

Rồi họ fit một hàm một tham số vào hình dạng đó — không dẫn xuất, mà xấp xỉ:

tf / (k1 + tf)

Đó là toàn bộ nguồn gốc của bão hoà tần suất. Và nó trả lời một câu hỏi hay bị trả lời sai:

Vì sao không dùng 1 + log(tf)? Hàm đó cũng cong, cũng giảm tốc. Nhưng nó không có tiệm cận — nó ra vô cùng, chỉ chậm hơn tf. Dùng nó nghĩa là giả định lần xuất hiện thứ 1000 vẫn mang thông tin mới, tức là bỏ luôn giả thiết eliteness.

Có đúng một trường hợp trần biến mất: nếu giả định eliteness trùng khít với relevance (p1 = 1, p0 = 0) thì giới hạn thành vô cùng và trọng số tuyến tính theo tf. Nói cách khác: TF-IDF tuyến tính là BM25 với một giả định quá tự tin — “cứ nói về nó nhiều là chắc chắn liên quan”. Ba mươi năm thực nghiệm nói giả định đó tệ hơn.

Bão hoà, bằng số (đóng góp của một term, đã chuẩn hoá idf = 1, dl = avgdl, dạng có (k1+1) ở tử số):

k1tf=1tf=2tf=3tf=5tf=10tf=50tiệm cận
0.51.0001.2001.2861.3641.4291.4851.5
1.21.0001.3751.5711.7741.9642.1482.2
2.01.0001.5001.8002.1432.5002.8853.0

Đọc hàng k1 = 1.2: từ tf=1 sang tf=2 được thêm 0.375; từ tf=10 sang tf=50 — bốn mươi lần xuất hiện nữa — chỉ thêm 0.184. Tuyệt đại đa số giá trị của một term nằm ở lần xuất hiện đầu tiên. Đây là lý do kỹ thuật vì sao nhồi từ khoá không hiệu quả với BM25, và vì sao k1 là “độ bão hoà” chứ không phải “trọng số của tf”.

Tài liệu dài hơn trung bình vì hai lý do rất khác nhau, và PRF gọi tên chúng:

Giả thuyếtTác giả dài vìSuy ra nên làm gì
Verbositynói cùng một lượng nội dung bằng nhiều từ hơnchia tf cho dl — dài dòng không được thưởng
Scopethật sự có nhiều nội dung hơn (như nối hai tài liệu lại)không chia — nhiều nội dung thì đáng nhiều điểm hơn

Một corpus thật là pha trộn của hai nguyên nhân đó, và không có cách nào biết tỉ lệ. Nên PRF chọn chuẩn hoá mềm, với b là núm trộn:

B=(1b)+bdlavgdl,0b1tf=tfBwBM25=tfk1+tfwRSJ  =  tfk1B+tfwRSJ\begin{aligned} B &= (1 - b) + b \cdot \frac{dl}{avgdl}, \qquad 0 \le b \le 1 \\[2pt] tf' &= \frac{tf}{B} \\[2pt] w_{\mathrm{BM25}} &= \frac{tf'}{k_1 + tf'} \cdot w_{\mathrm{RSJ}} \;=\; \frac{tf}{k_1 B + tf} \cdot w_{\mathrm{RSJ}} \end{aligned}

b = 1 là “tin hoàn toàn vào verbosity”, b = 0 là “tin hoàn toàn vào scope”. Điều này biến quy tắc chọn bTầng 0 từ trực giác thành một phát biểu có nguồn: tài liệu dài vì nói nhiều chủ đề → b cao; dài vì chi tiết về đúng một chủ đề (scope) → b thấp.

Nó cũng giải thích vì sao 3.15 đo được rằng chunk đều làm b mất tác dụng: chunk cố định kích thước đã loại bỏ gần hết phương sai độ dài, tức là loại bỏ chính cái mà b được sinh ra để xử lý.

Một chi tiết dễ bỏ qua, có trong bản gốc: định nghĩa dl không quan trọng lắm.B chỉ dùng tỉ lệ dl/avgdl, nên đếm số term, số từ trước khi tokenize, hay số ký tự đều cho kết quả rất gần nhau. Điều thật sự quan trọng là dlavgdl phải được đếm bằng cùng một đơn vị, và đơn vị đó là term sau tokenize của chính index đó — không phải token của LLM (3.15 §4.1).

4. Bản gốc còn ba thứ mà bạn không dùng

Phần tiêu đề “4. Bản gốc còn ba thứ mà bạn không dùng”

Ghép ba bước trên lại là “BM25 kinh điển”. Nhưng bản gốc (Robertson et al. 1994) dài hơn thế. Ba thành phần đã rụng trên đường vào Lucene:

score(q,d)=twRSJ(t)tf(k1+1)k1B+tfqtf(k3+1)k3+qtfthaˋnh phaˆˋqtf\mathrm{score}(q,d) = \sum_t w_{\mathrm{RSJ}}(t) \cdot \frac{tf\,(k_1+1)}{k_1 B + tf} \cdot \underbrace{\frac{qtf\,(k_3+1)}{k_3 + qtf}}_{\text{thành phần } qtf}
Thành phầnBản gốcVì sao rụng
k3, qtf — bão hoà tần suất trong truy vấnterm lặp trong truy vấn cũng bão hoà, có hằng số riêng k3thực nghiệm cho thấy bão hoà theo qtf không quan trọng → dùng tuyến tính theo qtf, tức coi term lặp trong truy vấn là hai term khác nhau. Truy vấn ngắn thì qtf = 1 và thành phần này biến mất hoàn toàn
k2 — hiệu chỉnh độ dài lên tổng điểm tài liệuđo ra không quan trọng
(k1+1) ở tử sốbiến thể phổ biếnkhông đổi thứ hạng — nó là hằng số nhân cho mọi term. Lý do đưa vào là để khi tf = 1 thì điểm khớp đúng bằng w_RSJ. Lucene bỏ nó từ LUCENE-8563

Hệ quả khi debug: hai hệ có thể cùng “cài BM25 đúng” mà cho điểm khác nhau (khác hằng số nhân, khác dạng idf) trong khi thứ hạng giống nhau. Nếu bạn đang so điểm tuyệt đối giữa hai thư viện, phần lớn khả năng là bạn đang so hai biến thể — không phải vừa tìm ra một bug.

Nhìn lại w_IDF = log((N − df + 0.5)/(df + 0.5)). Khi df > N/2 thì tử nhỏ hơn mẫu, log ra số âm. Term xuất hiện ở hơn nửa corpus không chỉ “gần như miễn phí” — nó trừ điểm.

Đo trên data/corpus.jsonl (50 tài liệu, tokenizer code-aware, index = title + text, 594 term phân biệt):

termdfidf bản gốcidf Luceneidf ATIRE
39−1.23400.25550.2485
được26−0.07850.65470.6539
không250.00000.69600.6931

Chỉ 2 term trên 594idf âm. Nghe như chuyện nhỏ, nhưng nó không nhỏ — vì stopword có mặt ở khắp nơi:

  • 10 / 24 truy vấn trong golden set chứa ít nhất một term như vậy.
  • 205 cặp (truy vấn, tài liệu)tổng điểm âm với biến thể gốc, trải trên 44 trong 50 tài liệu.
  • 120 cặp ở tình trạng bệnh lý rõ ràng: mọi term khớp đều có idf âm. Ví dụ truy vấn A4 (cam kết uptime 99,9% và phân vị 99) với d001 — khớp đúng một từ — được −0.787, trong khi một tài liệu không khớp gì cả được 0.

Đó là phát biểu “khớp thêm một từ khoá làm tài liệu tệ hơn so với không khớp gì”. Không mô hình ranking nào nên nói được câu đó.

Ba cách chữa đã có trong các hệ thật:

Cách chữaidfAi dùng
thêm 1 trước khi lấy loglog(1 + (N − df + 0.5)/(df + 0.5))Lucene / Elasticsearch / OpenSearch
đổi hẳn sang dạng cổ điểnlog(N / df)ATIRE
chặn dưới ở 0max(0, w_IDF)một số cài đặt tự viết

nDCG@5 trên golden set (24 truy vấn, k1 = 1.2, b = 0.75):

biến thể idftổngA (exact)B (paraphrase)C (multi-hop)D (phủ định)
bản gốc (cho phép âm)0.7670.9710.4060.7980.893
Lucene (+1 trong log)0.7790.9710.4350.8190.893

−1,6% chỉ vì để idf âm, và mất đúng ở hai lớp có truy vấn dài nhiều stopword (B, C). Lớp A không nhúc nhích — truy vấn mã lỗi, endpoint gần như không có stopword.

Còn một lý do kỹ thuật nữa, nằm ngoài chất lượng: điểm âm phá vỡ giả thiết mà WAND / MaxScore / Block-Max dựa vào. Các thuật toán đó bỏ qua tài liệu bằng cách so chặn trên của điểm với ngưỡng top-k, và chặn trên chỉ hợp lệ khi mỗi term đóng góp không âm (3.13). Có term trừ điểm thì hoặc bỏ sai tài liệu, hoặc phải nới chặn trên và mất hết tác dụng tăng tốc.

Câu “hệ này dùng BM25” chưa đủ để tái lập một con số. Kamphuis et al. (ECIR 2020) đếm được tám biến thể đang lưu hành, và điểm chung của gần như tất cả là chúng vá đúng hai chỗ: idf âmphạt tài liệu dài quá tay.

Biến thểSửa gìKhác thế nào
Robertson et al. (gốc)idf = log((N−df+0.5)/(df+0.5)), có thể âm
Lucene (default)idf âm + tốc độidf = log(1 + …); dl nén mất mát xuống 1 byte (256 giá trị) để tính sẵn k1·B
Lucene (accurate)như trên nhưng dl chính xác — dùng để đo cái giá của việc nén
ATIREidf âmidf = log(N/df), tử số nhân (k1+1)
BM25Ltài liệu dài bị phạt quác = tf/B, rồi ((k1+1)(c+δ))/(k1+c+δ), δ = 0.5
BM25+như trên, cách kháccộng thẳng δ vào đóng góp mỗi term (chặn dưới khi tf ≥ 1), δ = 1.0
BM25-adptmột k1 cho mọi term là thôk1 riêng từng term, chọn theo information gain
TFl∘δ∘p×IDFdạng hàm tf1 + log(1 + log(tf/B + δ))

Đo lại sáu biến thể trên corpus của repo (k1 = 1.2, b = 0.75, δ theo giá trị tốt nhất mà paper gốc báo cáo):

biến thểnDCG@5ABCD
Robertson et al. (gốc)0.7670.9710.4060.7980.893
Lucene (default)0.7790.9710.4350.8190.893
ATIRE0.7790.9710.4350.8190.893
BM25L (δ=0.5)0.7670.9710.4410.7920.862
BM25+ (δ=1.0)0.7650.9710.4350.7920.862
TFl∘δ∘p×IDF (δ=0.5)0.7670.9710.4410.7920.862

Ba điều đọc được:

  1. Toàn bộ khoảng cách là 0.765 → 0.779, tức 1,8% — nhỏ hơn khoảng cách do đổi tokenizer (+20,8%), nhỏ hơn cả khoảng cách do tinh chỉnh k1/b (+2,6%).
  2. Lucene và ATIRE trùng khít trên corpus này. Không phải vì công thức giống nhau — log(1 + (N−df+0.5)/(df+0.5)) khác log(N/df) — mà vì trên corpus này hai hàm đó đơn điệu cùng chiều đủ giống để không đổi thứ hạng nào. Đừng suy ra là chúng tương đương.
  3. δ > 0 làm tệ đi ở đây, đúng như lý thuyết dự đoán. δ của BM25L / BM25+ tồn tại để đỡ tài liệu dài bị b phạt oan; corpus này trung bình 48 term và rất đều nên không có bệnh đó, thêm δ chỉ làm phẳng bớt tín hiệu:
δBM25+BM25L
0.00.7790.779
0.50.7700.767
1.00.7650.761
1.50.7610.761

Và đây là kết luận từ nguồn ngoài, trên dữ liệu tử tế — quan trọng hơn bảng trên, vì 24 truy vấn quá nhỏ để kết luận: Kamphuis et al. chạy tám biến thể trên ba collection TREC (Robust04, Core17, Core18) với k1 = 0.9, b = 0.4; ANOVA và Tukey HSD đều cho thấy không có khác biệt có ý nghĩa giữa bất kỳ hai biến thể nào, kể cả cái dl nén 1 byte của Lucene vốn hay bị chê. Trotman et al. (2014) kết luận y hệt trên 9 hàm ranking và 10 collection: sau khi đã tinh chỉnh tham số, chênh lệch gần như biến mất.

Thực dụng: đừng đi tìm biến thể BM25 tốt hơn — đó là ngõ cụt đã có người đo hộ. Nhưng hãy chọn biến thể không cho idf âm (mục 5), và **hãy ghi rõ biến thể

  • k1/b + tokenizer** vào báo cáo, vì nếu không thì con số đó không tái lập được.

7. BM25F — nhiều field, và cái bẫy cộng điểm

Phần tiêu đề “7. BM25F — nhiều field, và cái bẫy cộng điểm”

Tài liệu thật có cấu trúc: title, text, anchor, tags. Cách rõ ràng nhất — tính BM25 cho từng field rồi cộng lại có trọng số — sai theo đúng lý thuyết ở mục 2, và lý do rất gọn:

Eliteness là thuộc tính của cặp (term, tài liệu), không phải của (term, field). Tài liệu nói về “mật khẩu” thì nó nói về “mật khẩu”, bất kể chữ đó nằm ở tiêu đề hay thân bài. Nên phải gom bằng chứng từ mọi field trước, rồi mới bão hoà một lần. Cộng điểm từng field = bão hoà nhiều lần = phá đúng tính chất mà mục 2 xây nên.

Tính bằng tay cho thấy nó vỡ ra sao. k1 = 1.2, cho B = 1 ở cả hai field, idf = 1, trọng số hai field bằng nhau:

term xuất hiệncộng điểm từng fieldBM25F (gom tf trước)
d_A3 lần trong text2.2·3/(1.2+3) = 1.571tf̃ = 31.571
d_B1 lần title + 1 lần text1.0 + 1.0 = 2.000tf̃ = 21.375

Cộng điểm từng field nói d_B > d_A: một từ xuất hiện một lần ở hai field đáng giá hơn ba lần ở một field. BM25F nói ngược lại. Càng nhiều field, sai số càng phóng đại — với 5 field, một term xuất hiện đúng 1 lần ở mỗi field ăn gần 5× trần bão hoà.

BM25F (Robertson, Zaragoza & Taylor, CIKM 2004) gom trước, bão hoà sau, và cho phép mỗi field có b riêng:

tf~(t)=svstfs(t)Bs,Bs=(1bs)+bsslsavslsscore=twRSJ(t)tf~(t)k1+tf~(t)\begin{aligned} \tilde{tf}(t) &= \sum_s v_s \cdot \frac{tf_s(t)}{B_s}, \qquad B_s = (1 - b_s) + b_s \cdot \frac{sl_s}{avsl_s} \\[2pt] \mathrm{score} &= \sum_t w_{\mathrm{RSJ}}(t) \cdot \frac{\tilde{tf}(t)}{k_1 + \tilde{tf}(t)} \end{aligned}

Bản đơn giản (một b chung) có một cách hiểu rất trực quan: nếu v_s là số nguyên, BM25F tương đương BM25 thường chạy trên tài liệu đã nhân bản fieldv_title = 5 nghĩa là “lặp tiêu đề 5 lần rồi tính BM25 như bình thường”.

Cảnh báo về tham số, lấy từ chính bài BM25F: trên TREC Web 2003, bộ tốt nhất cho tác vụ topic distillation là k1 = 27.5, b_title = 0.95, v_title = 38.4, v_anchor = 35, v_body = 1.0. k1 = 27.5 — xa mặc định 1.2 gần một bậc độ lớn, vì khi tf̃ đã cộng dồn qua nhiều field có trọng số lớn thì thang của nó đổi hoàn toàn. Bê k1 = 1.2 sang BM25F là một lỗi im lặng.

Trong Elasticsearch:

Truy vấnKiểuGhi chú
multi_match (best_fields, most_fields)field-centriclấy max / cộng điểm từng field — chính là thứ mục này đang cảnh báo
combined_fields (7.13+, dựa trên Lucene CombinedFieldsQuery)term-centriccài BM25F: gộp thống kê nhiều field như thể chúng là một field. Ràng buộc: mọi field phải cùng search analyzer, boost ≥ 1.0, chỉ text field, chỉ similarity BM25

Ràng buộc “cùng analyzer” là lý do cấu hình mẫu ở 3.15 §6 — field có dấu và field bỏ dấu dùng hai analyzer khác nhau — không dùng được combined_fields, và ở đó multi_match mới là lựa chọn đúng. Hai kỹ thuật giải hai bài khác nhau: combined_fields cho nhiều phần của cùng một văn bản, multi_match cho nhiều cách biểu diễn của cùng một văn bản.

Và đây là chỗ số đo không nói cùng một câu với lý thuyết. Corpus có sẵn hai field (title trung bình 4.8 term, text trung bình 43.6 term), nên đo được trực tiếp, nDCG@5:

trọng số titleBM25Fcộng điểm field (df toàn cục)cộng điểm field (df theo từng field)
10.7790.7740.803
20.7880.8200.822
30.8050.8180.764
50.8040.8150.765

Đọc bảng này cho đúng, vì rất dễ đọc sai:

  • Không kết luận được “cộng điểm field tốt hơn BM25F”. Điều kiện để cái sai ở trên lộ ra là tf > 1 ở nhiều field cùng lúc; corpus này có 50 tài liệu, tiêu đề 4.8 term, gần như mọi term chỉ xuất hiện một lần ở mỗi field — đúng vùng mà hai cách tính gần trùng nhau.
  • Cái đọc được là hình dạng: BM25F đi lên rồi phẳng (0.779 → 0.805 → 0.804), còn cộng điểm với df theo từng field thì nhảy loạn (0.803 → 0.822 → 0.764). Đỉnh của nó là một cái kim, không phải một cao nguyên — tinh chỉnh boost trên 24 truy vấn sẽ overfit ngay. Nguyên nhân: df khác nhau giữa các field làm cùng một term có idf khác nhau ở title và ở text — từ lỗidf(title) = 6 nhưng df(text) = 2, nên nó vừa “phổ biến” ở tiêu đề vừa “hiếm” ở thân bài trong cùng một corpus. BM25F không có bệnh này vì nó dùng một idf toàn cục cho mỗi term.
  • 24 truy vấn là mẫu quá nhỏ để so hai phương pháp (C.6). Bảng này đáng tin ở chỗ độ ổn định, không đáng tin ở chỗ cái nào cao hơn.

8. BM25 giả định gì — và mỗi giả định vỡ ở đâu

Phần tiêu đề “8. BM25 giả định gì — và mỗi giả định vỡ ở đâu”
Giả địnhỞ đâu trong công thứcVỡ khi nàoChữa bằng
Term độc lập (bag of words)tổng Σ_ttruy vấn là một cụm từ"mật khẩu" khác mật + khẩu; thứ tự và khoảng cách bị bỏ hoàn toànpositional index, phrase / proximity (3.5); bigram âm tiết (3.15 §5)
Term của truy vấn xuất hiện đúng như thế trong tài liệutf là phép đếm khớp chính xácngười hỏi dùng từ khác — vocabulary mismatchembedding, learned sparse (Tầng 0, Tầng 1)
tf là bằng chứng trung thực về nội dungphần bão hoàboilerplate, footer lặp, SEO spam, templatebão hoà đã đỡ phần lớn; hạ k1; làm sạch ở tầng index
Eliteness nhị phânmô hình 2-Poisson“nói về” là chuyện có mức độ, không phải có/khôngreranker cross-encoder (Phần 5)
Độ lệch độ dài giải thích được bằng một b chungBcorpus trộn chunk 512 token với tài liệu 5000 tokenđừng trộn — sửa ở chunker (3.15 §4.1)
df, N, avgdl là thống kê toàn cụcidf, Bindex chia shard, hoặc dữ liệu ghi liên tụcxem ngay dưới

Giả định cuối gây đau nhất trong hệ thật, vì nó không nằm trong công thức mà nằm trong hạ tầng:

  • ES / OpenSearch tính df theo từng shard. Cùng một tài liệu, cùng một truy vấn, nằm ở shard khác thì điểm khác. Corpus lớn thì sai số bình quân đi và không ai thấy; index nhỏ hoặc phân bố lệch thì thấy rõ. dfs_query_then_fetch gom thống kê toàn cục trước khi cho điểm, giá là thêm một round-trip.
  • Vector DB dùng BM25 dạng sparse vector gặp đúng vấn đề này. idf cần biết toàn bộ collection, nhưng sparse vector lại được tính ở phía client, từng điểm một. Thêm hoặc xoá tài liệu là idf cũ sai. Qdrant từ 1.10 xử lý bằng cách đưa idf vào trong engine (modifier: idf) để cập nhật theo thời gian thực thay vì bắt client tính lại toàn bộ.
  • Và vì idf, avgdl, cùng hằng số nhân đều phụ thuộc corpus: điểm BM25 không so được giữa hai truy vấn, hai index, hay hai thời điểm. Đó là lý do fusion phải dùng hạng (RRF) chứ không phải điểm thô, hoặc phải chuẩn hoá điểm một cách tường minh (Tầng 2).

9. Vì sao một công thức 1994 vẫn còn ở đây

Phần tiêu đề “9. Vì sao một công thức 1994 vẫn còn ở đây”

BM25 là hàm ranking của một mô hình xác suất đơn giản, xấp xỉ bằng một curve fit, với hai tham số mà không lý thuyết nào chỉ cách đặt. Nghe như thứ đáng bị thay từ lâu. Nó vẫn ở đây vì bốn lý do:

  1. Baseline zero-shot rất khó vượt. BEIR (2021) cho thấy BM25 mạnh trên nhiều domain ngoài phân phối huấn luyện, nơi dense retriever tụt.
  2. Không cần huấn luyện, không cần GPU, không cần nhãn. Chi phí biên gần như bằng 0, và nó chạy được ngay ngày đầu của một sản phẩm mới.
  3. Điểm mù của nó bù cho dense — nó chính xác ở định danh, mã lỗi, số hiệu, tên riêng mới (Tầng 0). Đó là lý do hybrid tồn tại.
  4. Nó là stage-1 tốt cho kiến trúc hai tầng. Thứ hạng thô của BM25 đủ tốt để reranker làm phần còn lại (Tầng 3), và nó nhanh — BM25S chạy thuần Python đạt tốc độ ngang Lucene bằng cách tính sẵn điểm sparse.
  • Báo cáo “nDCG với BM25” mà không nói biến thể, k1, b, tokenizer. Không tái lập được, và bốn thứ đó ảnh hưởng nhiều hơn khoảng cách giữa các biến thể.
  • Đổi biến thể BM25 để mong tăng chất lượng. Đã có người đo: không khác biệt có ý nghĩa. Đổi tokenizer thì có (+20,8%).
  • Dùng 1 + log(tf) rồi gọi nó là BM25. Mất tiệm cận là mất luôn giả thiết eliteness.
  • Để idf âm. 120 cặp bệnh lý trên 50 tài liệu, và nó phá chặn trên của WAND.
  • Cộng điểm BM25 từng field và tưởng đó là BM25F. Sai ở nguyên tắc; df theo từng field còn làm điểm bất ổn theo boost.
  • k1 = 1.2 vào BM25F. Thang của tf̃ đã khác; bài gốc dùng k1 = 27.5.
  • So điểm BM25 giữa hai truy vấn hoặc giữa hai index. Không có nghĩa.
  • Tin điểm ES khớp với công thức tính tay. Lucene nén dl xuống 1 byte và bỏ (k1+1) — thứ hạng đúng, số thì không.
Phần 3 — Nền tảng kỹ thuật