搜索"如何学习编程"和"编程入门方法",传统关键词匹配系统会认为这两个查询毫无关系——它们没有共享任何关键词。但人类一眼就能看出这是同一类问题。这个鸿沟困扰了信息检索领域数十年,直到向量嵌入技术给出了一个优雅的数学答案:把文字映射到连续向量空间,让语义相似的文本在几何空间中靠近。

这不是简单的"文字转数字"技术,而是一场关于如何表示意义的范式转变。从2013年Mikolov的Word2Vec论文到今天的百亿参数嵌入模型,这条技术路线改变了搜索引擎、推荐系统、对话AI等几乎所有自然语言处理应用的底层逻辑。

分布式假设:嵌入的理论基石

1957年,英国语言学家John Rupert Firth在《A Synopsis of Linguistic Theory》中写下了一句后来被无数次引用的话:“You shall know a word by the company it keeps”(一个词的含义由它周围的词决定)。这就是著名的分布式假设(Distributional Hypothesis)。

这个假设的数学表述是:如果两个词出现在相似的上下文中,它们应该具有相似的语义。形式化地说,对于词 $w_1$ 和 $w_2$,如果它们的上下文分布 $P(context|w_1)$ 和 $P(context|w_2)$ 相似,那么它们的语义也应该相似。

这听起来像是常识,但实现起来却面临一个根本性困难:如何表示"上下文分布"?传统方法使用共现矩阵(Co-occurrence Matrix),比如一个 $|V| \times |V|$ 的矩阵,其中 $|V|$ 是词汇表大小,矩阵元素 $M_{ij}$ 表示词 $i$ 和词 $j$ 在某个窗口内共同出现的次数。这种方法有两个致命问题:维度灾难(百万级词汇表意味着百万维向量)和稀疏性(大部分矩阵元素为零)。

Word2Vec的突破在于:不直接计算共现矩阵,而是训练一个神经网络来预测上下文,网络隐藏层的权重就是我们需要的词向量。

Word2Vec:从预测任务到向量表示

2013年,Tomas Mikolov等人在论文《Efficient Estimation of Word Representations in Vector Space》中提出了Word2Vec。这篇论文引入了两种模型架构:CBOW(Continuous Bag of Words)和Skip-gram。

CBOW的目标是根据上下文预测中心词。给定上下文词 $w_{t-c}, ..., w_{t-1}, w_{t+1}, ..., w_{t+c}$,模型要预测 $w_t$。形式化目标是最小化:

$$J = -\log P(w_t | w_{t-c}, ..., w_{t+c})$$

Skip-gram则相反:给定中心词 $w_t$,预测其上下文。目标函数为:

$$J = -\sum_{-c \leq j \leq c, j \neq 0} \log P(w_{t+j} | w_t)$$

直觉上,CBOW更适合小数据集,因为它通过平均上下文词向量来"平滑"噪声;Skip-gram更适合大数据集,因为它为每个上下文词产生独立的训练样本。实践中,Skip-gram通常表现更好,尤其是在处理罕见词时。

负采样:让训练变得可行

原始的Softmax计算需要对整个词汇表求和:

$$P(w_o | w_i) = \frac{\exp(v_{w_o}^T v_{w_i})}{\sum_{w \in V} \exp(v_w^T v_{w_i})}$$

当词汇表有百万级词时,这个分母的计算代价不可接受。Mikolov在后续论文《Distributed Representations of Words and Phrases and their Compositionality》中提出了负采样(Negative Sampling)作为解决方案。

负采样的核心思想是:不计算完整的Softmax,而是把多分类问题转化为二分类问题。对于真实词对 $(w_i, w_o)$,我们希望最大化:

$$\sigma(v_{w_o}^T v_{w_i})$$

其中 $\sigma(x) = \frac{1}{1+e^{-x}}$ 是Sigmoid函数。同时,对于 $k$ 个随机采样的"负样本"词 $\tilde{w}$,我们希望最小化:

