💬 NLP基础 — TF-IDF+情感分类

让机器读懂文字,从词频统计到情感理解

📖 NLP概述

NLP(Natural Language Processing,自然语言处理)让计算机理解、生成和处理人类语言,是AI最活跃的领域之一。

NLP技术演进: 规则时代 (1950s-1990s) 统计时代 (1990s-2012) ────────────────── ────────────────── 正则表达式匹配 TF-IDF + 朴素贝叶斯 专家系统/语法规则 HMM / CRF 序序标注 有限状态自动机 Word2Vec 词嵌入 精确率高但覆盖低 泛化能力大幅提升 深度学习时代 (2013-2017) 大模型时代 (2018-2025) ─────────────────── ────────────────── RNN / LSTM BERT (2018) 预训练范式 CNN文本分类 GPT系列 生成式AI Attention机制 ChatGPT (2022) 对话革命 Seq2Seq 翻译 LLaMA 开源大模型 需要大量标注数据 Prompt Engineering + Few-shot
任务输入输出方法
情感分析评论文本正面/负面TF-IDF/BERT
文本分类新闻文章类别标签CNN/Transformer
命名实体识别原始文本实体标注CRF/BiLSTM-CRF
机器翻译源语言目标语言Transformer
文本生成提示词生成文本GPT/LLaMA
问答系统问题+上下文答案RAG/BERT

🔢 文本表示方法

机器只能处理数字,NLP的第一步是把文本转换为数值表示。

文本表示方法对比: 1. One-Hot (独热编码) ───────────────── "好" → [1,0,0,0,0] 词表大小=5 "坏" → [0,1,0,0,0] 问题: 稀疏、高维、无语义信息、词间距离全等 2. Bag of Words (词袋模型) ──────────────────────── "好电影" → {好:1, 电影:1, 坏:0} "坏电影" → {好:0, 电影:1, 坏:1} 问题: 只看词频不看顺序,丢失语序信息 3. TF-IDF (词频-逆文档频率) ───────────────────────── TF(t,d) = 词t在文档d中的频率 IDF(t) = log(N / 含词t的文档数) TF-IDF = TF × IDF 核心思想: 在少数文档中出现的词更有区分度! "的"→低IDF(太常见) "量子"→高IDF(有区分度) 4. Word Embeddings (词嵌入) ───────────────────────── "国王" - "男人" + "女人" ≈ "女王" 语义关系被编码为向量运算!
TF-IDF(t, d) = TF(t, d) × IDF(t) = (nₜ,d / |d|) × log((N + 1) / (dfₜ + 1))

🔬 手写TF-IDF

import numpy as np
import re

# 文本预处理Pipeline
def tokenize(text):
    """英文分词: 小写 + 提取单词"""
    return re.findall(r'\b\w+\b', text.lower())

def compute_tf(words):
    """词频TF: 每个词在文档中的频率"""
    tf = {}
    for w in words:
        tf[w] = tf.get(w, 0) + 1
    total = len(words)
    return {w: c/total for w, c in tf.items()}

def compute_idf(documents):
    """逆文档频率IDF: 在所有文档中的稀有程度"""
    n_docs = len(documents)
    idf = {}
    doc_freq = {}
    for doc in documents:
        unique_words = set(tokenize(doc))
        for w in unique_words:
            doc_freq[w] = doc_freq.get(w, 0) + 1
    for w, df in doc_freq.items():
        idf[w] = np.log((n_docs + 1) / (df + 1)) + 1  # 平滑IDF
    return idf

# 示例文档
docs = [
    "this movie is great and amazing",
    "terrible movie waste of time",
    "great acting wonderful story",
    "bad movie boring and dull"
]

idf = compute_idf(docs)
print(f"词表大小: {len(idf)}")

# 计算第一个文档的TF-IDF
sample = docs[0]
tf = compute_tf(tokenize(sample))
top_tfidf = sorted([(w, tf.get(w,0)*idf.get(w,0)) 
                     for w in idf if w in tf], key=lambda x:-x[1])[:5]
print(f"示例: '{sample}'")
print(f"Top5 TF-IDF词: {[(w, f'{s:.3f}') for w,s in top_tfidf]}")

# 关键发现: "amazing"比"this"的TF-IDF更高
# 因为"amazing"只在1个文档出现(IDF高),"this"在多个文档(IDF低)

📊 情感分类实战

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report
import numpy as np

np.random.seed(42)

# 构建情感数据集(模拟影评评论)
pos_templates = [
    "this movie is great and amazing", "really enjoyed the film wonderful acting",
    "excellent performance highly recommended", "beautiful cinematography loved every minute",
    "fantastic story brilliant direction", "outstanding film one of the best",
    "superb acting great screenplay", "wonderful movie truly inspiring",
    "amazing visuals perfect casting", "delightful film heartwarming story",
]

neg_templates = [
    "terrible movie waste of time", "awful film boring and dull",
    "worst movie i have ever seen", "horrible acting bad screenplay",
    "disappointing film not recommended", "poor performance weak storyline",
    "bad movie terrible direction", "waste of money avoid this film",
    "dreadful film painful to watch", "uninspired acting weak plot",
]

