前文中我们告别了NLP的规则时代和统计时代。接着对Word2Vec等静态词向量模型的介绍初步迈进了深度学习时代。NLP此时面临的问题变成了:"如何在不同上下文语境中动态计算词向量?"
| 时代 | 标志 | 核心变化 |
|---|---|---|
| 规则时代 | 语法规则、词典、模板 | 人写规则,机器按规则匹配 |
| 统计时代 | N-gram、HMM、CRF、TF-IDF | 从语料里数频次、估概率 |
| 深度学习时代 | Word2Vec、RNN、Transformer | 用神经网络学连续表示 |
一、文本向量化
上文我们已经通过Word2Vec初探了神经网络在文本向量化的应用,但仍然存在诸多问题。渐渐人们发现FNN(前馈神经网络)局限性:没有记忆,看不懂上下文。死板的输入长度,容不下不同长短的队列。
正是因为NN有这两大死穴,NLP领域才催生了RNN(循环神经网络)解决前后依赖问题。RNN引入了"状态(State)"和"循环(Loop)"机制。它读句子是从左到右一个字一个字读的,并且会把前一个字的状态传递给后一个字。就像人读书一样,读到"银行"的时候,脑子里还记着前面读过的"去",从而能理解"去银行"的含义。
(1)ELMo
ELMo 是从 Word2Vec 走向上下文动态表示的关键一步。它解决的核心问题正是:同一个词在不同语境里应该有不同的向量。
| 维度 | Word2Vec | ELMo |
|---|---|---|
| 表示方式 | 静态词向量 | 动态上下文向量 |
| 词向量来源 | 查表:一个词一个固定向量 | 由语言模型实时计算 |
| 多义词处理 | 无法区分 | 能区分 |
| 架构 | 浅层预测模型 | 双向 LSTM 语言模型 |
| 训练目标 | 预测上下文或中心词 | 前向 + 后向语言建模 |
| 未登录词 | 处理差 | 用字符级 CNN 缓解 |
Word2Vec 训练完成后,每个词只有一个固定向量,所以 "苹果" 在水果语境和公司语境里向量相同。ELMo 则不再查表,而是把句子输入一个双向语言模型,根据整句话动态算出每个词的向量。ELMo 的工作流程:
-
字符级编码
每个词先通过字符级 CNN 编码,得到一个初始向量。这样即使遇到训练时没出现过的词,也能根据字符片段生成表示。
-
双向语言模型编码
前向 LSTM 从左到右预测下一个词,后向 LSTM 从右到左预测前一个词。这样每个词都能同时看到左边和右边的上下文。
-
多层表示融合
对每个 token,ELMo 会得到多个向量:底层词嵌入 + 各层前向 LSTM 输出 + 各层后向 LSTM 输出。底层更多携带词法、句法信息,高层更多携带语义信息。最终向量是这些层的加权组合,权重可以随下游任务调整。
Word2Vec 是"查表式静态词向量",一个词永远只有一个表示;ELMo 是"语言模型式动态词向量",同一个词在不同句子里会由双向 LSTM 实时算出不同向量,从而解决一词多义问题。ELMo 虽然实现了动态上下文向量,但它本质上是一个"拼凑"出来的双向模型。它有一个单向的 LSTM(从左往右读)和一个单向的 LSTM(从右往左读)。最后,它只是把前向、后向和初始词向量简单拼接在一起。这导致了两个严重的短板:
- 双向交互不够彻底: 前向和后向 LSTM 之间完全没有交互,直到最后输出时才强行拼接。
- LSTM 架构的天然缺陷: 串行计算效率低,且无法像 Transformer 那样一次性捕捉全局依赖(长距离上下文)。
(2)BERT
BERT 的核心突破在于它用一个统一的 Transformer 架构,把 ELMo 的"浅层双向拼接"升级成了"深层双向融合"。 BERT 扔掉了 ELMo 的双向 LSTM 拼接方案,直接采用了 Transformer Encoder。BERT是怎么做到的?(核心原理)
BERT 的全称是 Bidirectional Encoder Representations from Transformers。它的做法可以概括为"一个架构 + 一个预训练任务":
-
架构:Transformer Encoder
它由多层 Transformer Encoder 堆叠而成。每个 Token 输入后,会经过多层自注意力(捕捉上下文)和前馈网络(提炼特征)的处理,最后输出一个极其丰富的上下文向量。
-
预训练任务:掩码语言建模 (MLM)
为了逼迫模型真正理解上下文,BERT 在预训练时随机把 15% 的词遮掉(变成 MASK),然后让模型去猜被遮掉的词是什么。
例如:
我 去 [MASK] 存钱→ 模型必须结合前后文,推断出这里是"银行"。
那聪明的你会想了,难道BERT就没有缺点吗?有的当然有,它最大的问题是它本来就不是为"把一段文本变成一个检索向量"而设计的。输出长度受限,经典的BERT模型通常只支持512 tokens,长文档容易被截断,上下文丢失。推理效率也低,不适合大规模的知识库离线建索引。
(3)BGE
于是人们开始专门设计用于通用文本嵌入的模型,BGE 仍然基于 Transformer Encoder,但它不是"普通 BERT",而是专门优化过的嵌入模型 。BGE 的核心思路是:用重建任务逼出高质量句向量 → 用对比学习教会检索排序 → 用三种表示互补覆盖不同检索场景 → 用长文本和多语言支持适配真实世界。 它不是发明了新架构,而是把已有技术重新组合,专门针对"检索"这个任务做了端到端优化。
① 设计一:一个模型同时输出三种表示
BGE-M3 一次推理会同时给出 稠密向量、稀疏向量、多向量 三种结果。
- 稠密向量:把整段话压缩成一个向量,擅长理解语义。比如"手机没电了"和"移动设备电量耗尽"能被识别为相似。
- 稀疏向量:类似升级版关键词匹配,擅长记住专有名词、编号、术语。比如搜 "GPT-4o" 就不该被"GPT-3.5"糊弄过去。
- 多向量:保留每个 token 的独立向量,检索时做细粒度对齐,擅长长文档里精确定位匹配片段。
为什么这么设计? 因为单一检索方式有盲区:纯语义检索容易漏掉精确关键词,纯关键词检索又不懂同义替换。三者互补,混合使用效果更好。
解决的问题: 以前做 RAG 要同时部署 BM25 + Sentence-BERT + ColBERT 三个模型,现在一个模型搞定,部署成本大幅降低。
② 设计二:用"重建任务"代替"猜词任务"做预训练
传统 BERT 预训练是 MLM:随机遮住几个词,让模型猜。这个任务更关注"单个词"的预测,而不是"整句话"的表示质量。BGE 用了 RetroMAE :编码器把句子压缩成一个向量,然后一个故意很弱的解码器要基于这个向量去重建原文。
为什么这么设计? 解码器被故意设计得很弱(层数少、输入信息少),它要想重建成功,就必须依赖编码器输出的那个句子向量。这就倒逼编码器学会把整句话的信息尽可能完整地压缩进向量里。
解决的问题: 让模型输出的不是"一堆 token 向量的集合",而是真正能代表整段话语义的高质量句向量。
③ 设计三:用对比学习提升了语义区分度
训练时给模型大量三元组:(问题, 正确答案, 错误答案)。目标是让问题和正确答案的向量靠近,和错误答案的向量远离。这里还用了两个技巧:
- 大批量负样本:一个 batch 里除了自己的正例,其他全是负例,batch 越大负例越多,学得越精细。
- 难负例挖掘:专门挑那些"看起来很像但实际不对"的样本来训练,逼模型学会更精细的区分。
为什么这么设计? 检索的本质就是排序------从一堆候选里找出最相关的那几个。对比学习直接优化这个目标,比 MLM 那种"猜词"任务更对口。
解决的问题: 让向量空间的几何距离直接对应"语义相关程度",而不是只学会语言本身。
二、小结
NLP的每一步突破都源于前一步留下的不足。ELMo 则把信息量从"一个词的固定含义",提升到了"一个词在当前上下文中的具体含义"。同一个词不再只有一个向量,而是根据它所在的句子动态生成表示。信息量从"这个词本身是什么",变成了"这个词在这里是什么意思"。
BERT 进一步把信息量从"单向或拼接式的上下文",提升到了"整句话中所有词之间两两交互后的全局理解"。通过 Transformer 的自注意力机制,每个词都能同时看到句子中所有其他词,并且知道它们之间的关联强度。信息量从"这个词的左右邻居是谁",变成了"这个词和整句话中每一个词的关系是什么"。
BGE 又把信息量从"语言层面的理解",提升到了"检索任务层面的判别"。它不再只是让向量"代表一段文本",而是让向量"能区分相似与不相似"。信息量从"这段话说了什么",变成了"这段话和另一段话有多相关"。
本文涉及的神经网络框架点到为止,重点在于理清演进脉络。后续可以专门拆解 Transformer 的自注意力机制------它是 ELMo 到 BERT 跃迁的架构基础,也是今天所有大模型的共同起点。
| 阶段 | 代表方法 | 向量含义 | 关键突破 |
|---|---|---|---|
| 静态词向量 | Word2Vec | 一个词一个固定向量 | 把词放进连续语义空间 |
| 上下文动态词向量 | ELMo | 同一个词在不同句子里有不同向量 | 用双向语言模型解决一词多义 |
| 深度双向上下文表示 | BERT | 每个 token 的向量由整句话共同决定 | 用 Transformer + MLM 实现深层双向建模 |
| 句子/段落级语义向量 | BGE、Sentence-BERT | 整段文本一个语义向量 | 面向检索、相似度、RAG 召回优化 |