共计 2692 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点:中文文本处理的特殊挑战
中文文本处理相比英文有显著差异,主要面临以下挑战:

- 分词歧义:例如 ” 南京市长江大桥 ” 可以切分为 ” 南京 / 市长 / 江大桥 ” 或 ” 南京市 / 长江 / 大桥 ”
- 领域术语:医疗、金融等垂直领域存在大量专业术语,通用词表覆盖不足
- 标注稀缺:高质量标注数据获取成本高,尤其在小语种和垂直领域
- 语义复杂度:同义词、近义词、多义词现象普遍,且受上下文影响大
传统方法如 TF-IDF、Word2Vec 存在明显局限:
- 无法建模词语的深层语义关系
- 对未登录词 (OOV) 处理能力弱
- 需要复杂的特征工程
技术选型:预训练模型对比
| 模型 | 参数量 | 中文支持 | 训练速度 | 下游任务表现 |
|---|---|---|---|---|
| BERT-base | 110M | ★★★★ | 中等 | ★★★★ |
| ALBERT | 12M | ★★★★ | 快 | ★★★☆ |
| RoBERTa | 110M | ★★★★ | 慢 | ★★★★☆ |
| Electra | 110M | ★★★☆ | 中等 | ★★★★ |
选型建议:
- 计算资源充足时首选 RoBERTa
- 追求推理速度选 ALBERT
- 平衡型选择 BERT 或 Electra
核心实现
1. 环境准备
# 安装必要库
pip install transformers torch pandas
2. 文本预处理 Pipeline
import re
import jieba
from transformers import BertTokenizer
# 清洗函数
def clean_text(text):
text = re.sub(r'[\s\n\r\t]+', ' ', text) # 合并空白符
text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9,。?!]', '', text) # 去除非中英文数字和标点
return text.strip()
# 加载 BERT 分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
# 完整处理流程
def process_text(text, max_len=128):
text = clean_text(text)
# 使用 BERT 原生分词
inputs = tokenizer(
text,
max_length=max_len,
padding='max_length',
truncation=True,
return_tensors='pt'
)
return inputs
3. Fine-tuning 代码示例
from transformers import BertForSequenceClassification, Trainer, TrainingArguments
from sklearn.model_selection import train_test_split
# 加载预训练模型
model = BertForSequenceClassification.from_pretrained(
'bert-base-chinese',
num_labels=10 # 假设 10 分类
)
# 训练参数配置
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=3,
per_device_train_batch_size=32,
warmup_steps=500,
weight_decay=0.01,
logging_dir='./logs',
logging_steps=100,
evaluation_strategy='steps',
load_best_model_at_end=True,
fp16=True # 启用混合精度
)
# 自定义早停回调
from transformers import EarlyStoppingCallback
early_stop = EarlyStoppingCallback(early_stopping_patience=2)
# 创建 Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
callbacks=[early_stop]
)
# 开始训练
trainer.train()
进阶优化
领域自适应
- 收集领域文本构建自定义词表
- 使用 MLM 任务继续预训练
from transformers import BertForMaskedLM mlm_model = BertForMaskedLM.from_pretrained('bert-base-chinese') # 继续预训练代码框架 for batch in domain_dataloader: inputs = tokenizer(batch, return_tensors='pt', padding=True) outputs = mlm_model(**inputs, labels=inputs['input_ids']) loss = outputs.loss loss.backward() optimizer.step()
知识蒸馏
- 训练大模型(教师)
- 用小模型 (学生) 模仿教师输出
- 关键实现:
# 使用 logits 和 hidden_states 作为监督信号 student_loss = (teacher_logits - student_logits)**2 + 0.5*MSE(teacher_hidden, student_hidden)
生产考量
GPU 内存优化
- 使用梯度检查点
model.gradient_checkpointing_enable() - 动态批处理
- 混合精度推理
ONNX 加速
# 转换模型
torch.onnx.export(model, inputs, "model.onnx")
# 使用 ONNX Runtime 推理
import onnxruntime
sess = onnxruntime.InferenceSession("model.onnx")
outputs = sess.run(None, {"input_ids": inputs})
实测效果:
– CPU 推理速度提升 2 - 3 倍
– GPU 推理提升 30%-50%
避坑指南
类别不平衡解决方案
- 重采样(过采样少数类 / 欠采样多数类)
- 类别权重调整
weights = torch.tensor([1.0, 5.0, 3.0]) # 假设 3 分类 loss_fn = nn.CrossEntropyLoss(weight=weights) - Focal Loss
聚类维度灾难预防
- 先使用 BERT 提取特征
- 用 PCA 降维(保留 95% 方差)
- 适当调整聚类算法参数
互动讨论
在多义词处理中,比如 ” 苹果 ” 既可指水果也可指科技公司,大家在实际业务中会采用哪些策略来区分不同场景下的语义?欢迎在评论区分享你的实战经验!
正文完
