
在推荐系统、问答系统、搜索引擎等依赖“检索 - 排序”的 AI 场景中,一个普遍被验证的结论是:检索质量的 80% 由排序算法决定,仅 20% 依赖大模型的最终优化。这并非否定大模型的价值,而是明确了“先算法精排、后大模型赋能”的核心逻辑,排序算法解决“把优质候选集筛选出来”的基础问题,大模型解决“把筛选后的结果打磨得更贴合人类需求”的增值问题。
今天我们结合核心概念、基础原理、执行流程,详细的梳理“粗排→算法精排(交叉熵排序 / 余弦重排序)→大模型生成”的完整体系,建立对检索排序的完整认知。

检索排序的本质是“从海量候选集中,按照与用户需求的匹配度输出最优结果序列”。比如:
这个过程中,排序是核心,如果排序算法把优质候选集排在后面,哪怕大模型再强,也只能巧妇难为无米之炊;反之,排序算法筛选出高质量候选集后,大模型只需做精细化优化,就能让结果更贴合需求。
粗排的核心目标是快速缩小候选集范围,要在海量数据中,用低成本、高效率的方式筛选出 Top N候选集,比如从100万条数据中筛选出1000条。
精排是检索排序的核心环节,为了提升排序准确性,也是决定 80% 检索质量的关键。它的目标是“在粗排的候选集基础上,用更复杂的算法和更丰富的特征,精准计算每个候选项的匹配度,输出 Top K 候选集”,比如从1000条中筛选出100条。
大模型生成是精细化优化结果的最后一公里,目标是“在精排的 Top K 候选集基础上,结合用户需求生成更贴合人类语言习惯、更精准的最终结果”。

2.4.1 内容整合与润色
2.4.2 意图对齐与个性化
2.4.3 自然语言交互与生成
2.4.4 少量样本的排序优化
未来的检索排序系统,会是“排序算法 + 大模型”的深度协同,而非谁替代谁:
余弦相似度是余弦重排序的核心,用于衡量两个向量之间的 “方向相似度”,值越大表示越相似(范围:[-1,1])。假设存在两个向量 A=(a1, a2,..., an)和B=(b1 ,b2 ,...,bn),余弦相似度计算公式为:
交叉熵是交叉熵排序的核心,用于衡量“模型预测概率分布”与“真实标签分布”之间的差异,值越小表示模型预测越准确。对于二分类场景(正样本/负样本),交叉熵损失公式为:
举一个直观的例子:
大模型的核心能力是“生成和理解自然语言”,而非“从海量数据中筛选优质候选集”,筛选候选集需要的是“高效的算法 + 精准的特征工程”,这正是排序算法的强项。因此,检索质量的核心瓶颈在排序算法,而非大模型。
交叉熵排序是“有监督的排序算法”,核心是训练一个模型,通过交叉熵损失优化模型参数,让模型能给“匹配的候选项”输出更高的分数,给不匹配的输出更低的分数。

图示说明:正样本概率越接近 1、负样本概率越接近 0,损失越小

步骤 1:数据准备(正负样本标注)
步骤 2:特征工程(排序算法的灵魂)
特征是模型判断“匹配度”的依据,交叉熵排序常用的特征分为三类:
举例:对于问答系统的答案排序,特征可以是:
步骤 3:模型构建
交叉熵排序的模型可以是简单的线性模型(如 LR),也可以是复杂的深度学习模型(如 DNN、Transformer),核心是 “输入特征→输出匹配概率”。
以简单的 LR 模型为例:
步骤 4:损失计算与参数优化
将模型输出的概率p代入交叉熵损失公式,通过梯度下降算法最小化损失,更新权重W和偏置b。
梯度下降的核心逻辑:
步骤 5:模型评估
排序模型的评估不能用“准确率”,而是用“排序指标”,常用的有:
核心逻辑:排名越靠前的正确候选项,贡献的“增益”越高,排名靠后的增益被“折损”。
步骤 6:线上推理
训练好的模型部署后,输入粗排后的候选集特征,模型输出每个候选项的匹配概率,按概率从高到低排序,得到精排结果。
余弦重排序是“无监督的二次排序算法”,核心是在交叉熵排序或其他精排算法的结果基础上,通过计算候选项之间的余弦相似度,调整排序顺序,解决算法排序后仍存在的局部相似度偏差。
交叉熵排序是“基于单个候选项与用户需求的匹配度”排序,但忽略了“候选项之间的相似性”,比如:

