相關性描述的是⼀個⽂檔和查詢語句匹配的程度。我們從搜索引擎召回時,肯定希望召回相關性高的數據,那麼如何來量化相關度呢。
首先,我們定義,一個文檔doc,由多個詞語 term 組成。
最早,通過最簡單的TF-IDF來衡量。
TF-IDF
樸素的思想,相關度應該是詞語權重、文檔權重的融合。
- 詞頻 TF(Term Frequency)樸素的理解,一個詞語term在一個doc中出現的頻率越高,那麼doc的相關性也越高。
$$ \text{TF}(t, d) = \frac{\text{詞t在文檔d中的出現次數}}{\text{文檔d的總詞數}} $$
- 逆向文檔頻率 IDF(Inverse Document Frequency),通俗的解釋每個檢索詞在索引中出現的頻率,頻率越高,相關性越低。 比如"的“ 這樣的詞語,可能每個doc都有,相反一些專業術語出現的文檔數很少,這些文檔的權重就很高。
$$ \text{IDF}(t, D) = \log\left(\frac{\text{文檔集合D的總文檔數}}{\text{包含詞t的文檔數} + 1}\right) $$
- TF和IDF結合起來計算一個詞的TF-IDF值,公式爲:
$$ \text{TF-IDF}(t, d, D) = \text{TF}(t, d) \times \text{IDF}(t, D) $$
TF-IDF
的優點在於簡單有效,但它也有一些侷限性,例如不考慮詞序和上下文信息。
BM25(Best Matching 25)
BM25是一種用於文本信息檢索的算法,是BM(Best Matching)系列中的一員。它在信息檢索領域中廣泛應用,特別是在搜索引擎中用於排序搜索結果。整體而言BM25 就是對 TF-IDF 算法的改進**,以下是BM25算法的公式:
$$ \text{BM25}(D, Q) = \sum_{i=1}^{n} \frac{{(k+1) \cdot f_i}}{{f_i + k \cdot (1 - b + b \cdot \frac{{\lvert D \rvert}}{{\text{avgdl}}})}} \cdot \log\left(\frac{{N - n_i + 0.5}}{{n_i + 0.5}}\right) $$
- D:文檔
- Q:查詢
- n:查詢中的term數
- N:文檔集中的文檔數
- fi:文檔中term i的頻率
- ni:包含術語 term i 的文檔數
- DL:文檔長度(term數)
- AVG_DL:文檔集的平均長度(term數量)
對於 TF-IDF 算法,TF(t) 部分的值越大,整個公式返回的值就會越大,如果一個doc文章很長,詞語很多,tf頻率就會很大。BM25 針對這個問題做了優化,通過b參數,對文檔長度進行打壓,隨着TF(t) 的逐步加大,該算法的返回值會趨於一個數值。
BM25的優勢在於它對於長文本和短文本的處理更爲靈活,並且能夠適應不同查詢的特徵。這些可調整的參數使得BM25能夠通過調整來適應不同的信息檢索場景。
-
b 參數 ,b 默認 0.75(經驗值),主要是對長文檔做懲罰,如果不希望文檔長度更大的相似度搜索更好,可以把 b 設置得更大,如果設置爲 0,文檔的長度將與分數無關。從下圖可以看到,b=0時,L與分數無關,b=1時,L越大,分數打壓越厲害。
-
k 參數, 默認值 1.2,會影響詞語在文檔中出現的次數對於得分的重要性,如果希望詞語出現次數越大,文檔越相關,這個參數可以設置更大。
BM25+
BM只考慮了term和doc的維度,對query 裏term的頻率沒有考慮,BM25+(Best Matching 25 Plus)正是基於這一點,來改進BM25算法,以下是BM25+的公式,以及每個參數的含義:
$$ \text{BM25+}(D, Q) = \sum_{i=1}^{n} \frac{{(k_1+1) \cdot f_i \cdot (k_3+1) \cdot qf}}{{(f_i + k_1 \cdot (1 - b + b \cdot \frac{{\lvert D \rvert}}{{\text{avgdl}}})) \cdot (k_3 + qf)}} \cdot \log\left(\frac{{N - n_i + 0.5}}{{n_i + 0.5}}\right) $$
相比BM25,增加k3 和qf,
其中:
- qf:查詢中詞項的頻率(Query Term Frequency)
- k3:控制查詢中詞項頻率對得分的影響程度的參數,下面是不同的k3,在不同的query weight情況下,對分數的影響
![[Pasted image 20240202151809.png]]
- 注意k1就是BM25裏的k
這裏的qf,我們可以當做term weight來使用,訓練term weight模型,來對重點的term做激勵。
谷歌的End-to-End Query Term Weighting
谷歌在2023年發佈了一篇論文,介紹如何端到端學習term weighting。
背景是term based recall的方法相對於embedding recall來說很繁瑣,好處就是時延低且對基建要求低,向量召回會遇到分不清楚query中哪個詞是核心詞的問題,導致召回出現了非核心詞的結果。
谷歌論文
在左側,展示了傳統的信息檢索(IR),所有的term都是默認的權重,在右側,我們插入了一個BERT模型來評估term的權重,BM25+ 打分時將這個weighting作爲query freq,就是上面說的qf。
可以看谷歌論文的打分:
上面的$f(T_i, T,w)$ 就是模型學習的權重。
附錄
可視化不同參數變化,BM25分數的變化
import numpy as np
import matplotlib.pyplot as plt
def calculate_bm25(tf, term_weight, corpus_freq, total_docs,
k1=1.5, k3=8, doc_len=100, avg_doc_len=120, b=0.75):
idf = np.log((total_docs - corpus_freq + 0.5) / (corpus_freq + 0.5) + 1) # IDF計算
K = k1 * ((1.0 - b) + b * doc_len / avg_doc_len + tf)
tf_term = tf * (k3 + 1) * term_weight / (K * (k3 + term_weight)) # TF計算
return idf * tf_term # BM25計算
def visualize_bm25_scores_k3(term_weight_values, tf_values, corpus_freq, total_docs, k1=1.5):
# Plotting
plt.figure(figsize=(12, 8))
for k3 in np.linspace(2, 10, 5):
scores = [calculate_bm25(5, query_weight, corpus_freq, total_docs, k1=k1, k3=k3)
for query_weight in term_weight_values]
plt.plot(term_weight_values, scores, label=f'k3={k3}')
plt.title('BM25 Scores with Different Query_freq Values (b=0)')
plt.xlabel('Query weight')
plt.ylabel('BM25 Score')
plt.legend()
plt.show()
def visualize_bm25_scores_b(term_weight_values, tf_values, corpus_freq, total_docs, k1=1.5):
# b 用於懲罰文檔長度
plt.figure(figsize=(12, 8))
doc_lens = np.linspace(100, 1000, 10)
avg_doc_len = 100
L = [doc_len /avg_doc_len for doc_len in doc_lens]
for b in [0, 0.5, 0.75, 1.0]:
scores = [calculate_bm25(5, 1, corpus_freq, total_docs, k1=k1, b=b, doc_len=doc_len)
for doc_len in doc_lens]
plt.plot(L, scores, label=f'b={b}')
plt.title('BM25 Scores with Different b')
plt.xlabel('L(doc_length/avg_doc_length')
plt.ylabel('BM25 Score')
plt.legend()
plt.show()
term_weight_values = np.linspace(0.1, 10, 10) # query weight
corpus_freq = 500 # 包含term的文檔數量,文檔頻率
total_docs = 5000000 # 總文檔數量
tf_values = list(range(1, 101))
visualize_bm25_scores_k3(term_weight_values, tf_values, corpus_freq, total_docs)
visualize_bm25_scores_b(term_weight_values, tf_values, corpus_freq, total_docs)