$$\sigma(v_{\tilde{w}}^T v_{w_i})$$

最终的训练目标变为:

$$J = -\left[ \log \sigma(v_{w_o}^T v_{w_i}) + \sum_{j=1}^{k} \mathbb{E}_{\tilde{w} \sim P_n(w)} \log \sigma(-v_{\tilde{w}}^T v_{w_i}) \right]$$

其中 $P_n(w)$ 是噪声分布,通常取 $P_n(w) \propto f(w)^{0.75}$,$f(w)$ 是词频。为什么要取0.75次方?因为这样可以提高低频词被采样的概率,防止高频词主导训练。

图片来源: Baeldung - NLP’s word2vec: Negative Sampling Explained

负采样把计算复杂度从 $O(|V|)$ 降到了 $O(k)$,其中 $k$ 通常取5-20。这使得在大规模语料上训练词向量变得可行。

向量运算揭示语义关系

Word2Vec最令人兴奋的发现是:词向量之间的线性运算可以捕捉语义关系。最著名的例子是:

$$v_{king} - v_{man} + v_{woman} \approx v_{queen}$$

这意味着"国王"与"男人"的差异向量,近似等于"女王"与"女人"的差异向量——性别关系被编码在向量空间的方向上。

类似的关系还有很多:

  • 国家-首都:$v_{Paris} - v_{France} + v_{Italy} \approx v_{Rome}$
  • 动词时态:$v_{walked} - v_{walk} + v_{swim} \approx v_{swam}$
  • 比较级:$v_{bigger} - v_{big} + v_{small} \approx v_{smaller}$

这些关系的存在不是巧合。它们源于分布式假设的自然推论:如果"king"经常出现在"queen"附近(如"the king and queen"),而"man"经常出现在"woman"附近,那么这些共现模式的差异就会反映在向量差异上。

从词到句:句子嵌入的困境与突破

Word2Vec成功解决了词级别的语义表示,但实际应用中我们更常处理句子或段落。一个朴素的想法是:把句子中所有词向量平均,得到句子向量。

这个方法在早期(2014年左右的Paragraph Vector)被广泛使用,但效果不佳。为什么?因为简单的平均会丢失词序信息。“狗咬人"和"人咬狗"的词向量平均结果完全相同,但语义截然不同。

另一个问题是无意义词的干扰。停用词(“的”、“是”、“在”)在几乎所有句子中都出现,它们的向量会"稀释"有意义的语义信息。

BERT的问题:Cross-Encoder不可扩展

2018年,BERT的出现改变了NLP格局。BERT输出的token级别嵌入富含上下文信息,同一个词在不同语境下有不同的向量表示。但BERT本身不是为句子相似度设计的。

用BERT计算两句话的相似度,标准做法是把两句话拼接后输入模型,让BERT输出一个相似度分数。这叫Cross-Encoder架构。

图片来源: Pinecone - Sentence Transformers: Meanings in Disguise

Cross-Encoder效果很好,但有一个致命缺陷:不可扩展。要在10万个句子中找到与查询最相似的句子,需要把查询和每个句子配对后输入BERT,总共10万次前向传播。BERT-base一次前向传播约需10毫秒,10万次就是1000秒——完全无法用于实时搜索。

Sentence-BERT:Siamese架构的优雅解法

2019年,Nils Reimers和Iryna Gurevych在论文《Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks》中提出了Sentence-BERT(SBERT)。核心思想是:用Siamese网络结构训练BERT,让它输出可以直接比较的句子向量。

Siamese网络由两个共享权重的子网络组成。对于句子对 $(A, B)$,两个句子分别通过同一个BERT,得到两个句子嵌入 $u$ 和 $v$。训练目标是让相似句子的嵌入接近,不相似句子的嵌入远离。

SBERT使用三种信息拼接作为分类器输入:

$$feature = (u, v, |u-v|)$$

其中 $|u-v|$ 是元素级差值的绝对值。为什么要加入差值?因为 $u$ 和 $v$ 本身编码的是句子的绝对语义,而 $|u-v|$ 编码的是它们的相对差异。这种设计让模型同时学习绝对语义和相对差异。

