情感分析实战:用Python构建电影评论分类器
你是否曾好奇电影评分网站如何自动识别好评和差评?是否想过如何让程序理解文字背后的情感?本文将带你从零开始,用Python机器学习技术构建一个电影评论情感分析工具,无需深厚的自然语言处理(NLP)背景,也能让计算机"读懂"人类情感。
情感分析基础:让机器理解文字
情感分析(Sentiment Analysis)是NLP的一个重要应用,它能自动识别文本中的主观信息和情感倾向。在电商评论分析、社交媒体监控、舆情分析等场景中有着广泛应用。本项目基于IMDb电影评论数据集,通过机器学习算法实现对评论情感的二分类(正面/负面)。
项目核心代码位于code/ch08/ch08.ipynb和code/ch09/movieclassifier/目录,包含从数据预处理到模型部署的完整流程。
数据准备:从原始文本到机器学习特征
数据集构建
首先需要准备带标签的训练数据。我们使用IMDb的50,000条电影评论,其中25,000条用于训练,25,000条用于测试,正负面评论各占一半。数据预处理代码如下:
import pandas as pd
import os
basepath = 'aclImdb'
labels = {'pos': 1, 'neg': 0}
df = pd.DataFrame()
for s in ('test', 'train'):
for l in ('pos', 'neg'):
path = os.path.join(basepath, s, l)
for file in sorted(os.listdir(path)):
with open(os.path.join(path, file), 'r', encoding='utf-8') as infile:
txt = infile.read()
df = df.append([[txt, labels[l]]], ignore_index=True)
df.columns = ['review', 'sentiment']
预处理后的数据集保存为code/ch08/movie_data.csv.gz,方便后续使用。
文本清洗与预处理
原始文本包含HTML标签、特殊符号等噪声数据,需要进行清洗。项目中使用的预处理函数如下:
import re
def preprocessor(text):
# 移除HTML标签
text = re.sub('<[^>]*>', '', text)
# 提取表情符号
emoticons = re.findall('(?::|;|=)(?:-)?(?:\)|\(|D|P)', text)
# 移除特殊字符,转换为小写,并保留表情符号
text = re.sub('[\W]+', ' ', text.lower()) + ' '.join(emoticons).replace('-', '')
return text
这个预处理函数会清除文本中的HTML标签、特殊符号,将文本转换为小写,并保留表情符号等情感丰富的元素,为后续特征提取做准备。
特征提取:词袋模型与TF-IDF
计算机无法直接理解文本,需要将文本转换为数值特征。项目中使用了两种常用的文本表示方法:
- 词袋模型(Bag-of-Words):将文本视为词语的集合,忽略语法和词序,仅考虑词频
- TF-IDF:通过词频-逆文档频率加权,突出重要词汇,抑制常见词汇
核心实现代码位于code/ch09/movieclassifier/vectorizer.py,使用scikit-learn的HashingVectorizer:
from sklearn.feature_extraction.text import HashingVectorizer
import re
import pickle
# 加载停用词
cur_dir = os.path.dirname(__file__)
stop = pickle.load(open(os.path.join(cur_dir, 'pkl_objects', 'stopwords.pkl'), 'rb'))
def tokenizer(text):
text = re.sub('<[^>]*>', '', text)
emoticons = re.findall('(?::|;|=)(?:-)?(?:\)|\(|D|P)', text.lower())
text = re.sub('[\W]+', ' ', text.lower()) + ' '.join(emoticons).replace('-', '')
tokenized = [w for w in text.split() if w not in stop]
return tokenized
# 初始化HashingVectorizer
vect = HashingVectorizer(decode_error='ignore',
n_features=2**21,
preprocessor=None,
tokenizer=tokenizer)
模型训练:从算法到分类器
选择合适的算法
项目中使用逻辑回归(Logistic Regression)作为分类算法,它在文本分类任务中表现优异,且训练速度快,适合处理高维数据。模型训练代码如下:
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
# 构建流水线
tfidf = TfidfVectorizer(strip_accents=None,
lowercase=False,
preprocessor=None)
param_grid = [{'vect__ngram_range': [(1, 1)],
'clf__penalty': ['l1', 'l2'],
'clf__C': [1.0, 10.0, 100.0]},
{'vect__ngram_range': [(1, 1)],
'vect__use_idf':[False],
'vect__norm':[None],
'clf__penalty': ['l1', 'l2'],
'clf__C': [1.0, 10.0, 100.0]}]
lr_tfidf = Pipeline([('vect', tfidf),
('clf', LogisticRegression(random_state=0))])
# 网格搜索优化参数
gs_lr_tfidf = GridSearchCV(lr_tfidf, param_grid,
scoring='accuracy',
cv=5, verbose=1,
n_jobs=-1)
# 训练模型
gs_lr_tfidf.fit(X_train, y_train)
通过网格搜索(Grid Search)优化超参数,最终模型在测试集上达到约88%的准确率。
模型持久化
训练好的模型需要保存为文件,以便后续部署。使用Python的pickle模块实现模型序列化:
import pickle
import os
# 保存分类器
dest = os.path.join('movieclassifier', 'pkl_objects')
if not os.path.exists(dest):
os.makedirs(dest)
pickle.dump(clf, open(os.path.join(dest, 'classifier.pkl'), 'wb'), protocol=4)
# 保存停用词
import nltk
nltk.download('stopwords')
from nltk.corpus import stopwords
stop = stopwords.words('english')
pickle.dump(stop, open(os.path.join(dest, 'stopwords.pkl'), 'wb'), protocol=4)
保存后的模型文件位于code/ch09/movieclassifier/pkl_objects/classifier.pkl,大小约为10MB,便于部署。
Web应用部署:构建交互式情感分析工具
为了让模型更易用,我们使用Flask框架构建了一个Web应用,允许用户输入电影评论并实时查看情感分析结果。
应用架构
Web应用的核心文件结构如下:
movieclassifier/
├── app.py # Flask应用入口
├── vectorizer.py # 文本向量化工具
├── pkl_objects/ # 模型和资源文件
│ ├── classifier.pkl # 训练好的分类器
│ └── stopwords.pkl # 停用词表
├── static/ # 静态资源
│ └── style.css # 样式表
└── templates/ # HTML模板
├── _formhelpers.html
├── reviewform.html # 评论输入表单
├── results.html # 分析结果页面
└── thanks.html # 反馈感谢页面
核心实现
应用入口文件code/ch09/movieclassifier/app.py的关键代码:
from flask import Flask, render_template, request
from wtforms import Form, TextAreaField, validators
import pickle
import sqlite3
import os
import numpy as np
from vectorizer import vect
app = Flask(__name__)
# 加载分类器
cur_dir = os.path.dirname(__file__)
clf = pickle.load(open(os.path.join(cur_dir, 'pkl_objects', 'classifier.pkl'), 'rb'))
db = os.path.join(cur_dir, 'reviews.sqlite')
def classify(document):
"""分类函数:返回情感标签和概率"""
label = {0: 'negative', 1: 'positive'}
X = vect.transform([document])
y = clf.predict(X)[0]
proba = np.max(clf.predict_proba(X))
return label[y], proba
def train(document, y):
"""在线更新模型"""
X = vect.transform([document])
clf.partial_fit(X, [y])
def sqlite_entry(path, document, y):
"""将评论存入数据库"""
conn = sqlite3.connect(path)
c = conn.cursor()
c.execute("INSERT INTO review_db (review, sentiment, date)"
" VALUES (?, ?, DATETIME('now'))", (document, y))
conn.commit()
conn.close()
# Web表单
class ReviewForm(Form):
moviereview = TextAreaField('', [validators.DataRequired(), validators.length(min=15)])
# 路由
@app.route('/')
def index():
form = ReviewForm(request.form)
return render_template('reviewform.html', form=form)
@app.route('/results', methods=['POST'])
def results():
form = ReviewForm(request.form)
if request.method == 'POST' and form.validate():
review = request.form['moviereview']
y, proba = classify(review)
return render_template('results.html',
content=review,
prediction=y,
probability=round(proba*100, 2))
return render_template('reviewform.html', form=form)
@app.route('/thanks', methods=['POST'])
def feedback():
feedback = request.form['feedback_button']
review = request.form['review']
prediction = request.form['prediction']
# 根据用户反馈更新模型
inv_label = {'negative': 0, 'positive': 1}
y = inv_label[prediction]
if feedback == 'Incorrect':
y = int(not(y))
train(review, y)
sqlite_entry(db, review, y)
return render_template('thanks.html')
if __name__ == '__main__':
app.run(debug=True)
应用界面
启动应用后,访问http://localhost:5000即可看到如下界面:
用户输入评论后,系统会返回情感分析结果,包括预测标签(positive/negative)和置信度:
应用还支持用户反馈功能,如果分类结果不正确,用户可以点击"Correct"或"Incorrect"按钮,系统会根据反馈更新模型,实现持续优化。
模型优化与性能提升
关键性能指标
在测试集上的模型表现:
- 准确率(Accuracy):88.5%
- 精确率(Precision):89.2%
- 召回率(Recall):87.8%
- F1分数:88.5%
优化策略
1.** 特征工程优化 **:
- 使用n-gram捕捉词语间关系
- 加入情感词典特征
- 尝试词嵌入(Word Embedding)技术
2.** 算法调优 **:
- 尝试不同分类算法(SVM、随机森林、深度学习模型)
- 使用网格搜索优化超参数
- 集成多个模型提升性能
3.** 在线学习 **:
- 通过用户反馈持续更新模型
- 实现增量学习,适应新的数据分布
实际应用与扩展
潜在应用场景
1.** 电商评论分析 :自动分析商品评论情感,提取用户关注点 2. 社交媒体监控 :实时追踪品牌提及和情感变化 3. 客户服务 :自动分类客户反馈,优先处理负面情绪 4. 内容推荐 **:基于用户评论情感推荐相似内容
项目扩展建议
1.** 多语言支持 :扩展至中文、日文等其他语言 2. 情感强度分析 :不仅区分正负,还能评估情感强度 3. 领域适应 :针对特定领域(如餐饮、酒店)优化模型 4. 实体级情感分析 **:识别文本中不同实体的情感倾向
总结与下一步
通过本文,你已经掌握了构建情感分析系统的完整流程,包括数据预处理、特征工程、模型训练和Web部署。核心代码位于code/ch08/和code/ch09/目录,你可以基于此进行二次开发。
学习资源
- 官方教程:README.md
- 详细代码注释:code/ch08/ch08.ipynb
- 模型部署示例:code/ch09/movieclassifier/
后续学习路径
- 学习深度学习在NLP中的应用(RNN、LSTM、Transformer)
- 探索预训练语言模型(BERT、GPT)在情感分析中的表现
- 研究情感分析的最新进展和前沿技术
- 尝试将模型部署到云平台,实现高可用服务
希望本文能帮助你入门情感分析和NLP应用开发。如有任何问题,欢迎查看项目文档或提交issue。现在,开始构建你自己的情感分析应用吧!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考






