图片

在 RAG(检索增强生成)系统中,文本 Embedding 与向量相似度检索是核心环节 —— 把文本转为高维向量、再通过余弦相似度找到语义最相关内容,让大模型能精准召回知识。本文将基于 ModelScope 上的intfloat/multilingual-e5-small模型,下载 ONNX 格式后,用 Python 实现本地高效 Embedding 生成 + 余弦相似度计算,并适配 RAG 场景的向量检索需求。

一、模型与技术选型

1.1 为什么选 multilingual-e5-small?

multilingual-e5-small是 intfloat 开源的轻量多语言文本 Embedding 模型,适配 RAG / 语义检索场景,核心优势:

  • 多语言覆盖

    :支持 100 + 语言(含中文、英文、日文等),跨语言语义统一

  • 轻量高效

    :12 层 Transformer、384 维输出向量,体积小、推理快,适合本地部署

  • ONNX 友好

    :可导出 / 直接使用 ONNX 格式,搭配onnxruntime实现 CPU 加速推理

  • RAG 适配

    :专为检索优化,Mean Pooling 输出句子向量,余弦相似度效果稳定

模型来源:ModelScope intfloat/multilingual-e5-small

1.2 技术栈

  • 模型格式

    :ONNX(本地推理、无依赖、跨平台)

  • 推理引擎

    onnxruntime(高效执行 ONNX 模型)

  • 分词工具

    transformers(加载模型配套 Tokenizer)

  • 相似度计算

    scikit-learn(余弦相似度)

  • 向量处理

    numpy(张量运算、均值池化)

二、环境准备与模型下载

2.1 安装依赖

pip install onnxruntime transformers scikit-learn numpy

2.2 下载 ONNX 模型

  1. 打开 ModelScope 模型页:https://www.modelscope.cn/models/intfloat/multilingual-e5-small/

  2. 进入模型文件,找到并下载ONNX 格式模型包(含model.onnxtokenizer.jsonvocab.txt等)

  3. 解压到本地目录,例如:E:\git_project\factory-qa\models\multilingual-e5-small-onnx

提示:也可通过transformers.onnx工具将 PyTorch 版导出为 ONNX,社区已提供预导出版本,直接下载更高效。

三、代码重构与实现(模块化优化版)

基于原代码重构,拆分为模型加载、Embedding 生成、相似度计算、批量检索、结果打印五大函数,逻辑清晰、可复用、易扩展。

3.1 完整代码

import time
import numpy as np
import onnxruntime as ort
from sklearn.metrics.pairwise import cosine_similarity
from transformers import AutoTokenizer
# -------------------------- 配置常量(按需修改)--------------------------
# 本地ONNX模型目录
MODEL_DIR = r"E:\git_project\factory-qa\models\multilingual-e5-small-onnx"
# ONNX模型文件名
ONNX_MODEL_FILE = "model.onnx"
# 文本最大长度(截断/填充)
MAX_SEQ_LENGTH = 512
# ---------------------------------------------------------------------------
def load_onnx_embedding_model(model_dir: str):
    """
    加载ONNX推理会话与Tokenizer
    :param model_dir: 模型本地目录
    :return: tokenizer, ort_session(分词器、ONNX会话)
    """
    # 加载Tokenizer
    tokenizer = AutoTokenizer.from_pretrained(model_dir)
    # 加载ONNX模型
    onnx_path = f"{model_dir}/{ONNX_MODEL_FILE}"
    ort_session = ort.InferenceSession(onnx_path)
    print(f"✅ 模型加载完成:{onnx_path}")
    return tokenizer, ort_session
def generate_text_embedding(ort_session, tokenizer, text: str) -> np.ndarray:
    """
    单文本生成Embedding向量(ONNX推理+均值池化)
    :param ort_session: ONNX推理会话
    :param tokenizer: 分词器
    :param text: 输入文本
    :return: 384维句子Embedding向量
    """
    # 文本分词预处理
    inputs = tokenizer(
        text,
        return_tensors="pt",
        padding=True,
        truncation=True,
        max_length=MAX_SEQ_LENGTH
    )

    # 构造ONNX输入(转为numpy,补全token_type_ids)
    input_feed = {
        "input_ids": inputs["input_ids"].numpy(),
        "attention_mask": inputs["attention_mask"].numpy(),
        "token_type_ids": np.zeros_like(inputs["input_ids"].numpy())
    }

    # ONNX推理
    outputs = ort_session.run(None, input_feed)
    token_embeddings = outputs[0]  # (1, seq_len, 384)

    # 均值池化:token级向量 → 句子级向量
    sentence_embedding = np.mean(token_embeddings, axis=1)
    return sentence_embedding
def compute_cosine_similarity(emb1: np.ndarray, emb2: np.ndarray) -> float:
    """
    计算两个句子Embedding的余弦相似度
    :param emb1: 向量1
    :param emb2: 向量2
    :return: 相似度(0~1,值越高越相似)
    """
    return cosine_similarity(emb1, emb2)[0][0]
