BM25 是一种常用的文本相关性排序算法,广泛应用于搜索引擎和信息检索系统。它能根据关键词和文档的匹配程度,为每个文档打分并排序。
BM25 公式的本质就是:
"关键词出现得多、文档短、关键词稀有" → 分数高,排序靠前。
输入关键词,系统会用BM25算法为下方文档打分并排序:
文档:
// 定义一个包含若干中文文档的数组
const docs = [
'人工智能正在改变世界。',
'机器学习和深度学习是人工智能的重要分支。',
'猫和狗是常见的宠物。',
'AI可以帮助医生诊断疾病。',
'篮球是一项受欢迎的运动。',
'人工智能与大数据密不可分。',
'天气预报依赖于大量数据分析。'
];
// 分词函数:将文本按中文、字母、数字和空格分割成词语
function tokenize(text) {
// 先去除除中文、字母、数字、空格以外的字符,再按空格分割,最后过滤掉空字符串
return text.replace(/[^\u4e00-\u9fa5a-zA-Z0-9 ]/g, '').split(/\s+/).filter(Boolean);
}
// 计算IDF(逆文档频率):衡量关键词在所有文档中的稀有程度
function computeIDF(term, docs) {
// 统计包含该关键词的文档数
let df = 0;
for (const doc of docs) {
// 如果该文档分词后包含该关键词,则df加1
if (tokenize(doc).includes(term)) df++;
}
// 按照BM25的IDF公式计算并返回
return Math.log((docs.length - df + 0.5) / (df + 0.5) + 1);
}
// BM25打分函数:计算某个文档与查询关键词的相关性得分
function bm25Score(query, doc, docs, k1 = 1.5, b = 0.75) {
// 对查询进行分词
const terms = tokenize(query);
// 对文档进行分词
const docTerms = tokenize(doc);
// 当前文档的长度
const docLen = docTerms.length;
// 所有文档的平均长度
const avgdl = docs.reduce((sum, d) => sum + tokenize(d).length, 0) / docs.length;
// 初始化得分
let score = 0;
// 遍历每个查询词
for (const q of terms) {
// 统计该查询词在当前文档中出现的次数
const fq = docTerms.filter(t => t === q).length;
// 如果该词在文档中未出现,跳过
if (fq === 0) continue;
// 计算该词的IDF
const idf = computeIDF(q, docs);
// 按照BM25公式累加得分
score += idf * fq * (k1 + 1) / (fq + k1 * (1 - b + b * docLen / avgdl));
}
// 返回最终得分
return score;
}
// 展示排序结果的函数
function showResults() {
// 获取用户输入的查询关键词,并去除首尾空格
const query = document.getElementById('query-input').value.trim();
// 获取用于显示结果的DOM元素
const resultList = document.getElementById('result-list');
// 如果没有输入关键词,提示用户输入
if (!query) {
resultList.innerHTML = '请输入关键词';
return;
}
// 对每个文档计算BM25得分,并过滤掉得分为0的文档
const scored = docs.map((doc, idx) => ({
doc,
score: bm25Score(query, doc, docs)
}))
// 按得分从高到低排序
scored.sort((a, b) => b.score - a.score);
// 如果没有相关内容,提示用户
if (scored.length === 0) {
resultList.innerHTML = '没有找到相关内容';
return;
}
// 将排序后的结果渲染到页面
resultList.innerHTML = scored.map(item => `
${item.doc}
分数:${item.score.toFixed(3)}
`).join('');
}
// 给"相关性排序"按钮绑定点击事件,点击时执行showResults函数
document.getElementById('search-btn').onclick = showResults;