图片来源: Pinecone - Sentence Transformers: Meanings in Disguise

训练数据来自SNLI(Stanford Natural Language Inference)和MNLI(Multi-Genre NLI)数据集,包含57万句对,标注为"蕴含”、“中立"或"矛盾”。这个分类任务强迫模型学习区分语义相似和不同的句子。

训练完成后,SBERT可以直接输出句子向量。在10万句子的数据集中查找最相似句子,只需要预先计算所有句子的嵌入(一次性),然后用余弦相似度比较——整个过程约5秒,比Cross-Encoder快了720倍。

池化策略:CLS vs Mean

BERT输出的是一个序列的token嵌入,如何得到单个句子向量?有两种主流策略:

CLS Pooling:取BERT特殊token [CLS] 的输出嵌入。BERT预训练时,[CLS] 被设计为编码整个句子的信息(用于下一句预测任务)。

Mean Pooling:对所有token嵌入取平均。直觉上,平均可以捕捉整个句子的信息。

SBERT论文的实验表明,Mean Pooling通常优于CLS Pooling。原因在于:[CLS] 在预训练时被优化用于二分类任务(是否为连续句子),这与句子相似度任务的目标不完全一致。而Mean Pooling不依赖预训练假设,更通用。

# Mean Pooling实现示例
import torch

def mean_pooling(model_output, attention_mask):
    token_embeddings = model_output[0]  # [batch_size, seq_len, hidden_dim]
    input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
    sum_embeddings = torch.sum(token_embeddings * input_mask_expanded, 1)
    sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9)
    return sum_embeddings / sum_mask

注意注意力掩码的使用:padding token不应该参与平均计算。

嵌入空间的几何:维度、相似度与各向异性

维度的权衡:表达能力 vs 计算成本

嵌入维度是一个关键超参数。维度太低,语义信息无法充分编码;维度太高,计算和存储成本增加,还可能导致过拟合。

BERT-base的嵌入维度是768,BERT-large是1024。为什么是这些数字?它们与Transformer的注意力头数相关:768 = 12 heads × 64 dim/head,1024 = 16 heads × 64 dim/head。这反映了嵌入维度受限于模型架构。

现代嵌入模型的维度范围很大:

  • 小模型:384维(如all-MiniLM-L6-v2)
  • 中模型:768维(如all-mpnet-base-v2)
  • 大模型:1024-1536维(如E5-large、text-embedding-3-large)

经验法则:对于大多数RAG应用,768维是甜点。384维适合对延迟敏感的场景,1024维以上适合对精度要求极高的场景。

维度的边际效益递减。从128维到384维,性能提升明显;从768维到1536维,提升有限但成本翻倍。这是因为语义信息的"有效维度"可能远低于原始嵌入维度——大部分方差集中在少数主成分上。

相似度度量:Cosine vs Dot Product vs Euclidean

给定两个嵌入向量 $u$ 和 $v$,如何度量它们的相似度?

余弦相似度

$$\text{cos}(u, v) = \frac{u \cdot v}{\|u\| \|v\|}$$

点积

$$u \cdot v = \sum_{i=1}^{d} u_i v_i$$

欧几里得距离

$$\|u - v\|_2 = \sqrt{\sum_{i=1}^{d} (u_i - v_i)^2}$$

这三者有什么区别?

余弦相似度只关注方向,忽略模长。两个向量如果指向同一方向,余弦相似度为1,即使它们模长相差很大。这对文本嵌入很合理,因为嵌入模长可能受句子长度、词频等因素影响,不应该影响语义相似度判断。

点积同时考虑方向和模长。如果嵌入被L2归一化(模长为1),点积等于余弦相似度。某些训练目标(如对比学习)会直接优化点积,此时使用点积作为度量更合理。

欧几里得距离度量绝对位置差异。对归一化嵌入,它与余弦相似度有确定关系:

