一、初识Embedding:一个高维的“语义坐标”
在深入语义检索之前,我们必须先理解它的基石——Embedding向量。你可以把它想象成一个给“概念”或“物体”赋予的、由数字组成的唯一身份标签。这个标签不是随机的,它在多维空间中的位置,深刻地反映了这个概念的“语义”。比如,“国王”和“王后”的向量在空间中的距离会很近,而与“苹果”的向量则相距甚远。
更具体地说,Embedding是一个将离散对象(如一个词、一个句子、一张图片)映射到一个连续、稠密的低维实数向量空间的数学过程。这个过程通过模型学习得到,使得语义相似的对象在这个向量空间中的距离也相近。例如,Word2Vec、GloVe是词向量的经典代表;而如今,BERT、Sentence-Transformers等预训练模型则能为整个句子或文档生成高质量的Embedding向量。
提示:不要将Embedding与One-Hot编码混淆。One-Hot编码是高维稀疏的(如一个10000维的向量,只有一个位置是1),且完全无法表达语义关系。Embedding则是低维稠密的,每个维度都承载着某种抽象的语义特征。
二、为什么我们需要Embedding:从词袋到语义理解
在Embedding大行其道之前,信息检索主要依赖关键词匹配,如TF-IDF和BM25算法。这些方法通过统计词频、逆文档频率等手段来评估文档与查询的相关性。它们高效、可解释,在很长一段时间内是搜索引擎的核心。
然而,关键词匹配有其固有的局限性,它无法理解同义词和语义关联。例如,用户搜索“如何让电脑速度变快”,但文档中可能只提到了“优化系统性能”。在关键词层面,二者几乎没有交集,导致无法匹配。这就是所谓的“词汇鸿沟”问题。
Embedding向量的出现,正是为了弥合这道鸿沟。它通过将“语义”编码为向量,让机器能够理解“电脑速度变快”和“优化系统性能”在意思上是高度相关的。这使得搜索从“找字眼”升级到了“找意思”,开启了语义检索的时代。
三、如何获得Embedding向量:模型与范式
获取高质量的Embedding向量是实践语义检索的关键。其核心在于使用一个强大的编码器模型来处理输入文本。对于句子级的Embedding,当前的主流范式是使用基于Transformer架构的预训练模型进行微调或直接推理。
- 使用预训练模型:像
sentence-transformers/all-MiniLM-L6-v2、text2vec-base-chinese这类模型,它们在海量文本上进行了预训练和语义相似度任务的微调,可以直接输入一个句子,输出一个固定长度的向量(如384维或768维)。这是最直接的方式。 - API服务:许多云服务商(如OpenAI、Cohere、百度等)提供Embedding API,你只需将文本发送过去,就能获得返回的向量,无需管理模型和算力。
- 本地部署微调:对于有特定领域数据或高要求的场景,可以在预训练模型基础上,用领域的数据对模型进行微调,使其更贴合你的业务语义。
四、语义检索的核心原理:向量空间中的相似度计算
语义检索的原理可以简单概括为:“万物皆可向量化,检索即相似度计算”。其工作流程如下:
- 离线索引:将你的所有文档(或文档块)通过Embedding模型转换为向量,存入一个支持向量搜索的数据库(如FAISS, Milvus, Qdrant或Elasticsearch的向量检索插件)中。
- 在线检索:当用户输入一个查询(Query)时,也将其转换为查询向量。
- 相似度匹配:计算查询向量与索引库中所有文档向量的相似度,并返回相似度最高的Top-K个文档作为结果。
最常用的相似度计算方法是余弦相似度,它衡量的是两个向量在方向上的夹角,而不考虑其长度。余弦相似度的值在-1到1之间,越接近1表示向量方向越一致,语义越相似。
# 示例:使用sentence-transformers计算两个句子的余弦相似度
from sentence_transformers import SentenceTransformer, util
# 加载一个预训练模型
model = SentenceTransformer('all-MiniLM-L6-v2')
# 待比较的句子
sentences = [
"如何让电脑速度变快",
"优化系统性能",
"今天中午吃什么"
]
# 将句子编码为向量
embeddings = model.encode(sentences)
# 计算第一个句子向量与其它所有句子向量的余弦相似度
cosine_scores = util.cos_sim(embeddings[0], embeddings[1:])
print(f"查询: '{sentences[0]}'")
print(f"与 '{sentences[1]}' 的相似度: {cosine_scores[0][0]:.4f}")
print(f"与 '{sentences[2]}' 的相似度: {cosine_scores[0][1]:.4f}")
# 预期输出:与第二个句子的相似度远高于与第三个句子的相似度
五、一个简单的端到端语义检索示例
下面我们用一个更完整的例子,模拟一个极简的语义搜索引擎。我们将一个小型语料库向量化,然后根据查询进行检索。
from sentence_transformers import SentenceTransformer, util
import numpy as np
# 1. 初始化模型和语料库
model = SentenceTransformer('all-MiniLM-L6-v2')
corpus = [
"机器学习是人工智能的一个子领域。",
"深度学习模型通常包含多个神经网络层。",
"自然语言处理让计算机理解人类语言。",
"今天天气真好,适合出去散步。",
"我最喜欢的水果是苹果和香蕉。"
]
# 2. 离线阶段:将整个语料库编码为向量,建立“索引”
corpus_embeddings = model.encode(corpus, convert_to_tensor=True)
# 3. 在线阶段:处理用户查询
query = "什么是深度学习?"
query_embedding = model.encode(query, convert_to_tensor=True)
# 4. 使用余弦相似度进行检索,找出最相关的文档
cos_scores = util.cos_sim(query_embedding, corpus_embeddings)[0]
top_results = np.argpartition(-cos_scores, range(1))[:1] # 取Top1
print(f"查询: '{query}'\n最相关的文档:")
for idx in top_results:
print(f"- {corpus[idx]} (相似度: {cos_scores[idx]:.4f})")
# 预期输出:应该会返回关于“深度学习模型通常包含多个神经网络层。”的那句话。
六、关键要点与进阶思考
Embedding向量与语义检索是一对相辅相成的技术。总结一下核心要点:
- Embedding是基础:它提供了将复杂语义“数字化”表示的工具,是后续所有计算的前提。
- 模型决定上限:Embedding模型的质量直接决定了语义检索的精度。选择或训练一个好的模型至关重要。
- 检索是应用:向量相似度计算(如余弦相似度)是连接用户查询与海量知识的高效桥梁。
关键提示:在实际应用中,还需要考虑几个问题:1. 维数灾难:随着向量维度增加,精确的最近邻搜索会变得非常慢,因此需要使用近似最近邻算法(ANN)如HNSW、IVF等。2. 混合检索:纯粹的语义检索有时会忽略关键词的精确匹配,因此生产系统常采用“向量检索 + 关键词检索”的混合模式,并通过一个可调的权重来融合两者的结果。
希望这篇笔记能帮助你建立起对Embedding和语义检索的清晰认知,并鼓励你动手实践。这是通往更智能的信息检索和推荐系统的重要一步。