# Transformer模型中"添加单词"的完整指南
## 1. 基础概念:词表与词嵌入
在Transformer中,"添加单词"实际上是指**扩展模型的词汇表**。每个Transformer模型都有一个固定的词表,所有输入文本都需要先被转换成词表中的ID。
### 词表的基本结构:
```
{
"[PAD]": 0,
"[UNK]": 1,
"[CLS]": 2,
"[SEP]": 3,
"the": 4,
"cat": 5,
"dog": 6,
...
}
```
## 2. 具体实现方法
### 2.1 方法一:重新训练词嵌入(推荐用于新领域)
```python
import torch
import torch.nn as nn
from transformers import AutoTokenizer, AutoModel
class VocabularyExpander:
def __init__(self, model_name="bert-base-uncased"):
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModel.from_pretrained(model_name)
self.original_vocab_size = len(self.tokenizer)
def add_tokens(self, new_tokens):
"""添加新词到tokenizer和模型"""
# 1. 扩展tokenizer词汇表
num_added = self.tokenizer.add_tokens(new_tokens)
print(f"成功添加 {num_added} 个新词")
# 2. 扩展模型的嵌入层
self.model.resize_token_embeddings(len(self.tokenizer))
return num_added
def initialize_new_embeddings(self, initialization_strategy="average"):
"""初始化新添加词的嵌入向量"""
new_token_ids = range(self.original_vocab_size, len(self.tokenizer))
with torch.no_grad():
for token_id in new_token_ids:
token = self.tokenizer.convert_ids_to_tokens(token_id)
if initialization_strategy == "average":
# 策略1:使用现有词嵌入的平均值
new_embedding = self.model.embeddings.word_embeddings.weight[:self.original_vocab_size].mean(dim=0)
elif initialization_strategy == "random":
# 策略2:随机初始化(与原始模型相同分布)
original_std = self.model.embeddings.word_embeddings.weight[:self.original_vocab_size].std()
new_embedding = torch.randn_like(self.model.embeddings.word_embeddings.weight[0]) * original_std
elif initialization_strategy == "similar_word":
# 策略3:使用语义相近词的嵌入
new_embedding = self._find_similar_embedding(token)
self.model.embeddings.word_embeddings.weight[token_id] = new_embedding
def _find_similar_embedding(self, token):
"""为新增词找到语义相近的嵌入"""
# 简单的基于字符串匹配的相似词查找
similar_words = []
for existing_token in self.tokenizer.get_vocab():
if token.lower() in existing_token.lower() or existing_token.lower() in token.lower():
similar_words.append(existing_token)
if similar_words:
similar_ids = [self.tokenizer.convert_tokens_to_ids(word) for word in similar_words]
similar_embeddings = self.model.embeddings.word_embeddings.weight[similar_ids]
return similar_embeddings.mean(dim=0)
else:
return self.model.embeddings.word_embeddings.weight[:self.original_vocab_size].mean(dim=0)
```
### 2.2 方法二:使用子词分解(处理未登录词)
```python
def handle_oov_with_subwords(text, tokenizer):
"""处理不在词表中的词通过子词分解"""
tokens = tokenizer.tokenize(text)
print(f"文本 '{text}' 被分解为: {tokens}")
# 示例
input_text = "deeplearning"
tokens = tokenizer.tokenize(input_text)
# 可能输出: ['deep', '##learn', '##ing']
return tokens
# 使用示例
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
tokens = handle_oov_with_subwords("chatgpt", tokenizer)
```
### 2.3 方法三:领域自适应训练
```python
def domain_adaptive_training(model, tokenizer, domain_texts, new_tokens):
"""在特定领域文本上微调模型,适应新词汇"""
# 1. 添加新词
tokenizer.add_tokens(new_tokens)
model.resize_token_embeddings(len(tokenizer))
# 2. 准备训练数据
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=3,
per_device_train_batch_size=16,
logging_dir='./logs',
)
# 3. 创建训练器并微调
trainer = Trainer(
model=model,
args=training_args,
train_dataset=domain_texts, # 需要转换为合适的dataset格式
)
trainer.train()
```
## 3. 实际操作步骤
### 步骤1:识别需要添加的词
```python
def analyze_vocabulary_gaps(text_corpus, tokenizer):
"""分析文本语料中不在当前词表中的词"""
oov_words = set()
for text in text_corpus:
tokens = tokenizer.tokenize(text)
for token in tokens:
if token.startswith("##"):
continue # 跳过子词
if token not in tokenizer.get_vocab():
oov_words.add(token)
print(f"发现 {len(oov_words)} 个未登录词: {list(oov_words)[:10]}...")
return oov_words
# 使用示例
corpus = ["区块链技术", "人工智能", "机器学习", "深度学习"]
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
oov_words = analyze_vocabulary_gaps(corpus, tokenizer)
```
### 步骤2:批量添加新词
```python
def batch_add_tokens(tokenizer, model, new_tokens_list):
"""批量添加新词到模型"""
# 添加前的词表大小
original_size = len(tokenizer)
# 添加新词
added_count = tokenizer.add_tokens(new_tokens_list)
# 调整模型嵌入层大小
model.resize_token_embeddings(len(tokenizer))
print(f"词表从 {original_size} 扩展到 {len(tokenizer)}")
print(f"成功添加 {added_count} 个新词")
return added_count
```
### 步骤3:初始化新词嵌入
```python
def smart_initialization(model, tokenizer, new_tokens):
"""智能初始化新词的嵌入向量"""
# 获取原始嵌入的统计信息
original_embeddings = model.embeddings.word_embeddings.weight[:len(tokenizer)-len(new_tokens)]
mean_embedding = original_embeddings.mean(dim=0)
std_embedding = original_embeddings.std(dim=0)
with torch.no_grad():
for token in new_tokens:
token_id = tokenizer.convert_tokens_to_ids(token)
# 根据词性选择初始化策略
if any(char.isdigit() for char in token):
# 数字类词汇:使用特殊token的嵌入
num_embedding = tokenizer.convert_tokens_to_ids("[NUM]") if "[NUM]" in tokenizer.vocab else mean_embedding
model.embeddings.word_embeddings.weight[token_id] = num_embedding
elif token.isupper():
# 大写词汇(可能是缩写):使用随机初始化
model.embeddings.word_embeddings.weight[token_id] = torch.randn_like(mean_embedding) * std_embedding
else:
# 普通词汇:使用平均嵌入
model.embeddings.word_embeddings.weight[token_id] = mean_embedding
```
## 4. 完整工作流程示例
```python
def complete_vocabulary_expansion_pipeline():
"""完整的词表扩展流程"""
# 1. 加载预训练模型和tokenizer
from transformers import BertTokenizer, BertModel
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
# 2. 定义新词(以科技领域为例)
new_tech_words = [
"blockchain", "cryptocurrency", "NFT", "DeFi",
"metaverse", "web3", "DAO", "smartcontract"
]
# 3. 执行扩展
expander = VocabularyExpander()
expander.add_tokens(new_tech_words)
expander.initialize_new_embeddings(initialization_strategy="average")
# 4. 验证扩展结果
test_text = "Blockchain and NFT are important in web3 ecosystem."
inputs = tokenizer(test_text, return_tensors="pt")
outputs = model(**inputs)
print("词表扩展完成!")
print(f"新词表大小: {len(tokenizer)}")
print(f"测试文本编码成功: {inputs['input_ids'].shape}")
return model, tokenizer
# 运行完整流程
model, tokenizer = complete_vocabulary_expansion_pipeline()
```
## 5. 注意事项和最佳实践
### 5.1 重要考虑因素
1. **嵌入初始化策略选择**:
- 相似领域:使用平均嵌入
- 全新概念:使用随机初始化
- 专业术语:寻找近似词嵌入
2. **后续训练需求**:
```python
# 扩展后建议进行微调
def fine_tune_expanded_model(model, train_dataset):
# 冻结部分层,只训练新添加的嵌入
for name, param in model.named_parameters():
if "word_embeddings" not in name:
param.requires_grad = False
# 进行训练...
```
3. **性能监控**:
- 监控新词的处理效果
- 检查模型整体性能变化
- 验证不会破坏原有知识
### 5.2 常见问题解决
```python
# 问题:添加新词后模型性能下降
def diagnose_embedding_issues(model, tokenizer, test_words):
"""诊断嵌入问题"""
for word in test_words:
if word in tokenizer.vocab:
word_id = tokenizer.convert_tokens_to_ids(word)
embedding = model.embeddings.word_embeddings.weight[word_id]
norm = torch.norm(embedding)
print(f"词 '{word}' 的嵌入范数: {norm:.4f}")
```
## 总结
将新词添加到Transformer模型中是一个系统性的过程,需要:
1. **分析词汇需求**:识别真正需要添加的词
2. **扩展词表和嵌入层**:使用`add_tokens`和`resize_token_embeddings`
3. **智能初始化**:选择合适的嵌入初始化策略
4. **后续优化**:通过微调让模型学会使用新词
这种方法可以显著提升模型在特定领域的表现,同时保持原有的语言理解能力。