83·实战项目进阶

实战:情感分析项目

projectnlp

第 83 课 - 实战:情感分析项目

学习目标

完成本课后,你将能够:

  1. 理解情感分析任务的核心思想与应用场景。
  2. 掌握使用Python对文本数据进行预处理(分词、去停用词、向量化)的完整流程。
  3. 运用朴素贝叶斯(Naive Bayes)分类器构建一个基础的情感分析模型。
  4. 学会使用准确率、分类报告等指标评估模型的性能。
  5. 了解如何将训练好的模型应用于新的、未见过的文本数据。

核心概念

情感分析(Sentiment Analysis),也称为意见挖掘,是自然语言处理(NLP)中的一项任务,旨在识别和提取文本中蕴含的主观信息,如情感倾向(积极、消极、中性)和情感强度。

想象一下,你经营一家网店,每天收到成千上万条用户评论。人工阅读和分类这些评论效率极低。情感分析模型就是一个可以自动帮你阅读评论,并告诉你是好评还是差评的“数字员工”。

如何让机器理解“文字”? 机器只认识数字。因此,我们需要将文本转换成数字表示,这个过程叫做文本向量化。最经典的方法之一是 词袋模型(Bag-of-Words, BoW)

  • 思想:忽略词语的顺序和语法,只关注每个词出现的次数。
  • 过程:为所有文档(句子)建立一个包含所有唯一单词的“词典”,每个句子可以表示为一个向量,向量的每个维度对应词典中的一个词,其值是该词在这个句子中出现的次数。
  • 进化版 - TF-IDF:单纯的词频可能被常用词(如“的”、“是”)主导。TF-IDF(词频-逆文档频率)会降低那些在所有文档中频繁出现的词的权重,从而突出更能代表文档独特性的关键词。

核心分类器:朴素贝叶斯(Naive Bayes) 朴素贝叶斯是一系列基于贝叶斯定理的简单概率分类器,它“朴素”地假设每个特征(词)与其他特征都是独立的。尽管这个假设在真实语言中并不成立,但它在文本分类任务中表现得出奇地好,尤其是在处理高维稀疏数据(如文本向量)时效率很高。

代码示例

我们将使用Python的 scikit-learn 库来完成这个情感分析项目。数据集可以使用nltk中的电影评论数据集,它包含正面和负面评论。

# 第一步:导入必要的库
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline
from sklearn.metrics import accuracy_score, classification_report
import nltk
from nltk.corpus import movie_reviews, stopwords
from nltk.stem import WordNetLemmatizer
import re

# 第二步:加载并准备数据(使用NLTK的电影评论数据集)
nltk.download('movie_reviews')
nltk.download('stopwords')
nltk.download('wordnet')

# 获取文件ID和对应的情感标签
documents = [(movie_reviews.raw(fileid), category)
             for category in movie_reviews.categories()
             for fileid in movie_reviews.fileids(category)]
np.random.shuffle(documents) # 打乱顺序

# 将数据分为文本和标签
texts, labels = zip(*documents)
labels = [1 if label == 'pos' else 0 for label in labels] # 将标签转为数字:1代表正面,0代表负面

print(f"数据集大小: {len(texts)} 条评论")
print(f"示例文本 (前200字符): {texts[0][:200]}...")
print(f"示例标签: {'正面' if labels[0] == 1 else '负面'}")

# 第三步:文本预处理函数
def preprocess_text(text):
    """一个简单的文本预处理函数:分词、转小写、去停用词、词形还原"""
    # 1. 移除非字母字符并转为小写
    text = re.sub(r'[^a-zA-Z\s]', '', text).lower()
    # 2. 分词
    tokens = text.split()
    # 3. 去停用词
    stop_words = set(stopwords.words('english'))
    tokens = [token for token in tokens if token not in stop_words and len(token) > 2] # 同时去除过短的词
    # 4. 词形还原 (例如: “running” -> “run”)
    lemmatizer = WordNetLemmatizer()
    tokens = [lemmatizer.lemmatize(token) for token in tokens]
    return ' '.join(tokens)

# 应用预处理函数
print("正在预处理文本,请稍候...")
processed_texts = [preprocess_text(text) for text in texts]

# 第四步:划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
    processed_texts, labels, test_size=0.2, random_state=42
)
print(f"训练集大小: {len(X_train)}, 测试集大小: {len(X_test)}")

