共计 2222 个字符,预计需要花费 6 分钟才能阅读完成。
背景介绍
自然语言处理(NLP)的核心挑战之一是如何将词汇转换为计算机可以理解的数值形式。传统方法如 Word2Vec 和 GloVe 通过静态词向量表示每个词,但这些方法忽略了上下文信息。例如,”bank” 在 ”river bank” 和 ”bank account” 中的含义不同,但 Word2Vec 会为这两个上下文生成相同的向量。

BERT(Bidirectional Encoder Representations from Transformers)的出现改变了这一局面。它通过 Transformer 架构和掩码语言模型(MLM)任务,能够生成动态的、上下文相关的词嵌入。这意味着同一个词在不同句子中会得到不同的向量表示,从而更准确地捕捉语义和语法信息。
技术实现
-
Tokenization
BERT 使用 WordPiece 分词器,将词汇拆分为子词单元(subwords)。例如,”unhappiness” 可能被拆分为 ”un”, “happiness”。这种处理方式大大减少了词汇表大小,同时能处理未见过的词汇。 -
输入表示
每个输入序列由三部分组成: - Token embeddings:词或子词的嵌入
- Segment embeddings:区分句子 A 和句子 B(对于单句任务,通常只有 A)
-
Position embeddings:表示词在序列中的位置
-
Transformer 编码
BERT 使用多层 Transformer 编码器处理输入。关键特点是: - 自注意力机制:每个词可以关注序列中的任意位置
- 双向上下文:与单向模型不同,BERT 同时考虑左右上下文
- 深度表示:通过 12 或 24 层 Transformer,逐步抽象语义信息
代码实战
以下是使用 HuggingFace Transformers 库获取 BERT 词嵌入的完整示例:
from transformers import BertTokenizer, BertModel
import torch
# 初始化模型和分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
# 输入文本
text = "BERT provides contextualized word embeddings."
# 分词和转换为模型输入
inputs = tokenizer(text, return_tensors="pt")
# 获取词嵌入
with torch.no_grad():
outputs = model(**inputs)
# 最后一层的隐藏状态(768 维向量)last_hidden_states = outputs.last_hidden_state
# 第一个 token 的嵌入(CLS 标记)cls_embedding = last_hidden_states[0, 0, :]
# 特定词的嵌入(如 "contextualized")token_index = 3 # 需要根据实际分词结果调整
word_embedding = last_hidden_states[0, token_index, :]
性能优化
- 分块处理
对于长文本,可以分段处理后再合并结果:
max_length = 512 # BERT 的最大输入长度
chunks = [text[i:i+max_length] for i in range(0, len(text), max_length)]
embeddings = []
for chunk in chunks:
inputs = tokenizer(chunk, return_tensors="pt", truncation=True, padding=True)
with torch.no_grad():
outputs = model(**inputs)
embeddings.append(outputs.last_hidden_state)
- 量化
使用 16 位或 8 位浮点数减少内存占用:
model = model.half() # 转换为 16 位浮点数
- 批处理
通过批处理提高 GPU 利用率:
texts = ["text1", "text2", "text3"]
inputs = tokenizer(texts, return_tensors="pt", padding=True, truncation=True)
with torch.no_grad():
outputs = model(**inputs)
生产建议
-
模型版本控制
固定使用的 BERT 版本,避免因更新导致嵌入变化。 -
嵌入缓存
对不变的文本预计算并缓存嵌入,减少重复计算。 -
批处理设计
根据业务场景设计合适的批处理大小,平衡延迟和吞吐量。 -
监控
跟踪内存使用、计算时间和模型效果的变化。
避坑指南
-
上下文窗口对齐
确保在分块处理时不会在重要短语中间切断上下文。 -
特殊标记处理
BERT 添加了 [CLS] 和[SEP]等特殊标记,使用时需要注意它们的位置。 -
层选择
实验表明,不同任务可能适合不同层的输出(最后几层通常对下游任务最有用)。 -
归一化
BERT 嵌入的范围可能很大,使用时考虑进行归一化。
结语
BERT 词嵌入为 NLP 任务提供了强大的语义表示能力。通过本文介绍的技术实现、优化方法和实践经验,开发者可以更高效地在实际项目中应用这一技术。建议尝试在不同任务(如文本分类、命名实体识别等)中比较 BERT 嵌入与传统方法的效果差异,并根据具体需求调整实现方式。随着模型压缩和加速技术的发展,BERT 在资源受限环境中的应用也变得日益可行。
