07. 自然语言处理 NLP

NLP 核心技术:文本预处理、词向量 Word2Vec/GloVe、BERT 微调、文本分类与命名实体识别实战

自然语言处理 (NLP) 是人工智能的核心领域,涵盖文本理解、生成与转换。从传统的词袋模型到 BERT 预训练,再到 GPT 生成,NLP 技术经历了革命性发展。本文系统讲解从预处理到实际任务的完整流程。

1. 文本预处理

1.1 基础处理

import re
import jieba
from collections import Counter

def preprocess_text(text):
    # 1. 清洗
    text = re.sub(r'[^一-鿿\w\s]', '', text)  # 保留中文、英文、数字
    text = re.sub(r'\s+', ' ', text).strip()
    
    # 2. 分词(中文)
    tokens = jieba.lcut(text)
    
    # 3. 去停用词
    stopwords = set(['的', '了', '在', '是', '和', '就', '都', '而', '及', '与'])
    tokens = [t for t in tokens if t not in stopwords and len(t) > 1]
    
    return tokens

# 英文预处理
import nltk
from nltk.tokenize import word_tokenize
from nltk.stem import WordNetLemmatizer

def preprocess_eng(text):
    text = text.lower()
    tokens = word_tokenize(text)
    lemmatizer = WordNetLemmatizer()
    tokens = [lemmatizer.lemmatize(t) for t in tokens if t.isalpha()]
    return tokens

1.2 文本表示

from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer

# 词袋模型 (BoW)
count_vec = CountVectorizer(max_features=5000)
X_bow = count_vec.fit_transform(documents)

# TF-IDF(词频-逆文档频率)
tfidf_vec = TfidfVectorizer(max_features=5000, ngram_range=(1, 2))
X_tfidf = tfidf_vec.fit_transform(documents)

TF-IDF 公式:

$$TF-IDF(t, d) = TF(t, d) \times IDF(t) = \frac{f_{t,d}}{\sum f_{t’,d}} \times \log\frac{N}{1 + |{d: t \in d}|}$$

2. 词向量 (Word Embeddings)

2.1 Word2Vec

Mikolov 2013 年提出,通过上下文预测学习词向量。

CBOW:用上下文预测中心词
Skip-gram:用中心词预测上下文

from gensim.models import Word2Vec

sentences = [
    ['我', '喜欢', '机器学习'],
    ['深度', '学习', '是', '机器学习', '的', '分支'],
    ['自然语言', '处理', '属于', '人工智能'],
    ['人工智能', '改变', '世界']
]

model = Word2Vec(
    sentences=sentences,
    vector_size=100,   # 词向量维度
    window=5,          # 上下文窗口
    min_count=1,       # 最小词频
    sg=1,              # 1=Skip-gram, 0=CBOW
    workers=4,
    epochs=100
)

# 相似词
similar = model.wv.most_similar('机器学习', topn=5)
print(similar)

# 词向量运算
result = model.wv.most_similar(
    positive=['人工智能', '学习'], 
    negative=['人类'],
    topn=1
)

2.2 GloVe

基于全局词-词共现统计,结合全局矩阵分解与局部上下文窗口。

import gensim.downloader as api

# 加载预训练词向量
glove_vectors = api.load("glove-wiki-gigaword-100")

# 相似度计算
glove_vectors.most_similar('computer', topn=5)
glove_vectors.similarity('king', 'queen')

# 类比推理: king - man + woman ≈ queen
result = glove_vectors.most_similar(
    positive=['king', 'woman'], 
    negative=['man'],
    topn=1
)

2.3 词向量特性

# 可视化
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

words = ['人工智能', '机器学习', '深度学习', '自然语言', '计算机', '人脑', '思维']
vectors = [model.wv[w] for w in words if w in model.wv]

pca = PCA(n_components=2)
reduced = pca.fit_transform(vectors)

plt.scatter(reduced[:, 0], reduced[:, 1])
for i, word in enumerate(words):
    plt.annotate(word, (reduced[i, 0], reduced[i, 1]))