# 第五步:构建模型管道
# 使用Pipeline可以方便地将预处理步骤(TfidfVectorizer)和模型(MultinomialNB)串联起来
model = make_pipeline(
    TfidfVectorizer(max_features=10000), # 限制特征数量,防止维度过高
    MultinomialNB()
)

# 第六步:训练模型
print("正在训练模型...")
model.fit(X_train, y_train)
print("模型训练完成!")

# 第七步:在测试集上进行预测与评估
y_pred = model.predict(X_test)

# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"\n模型在测试集上的准确率: {accuracy:.4f}")

# 打印更详细的分类报告
print("\n详细分类报告:")
print(classification_report(y_test, y_pred, target_names=['负面', '正面']))

# 第八步:对新文本进行预测示例
def predict_sentiment(text, model):
    """使用训练好的模型预测单个文本的情感"""
    processed_text = preprocess_text(text)
    prediction = model.predict([processed_text])[0]
    probability = model.predict_proba([processed_text])[0]
    sentiment = "正面" if prediction == 1 else "负面"
    confidence = max(probability)
    return sentiment, confidence

# 测试新文本
test_texts = [
    "This movie is absolutely fantastic! The acting is superb and the story is captivating.",
    "I really hated this film. It was a complete waste of time and money.",
    "The movie was okay, nothing special but not terrible either."
]

print("\n--- 新文本情感预测 ---")
for text in test_texts:
    sentiment, confidence = predict_sentiment(text, model)
    print(f"文本: {text[:50]}...")
    print(f"预测情感: {sentiment} (置信度: {confidence:.2%})\n")

实践练习

  1. 基础应用:使用本课代码,但将数据集换为其他文本分类数据集(例如 fetch_20newsgroups 中的 'alt.atheism''comp.graphics' 两个类别,可视为二分类)。观察模型表现。

    • 要求:修改数据加载部分,复用预处理和模型训练流程。
    • 预期输出:新数据集上的分类报告。
  2. 模型对比:在当前电影评论数据集上,将MultinomialNB替换为LogisticRegressionsklearn.linear_model.LogisticRegression)。比较两个模型的准确率和分类报告。

    • 要求:使用相同的训练集和测试集,构建一个新的模型管道并进行评估。
    • 预期输出:两个模型的准确率对比及简要分析。
  3. 进阶挑战:尝试改进文本预处理步骤。例如,在preprocess_text函数中,尝试增加 n-gram 特征(在TfidfVectorizer中设置ngram_range=(1, 2)),即同时考虑单个词和连续两个词的组合(如“not good”)。观察这是否对模型性能有提升。

    • 要求:修改TfidfVectorizer的参数,并重新训练评估模型。
    • 预期输出:使用n-gram前后的准确率对比。

常见错误

  1. 忽略预处理:直接将原始文本送入向量化器。原始文本中的大小写、标点、停用词等会引入大量噪音,严重影响模型性能。
  2. 数据泄露:在整个数据集(包括测试集)上进行了fit_transform操作,导致模型在训练阶段就“偷看”了测试集信息。正确的做法是只在训练集上fit,然后用同一个fit好的transformertransform训练集和测试集。使用Pipeline可以很好地避免此问题。
  3. 过度依赖准确率:在数据类别不平衡时(例如,99%正面,1%负面),准确率会具有欺骗性(一个只预测“正面”的模型准确率就有99%)。必须结合分类报告查看每个类别的精确率、召回率和F1分数。
  4. 特征数量失控TfidfVectorizermax_features参数不设限制,可能导致特征维度极高(数万甚至数十万维),不仅计算慢,还容易过拟合。通常需要根据数据量合理设置。

小结

在本课中,我们完成了一个完整的端到端的情感分析实战项目:

  1. 问题定义:将用户评论自动分类为正面或负面。
  2. 数据准备:使用NLTK电影评论数据集,并进行关键的文本预处理(清洗、分词、去停用词、词形还原)。
  3. 特征工程:应用TF-IDF方法将文本转换为数值特征向量,捕捉词的重要性。
  4. 模型构建与训练:选择了经典且高效的朴素贝叶斯分类器,并通过Pipeline将其与特征工程步骤集成。
  5. 模型评估:使用准确率分类报告全面评估了模型在测试集上的表现。
  6. 模型应用:封装了预测函数,能够对新的、未见过的文本进行情感倾向预测。

这是一个经典的NLP入门项目。掌握了这个流程后,你可以尝试更复杂的深度学习模型(如LSTM、Transformer),或者将其应用于更细粒度的任务(如情感强度预测、方面级情感分析)。

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「实战:表格数据竞赛技巧」 以巩固所学知识。