如果你刚开始接触中文NLP,可能会被各种框架和模型搞得眼花缭乱。我刚开始做中文文本处理时也踩过不少坑,直到发现了Hugging Face这个宝藏平台和Bert-base-Chinese这个神器。Hugging Face就像是机器学习界的GitHub,它最厉害的地方在于把各种复杂的模型封装得特别友好,让普通开发者也能轻松调用最先进的NLP模型。
Bert-base-Chinese则是专门针对中文优化的BERT模型。你可能听说过BERT在英文任务上表现很出色,但直接用在中文上效果会打折扣。Bert-base-Chinese最大的特点是它采用单字切分(character-level)而不是词语切分,这对中文特别友好。我做过对比实验,同样的中文分类任务,使用Bert-base-Chinese比直接用原始BERT准确率能提升8-12%。
我建议直接用Anaconda创建独立的Python环境,避免包冲突。这是我常用的配置命令:
bash复制conda create -n nlp python=3.8
conda activate nlp
pip install torch transformers datasets
如果你有GPU,记得安装对应版本的PyTorch。我遇到过不少同学在这里踩坑,明明有GPU却跑在CPU上。检查GPU是否可用的方法很简单:
python复制import torch
print(torch.cuda.is_available()) # 应该输出True
Hugging Face提供了自动和手动两种下载方式。自动下载最简单:
python复制from transformers import BertTokenizer, BertModel
model_name = "bert-base-chinese"
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertModel.from_pretrained(model_name)
但国内网络有时不稳定,我建议手动下载备用。模型文件不大,约400MB。下载后存放在项目目录下,比如models/bert-base-chinese,然后这样加载:
python复制tokenizer = BertTokenizer.from_pretrained("models/bert-base-chinese")
model = BertModel.from_pretrained("models/bert-base-chinese")
BertTokenizer提供了三种编码方法,我用实际例子说明它们的区别:
python复制text = "自然语言处理很有趣"
encoded = tokenizer.encode(text, max_length=20, padding='max_length')
print(tokenizer.decode(encoded))
python复制encoded_plus = tokenizer.encode_plus(
text,
return_tensors='pt',
return_attention_mask=True,
return_token_type_ids=True
)
print(encoded_plus)
python复制texts = ["今天天气真好", "机器学习很有挑战性"]
batch_encoded = tokenizer.batch_encode_plus(
texts,
max_length=15,
padding='max_length'
)
实际项目中,90%的情况我都会用batch_encode_plus,因为它最灵活高效。
Bert-base-Chinese的最大长度是512个token,但中文长文章很容易超限。我的解决方案是:
这里有个实用的分段处理函数:
python复制def split_text(text, max_len=500):
tokens = tokenizer.tokenize(text)
return [tokenizer.convert_tokens_to_string(tokens[i:i+max_len])
for i in range(0, len(tokens), max_len)]
我推荐使用ChnSentiCorp数据集,它包含近万条中文评论和情感标签。加载非常简单:
python复制from datasets import load_dataset
dataset = load_dataset("seamew/ChnSentiCorp")
print(dataset["train"][0]) # 查看第一条数据
如果下载慢,可以先把数据集clone到本地:
bash复制git lfs install
git clone https://huggingface.co/datasets/seamew/ChnSentiCorp
这里分享一个经过优化的分类模型架构:
python复制import torch.nn as nn
class BertClassifier(nn.Module):
def __init__(self, dropout=0.1):
super().__init__()
self.bert = BertModel.from_pretrained("bert-base-chinese")
self.dropout = nn.Dropout(dropout)
self.classifier = nn.Linear(768, 2) # 二分类
def forward(self, input_ids, attention_mask):
outputs = self.bert(input_ids, attention_mask=attention_mask)
pooled = outputs.last_hidden_state[:, 0] # 取[CLS]位置
pooled = self.dropout(pooled)
return self.classifier(pooled)
关键点在于:
这是我总结的高效训练方法:
python复制from transformers import AdamW
# 分组设置学习率
optimizer = AdamW([
{'params': model.bert.parameters(), 'lr': 2e-5},
{'params': model.classifier.parameters(), 'lr': 5e-4}
])
# 学习率预热
scheduler = get_linear_schedule_with_warmup(
optimizer,
num_warmup_steps=100,
num_training_steps=1000
)
训练好的模型可以这样保存:
python复制model.save_pretrained("my_bert_classifier")
tokenizer.save_pretrained("my_bert_classifier")
加载时更简单:
python复制from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained("my_bert_classifier")
python复制quantized_model = torch.quantization.quantize_dynamic(
model, {torch.nn.Linear}, dtype=torch.qint8
)
python复制torch.onnx.export(model, inputs, "model.onnx")
用Flask快速搭建一个分类API:
python复制from flask import Flask, request, jsonify
import torch
app = Flask(__name__)
model = BertForSequenceClassification.from_pretrained("my_bert_classifier")
@app.route('/predict', methods=['POST'])
def predict():
text = request.json['text']
inputs = tokenizer(text, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
return jsonify({"label": torch.argmax(outputs.logits).item()})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
BERT原本就是通过填空任务预训练的,我们可以直接利用这个能力:
python复制text = "中国的首都是[MASK]京"
inputs = tokenizer(text, return_tensors="pt")
mask_index = torch.where(inputs["input_ids"][0] == tokenizer.mask_token_id)[0]
with torch.no_grad():
outputs = model(**inputs)
predictions = outputs.logits[0, mask_index]
predicted_token = tokenizer.decode(predictions.argmax(-1))
print(f"预测结果: {predicted_token}") # 应该输出"北"
计算两个句子的语义相似度:
python复制from sklearn.metrics.pairwise import cosine_similarity
def get_sentence_embedding(text):
inputs = tokenizer(text, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
return outputs.last_hidden_state[:, 0].numpy()
emb1 = get_sentence_embedding("今天天气真好")
emb2 = get_sentence_embedding("阳光明媚的一天")
similarity = cosine_similarity(emb1, emb2)[0][0]
print(f"相似度: {similarity:.2f}")
遇到专业术语时,可以扩展词典:
python复制new_tokens = ["深度学习", "神经网络"]
tokenizer.add_tokens(new_tokens)
model.resize_token_embeddings(len(tokenizer)) # 重要!调整模型embedding大小
我在实际项目中发现,对于专业领域文本,先用领域语料继续预训练1-2个epoch,效果能提升15%以上。方法很简单:
python复制from transformers import BertForMaskedLM
mlm_model = BertForMaskedLM.from_pretrained("bert-base-chinese")
# 准备领域文本数据集
trainer = Trainer(model=mlm_model, args=training_args, train_dataset=dataset)
trainer.train()