3. 预训练语言模型

3.1 模型演进

Word2Vec/GloVe → ELMo(上下文相关)→ GPT(Transformer Decoder)
→ BERT(Transformer Encoder,双向)→ RoBERTa/ALBERT/ELECTRA
→ GPT-2/3/4(生成式,规模扩大)

3.2 BERT 微调:文本分类

from transformers import BertTokenizer, BertForSequenceClassification
from transformers import Trainer, TrainingArguments
import torch

# 加载预训练模型和分词器
model_name = 'bert-base-chinese'
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForSequenceClassification.from_pretrained(
    model_name, num_labels=2)

# 数据准备
class TextDataset(torch.utils.data.Dataset):
    def __init__(self, texts, labels, tokenizer, max_len=128):
        self.texts = texts
        self.labels = labels
        self.tokenizer = tokenizer
        self.max_len = max_len
    
    def __len__(self):
        return len(self.texts)
    
    def __getitem__(self, idx):
        text = str(self.texts[idx])
        label = self.labels[idx]
        
        encoding = self.tokenizer(
            text,
            truncation=True,
            padding='max_length',
            max_length=self.max_len,
            return_tensors='pt'
        )
        return {
            'input_ids': encoding['input_ids'].flatten(),
            'attention_mask': encoding['attention_mask'].flatten(),
            'labels': torch.tensor(label, dtype=torch.long)
        }

# 训练配置
training_args = TrainingArguments(
    output_dir='./results',
    num_train_epochs=3,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=32,
    warmup_steps=500,
    weight_decay=0.01,
    logging_dir='./logs',
    evaluation_strategy='epoch',
    save_strategy='epoch',
    load_best_model_at_end=True,
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=val_dataset,
)

trainer.train()

3.3 BERT 微调:命名实体识别 (NER)

from transformers import BertForTokenClassification

model = BertForTokenClassification.from_pretrained(
    'bert-base-chinese',
    num_labels=7  # B-PER, I-PER, B-LOC, I-LOC, B-ORG, I-ORG, O
)

# BIO 标注格式
# B-PER: 人名开头, I-PER: 人名中间, O: 非实体
labels_map = {'O': 0, 'B-PER': 1, 'I-PER': 2, 'B-LOC': 3, 
              'I-LOC': 4, 'B-ORG': 5, 'I-ORG': 6}

3.4 BERT 变体对比

模型架构参数特点
BERT-BaseEncoder110M12 层,768 维
BERT-LargeEncoder340M24 层,1024 维
RoBERTaEncoder125M更优训练策略
ALBERTEncoder12M参数共享,轻量
ELECTRADiscriminator-判别式预训练
DeBERTaEncoder-增强注意力

4. 文本分类实战

import torch.nn as nn
from transformers import AutoModel, AutoTokenizer

class TextClassifier(nn.Module):
    def __init__(self, model_name, num_classes, dropout=0.1):
        super().__init__()
        self.bert = AutoModel.from_pretrained(model_name)
        self.dropout = nn.Dropout(dropout)
        self.classifier = nn.Linear(self.bert.config.hidden_size, num_classes)
    
    def forward(self, input_ids, attention_mask):
        outputs = self.bert(
            input_ids=input_ids,
            attention_mask=attention_mask
        )
        # 取 CLS token 的隐藏状态
        pooled = outputs.last_hidden_state[:, 0]  # [CLS]
        pooled = self.dropout(pooled)
        return self.classifier(pooled)

# 训练
def train_epoch(model, dataloader, optimizer, criterion, device):
    model.train()
    total_loss = 0
    
    for batch in dataloader:
        input_ids = batch['input_ids'].to(device)
        attention_mask = batch['attention_mask'].to(device)
        labels = batch['labels'].to(device)
        
        optimizer.zero_grad()
        outputs = model(input_ids, attention_mask)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        
        total_loss += loss.item()
    
    return total_loss / len(dataloader)

5. 文本生成

5.1 GPT-2 生成