图示说明:余弦重排序对 “高相似度候选答案” 的分数惩罚效果,答案分数会下降

步骤 1:候选项向量化
将每个候选项(如答案文本、商品描述)转换成固定维度的向量(Embedding),常用方法:
步骤 2:计算相似度矩阵
假设有K个候选项,生成K×K的相似度矩阵S,其中S_i,j表示第i个候选项和第j个候选项的余弦相似度。
步骤 3:相似度加权
核心思路:“惩罚”与前面候选项高度相似的项,“奖励”差异化高的项。常用加权公式:

图示说明:这张热力图是余弦重排序的“决策依据”,它直观告诉我们:哪些候选答案是高度相似的,需要被“惩罚”。
步骤 4:重新排序
按score_i′从高到低排序,得到余弦重排序后的结果。
将两种算法结合,能发挥 1+1>2 的效果:
以“问答系统答案排序”为例,完整流程如下:

流程说明:
我们以问答系统示例,实现从粗排、算法精排(交叉熵排序、余弦重排序)、大模型生成的完整示例;
我们模拟一个简单的问答知识库,包含“Python 学习”相关的答案:
import pandas as pd
import numpy as np
# 模拟问答知识库
knowledge_base = pd.DataFrame({
"answer_id": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
"answer_text": [
"Python安装需要下载官网安装包,安装时勾选Add Python to PATH",
"零基础学Python先学基础语法:变量、循环、条件判断",
"Python新手推荐廖雪峰教程、菜鸟教程、B站黑马视频",
"Python小项目推荐:计算器、简易爬虫、天气查询工具",
"Java安装需要下载JDK,配置环境变量", # 负样本
"Python进阶学习:函数、类、模块、异常处理",
"Python办公自动化:Excel处理、Word生成、邮件发送",
"C++基础语法:变量、循环、指针", # 负样本
"Python环境管理:Anaconda安装与使用",
"Python调试技巧:print调试、pdb调试工具",
"Python付费课程推荐:XX训练营、XX网课",
"机器学习入门:Python+Scikit-learn实现线性回归",
"Python书籍推荐:《Python编程:从入门到实践》",
"HTML基础语法:标签、属性、布局", # 负样本
"Python代码规范:PEP8规范、注释编写"
],
"label": [1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1] # 1=正样本,0=负样本
})
# 用户问题
user_question = "新手怎么学Python?"使用 Sentence-BERT 生成向量,FAISS 做向量检索:
from sentence_transformers import SentenceTransformer
from modelscope import snapshot_download
import faiss
cache_dir = "D:\\modelscope\\hub"
model_dir = snapshot_download(
model_id="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
cache_dir=cache_dir,
revision="master" # 或指定分支/commit
)
# 1. 加载Embedding模型(轻量级Sentence-BERT)
embedding_model = SentenceTransformer("D:\\modelscope\\hub\\sentence-transformers\\paraphrase-multilingual-MiniLM-L12-v2")
# 2. 知识库文本向量化
answer_texts = knowledge_base["answer_text"].tolist()
answer_embeddings = embedding_model.encode(answer_texts, convert_to_numpy=True)
# 3. 构建FAISS索引
dimension = answer_embeddings.shape[1] # 向量维度:384
index = faiss.IndexFlatL2(dimension) # 暴力检索(适合小数据集)
index.add(answer_embeddings)
# 4. 用户问题向量化
question_embedding = embedding_model.encode([user_question], convert_to_numpy=True)
# 5. 向量检索:筛选Top 10候选集(粗排)
k = 15 # 增大候选集,确保有足够的正负样本
distances, indices = index.search(question_embedding, k)
# 6. 提取粗排结果
rough_ranking_results = knowledge_base.iloc[indices[0]]
print("粗排结果:")
print(rough_ranking_results[["answer_id", "answer_text", "label"]])输出结果:
粗排结果: answer_id answer_text label 12 13 Python书籍推荐:《Python编程:从入门到实践》 1 5 6 Python进阶学习:函数、类、模块、异常处理 1 2 3 Python新手推荐廖雪峰教程、菜鸟教程、B站黑马视频 1 1 2 零基础学Python先学基础语法:变量、循环、条件判断 1 10 11 Python付费课程推荐:XX训练营、XX网课 1 11 12 机器学习入门:Python+Scikit-learn实现线性回归 1 3 4 Python小项目推荐:计算器、简易爬虫、天气查询工具 1 9 10 Python调试技巧:print调试、pdb调试工具 1 0 1 Python安装需要下载官网安装包,安装时勾选Add Python to PATH 1 14 15 Python代码规范:PEP8规范、注释编写 1 7 8 C++基础语法:变量、循环、指针 0 6 7 Python办公自动化:Excel处理、Word生成、邮件发送 1 8 9 Python环境管理:Anaconda安装与使用 1 13 14 HTML基础语法:标签、属性、布局 0 4 5 Java安装需要下载JDK,配置环境变量 0 标签分布:{1: 12, 0: 3}
使用逻辑回归模型,基于交叉熵损失训练,对粗排结果做精排:
from sklearn.linear_model import LogisticRegression
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.preprocessing import StandardScaler
# 1. 特征工程:提取文本匹配特征
def extract_features(question_embedding, answer_embeddings, distances, answer_texts, user_question):
"""提取特征:向量相似度、向量距离、关键词重合数"""
# (1)向量余弦相似度
question_vec = question_embedding[0].reshape(1, -1)
cosine_sim = cosine_similarity(question_vec, answer_embeddings)[0]
# (2)向量距离(L2)
# distances 已经是L2距离了
# (3)关键词重合数(简单中文分词:按字符提取)
# 提取常见中文关键词
import re
question_keywords = set(re.findall(r'[\u4e00-\u9fa5]+|[a-zA-Z]+', user_question))
# 过滤掉标点和停用词
stop_words = {'怎么', '如何', '是', '的', '吗'}
question_keywords = question_keywords - stop_words
keyword_overlap = []
for text in answer_texts:
answer_keywords = set(re.findall(r'[\u4e00-\u9fa5]+|[a-zA-Z]+', text))
overlap = len(question_keywords & answer_keywords)
keyword_overlap.append(overlap)
# (4)特征合并:相似度越大越好,距离越小越好
# 距离取负值,使得距离越小分数越高
features = np.column_stack([cosine_sim, -distances, keyword_overlap])
return features
# 2. 为粗排结果提取特征
rough_answer_texts = rough_ranking_results["answer_text"].tolist()
rough_labels = rough_ranking_results["label"].values
rough_answer_embeddings = answer_embeddings[indices[0]]
features = extract_features(question_embedding, rough_answer_embeddings, distances[0], rough_answer_texts, user_question)
print(f"\n特征矩阵形状: {features.shape}")
print(f"特征列含义: [余弦相似度, -L2距离, 关键词重合数]")
print(f"前3个样本的特征:")
print(features[:3])
import re
question_keywords = set(re.findall(r'[\u4e00-\u9fa5]+|[a-zA-Z]+', user_question))
print(f"问题关键词: {question_keywords}")
# 3. 训练交叉熵排序模型(逻辑回归,损失函数为交叉熵)
# 使用 lbfgs 求解器,支持二分类
ranking_model = LogisticRegression(solver='lbfgs', random_state=42, max_iter=1000)
# 检查是否有足够的正负样本
if len(set(rough_labels)) < 2:
print("\n警告:粗排结果中只有一个类别,无法训练排序模型!")
print("直接使用粗排距离作为排序分数")
rough_ranking_results["fine_score"] = -distances[0] # 距离越小分数越高
else:
print(f"\n标签分布: {rough_labels}")
ranking_model.fit(features, rough_labels)
print(f"模型系数: {ranking_model.coef_}")
print(f"模型截距: {ranking_model.intercept_}")
# 4. 预测匹配概率(精排分数)
fine_ranking_scores = ranking_model.predict_proba(features)[:, 1] # 正样本概率
print(f"预测分数: {fine_ranking_scores[:5]}")
rough_ranking_results["fine_score"] = fine_ranking_scores
# 5. 精排结果:按分数排序,取Top 5
fine_ranking_results = rough_ranking_results.sort_values(by="fine_score", ascending=False).head(10)
print("\n交叉熵精排结果(Top 10):")
print(fine_ranking_results[["answer_id", "answer_text", "fine_score", "label"]])输出结果:
特征矩阵形状: (15, 3) 特征列含义: [余弦相似度, -L2距离, 关键词重合数] 前3个样本的特征: [[ 0.77090418 -7.90510368 1. ] [ 0.67816496 -10.6061821 1. ] [ 0.61285162 -12.32204628 1. ]] 问题关键词: {'新手怎么学', 'Python'} 标签分布: [1 1 1 1 1 1 1 1 1 1 0 1 1 0 0] 模型系数: [[0.0898246 0.34052198 0.78514702]] 模型截距: [8.38018606] 预测分数: [0.9985615 0.996369 0.99346778 0.99234575 0.99102127] 交叉熵精排结果(Top 10): answer_id answer_text fine_score label 12 13 Python书籍推荐:《Python编程:从入门到实践》 0.998562 1 5 6 Python进阶学习:函数、类、模块、异常处理 0.996369 1 2 3 Python新手推荐廖雪峰教程、菜鸟教程、B站黑马视频 0.993468 1 1 2 零基础学Python先学基础语法:变量、循环、条件判断 0.992346 1 10 11 Python付费课程推荐:XX训练营、XX网课 0.991021 1 11 12 机器学习入门:Python+Scikit-learn实现线性回归 0.982300 1 3 4 Python小项目推荐:计算器、简易爬虫、天气查询工具 0.979337 1 9 10 Python调试技巧:print调试、pdb调试工具 0.962889 1 0 1 Python安装需要下载官网安装包,安装时勾选Add Python to PATH 0.957313 1 14 15 Python代码规范:PEP8规范、注释编写 0.937097 1
对精排结果做余弦重排序,优化多样性:
def cosine_re_ranking(candidates, embedding_model, alpha=0.8):
"""
余弦重排序
参数:
- candidates: 精排结果DataFrame,包含answer_text和fine_score
- embedding_model: Sentence-BERT模型
- alpha: 惩罚系数
返回:
- re_ranked_candidates: 重排序后的结果
"""
# 1. 候选项向量化
answer_texts = candidates["answer_text"].tolist()
embeddings = embedding_model.encode(answer_texts, convert_to_numpy=True)
# 2. 计算相似度矩阵
similarity_matrix = cosine_similarity(embeddings)
# 3. 初始化重排序分数
re_ranking_scores = candidates["fine_score"].values.copy()
# 4. 遍历每个候选项,计算加权分数
for i in range(1, len(re_ranking_scores)):
# 取当前项与前面所有项的最大相似度
max_similarity = np.max(similarity_matrix[i, :i])
# 加权分数
re_ranking_scores[i] = re_ranking_scores[i] * (1 - alpha * max_similarity)
# 5. 更新分数并排序
candidates["re_score"] = re_ranking_scores
re_ranked_candidates = candidates.sort_values(by="re_score", ascending=False)
return re_ranked_candidates
# 执行余弦重排序
re_ranked_results = cosine_re_ranking(fine_ranking_results, embedding_model, alpha=0.8)
print("\n余弦重排序结果:")
print(re_ranked_results[["answer_id", "answer_text", "fine_score", "re_score"]])输出结果:
余弦重排序结果: answer_id answer_text fine_score re_score 12 13 Python书籍推荐:《Python编程:从入门到实践》 0.998562 0.998562 0 1 Python安装需要下载官网安装包,安装时勾选Add... 0.957313 0.527326 9 10 Python调试技巧:print调试、pdb调试工具 0.962889 0.486215 11 12 机器学习入门:Python+Scikit-learn实现线性回归 0.982300 0.478992 3 4 Python小项目推荐:计算器、简易爬虫、天气查询工具 0.979337 0.471634 14 15 Python代码规范:PEP8规范、注释编写 0.937097 0.461750 1 2 零基础学Python先学基础语法:变量、循环、条件判断 0.992346 0.440093 10 11 Python付费课程推荐:XX训练营、XX网课 0.991021 0.414431 5 6 Python进阶学习:函数、类、模块、异常处理 0.996369 0.402303 2 3 Python新手推荐廖雪峰教程、菜鸟教程、B站黑马视频 0.993468 0.401953
以调用混元大模型为例,整合重排序结果生成最终回答:
import os
from openai import OpenAI
import json
# 配置混元大模型API Key(需替换为自己的)
api_key = os.environ.get('TENCENT_API_KEY', 'sk-*******************vZ5NP8Ze')
client = OpenAI(
api_key=api_key,
base_url="https://api.hunyuan.cloud.tencent.com/v1",
)
def generate_answer_with_llm(question, candidates):
"""
调用混元大模型生成最终回答
参数:
- question: 用户问题
- candidates: 重排序后的候选答案
"""
# 构建Prompt
candidate_texts = candidates["answer_text"].tolist()
prompt = f"""
请你结合以下候选答案,为用户问题"{question}"生成一个通俗易懂、结构清晰的回答。要求:
1. 优先保留核心步骤,去掉重复内容;
2. 语言风格适合新手,避免专业术语;
3. 结构分为"基础准备""学习步骤""推荐资源"三部分;
4. 回答长度控制在300字以内。
候选答案:
"""
for i, text in enumerate(candidate_texts, 1):
prompt += f"{i}. {text}\n"
try:
# 调用混元大模型
completion = client.chat.completions.create(
model="hunyuan-lite",
messages=[
{'role': 'system', 'content': '你是一个知识问答助手,擅长用通俗易懂的语言解释技术问题'},
{'role': 'user', 'content': prompt}
],
temperature=0.7,
top_p=0.8
)
# 提取生成结果
return completion.choices[0].message.content
except Exception as e:
return f"生成失败:{str(e)}"
# 生成最终回答
final_answer = generate_answer_with_llm(user_question, re_ranked_results)
print("\n大模型生成的最终回答:")
print(final_answer)输出结果:
大模型生成的最终回答: ### 新手怎么学Python? #### 基础准备 首先,你需要准备一台可以运行Python的电脑,并且下载安装Python。你可以访问Python官方网站下载安装包,并在安装过程中勾选“Add Python to PATH”,这样你就可以直接在命令行中使用Python了。 #### 学习步骤 1. **学习基础语法**:了解Python的基本语法,包括变量、循环、条件判断等。 2. **阅读书籍**:推荐《Python编程:从入门到实践》,这本书适合初学者,内容通俗易懂。 3. **在线课程**:可以选择一些免费的零基础课程,比如廖雪峰教程、菜鸟教程或者B站上的黑马视频。 4. **实践项目**:通过做一些小项目来巩固学习成果,比如计算器、简易爬虫或天气查询工具。 5. **进阶学习**:当你对基础语法有一定了解后,可以进一步学习函数、类、模块和异常处理等内容。 #### 推荐资源 - **书籍**:《Python编程:从入门到实践》 - **在线课程**:廖雪峰教程、菜鸟教程、B站黑马视频 - **调试工具**:print调试和pdb调试工具 - **小项目**:计算器、简易爬虫、天气查询工具 通过以上步骤和资源,你可以系统地学习Python,并逐步提高自己的编程能力。
通过对粗排→交叉熵精排→余弦重排序→大模型生成这套流程的深入了解,明白检索系统的核心,真的不在大模型,而在排序算法。真正决定 80% 质量的,是前面的排序环节:粗排负责快速缩小范围,交叉熵负责精准打分,余弦重排序解决冗余和多样性问题。这三步把靠谱的候选内容筛出来,后面大模型才有的发挥。如果前面算法拉胯,给大模型一堆垃圾内容,再强的模型也救不回来。简单点就是先把向量检索、精排模型跑通,再用大模型做最后一公里的润色和整合,性价比极高。
我们实际操作优先把特征、相似度、排序损失函数吃透,把精排效果做上去;重排序用来提升多样性,大模型只做锦上添花。不要一上来就全链路大模型,又慢又贵还不稳定。总而言之:排序算法是地基,大模型是装修。地基打好,房子才稳;装修到位,住着才舒服。把这套“算法为主、大模型为辅”的思路用熟,不管是做项目还是面试,都会非常有竞争力。
import pandas as pd
import numpy as np
# 模拟问答知识库
knowledge_base = pd.DataFrame({
"answer_id": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
"answer_text": [
"Python安装需要下载官网安装包,安装时勾选Add Python to PATH",
"零基础学Python先学基础语法:变量、循环、条件判断",
"Python新手推荐廖雪峰教程、菜鸟教程、B站黑马视频",
"Python小项目推荐:计算器、简易爬虫、天气查询工具",
"Java安装需要下载JDK,配置环境变量", # 负样本
"Python进阶学习:函数、类、模块、异常处理",
"Python办公自动化:Excel处理、Word生成、邮件发送",
"C++基础语法:变量、循环、指针", # 负样本
"Python环境管理:Anaconda安装与使用",
"Python调试技巧:print调试、pdb调试工具",
"Python付费课程推荐:XX训练营、XX网课",
"机器学习入门:Python+Scikit-learn实现线性回归",
"Python书籍推荐:《Python编程:从入门到实践》",
"HTML基础语法:标签、属性、布局", # 负样本
"Python代码规范:PEP8规范、注释编写"
],
"label": [1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1] # 1=正样本,0=负样本
})
# 用户问题
user_question = "新手怎么学Python?"
from sentence_transformers import SentenceTransformer
from modelscope import snapshot_download
import faiss
cache_dir = "D:\\modelscope\\hub"
model_dir = snapshot_download(
model_id="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
cache_dir=cache_dir,
revision="master" # 或指定分支/commit
)
# 1. 加载Embedding模型(轻量级Sentence-BERT)
embedding_model = SentenceTransformer("D:\\modelscope\\hub\\sentence-transformers\\paraphrase-multilingual-MiniLM-L12-v2")
# 2. 知识库文本向量化
answer_texts = knowledge_base["answer_text"].tolist()
answer_embeddings = embedding_model.encode(answer_texts, convert_to_numpy=True)
# 3. 构建FAISS索引
dimension = answer_embeddings.shape[1] # 向量维度:384
index = faiss.IndexFlatL2(dimension) # 暴力检索(适合小数据集)
index.add(answer_embeddings)
# 4. 用户问题向量化
question_embedding = embedding_model.encode([user_question], convert_to_numpy=True)
# 5. 向量检索:筛选Top 10候选集(粗排)
k = 15 # 增大候选集,确保有足够的正负样本
distances, indices = index.search(question_embedding, k)
# 6. 提取粗排结果
rough_ranking_results = knowledge_base.iloc[indices[0]]
print("粗排结果:")
print(rough_ranking_results[["answer_id", "answer_text", "label"]])
print(f"\n标签分布:{rough_ranking_results['label'].value_counts().to_dict()}")
from sklearn.linear_model import LogisticRegression
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.preprocessing import StandardScaler
# 1. 特征工程:提取文本匹配特征
def extract_features(question_embedding, answer_embeddings, distances, answer_texts, user_question):
"""提取特征:向量相似度、向量距离、关键词重合数"""
# (1)向量余弦相似度
question_vec = question_embedding[0].reshape(1, -1)
cosine_sim = cosine_similarity(question_vec, answer_embeddings)[0]
# (2)向量距离(L2)
# distances 已经是L2距离了
# (3)关键词重合数(简单中文分词:按字符提取)
# 提取常见中文关键词
import re
question_keywords = set(re.findall(r'[\u4e00-\u9fa5]+|[a-zA-Z]+', user_question))
# 过滤掉标点和停用词
stop_words = {'怎么', '如何', '是', '的', '吗'}
question_keywords = question_keywords - stop_words
keyword_overlap = []
for text in answer_texts:
answer_keywords = set(re.findall(r'[\u4e00-\u9fa5]+|[a-zA-Z]+', text))
overlap = len(question_keywords & answer_keywords)
keyword_overlap.append(overlap)
# (4)特征合并:相似度越大越好,距离越小越好
# 距离取负值,使得距离越小分数越高
features = np.column_stack([cosine_sim, -distances, keyword_overlap])
return features
# 2. 为粗排结果提取特征
rough_answer_texts = rough_ranking_results["answer_text"].tolist()
rough_labels = rough_ranking_results["label"].values
rough_answer_embeddings = answer_embeddings[indices[0]]
features = extract_features(question_embedding, rough_answer_embeddings, distances[0], rough_answer_texts, user_question)
print(f"\n特征矩阵形状: {features.shape}")
print(f"特征列含义: [余弦相似度, -L2距离, 关键词重合数]")
print(f"前3个样本的特征:")
print(features[:3])
import re
question_keywords = set(re.findall(r'[\u4e00-\u9fa5]+|[a-zA-Z]+', user_question))
print(f"问题关键词: {question_keywords}")
# 3. 训练交叉熵排序模型(逻辑回归,损失函数为交叉熵)
# 使用 lbfgs 求解器,支持二分类
ranking_model = LogisticRegression(solver='lbfgs', random_state=42, max_iter=1000)
# 检查是否有足够的正负样本
if len(set(rough_labels)) < 2:
print("\n警告:粗排结果中只有一个类别,无法训练排序模型!")
print("直接使用粗排距离作为排序分数")
rough_ranking_results["fine_score"] = -distances[0] # 距离越小分数越高
else:
print(f"\n标签分布: {rough_labels}")
ranking_model.fit(features, rough_labels)
print(f"模型系数: {ranking_model.coef_}")
print(f"模型截距: {ranking_model.intercept_}")
# 4. 预测匹配概率(精排分数)
fine_ranking_scores = ranking_model.predict_proba(features)[:, 1] # 正样本概率
print(f"预测分数: {fine_ranking_scores[:5]}")
rough_ranking_results["fine_score"] = fine_ranking_scores
# 5. 精排结果:按分数排序,取Top 5
fine_ranking_results = rough_ranking_results.sort_values(by="fine_score", ascending=False).head(10)
print("\n交叉熵精排结果(Top 10):")
print(fine_ranking_results[["answer_id", "answer_text", "fine_score", "label"]])
def cosine_re_ranking(candidates, embedding_model, alpha=0.8):
"""
余弦重排序
参数:
- candidates: 精排结果DataFrame,包含answer_text和fine_score
- embedding_model: Sentence-BERT模型
- alpha: 惩罚系数
返回:
- re_ranked_candidates: 重排序后的结果
"""
# 1. 候选项向量化
answer_texts = candidates["answer_text"].tolist()
embeddings = embedding_model.encode(answer_texts, convert_to_numpy=True)
# 2. 计算相似度矩阵
similarity_matrix = cosine_similarity(embeddings)
# 3. 初始化重排序分数
re_ranking_scores = candidates["fine_score"].values.copy()
# 4. 遍历每个候选项,计算加权分数
for i in range(1, len(re_ranking_scores)):
# 取当前项与前面所有项的最大相似度
max_similarity = np.max(similarity_matrix[i, :i])
# 加权分数
re_ranking_scores[i] = re_ranking_scores[i] * (1 - alpha * max_similarity)
# 5. 更新分数并排序
candidates["re_score"] = re_ranking_scores
re_ranked_candidates = candidates.sort_values(by="re_score", ascending=False)
return re_ranked_candidates
# 执行余弦重排序
re_ranked_results = cosine_re_ranking(fine_ranking_results, embedding_model, alpha=0.8)
print("\n余弦重排序结果:")
print(re_ranked_results[["answer_id", "answer_text", "fine_score", "re_score"]])
import os
from openai import OpenAI
import json
# 配置混元大模型API Key(需替换为自己的)
api_key = os.environ.get('TENCENT_API_KEY', 'sk-*************e')
client = OpenAI(
api_key=api_key,
base_url="https://api.hunyuan.cloud.tencent.com/v1",
)
def generate_answer_with_llm(question, candidates):
"""
调用混元大模型生成最终回答
参数:
- question: 用户问题
- candidates: 重排序后的候选答案
"""
# 构建Prompt
candidate_texts = candidates["answer_text"].tolist()
prompt = f"""
请你结合以下候选答案,为用户问题"{question}"生成一个通俗易懂、结构清晰的回答。要求:
1. 优先保留核心步骤,去掉重复内容;
2. 语言风格适合新手,避免专业术语;
3. 结构分为"基础准备""学习步骤""推荐资源"三部分;
4. 回答长度控制在300字以内。
候选答案:
"""
for i, text in enumerate(candidate_texts, 1):
prompt += f"{i}. {text}\n"
try:
# 调用混元大模型
completion = client.chat.completions.create(
model="hunyuan-lite",
messages=[
{'role': 'system', 'content': '你是一个知识问答助手,擅长用通俗易懂的语言解释技术问题'},
{'role': 'user', 'content': prompt}
],
temperature=0.7,
top_p=0.8
)
# 提取生成结果
return completion.choices[0].message.content
except Exception as e:
return f"生成失败:{str(e)}"
# 生成最终回答
final_answer = generate_answer_with_llm(user_question, re_ranked_results)
print("\n大模型生成的最终回答:")
print(final_answer)原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。