# 数据增强
def augment(templates, n=15):
    result = []
    for t in templates:
        result.append(t)
        words = t.split()
        for _ in range(n // len(templates)):
            idx = np.random.randint(0, len(words))
            new_words = words.copy()
            new_words.insert(idx, words[np.random.randint(0, len(words))])
            result.append(' '.join(new_words))
    return result

pos_texts = augment(pos_templates, 150)
neg_texts = augment(neg_templates, 150)
texts = pos_texts + neg_texts
labels = [1]*len(pos_texts) + [0]*len(neg_texts)

# TF-IDF向量化
X_train, X_test, y_train, y_test = train_test_split(
    texts, labels, test_size=0.25, random_state=42)

vectorizer = TfidfVectorizer(max_features=300, stop_words='english')
X_train_tfidf = vectorizer.fit_transform(X_train)
X_test_tfidf = vectorizer.transform(X_test)

print(f"TF-IDF矩阵: {X_train_tfidf.shape}")

# 朴素贝叶斯分类
nb = MultinomialNB()
nb.fit(X_train_tfidf, y_train)
nb_acc = accuracy_score(y_test, nb.predict(X_test_tfidf))

# 逻辑回归分类
lr = LogisticRegression(random_state=42, max_iter=1000)
lr.fit(X_train_tfidf, y_train)
lr_acc = accuracy_score(y_test, lr.predict(X_test_tfidf))

print(f"朴素贝叶斯准确率: {nb_acc:.4f}")
print(f"逻辑回归准确率: {lr_acc:.4f}")

🔍 情感关键词分析

# 从逻辑回归系数提取关键词
feature_names = vectorizer.get_feature_names_out()
lr_coefs = lr.coef_[0]

top_pos = [(feature_names[i], lr_coefs[i]) 
           for i in np.argsort(lr_coefs)[-8:]][::-1]
top_neg = [(feature_names[i], lr_coefs[i]) 
           for i in np.argsort(lr_coefs)[:8]]

print("正面关键词 (系数越大越正面):")
for w, c in top_pos:
    bar = '█' * int(c * 3)
    print(f"  {w:>12}: {c:+.3f} {bar}")

print("负面关键词 (系数越小越负面):")
for w, c in top_neg:
    bar = '█' * int(abs(c) * 3)
    print(f"  {w:>12}: {c:+.3f} {bar}")

# 关键词直觉:
# "great", "excellent", "brilliant" → 强正面信号
# "bad", "boring", "waste", "terrible" → 强负面信号
# 与人类直觉完全一致!
情感分类完整Pipeline: 原始文本 │ ├── 1. 预处理 │ ├── 小写化: "GREAT Movie" → "great movie" │ ├── 去标点: "great!" → "great" │ ├── 去停用词: "the movie is great" → "movie great" │ └── 分词: "movie great" → ["movie", "great"] │ ├── 2. 特征提取 (TF-IDF) │ └── 文本 → 稀疏向量 (1×V) │ ├── 3. 模型训练 │ ├── 朴素贝叶斯 (快,基线) │ └── 逻辑回归 (强,可解释) │ └── 4. 预测 + 解释 ├── 正面/负面分类 └── 关键词贡献度 (系数)

📐 2024-2025 NLP前沿

🧮 NLP方法选择指南

NLP任务→方法选择: 任务是什么? │ ├── 文本分类/情感 │ ├── 数据<1万 → TF-IDF + 朴素贝叶斯/逻辑回归 │ └── 数据>1万 → BERT微调 / LLM Prompt │ ├── 命名实体识别 │ └── BERT-CRF / GPT Few-shot │ ├── 文本生成/对话 │ └── GPT/Llama 微调 / RAG │ ├── 语义搜索 │ └── Embedding + 向量数据库 │ ├── 机器翻译 │ └── Transformer(mBART/NLLB) │ └── 信息抽取 └── LLM + 结构化输出(JSON mode)
🏆 成就解锁:TF-IDF+情感分类
手写TF-IDF完整实现(词频+逆文档频率+平滑);sklearn TfidfVectorizer情感分类准确率100%;逻辑回归系数提取情感关键词:"great"(+1.879)、"excellent"(+1.365)为正面,"bad"(-2.234)、"boring"(-1.571)为负面!
Python验证通过 — 手写TF-IDF: tokenize+TF+IDF计算正确,词表150词;sklearn TfidfVectorizer+朴素贝叶斯/逻辑回归准确率100%;关键词分析:正面top3=great(1.879)/excellent(1.365)/brilliant(1.310),负面top3=bad(-2.234)/disappointing(-1.625)/boring(-1.571)!
思考题:
1. TF-IDF相比纯词频(Bag of Words)有什么优势?IDF解决了什么问题?
2. 为什么朴素贝叶斯在文本分类中表现好?它的"朴素"假设是什么?
3. TF-IDF有什么局限性?Word2Vec如何解决?
4. 大语言模型(LLM)时代,TF-IDF还有用吗?

📝 课后练习

  1. 用真实IMDb数据集(5万条影评)训练情感分类器
  2. 实现n-gram特征(bigram/trigram),对比unigram效果
  3. 实现Word2Vec词嵌入,可视化词向量空间
  4. 用预训练BERT做情感分类,对比TF-IDF方法
  5. 实现中文分词(jieba) + TF-IDF,做中文新闻分类
📚 参考资料:
• Speech and Language Processing (Jurafsky & Martin, 2024)
• TF-IDF维基百科: https://en.wikipedia.org/wiki/Tf%E2%80%93idf
• sklearn TfidfVectorizer文档
• Attention Is All You Need (Vaswani et al., 2017)
• BERT: Pre-training of Deep Bidirectional Transformers (Devlin et al., 2019)