def batch_similarity_retrieval(
    ort_session,
    tokenizer,
    query: str,
    candidate_texts: list
) -> tuple:
    """
    批量检索:查询 vs 候选文本,计算相似度+统计性能
    :param ort_session: ONNX会话
    :param tokenizer: 分词器
    :param query: 查询文本
    :param candidate_texts: 候选文本列表
    :return: 相似度结果、总耗时、总Token数、推理速度
    """
    # 仅生成1次查询向量(优化性能,避免重复计算)
    start = time.time()
    query_emb = generate_text_embedding(ort_session, tokenizer, query)
    query_time = time.time() - start
    results = []
    total_time = 0.0
    total_tokens = 0
    # 遍历候选文本,计算相似度
    for text in candidate_texts:
        t_start = time.time()
        text_emb = generate_text_embedding(ort_session, tokenizer, text)
        # 统计耗时与Token数
        cost = time.time() - t_start
        total_time += cost
        total_tokens += text_emb.shape[1]
        # 计算相似度
        sim = compute_cosine_similarity(query_emb, text_emb)
        results.append((query, text, sim))
    # 计算推理速度(Token/秒)
    speed = total_tokens / total_time if total_time > 0 else 0
    total_cost = query_time + total_time
    return results, total_cost, total_tokens, speed
def print_retrieval_result(results: list, total_time: float, total_tokens: int, speed: float):
    """格式化打印检索结果与性能数据"""
    print("\n" + "="*60)
    print("📊 语义相似度检索结果")
    print("="*60)
    for q, t, s in results:
        print(f"相似度:{s:.3f} | 查询:{q} | 候选:{t}")
    print("="*60)
    print(f"⏱️ 总耗时:{total_time:.4f}s | 总Token数:{total_tokens} | 推理速度:{speed:.2f} token/s")
def main():
    """主函数:执行完整流程"""
    # 1. 加载模型
    tokenizer, ort_session = load_onnx_embedding_model(MODEL_DIR)

    # 2. 定义查询与候选文本(模拟RAG知识库)
    query = "苹果手机多少钱"
    candidates = [
        "这个小米的手机什么价格",
        "苹果什么价值",
        "我想买一部分手机",
        "iPhone 15售价多少",
        "华为手机最新报价"
    ]

    # 3. 批量相似度检索
    results, total_time, total_tokens, speed = batch_similarity_retrieval(
        ort_session, tokenizer, query, candidates
    )

    # 4. 打印结果
    print_retrieval_result(results, total_time, total_tokens, speed)
if __name__ == "__main__":
    main()

3.2 代码核心说明

  1. 模型加载

    load_onnx_embedding_model 统一加载 Tokenizer 与 ONNX 会话,解耦初始化逻辑。

  2. Embedding 生成

    generate_text_embedding 完成分词→ONNX 推理→均值池化,输出 384 维句子向量(适配 RAG 向量库)。

  3. 相似度计算

    compute_cosine_similarity 基于余弦相似度衡量语义相关性,值越接近 1 越相似。

  4. 批量检索

    batch_similarity_retrieval 优化查询向量仅生成 1 次,大幅提升批量处理速度,并统计推理性能。

  5. 结果打印

    print_retrieval_result 格式化输出相似度与性能指标,便于调试与评估。

四、运行结果与效果解析

============================================================
📊 语义相似度检索结果
============================================================
相似度:0.922 | 查询:苹果手机多少钱 | 候选:这个小米的手机什么价格
相似度:0.946 | 查询:苹果手机多少钱 | 候选:苹果什么价值
相似度:0.905 | 查询:苹果手机多少钱 | 候选:我想买一部分手机
相似度:0.936 | 查询:苹果手机多少钱 | 候选:iPhone 15售价多少
相似度:0.918 | 查询:苹果手机多少钱 | 候选:华为手机最新报价
============================================================
⏱️ 总耗时:0.0270s | 总Token数:1920 

4.2 结果解读

  • 语义匹配精准

    :“苹果什么价值” 与查询相似度最高(0.946),“iPhone 15售价多少” 次之(0.936),符合语义逻辑。

  • 推理效率高

    :CPU 环境下速度快,满足 RAG 实时检索需求。

  • 多语言兼容

    :输入英文 / 日文文本同样可生成有效向量,适配跨境 RAG 场景。

五、在 RAG 系统中的应用

5.1 RAG 核心流程(Embedding + 检索)

  1. 知识库向量化

    :将文档 / FAQ 切分→用本模型生成 Embedding→存入向量数据库(如 FAISS、Qdrant、Milvus)。

  2. 用户查询向量化

    :用户提问→生成查询 Embedding。

  3. 向量检索

    :在向量库中查找 Top-K 最相似的文本片段→返回给大模型生成答案。

5.2 扩展建议

  • 向量归一化

    :对 Embedding 做 L2 归一化,提升余弦相似度计算稳定性(np.linalg.norm(emb, axis=1, keepdims=True))。

  • 批量处理优化

    :对候选文本批量分词 / 推理,进一步提升吞吐量。

  • 向量数据库集成

    :将生成的 Embedding 存入 FAISS/Qdrant,实现百万级数据毫秒级检索。

  • 模型量化

    :使用 ONNX Runtime INT8 量化,模型体积减少 60%、速度提升 45%。

六、总结

本文基于 ModelScope 的multilingual-e5-small ONNX 模型,实现了轻量、高效、多语言的文本 Embedding 与相似度检索,完全适配 RAG 系统的核心需求。相比闭源 API,本地 ONNX 推理无成本、无延迟、数据安全,且性能足以支撑中小规模 RAG 应用。

后续可进一步集成向量数据库、优化文档切分策略,打造完整的企业级 RAG 检索系统。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