$$\|u - v\|_2 = \sqrt{2(1 - \text{cos}(u, v))}$$

所以对归一化嵌入,欧几里得距离和余弦相似度是等价的(排序相同)。

实践中,余弦相似度是默认选择。但有一个微妙问题:如果嵌入没有被归一化,且模长与语义相关(比如更长句子的嵌入模长更大),点积可能会更合适。

各向异性:嵌入空间的隐藏陷阱

各向异性(Anisotropy)是指嵌入向量在空间中分布不均匀,趋向于聚集在一个狭窄的锥体内。这种情况下,任意两个向量的余弦相似度都会偏高,失去区分能力。

2020年的研究《On the Sentence Embeddings from Pre-trained Language Models》发现,BERT原始嵌入有严重的各向异性问题:所有嵌入都聚集在一个很小的锥体内,导致任意两个句子的余弦相似度都很高。

为什么会这样?Transformer的自注意力机制可能导致某些维度被"过度使用",而其他维度被忽视。具体机制仍在研究中,但现象是明确的。

解决方案包括:

  • 后处理归一化:对嵌入进行白化(Whitening)或去均值
  • 训练时约束:在损失函数中加入正则项,鼓励各向同性
  • 对比学习:通过对比正负样本,自然地拉大相似和不相似样本的距离

现代嵌入模型(如E5、BGE)通过对比学习训练,天然缓解了各向异性问题。

向量数据库:当规模超过暴力搜索的极限

有了句子嵌入,如何在大规模数据中快速检索?

暴力搜索的瓶颈

最直接的方法是暴力搜索(Flat Index):计算查询向量与所有数据库向量的相似度,返回top-k个。时间复杂度 $O(n \times d)$,其中 $n$ 是数据量,$d$ 是维度。

对于100万个768维向量,暴力搜索需要约7.68亿次浮点乘法。在现代CPU上,这可能需要几百毫秒。对于1000万、1亿级别的数据,延迟会增长到不可接受。

近似最近邻搜索(ANN)

近似最近邻搜索的核心思想是:牺牲少量精度,换取巨大的速度提升。不需要找到真正的最近邻,只需要找到"足够近"的邻居。

主流ANN算法分为三类:

  • 树方法:KD-Tree、Ball Tree(适合低维)
  • 哈希方法:LSH(Locality Sensitive Hashing)
  • 图方法:HNSW(Hierarchical Navigable Small World)

高维空间(d > 100)中,树方法和哈希方法效果下降,图方法成为主流选择。

HNSW:图搜索的巅峰之作

HNSW结合了两个经典思想:概率跳表(Skip List)和可导航小世界图(Navigable Small World)。

概率跳表是一种高效的数据结构,通过多层链表实现 $O(\log n)$ 搜索。顶层链表跨度大(跳过很多节点),底层链表跨度小。搜索时从顶层开始,快速定位到目标区域,然后逐层下钻。

可导航小世界图是一种特殊的图结构,每个节点连接若干邻居。如果图同时包含长程连接(连接远处节点)和短程连接(连接近处节点),那么从任意节点出发,可以通过贪婪路由快速到达目标节点。

HNSW把两者结合:顶层是稀疏的长程连接(快速跨越整个空间),底层是密集的短程连接(精确定位)。搜索时从顶层入口节点开始,贪婪地向查询向量靠近,到达局部最优后下降到下一层继续搜索。

图片来源: Pinecone - Hierarchical Navigable Small Worlds (HNSW)

HNSW有几个关键参数:

  • M:每个节点的最大连接数。M越大,图越密集,搜索越精确,但内存占用越高。
  • efConstruction:构建时的动态候选列表大小。越大,图质量越好,但构建越慢。
  • efSearch:搜索时的动态候选列表大小。越大,搜索越精确,但越慢。

经验值:M=16-64,efConstruction=100-400,efSearch可以根据实时需求动态调整。

# 使用Faiss构建HNSW索引
import faiss

