11 ELMo 论文精读:上下文词向量为什么重要?
在前面的文章中,我们已经讲过 Tokenizer、Embedding、Transformer Encoder、Transformer Decoder,以及语言模型训练目标。
现在我们要回到预训练语言模型发展史中的一个关键节点:ELMo。
ELMo 对应的论文是:
Deep Contextualized Word Representations这篇论文提出了一种新的词表示方法:上下文词向量。
在 ELMo 之前,NLP 中常见的词向量方法包括:
Word2Vec GloVe FastText这些方法都有一个共同特点:
每个词通常对应一个固定向量。
例如:
bank → 一个固定向量 apple → 一个固定向量 run → 一个固定向量但是自然语言中有大量一词多义现象。
例如:
I deposited money in the bank.这里的bank表示银行。
再看:
The boat stopped near the river bank.这里的bank表示河岸。
如果bank永远只有一个固定向量,那么模型很难区分这两个含义。
ELMo 想解决的正是这个问题:
同一个词在不同上下文中,应该有不同的表示。
这就是上下文词向量的核心思想。
如果说 Word2Vec / GloVe 是“静态词向量”,那么 ELMo 就是迈向“动态上下文表示”的重要一步。
一、为什么静态词向量不够?
在早期深度学习 NLP 中,词向量是非常重要的基础组件。
例如我们要处理一句话:
A man is playing guitar.通常会先把每个词转换成向量:
A → vector man → vector is → vector playing → vector guitar → vector这些向量可以来自 Word2Vec、GloVe 或 FastText。
这种方法比 one-hot 表示强很多。
因为词向量可以表达一定语义关系。
例如:
king 和 queen 可能比较接近 cat 和 dog 可能比较接近 Paris 和 France 可能存在某种关系但是静态词向量有一个非常明显的问题:
一个词只有一个向量。
这在多义词场景中会出现问题。
例如apple:
I ate an apple. Apple released a new iPhone.第一个apple是水果。
第二个Apple是公司。
如果模型给它们使用同一个词向量,那么下游模型必须自己根据上下文再去区分语义。
再比如play:
Children play in the park. The actor will play a role. I want to play music.这些句子里的play含义并不完全相同。
所以,仅仅依靠一个固定词向量是不够的。
自然语言真正需要的是:
词本身的信息 + 词所在上下文的信息也就是说,一个词的表示应该随着上下文变化。
二、什么是上下文词向量?
上下文词向量的核心思想是:
一个词的向量不是固定查表得到的,而是根据它所在的句子动态计算出来的。
例如同一个词bank:
I deposited money in the bank.和:
The boat stopped near the river bank.在 ELMo 中,这两个bank会得到不同的向量表示。
可以简单写成:
bank in sentence 1 → vector_1 bank in sentence 2 → vector_2为什么会不同?
因为 ELMo 会根据整个句子的上下文来计算当前词的表示。
对于第一个句子:
money deposited这些词会提示模型:这里的bank更可能是银行。
对于第二个句子:
boat river这些词会提示模型:这里的bank更可能是河岸。
这就是上下文词向量的价值。
它不是简单回答:
这个词是什么?而是进一步回答:
这个词在当前句子中是什么意思?三、ELMo 的名字是什么意思?
ELMo 是:
Embeddings from Language Models也就是:
来自语言模型的词向量这个名字非常准确。因为 ELMo 的核心做法是:
先训练一个双向语言模型,然后把语言模型内部的隐藏状态拿出来作为词表示。
也就是说,ELMo 不是单独学习一个静态 embedding 表,而是利用语言模型在大量文本上学到的上下文表示。完整流程可以概括为:
大规模无标注文本 ↓ 训练双向语言模型 biLM ↓ 输入一个句子 ↓ 获得每个 token 在不同层的隐藏状态 ↓ 对这些隐藏状态加权组合 ↓ 得到 ELMo 表示 ↓ 加入下游任务模型这就是 ELMo 的基本思路。
四、ELMo 和 Word2Vec / GloVe 的核心区别
我们可以先用一张表比较。
| 对比维度 | Word2Vec / GloVe | ELMo |
|---|---|---|
| 词向量类型 | 静态词向量 | 上下文词向量 |
| 同一个词是否固定 | 固定 | 随上下文变化 |
| 是否利用句子上下文 | 弱 | 强 |
| 是否基于语言模型 | 通常不是深层上下文 LM | 是,基于双向语言模型 |
| 是否多层表示 | 通常单层向量 | 使用多层 biLM 隐藏状态 |
| 适合处理多义词 | 较弱 | 更强 |
最关键的一点是:
Word2Vec / GloVe:一个词一个向量 ELMo:一个词在不同句子中有不同向量例如:
bank_银行 bank_河岸在静态词向量里,这两种含义被压进同一个向量中。而在 ELMo 中,它们可以根据上下文形成不同表示。
五、ELMo 的核心模型:双向语言模型 biLM
ELMo 使用的是bidirectional language model,简称:
biLM也就是双向语言模型。它由两个方向的语言模型组成:
Forward LM:从左到右建模 Backward LM:从右到左建模1. Forward Language Model
Forward LM 的目标是:
根据前面的词预测当前词。
给定句子:
A man is playing guitar .Forward LM 的建模方向是:
A → man → is → playing → guitar → .它学习的是:
其中:
:第 (k) 个 token;
:当前位置左边的上下文。
这和 GPT 的自回归语言模型目标很像。
2. Backward Language Model
Backward LM 的目标是:
根据后面的词预测当前词。
它的建模方向是从右到左:
. → guitar → playing → is → man → A可以写成:
其中:
:当前位置右边的上下文。
3. 双向结合
ELMo 把 Forward LM 和 Backward LM 结合起来。Forward LM 提供左侧上下文信息。Backward LM 提供右侧上下文信息。所以当前词的表示可以同时包含:
左边上下文 当前词信息 右边上下文这就是 ELMo 能够生成上下文词向量的关键。
六、ELMo 为什么不是 BERT?
这里需要特别注意:
ELMo 虽然使用了双向信息,但它和 BERT 的双向 Transformer 不一样。
ELMo 的双向是:
一个从左到右的 LSTM + 一个从右到左的 LSTM也就是说,Forward LM 和 Backward LM 是两个方向分别训练的语言模型。它们在每一层中并不是像 BERT 那样通过 Self-Attention 同时融合左右上下文。BERT 的双向是:
Transformer Encoder 中每个 token 可以同时关注左右所有 tokenELMo 的双向是:
前向 LSTM 编码左上下文 后向 LSTM 编码右上下文 最后把两个方向的状态拼接起来所以 ELMo 是 BERT 之前的重要过渡:
Word2Vec / GloVe:静态词向量 ↓ ELMo:基于双向语言模型的上下文词向量 ↓ BERT:基于 Transformer Encoder 的深层双向预训练模型ELMo 证明了上下文表示非常重要。BERT 则进一步用 Transformer Encoder 把这种思想做得更彻底。
七、ELMo 的输入表示:字符 CNN
ELMo 还有一个重要细节:它不是简单用 word id 查表作为输入,而是使用character CNN。也就是说,ELMo 会从字符级别构造词表示。例如单词:
playing可以拆成字符:
p l a y i n g字符 CNN 会从这些字符中提取词形特征。这样做有几个好处。
第一,可以更好处理未知词。如果一个词没有在词表中出现过,静态词表方法可能只能把它映射成<unk>。但字符 CNN 可以根据字符组成生成表示。
第二,可以捕捉词形信息。
例如:
play playing played plays这些词在字符层面有明显联系。字符 CNN 可以帮助模型学习词缀、词形变化等信息。
第三,对拼写变化和稀有词更友好。这也是 ELMo 相比普通词表 embedding 的一个优势。
八、ELMo 的层级表示
ELMo 不只使用语言模型最后一层的输出。它会利用 biLM 中不同层的表示。假设 biLM 有多层,每个 token 在不同层都会有隐藏状态。对于第 (k) 个 token,可以得到:
第 0 层:词本身的上下文无关表示 第 1 层:第一层 LSTM 输出 第 2 层:第二层 LSTM 输出论文中的 ELMo 表示可以写成:
它说明 ELMo 不是简单取最后一层,而是:
对语言模型不同层的表示做加权求和。
因为不同层可能捕捉不同类型的信息。低层可能更偏词形、局部语法。高层可能更偏语义和长距离依赖。不同下游任务需要的信息不同,所以 ELMo 让模型自己学习每一层应该占多大权重。例如:
词性标注可能更依赖低层语法信息 问答任务可能更依赖高层语义信息 情感分析可能需要综合词义和句子语义这就是层加权的意义。
九、ELMo 如何用于下游任务?
ELMo 的使用方式非常灵活。它不是完全替换下游模型,而是作为一种特征加入现有模型。假设原来一个下游模型使用词向量:
word embedding ↓ BiLSTM / CNN / Attention ↓ task output加入 ELMo 后,可以变成:
word embedding + ELMo representation ↓ BiLSTM / CNN / Attention ↓ task output也就是说,ELMo 可以作为额外的上下文特征。例如在文本分类任务中:
输入句子 ↓ 获得每个词的 ELMo 表示 ↓ 输入分类模型 ↓ 预测情感类别在阅读理解任务中:
问题 + 文章 ↓ 获得上下文词表示 ↓ 匹配问题和文章 ↓ 预测答案位置在命名实体识别任务中:
输入句子 ↓ 每个 token 得到 ELMo 表示 ↓ 序列标注模型 ↓ 预测实体标签ELMo 的一个优势是:
它可以比较容易地插入很多已有 NLP 模型中。
这也是它在当时影响很大的原因之一。
十、ELMo 为什么有效?
ELMo 有效的原因可以从三个角度理解。
1. 它解决了一词多义问题
静态词向量最大的问题是一个词只有一个表示。
ELMo 根据上下文动态生成词向量,因此可以区分不同语境中的词义。
例如:
bank + money → 银行 bank + river → 河岸这对自然语言理解非常重要。
2. 它利用了大规模无标注语料
很多 NLP 任务的标注数据很少。例如命名实体识别、问答、文本蕴含等任务,都需要人工标注。
但无标注文本非常多。ELMo 先在大量无标注文本上训练语言模型,再把学到的表示迁移到下游任务中。这就是预训练思想的雏形。可以写成:
大规模无标注预训练 ↓ 得到通用语言表示 ↓ 迁移到下游任务这一思路后来在 BERT、GPT、T5 中被进一步放大。
3. 它利用了深层语言模型内部状态
ELMo 不只使用输入层词向量,而是使用深层 biLM 的隐藏状态。这些隐藏状态包含了语言模型在预测过程中学到的上下文信息。因此,ELMo 表示不仅包含词本身的信息,还包含上下文语法和语义信息。
十一、ELMo 和 BERT 的关系
ELMo 和 BERT 都属于上下文表示模型,但二者有明显区别。
| 对比维度 | ELMo | BERT |
|---|---|---|
| 主要结构 | BiLSTM-based biLM | Transformer Encoder |
| 表示方式 | 多层 biLM 状态加权求和 | Transformer 深层双向表示 |
| 双向方式 | 前向 LM + 后向 LM | 所有层中联合利用左右上下文 |
| 预训练目标 | 双向语言模型 | Masked Language Modeling + NSP |
| 下游使用 | 作为特征加入任务模型 | 通常整体 fine-tuning |
| 词表示类型 | 上下文词向量 | 深层上下文表示 |
ELMo 更像是:
给原有模型增加一个强大的上下文词向量特征BERT 更像是:
直接预训练一个通用语言理解模型,然后整体微调到下游任务从这个角度看,ELMo 是 BERT 的重要前奏。
它证明了:
上下文表示比静态词向量更强 大规模语言模型预训练可以提升下游任务 不同层的语言模型表示包含不同类型的信息BERT 则进一步用 Transformer Encoder 和 MLM 训练目标,把这些思想推向了新的阶段。
十二、ELMo 和 GPT 的关系
ELMo 和 GPT 也有关系。它们都使用语言模型预训练。但目标和结构不同。GPT 使用的是从左到右的 Causal Language Modeling:
给定前文,预测下一个 token它使用 Decoder-only Transformer,非常适合生成。ELMo 使用的是双向语言模型:
前向 LM 捕捉左上下文 后向 LM 捕捉右上下文它更适合作为上下文表示,用于理解任务。可以这样理解:
ELMo:语言模型用于生成上下文词向量 GPT:语言模型本身就是生成模型 BERT:语言模型预训练变成通用理解模型三者都和语言模型预训练有关,但方向不同。
十三、ELMo 的贡献
ELMo 的主要贡献可以总结为四点。
1. 提出深层上下文词表示
ELMo 明确提出词表示应该依赖上下文。
这突破了静态词向量“一个词一个向量”的限制。
2. 使用双向语言模型生成词表示
ELMo 通过前向 LM 和后向 LM 同时利用左右上下文。
这使得词向量可以捕捉更丰富的语义信息。
3. 使用多层状态加权组合
ELMo 不是只取最后一层,而是让下游任务学习如何组合不同层的表示。
这说明不同层表示对不同任务可能有不同价值。
4. 推动预训练 + 下游迁移范式
ELMo 在大规模无标注语料上预训练,再迁移到多个下游任务。
这对后来的 BERT、GPT 等预训练语言模型影响很大。
十四、ELMo 的局限性
ELMo 很重要,但它也有局限。
1. 结构仍然基于 LSTM
ELMo 使用的是 BiLSTM。
相比 Transformer,LSTM 的并行能力较弱。
长距离依赖建模也不如 Self-Attention 直接。
这限制了模型规模和训练效率。
2. 前向和后向不是深度融合
ELMo 的双向信息来自两个独立方向的 LM。
它不是像 BERT 那样在每一层中让 token 同时和左右所有 token 交互。
所以它的双向建模不如 BERT 彻底。
3. 下游使用方式更像特征工程
ELMo 通常作为额外特征加入下游模型。
而 BERT 之后,主流方式变成:
预训练模型整体 fine-tuning这比手动把特征拼到任务模型中更统一。
4. 不适合大规模生成式 LLM 路线
ELMo 的目标是提供上下文词向量,而不是直接作为生成式大模型使用。
所以它不是今天 ChatGPT、LLaMA、Qwen 这类模型的直接架构路线。
但它在表示学习发展史上非常关键。
十五、从 ELMo 到 BERT:思想如何演进?
ELMo 到 BERT 的演进可以这样理解:
静态词向量阶段: 一个词一个固定向量 ELMo 阶段: 一个词在不同上下文中有不同向量 BERT 阶段: 整个句子通过 Transformer Encoder 生成深层双向上下文表示如果把 NLP 表示学习的发展画成一条线:
One-hot ↓ Word2Vec / GloVe ↓ ELMo ↓ BERT / RoBERTa ↓ GPT / T5 / 大语言模型ELMo 处在一个承前启后的位置。
它继承了词向量思想,但不再满足于静态表示。
它引入了上下文动态表示,又为后来的大规模预训练模型提供了方向。
所以学习 ELMo 的意义不是因为今天还要大量使用它,而是因为:
ELMo 让我们理解为什么“上下文”会成为现代语言模型的核心。
十六、一个简单代码示例:为什么静态词向量不够?
下面用一个非常简化的例子说明静态词向量的问题。
假设bank永远只有一个向量:
static_embedding = { "bank": [0.2, 0.7, -0.1], "money": [0.3, 0.8, -0.2], "river": [-0.4, 0.1, 0.9], }那么下面两个句子里的bank表示完全一样:
sentence1 = ["money", "in", "bank"] sentence2 = ["river", "bank"]静态词向量中:
bank_vector_1 = static_embedding["bank"] bank_vector_2 = static_embedding["bank"] print(bank_vector_1 == bank_vector_2)输出:
True也就是说,无论上下文是money还是river,bank的向量都一样。
而上下文词向量的思想是:
bank_vector_1 = contextual_encoder(["money", "in", "bank"])[2] bank_vector_2 = contextual_encoder(["river", "bank"])[1]此时:
bank_vector_1 != bank_vector_2这就是 ELMo 的核心价值。
它让词向量从:
查表得到变成:
根据上下文动态计算得到