一、从离散到连续:为什么需要 Embedding?
在计算机的世界里,文本、单词、图片等信息最初都是离散的符号。比如,“机器学习”和“深度学习”这两个词,在传统的字符串表示中,它们之间没有任何数学上的联系。但我们的大脑知道,这两个概念在语义上是非常相似的。
为了让计算机也能“理解”这种相似性,我们需要一种方法,把这些离散的符号映射到一个连续的、有意义的数学空间中。Embedding(嵌入)就是完成这个任务的核心技术。它本质上是一个映射函数,将一个高维、稀疏的符号(如一个单词或句子)映射为一个低维、稠密的实数向量。这个向量,就是它在“概念空间”中的坐标。
这个坐标的意义在于,向量的距离和方向可以量化地表示原始对象之间的语义关系。语义上相似的物体,它们的向量在空间中的距离会更近;反之,语义上相反或不相关的物体,它们的向量距离会更远。这为我们用数学方法处理语义关系打开了大门。
二、语义检索:超越关键词的搜索革命
传统搜索引擎或数据库查询基于关键词匹配,它精准但死板。例如,搜索“苹果的CEO是谁”,如果文档里写的是“蒂姆·库克领导着这家科技巨头”,那么关键词匹配很可能失败,因为“CEO”和“领导”不是同一个词。
语义检索正是为了解决这个问题。它的核心思想是:不直接比较字符串,而是比较它们所对应Embedding向量之间的语义相似度。我们不再关心“词形”是否匹配,而是关心“意思”是否相近。
最常用的相似度度量是余弦相似度。它衡量的是两个向量在方向上的夹角余弦值。值越接近1,表示两个向量方向越一致,语义越相似;值越接近-1,表示方向相反;接近0则表示几乎无关。通过计算查询文本的Embedding与数据库中所有文档Embedding的余弦相似度,我们就能找到语义上最相关的文档,即使它们表面用词完全不同。
提示:语义检索不是万能的。它擅长理解语义关联,但对于精确的数值、专有名词匹配,结合传统关键词搜索(混合检索)往往是更稳健的工程选择。
三、如何获得 Embedding 向量?
获取高质量的Embedding向量是整个流程的关键。目前主流方法是使用预训练语言模型。这些模型(如 BERT、RoBERTa、Sentence-BERT)已经在海量的文本数据上进行了无监督或自监督学习,学会了将文本编码为富含语义信息的向量。
我们通常不需要从头训练模型,而是采用迁移学习的范式:加载一个预训练模型,然后用它来将我们的文本(查询、文档、商品描述等)转换为向量。对于中文任务,可以选择像 paraphrase-multilingual-MiniLM-L12-v2 这样的多语言模型,或者专门针对中文优化的模型。
以下是一个使用 sentence-transformers 库获取句子Embedding的简单示例:
from sentence_transformers import SentenceTransformer
# 1. 加载一个预训练的句子嵌入模型
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
# 2. 定义一些待编码的句子
sentences = [
“机器学习是人工智能的一个分支。”,
“让计算机从数据中自动学习规律。”,
“今天晚饭吃什么?”
]
# 3. 模型将句子列表编码为向量列表
embeddings = model.encode(sentences)
print(f”向量维度: {embeddings.shape}“) # 输出如 (3, 384),表示3个384维的向量
print(f”第一个句子的向量前5维: {embeddings[0][:5]}“)
运行这段代码,你会看到每个句子都被转换成了一个固定长度的数字数组(例如384维),这就是它的Embedding向量。
四、构建一个完整的语义检索系统
一个最基本的语义检索流程可以概括为三个阶段:索引、查询、排序。我们用一个简单的例子来演示如何用Python实现。
第一步:离线索引。 将所有文档(假设是一个小的知识库)用Embedding模型转换为向量,并存入一个向量库中。
from sentence_transformers import SentenceTransformer, util
import torch
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
# 我们的“知识库”文档
documents = [
“Python是一种解释型、面向对象、动态数据类型的高级程序设计语言。”,
“Guido van Rossum于1991年底发布了Python的第一个公开版本。”,
“TensorFlow是一个开源的机器学习框架,由Google团队开发。”,
“PyTorch是另一个流行的深度学习框架,以其动态计算图著称。”
]
# 为所有文档生成Embedding向量
doc_embeddings = model.encode(documents, convert_to_tensor=True)
第二步:在线查询。 接收用户的查询文本,同样将其编码为向量。
第三步:相似度计算与排序。 计算查询向量与所有文档向量的余弦相似度,并按相似度降序排列。
query = “Python是谁创造的?”
query_embedding = model.encode(query, convert_to_tensor=True)
# 使用pytorch计算余弦相似度
cosine_scores = util.cos_sim(query_embedding, doc_embeddings)[0]
# 按相似度得分排序
top_results = torch.topk(cosine_scores, k=2)
print(“查询:”, query)
print(“\n最相关的文档:“)
for score, idx in zip(top_results.values, top_results.indices):
print(f”得分: {score:.4f}“)
print(f”文档: {documents[idx]}\n”)
输出的结果会显示,尽管查询文本“创造”与文档中的“开发”或“发布”词形不同,但语义检索系统依然能正确找到最相关的文档。
五、实践中的关键考量
在实际项目中构建语义检索系统,有几个关键点需要仔细斟酌:
- 模型选择:不同的Embedding模型在语义质量、推理速度、向量维度上差异很大。需要根据业务场景(如文本长度、领域专业性、性能要求)进行评估和选择。通用模型可能在特定领域(如医疗、法律)表现不佳,这时可以考虑在领域数据上微调。
- 向量维度:维度越高,理论上能捕捉的信息越丰富,但计算和存储成本也越高。通常384、768维是常用的平衡点。
- 相似度度量:除了余弦相似度,点积和欧氏距离也常被使用。对于归一化后的向量,余弦相似度和点积是等价的。选择哪种,有时取决于你使用的向量检索数据库的最佳实践。
- 索引与检索速度:当文档量达到百万、千万级别时,逐一计算相似度会非常慢。此时必须使用专业的向量数据库(如 Milvus, Pinecone, FAISS)或支持向量检索的数据库(如 Elasticsearch with dense_vector)。它们使用近似最近邻搜索算法,能在牺牲极小精度的情况下,将检索时间从秒级降到毫秒级。
六、总结与进阶思路
我们来回顾一下核心脉络:为了解决语义理解问题,我们使用Embedding将符号映射为向量;基于向量间的相似度计算(如余弦相似度),实现了超越关键词的语义检索;实现这一切的关键是强大的预训练模型。
未来可以深入的方向包括:
- 混合检索:结合传统的BM25关键词检索和向量语义检索,通过权重或排序融合来获得最佳效果。
- 检索增强生成:这是当前最火的应用之一。将语义检索的结果作为外部知识,输入给大语言模型,用于生成更准确、可溯源的回答。
- 多模态检索:不仅限于文本,还可以将图片、音频、视频也Embedding到同一个向量空间,实现跨模态的“以文搜图”或“以图搜图”。
提示:理解原理是第一步,动手实践是关键。可以从一个小型文本集合开始,亲自动手写一遍索引和查询的代码,直观地感受语义检索的魔力。遇到问题时,思考是模型质量问题、向量质量问题还是工程实现问题,这是提升调试能力的绝佳场景。