from transformers import GPT2LMHeadModel, GPT2Tokenizer

model = GPT2LMHeadModel.from_pretrained('gpt2')
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')

prompt = "The rapid development of artificial intelligence"
inputs = tokenizer(prompt, return_tensors='pt')

# 贪婪解码
greedy = model.generate(**inputs, max_length=50)
print(tokenizer.decode(greedy[0]))

# Beam Search
beam = model.generate(**inputs, max_length=50, num_beams=5,
                      early_stopping=True)

# 采样解码(更自然)
sample = model.generate(**inputs, max_length=50, 
                        do_sample=True, temperature=0.8,
                        top_k=50, top_p=0.95)

5.2 中文文本生成

from transformers import BertTokenizer, BartForConditionalGeneration

# 中文摘要
tokenizer = BertTokenizer.from_pretrained('fnlp/bart-base-chinese')
model = BartForConditionalGeneration.from_pretrained('fnlp/bart-base-chinese')

article = "这是一篇关于人工智能发展的长文..."
inputs = tokenizer([article], max_length=512, return_tensors='pt', 
                   truncation=True)

summary_ids = model.generate(
    inputs['input_ids'],
    num_beams=4,
    max_length=100,
    early_stopping=True
)
summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True)

6. 高级 NLP 技术

6.1 句向量表示

from sentence_transformers import SentenceTransformer

# 加载句向量模型
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

sentences = [
    "人工智能正在改变世界",
    "机器学习是人工智能的分支",
    "我今天吃了苹果"
]

embeddings = model.encode(sentences)

# 语义相似度
from sklearn.metrics.pairwise import cosine_similarity
sims = cosine_similarity(embeddings)
print(sims)

6.2 语义搜索

import faiss

# 构建索引
dimension = embeddings.shape[1]
index = faiss.IndexFlatIP(dimension)  # 内积相似度(归一化后等价于余弦)
index.add(embeddings)

# 搜索
query = model.encode(["AI 的应用场景"])
D, I = index.search(query, k=3)  # 返回 top-3
print(f"最相似的句子索引: {I[0]}")

6.3 文本摘要

from transformers import pipeline

# 抽取式摘要
summarizer = pipeline("summarization", model="facebook/bart-large-cnn")

article = """Long article text here..."""
summary = summarizer(article, max_length=130, min_length=30, 
                     do_sample=False)
print(summary[0]['summary_text'])

7. NLP 评估指标

任务指标说明
分类Accuracy / F1 / AUC关注不平衡时用 F1
NERspan-level F1实体级别匹配
机器翻译BLEUn-gram 精确率
摘要ROUGEn-gram 召回率
生成Perplexity困惑度,越低越好
from sklearn.metrics import f1_score, classification_report

# 序列标注 F1
f1 = f1_score(y_true, y_pred, average='weighted')

# BLEU(机器翻译)
from nltk.translate.bleu_score import sentence_bleu
score = sentence_bleu([reference.split()], hypothesis.split())

总结

技术适用场景推荐方案
基础文本分类情感分析、主题分类TF-IDF + SVM / BERT 微调
序列标注NER、分词、POSBiLSTM-CRF / BERT-CRF
语义相似度问答匹配、去重Sentence-BERT
文本生成摘要、对话GPT/BART/T5
语义搜索文档检索Dense Passage Retrieval

NLP 工程实践要点:

  1. 中文处理:分词质量直接影响下游任务,Jieba/HanLP 或直接用字符级 BERT
  2. 预训练模型:优先使用领域相关的预训练权重(如医疗、法律)
  3. 数据增强:同义词替换、回译、EDA(Easy Data Augmentation)
  4. 长文本:BERT 的 512 限制可用滑窗、Longformer/BigBird 解决
  5. 推理优化:使用 ONNX Runtime / TensorRT 加速部署

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ai-ml」更多文章

  1. 13. 大语言模型应用开发
  2. 12. MLOps 与实验管理
  3. 11. 模型部署与推理优化