2.1 文本嵌入与语义表示
📌 本节目标
学完这一节,你将理解什么是文本嵌入(Text Embedding),以及为什么它是 RAG 架构中检索器的基石。你将掌握如何使用预训练模型将任意句子转换为向量,并学会根据任务需求选择不同的嵌入模型。
1. 核心概念讲解
1.1 什么是文本嵌入?
计算机无法直接理解人类的语言,它只认识数字。文本嵌入就是将一段文本(比如一个词、一句话或一篇文章)转换成一个固定长度的数字列表(我们称之为“向量”)。
比如:
想象我们把“猫”和“狗”这两个词放在一个二维平面上。“猫”的位置是 [0.2, 0.8],“狗”的位置是 [0.3, 0.7]。因为它们都是宠物,所以在平面上的距离很近。而“汽车”的位置是 [-0.5, -0.2],离它们很远。这个“位置坐标”就是嵌入向量。坐标越接近,语义越相似。
1.2 嵌入向量的特点
- 固定长度:无论输入是“你好”还是“今天天气真不错,我们一起去公园散步吧”,输出的向量长度都是一样的(例如 384 维或 768 维)。
- 语义保留:相似的文本在向量空间中距离更近。例如“开心的”和“快乐的”的向量距离,会比“开心的”和“悲伤的”更近。
- 可计算性:我们可以用数学公式(如余弦相似度)计算两个向量之间的距离,从而量化文本的相似度。
1.3 什么是嵌入模型?
嵌入模型是一个预训练的神经网络,它的任务就是把文本“压缩”成向量。你不需要自己训练它,只需要下载使用即可。
比如: 就像你不需要学会做菜刀,只需要从超市买一把好用的菜刀来切菜。嵌入模型就是这把“刀”,帮你把文本“切”成向量。
1.4 代码示例:生成第一个句子嵌入
下面我们使用 sentence-transformers 库来生成一个简单的句子嵌入。
# 首先安装库(如果还没安装的话)
# pip install sentence-transformers
from sentence_transformers import SentenceTransformer
# 1. 加载一个轻量级的预训练模型
# 'all-MiniLM-L6-v2' 是一个常用的入门模型,速度快且效果好
model = SentenceTransformer('all-MiniLM-L6-v2')
# 2. 准备要编码的句子
sentence = "RAG架构让大语言模型变得更加强大。"
# 3. 生成嵌入向量
embedding = model.encode(sentence)
# 4. 查看结果
print(f"输入句子: {sentence}")
print(f"嵌入向量的维度: {len(embedding)}") # 输出: 384
print(f"嵌入向量的前10个数字: {embedding[:10]}")
输出示例:
输入句子: RAG架构让大语言模型变得更加强大。
嵌入向量的维度: 384
嵌入向量的前10个数字: [ 0.1234 -0.5678 0.9101 ... 0.2345 -0.6789 0.1112]
💡 注意:每次运行生成的向量值可能略有不同(取决于模型和随机种子),但语义关系是稳定的。
2. 深入理解
2.1 为什么嵌入这么重要?
在 RAG 系统中,检索器的核心任务是“找相似”。我们不可能把整个知识库的文本都塞给大模型,那样太慢了,也太贵了。正确的做法是:
- 离线阶段:把知识库中的所有文档都通过嵌入模型转换成向量,存入向量数据库。
- 在线阶段:当用户提问时,先把问题转换成向量,然后去向量数据库里找最相似的文档片段。
这个过程的成败,很大程度上取决于嵌入向量的质量。质量好的嵌入能准确捕捉语义,质量差的嵌入可能把“苹果好吃”和“苹果手机好用”混为一谈。
2.2 初学者常见的误区
| 误区 | 正确理解 | |------|----------| | 向量维度越高越好 | 维度高不一定好,可能带来“维度灾难”(数据稀疏、计算变慢)。384或768维对大多数任务已经足够。 | | 所有嵌入模型都一样 | 不同模型训练数据不同,擅长领域不同。有的擅长英文,有的擅长中文;有的擅长短文本,有的擅长长文档。 | | 嵌入向量可以直接比较 | 需要先归一化(Normalize)再计算余弦相似度。大多数库会自动处理,但自己实现时要注意。 | | 嵌入模型不需要选择 | 模型大小直接影响推理速度和精度。生产环境中需要在速度和效果之间做权衡。 |
2.3 如何选择嵌入模型?
选择嵌入模型时,可以问自己三个问题:
- 语言支持:你的文本主要是中文、英文还是多语言?如果是中文,优先考虑
text2vec-base-chinese或BAAI/bge-base-zh-v1.5。 - 性能要求:你的应用需要实时响应吗?如果每秒需要处理上千次请求,选择小模型(如
all-MiniLM-L6-v2);如果追求极致准确率,选择大模型(如gte-large)。 - 文本长度:你的文档平均有多长?有的模型最大输入长度是 128 token,有的是 512 token。超出长度的部分会被截断。
推荐入门组合:
- 英文任务:
all-MiniLM-L6-v2(快且稳) - 中文任务:
shibing624/text2vec-base-chinese(中文友好) - 多语言任务:
intfloat/multilingual-e5-small(支持多种语言)
3. 实战演示
实战目标:比较三个句子的语义相似度
我们将用嵌入模型计算三个句子的向量,然后用余弦相似度判断它们之间的语义关系。
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
# 1. 加载模型
model = SentenceTransformer('all-MiniLM-L6-v2')
# 2. 准备三个句子
sentences = [
"我喜欢在周末去公园散步。",
"周末去公园散步是我的爱好。", # 语义相近
"我最近在学习如何编程。", # 语义不同
]
# 3. 生成嵌入向量
embeddings = model.encode(sentences)
# 4. 计算余弦相似度矩阵
similarity_matrix = cosine_similarity(embeddings)
# 5. 打印结果
print("句子列表:")
for i, s in enumerate(sentences):
print(f"{i}: {s}")
print("\n相似度矩阵(值越接近1表示越相似):")
print(f"句子0 vs 句子1: {similarity_matrix[0][1]:.4f}") # 应该很高,比如 0.85+
print(f"句子0 vs 句子2: {similarity_matrix[0][2]:.4f}") # 应该很低,比如 0.30+
print(f"句子1 vs 句子2: {similarity_matrix[1][2]:.4f}") # 应该也很低
期望输出:
句子列表:
0: 我喜欢在周末去公园散步。
1: 周末去公园散步是我的爱好。
2: 我最近在学习如何编程。
相似度矩阵(值越接近1表示越相似):
句子0 vs 句子1: 0.8721
句子0 vs 句子2: 0.2134
句子1 vs 句子2: 0.1987
✅ 可以看到,语义相近的句子相似度很高(0.87),而语义不同的句子相似度很低(0.21)。这正是文本嵌入的魅力所在。
4. 关键要点
- 文本嵌入是将文本转换为固定长度的数字向量,是 RAG 检索器的基础。
- 嵌入质量决定检索效果,选择模型时要考虑语言、速度和文本长度。
- 余弦相似度是衡量向量相似度的常用方法,值越接近1表示越相似。
- 不要盲目追求高维度,384或768维对大多数场景已经足够。
- 不同模型适用不同场景,中文任务建议使用中文专用模型。
✏️ 练习任务
任务1:句子相似度排序
给定以下三个句子,使用 shibing624/text2vec-base-chinese 模型生成嵌入向量,并计算它们与句子 A 的余弦相似度,按相似度从高到低排序。
- A: "人工智能正在改变世界。"
- B: "机器学习是人工智能的一个分支。"
- C: "今天天气真不错。"
期望输出格式:
与A最相似的句子是: B (相似度: 0.xx)
其次是: C (相似度: 0.xx)
任务2(选做):自建小型语义搜索
创建一个包含 5 个句子的知识库(内容自定,比如关于水果的描述)。然后输入一个查询句(比如“一种红色、甜的水果”),找出知识库中与查询句最相似的句子。
提示步骤:
- 定义知识库列表。
- 对知识库中每个句子生成嵌入向量。
- 对查询句生成嵌入向量。
- 计算查询向量与每个知识库向量的相似度。
- 输出相似度最高的句子和相似度值。
💡 下一节我们将学习如何用向量数据库存储这些嵌入,并实现高效检索。
本节由 StudyAI8 AI 课程团队生成并审核。