d = 768  # 向量维度
M = 32   # 每个节点的连接数
index = faiss.IndexHNSWFlat(d, M)

# 设置构建参数
index.hnsw.efConstruction = 200

# 添加向量(这一步构建图结构)
index.add(vectors)  # vectors: [n, d] numpy array

# 设置搜索参数
index.hnsw.efSearch = 100

# 搜索
distances, indices = index.search(query_vector, k=10)

HNSW的性能令人印象深刻。在Sift1M数据集(100万个128维向量)上,M=32、efSearch=100的配置可以实现召回率95%,搜索延迟仅约1毫秒。

但HNSW不是银弹。它的内存占用较大,因为需要存储图结构。对于10亿级别的数据,内存可能成为瓶颈。这时需要结合量化技术(如PQ)来压缩向量。

现代嵌入模型:对比学习与硬负样本

E5:引入指令前缀

2023年,微软的研究者在论文《Text Embeddings by Weakly-Supervised Contrastive Pre-training》中提出了E5模型。E5的核心创新是"指令前缀"(Instruction Prefix):在输入文本前添加任务描述,让嵌入针对不同任务优化。

例如:

  • 检索任务:“query: …” 和 “passage: …”
  • 相似度任务:“Instruct: Retrieve semantically similar text\nQuery: …”

这种设计让同一个模型可以输出不同风格的嵌入,提高了通用性。

E5的训练分为两阶段:

  1. 弱监督预训练:使用大规模文本对(来自网页链接、Reddit等),通过对比学习训练
  2. 有监督微调:使用标注数据集(如MS MARCO、NQ)进一步优化

BGE:多阶段对比学习

BGE(BAAI General Embedding)由北京智源人工智能研究院开发,是多语言嵌入模型的代表。BGE的训练流程包括:

  1. RetroMAE预训练:通过重建被遮蔽的输入来学习文本表示
  2. 对比学习:使用大规模文本对进行对比训练
  3. 困难负样本挖掘:选择语义相似但不同的样本作为负样本,提高模型区分能力

困难负样本(Hard Negatives)是对比学习的关键技术。传统的负样本是随机采样的,它们与锚样本差异明显,模型很容易区分。困难负样本则选择那些与锚样本相似但实际上不匹配的样本。例如,对于查询"如何学习Python",困难负样本可能是"Python的历史",而不是随机文本"今日天气"。

困难负样本迫使模型学习更细粒度的语义区分。

MTEB:嵌入模型的竞技场

MTEB(Massive Text Embedding Benchmark)是评估嵌入模型的标准基准,包含56个数据集,覆盖8类任务:

  • 检索(Retrieval):从大规模文档中找到相关文档
  • 语义相似度(STS):判断句子对的相似度
  • 分类(Classification):使用嵌入作为特征进行分类
  • 聚类(Clustering):将相似文本聚成一类
  • 重排序(Reranking):对候选列表重新排序
  • 配对分类(Pair Classification):判断句子对的关系
  • 摘要(Summarization):评估摘要质量

截至2025年初,MTEB排行榜前列的模型包括:

  • NV-Embed:NVIDIA开发,在56个任务上平均分69.32
  • E5-large-v2:微软开发,平衡性能与效率
  • BGE-large-en-v1.5:开源模型的佼佼者

选择嵌入模型时,不能只看MTEB总分,要关注与你任务相关的子任务分数。比如构建RAG系统,检索任务的权重应该更高。

嵌入的实际应用:从语义搜索到RAG

语义搜索:超越关键词匹配

传统搜索依赖BM25等关键词匹配算法。BM25基于TF-IDF,考虑词频和逆文档频率。它假设:一个词在文档中出现越频繁,越重要;一个词在所有文档中出现越少,区分度越高。

BM25的优势是简单、可解释、无训练成本。但它的局限也很明显:

  • 无法处理同义词(“机器学习"和"ML”)
  • 无法处理改写(“如何学习编程"和"编程入门方法”)
  • 对罕见词和专有名词效果差

