前言
在海量信息爆炸的今天,一个优秀的搜索系统不仅是“能搜到”,更是“猜得准”。从你在 Python 爬虫课程中抓取到的海量数据,到用户输入关键词获取精准结果,中间的桥梁就是搜索排序算法。
对于算法工程师而言,构建搜索系统的核心难点在于处理“文本理解”与“用户意图匹配”的矛盾。本文将从基础的文本相似度计算,过渡到工业级的向量检索,带你深度实战搜索排序的核心算法。
一、 基础篇:基于 TF-IDF 的文本排序
TF-IDF(Term Frequency-Inverse Document Frequency)是搜索引擎的基石。它通过衡量一个词在文档中的重要程度,来计算文档与查询语句的相关性。
算法逻辑:
- TF (词频) :词在文章中出现得越多越重要。
- IDF (逆文档频率) :词在所有文章中出现得越少越独特(比如“Python”比“是”更重要)。
代码实战:手写 TF-IDF 相似度排序
python
复制
import math
import jieba
class SimpleSearchEngine:
def __init__(self):
self.documents = []
self.idf_cache = {}
def add_document(self, doc_id, text):
"""添加文档到索引"""
words = list(jieba.cut(text))
self.documents.append({"id": doc_id, "text": text, "words": words})
def compute_idf(self, query_word):
"""计算 IDF 值"""
if query_word in self.idf_cache:
return self.idf_cache[query_word]
N = len(self.documents)
# 包含该词的文档数
df = sum(1 for doc in self.documents if query_word in doc["words"])
if df == 0:
idf = 0
else:
idf = math.log(N / df) + 1
self.idf_cache[query_word] = idf
return idf
def search(self, query):
"""执行搜索并返回排序后的结果"""
query_words = list(jieba.cut(query))
scores = []
for doc in self.documents:
score = 0
for word in query_words:
# 计算 TF: 词在当前文档出现的频率
tf = doc["words"].count(word) / len(doc["words"])
# 计算 IDF
idf = self.compute_idf(word)
score += tf * idf
scores.append({"id": doc["id"], "score": score, "text": doc["text"]})
# 按照得分降序排序 (核心排序逻辑)
ranked_results = sorted(scores, key=lambda x: x["score"], reverse=True)
return ranked_results
# 模拟数据
engine = SimpleSearchEngine()
engine.add_document(1, "Python 是一种广泛使用的编程语言,适合数据分析和爬虫开发")
engine.add_document(2, "TensorFlow 是 Google 开发的深度学习框架")
engine.add_document(3, "使用 Python 开发爬虫可以高效获取互联网数据")
# 搜索测试
query = "Python 爬虫"
results = engine.search(query)
print(f"搜索关键词: '{query}' 的结果:")
for res in results:
if res['score'] > 0: # 只输出相关的
print(f"DocID: {res['id']} | 相似度得分: {res['score']:.4f} | 内容: {res['text']}")
代码解析:
- 分词:使用了
jieba库处理中文分词。 - 向量空间:虽然没有显式生成向量,但 TF-IDF 本质上是将文档映射到了向量空间进行计算。
- 排序:最后一行
sorted(..., reverse=True)实现了基础的排序逻辑,得分越高,排在越前面。
二、 进阶篇:基于向量相似度的语义搜索
传统的 TF-IDF 有一个致命缺陷:它是“关键词匹配”。如果用户搜“小狗”,文档里写的是“犬”,TF-IDF 可能认为它们不相关。
结合大模型时代的背景,现在的搜索系统更倾向于使用向量检索。我们将文本转化为高维向量,计算向量之间的余弦相似度。这也是你在大模型 Agent 课程中学习到的重要知识(RAG 系统的核心)。
代码实战:使用 Sentence-Transformers 进行语义排序
python
复制
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
# 1. 加载预训练模型 (支持中文)
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
# 2. 构建文档库
documents = [
"机器学习是人工智能的一个子集",
"深度学习使用神经网络模拟人脑",
"今天天气真好,适合出去散步",
"番茄炒蛋是一道美味的家常菜"
]
# 3. 将文档编码为向量 (Embedding)
# 这一步将文本变成了 [384维] 的浮点数数组
doc_vectors = model.encode(documents)
# 4. 用户搜索
query = "AI 神经网络技术"
query_vector = model.encode([query])
# 5. 计算余弦相似度
# 相似度范围 [-1, 1],越接近 1 越相似
similarities = cosine_similarity(query_vector, doc_vectors)
# 6. 排序并输出结果
print(f"搜索查询: '{query}'\n")
# argsort 返回的是从小到大的索引,所以取[::-1]进行反转
ranked_indices = np.argsort(similarities[0])[::-1]
for i, idx in enumerate(ranked_indices):
score = similarities[0][idx]
# 过滤掉相似度过低的结果
if score > 0.3:
print(f"Rank {i+1}: 相似度 {score:.4f} -> {documents[idx]}")
算法优势体现:
- 语义理解:即使用户搜索词“AI”和文档词“机器学习”字面不完全重叠,模型也能捕捉到它们在语义上的接近。
- 高效计算:虽然是高维数学运算,但在底层通过矩阵运算加速,非常适合 Python 科学计算栈。
三、 工业级排序:多因子综合打分
在实际的搜索引擎(如抖音、淘宝、Google)中,排序不是单一维度的。通常是多路召回 + 精排。
精排阶段通常会结合以下因子:
- 文本相关性(TF-IDF / 向量相似度)
- 时效性(新鲜的内容加权)
- 用户行为(点击率 CTR、停留时长)
- 质量分(内容是否原创、清晰度)
代码实战:多因子线性加权排序
python
复制
import datetime
def advanced_sort(query, docs):
results = []
# 模拟当前时间
now = datetime.datetime.now()
for doc in docs:
# 因子 1: 文本相关性 (模拟值 0-1)
text_score = doc.get('text_score', 0)
# 因子 2: 时效性 (假设发布时间越新,得分越高)
# 计算距离发布的天数,衰减系数
days_ago = (now - doc['publish_time']).days
time_score = max(0, 1 - days_ago / 30.0) # 30天内线性衰减
# 因子 3: 质量分 (基于点赞数)
# 简单的归一化逻辑
like_score = min(doc['likes'] / 1000.0, 1.0)
# --- 核心算法:加权融合 ---
# 算法工程师需要调整这里的权重 (alpha, beta, gamma)
alpha, beta, gamma = 0.5, 0.2, 0.3
final_score = (alpha * text_score) + \
(beta * time_score) + \
(gamma * like_score)
results.append({
"title": doc['title'],
"final_score": final_score,
"details": f"文本:{text_score:.2f}, 时效:{time_score:.2f}, 质量:{like_score:.2f}"
})
# 按照最终得分排序
return sorted(results, key=lambda x: x['final_score'], reverse=True)
# 模拟数据
docs_data = [
{'title': 'Python 爬虫教程', 'publish_time': now - datetime.timedelta(days=2), 'likes': 50, 'text_score': 0.9},
{'title': '深度学习基础', 'publish_time': now - datetime.timedelta(days=100), 'likes': 2000, 'text_score': 0.5}, # 老但优质
{'title': '今日新闻', 'publish_time': now - datetime.timedelta(days=0), 'likes': 10, 'text_score': 0.1}, # 新但不相关
]
ranked = advanced_sort("Python", docs_data)
print("=== 多因子综合排序结果 ===")
for item in ranked:
print(f"Title: {item['title']}")
print(f"Total Score: {item['final_score']:.3f} | Details: [{item['details']}]")
print("-" * 30)
总结
提升搜索体验是一个系统工程:
- 初学者从 TF-IDF 入手,理解关键词匹配。
- 进阶者拥抱 向量检索,利用 Embedding 解决语义鸿沟。
- 专家通过多因子加权模型,在相关性、时效性和用户体验之间寻找最优解。