语义搜索使用嵌入来解决这些问题。流程如下:

  1. 离线索引

    • 对所有文档计算嵌入
    • 用向量数据库(如Milvus、Qdrant)建立索引
  2. 在线检索

    • 对查询计算嵌入
    • 在向量数据库中搜索最近邻
    • 返回top-k相关文档

图片来源: Milvus - How do vector embeddings work in semantic search

混合检索:结合BM25与语义搜索

实践中,纯语义搜索可能漏掉精确匹配的场景。比如搜索特定产品型号,关键词匹配更准确。混合检索(Hybrid Search)结合两者优势:

  1. BM25检索,得到候选集A
  2. 语义检索,得到候选集B
  3. 融合A和B的结果(如RRF:Reciprocal Rank Fusion)

RRF的融合公式:

$$RRF(d) = \sum_{r \in R} \frac{1}{k + rank_r(d)}$$

其中 $R$ 是所有检索系统,$rank_r(d)$ 是文档 $d$ 在系统 $r$ 中的排名,$k$ 是平滑常数(通常取60)。

这种融合方式简单有效,不需要学习权重,适合大多数场景。

RAG:检索增强生成

RAG(Retrieval-Augmented Generation)是当前大模型应用的热门架构。核心思想是:生成答案前,先检索相关文档,把文档和问题一起输入大模型。

RAG的检索阶段完全依赖嵌入质量。一个常见问题是:用户的查询可能很短(“怎么解决这个问题”),而文档库中的内容很长且详细。这种长度不匹配会导致检索效果下降。

解决方案包括:

查询扩展:用大模型改写查询,生成多个语义等价的变体,分别检索后合并结果。

假设性文档嵌入(HyDE):先让大模型生成一个假设性答案,用答案的嵌入来检索。直觉是:答案可能比问题更接近相关文档。

重排序:检索更多候选(如100个),用Cross-Encoder或大模型进行精细重排,取top-k。Cross-Encoder比嵌入相似度更准确,但速度慢,只适合少量候选。

嵌入的量化与压缩

大规模向量检索的瓶颈往往是内存。100万个768维float32向量占用约3GB内存。对于10亿向量,需要300GB——超出大多数服务器的内存容量。

二值量化(Binary Quantization)

最激进的压缩是把float32变成1位。规则很简单:正数变成1,负数变成0。一个float32向量变成二值向量,压缩32倍。

相似度计算变成汉明距离(Hamming Distance):两个二值向量有多少位不同。这可以用异或和位计数指令高效实现。

import numpy as np

def binary_quantize(embeddings):
    """二值量化"""
    return (embeddings > 0).astype(np.uint8)

def hamming_distance(a, b):
    """汉明距离"""
    return np.count_nonzero(a != b)

二值量化的代价是精度损失。对于768维嵌入,二值化后召回率可能下降10-20%。但在某些场景(如粗排)可以接受。

标量量化(Scalar Quantization)

标量量化把float32压缩到int8。具体做法是找到向量的最小值和最大值,线性映射到[-128, 127]。

$$x_{int8} = \text{round}\left(\frac{x - x_{min}}{x_{max} - x_{min}} \times 255 - 128\right)$$

反量化时:

$$x \approx (x_{int8} + 128) / 255 \times (x_{max} - x_{min}) + x_{min}$$

标量量化压缩4倍,精度损失通常在1-2%以内,是性价比很高的选择。

乘积量化(Product Quantization)

乘积量化(PQ)是一种更精细的压缩方法。它把向量分成多个子向量,每个子向量独立量化。

对于768维向量,可以分成96个8维子向量。每个子向量学习256个质心(需要8位表示),整个向量压缩到96字节——压缩32倍。

PQ的相似度计算不需要反量化。预计算查询子向量与所有质心的距离表,检索时只需查表相加。这大大加速了搜索。

PQ的缺点是构建索引需要训练(学习质心),不适合动态更新的场景。

中文嵌入的特殊挑战

中文文本处理有一些独特挑战,影响嵌入质量。

分词与字符编码

中文没有空格分隔词语,分词是嵌入前的必要步骤。但分词本身有歧义:“南京市长江大桥"可以分成"南京市/长江/大桥"或"南京/市长/江大桥”。

现代嵌入模型通常使用子词分词(如BPE、WordPiece),在一定程度上缓解了这个问题。但中文的字与词之间没有明确边界,模型需要同时学习字级别和词级别的语义。

中文标点符号、数字、英文字母的混排也是挑战。“我有100个iPhone"这句话中,模型需要正确理解数字和英文的含义。

多语言嵌入模型

对于多语言场景(如中英混合检索),需要支持多语言的嵌入模型。主流选择包括:

  • mE5:多语言版E5,支持100+语言
  • BGE-M3:支持多语言、多粒度、多功能的嵌入模型
  • multilingual-e5-large:基于XLM-RoBERTa的多语言模型

多语言模型的挑战在于语言平衡:如果训练数据以英文为主,其他语言的嵌入质量会下降。优秀的多语言模型会进行语言平衡采样,确保每种语言都有足够的训练信号。

嵌入不是银弹

尽管嵌入技术强大,但它不是万能的。

长文本处理

大多数嵌入模型有长度限制,通常在512 token左右。对于长文档,需要分段处理后聚合。但分段会丢失文档结构信息,段落间的联系可能被切断。

解决方案包括:

  • 分层嵌入:对文档的不同层级(章节、段落、句子)分别建立嵌入
  • 摘要嵌入:用大模型生成摘要,对摘要建立嵌入
  • ColBERT式嵌入:保留每个token的嵌入,检索时计算MaxSim分数

领域适应

通用嵌入模型在特定领域可能效果不佳。比如医疗、法律、金融等专业领域,有很多术语和独特的语义结构。

领域适应方法:

  • 继续预训练:在领域语料上进行MLM预训练
  • 对比学习微调:使用领域内的正负样本对进行对比训练
  • Adapter微调:在冻结原模型的情况下,训练轻量级Adapter

嵌入的时效性

嵌入是静态的:文本改变后需要重新计算嵌入。对于频繁更新的内容(如新闻、社交媒体),这可能是性能瓶颈。

增量更新策略:只对新内容计算嵌入,定期重建索引以保证质量。

结语

从Word2Vec到现代的E5和BGE,向量嵌入走过了十二年演进。核心技术从简单的上下文预测发展到复杂的对比学习,应用场景从词相似度扩展到检索、聚类、重排序等全谱系任务。

嵌入的本质是把离散的符号映射到连续向量空间,让"意义"变成可以计算的数学对象。这个映射不是完美的——它依赖于分布式假设,忽略某些语义细节,受限于训练数据。但在实用层面,它已经改变了我们与信息交互的方式。

当你下次在搜索引擎输入一个问题,得到精准的回答时,背后可能就是几十亿个向量在毫秒间完成了一次语义空间的穿越。这是数学的力量,也是工程的艺术。


参考文献

  1. Mikolov, T., et al. (2013). Efficient Estimation of Word Representations in Vector Space. arXiv:1301.3781
  2. Mikolov, T., et al. (2013). Distributed Representations of Words and Phrases and their Compositionality. NIPS.
  3. Reimers, N., & Gurevych, I. (2019). Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. EMNLP.
  4. Wang, L., et al. (2022). Text Embeddings by Weakly-Supervised Contrastive Pre-training. arXiv:2212.03533
  5. Xiao, S., et al. (2023). C-Pack: Packed Resources For General Chinese Embeddings. arXiv:2309.07597
  6. Malkov, Y., & Yashunin, D. (2016). Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs. IEEE TPAMI.
  7. Muennighoff, N., et al. (2023). MTEB: Massive Text Embedding Benchmark. arXiv:2210.07316
  8. Li, B., et al. (2023). Towards General Text Embeddings with Multi-stage Contrastive Learning. arXiv:2308.03281