简介:这份资源包面向Python开发者、NLP初学者以及正在准备毕业设计或课程设计的学生,提供一套完整的电商买家评论情感分析实践项目。内容覆盖从数据预处理到模型训练的全流程,帮助读者理解情感分析的基本原理并动手构建可运行的系统。压缩包共860个文件,约52.44MB,以478个py源码文件为核心,辅以37个csv评论数据集、32个txt说明、15张png图表及若干模型与配置类文件,结构完整便于按模块学习。项目涉及NLTK、TextBlob、Scikit-learn、Pandas、NumPy及Matplotlib等常用库,涵盖分词、停用词移除、词干提取、TF-IDF与词袋特征工程,并对比朴素贝叶斯、SVM、LSTM、CNN及BERT等模型的训练与评估方法,代码注释详尽。目前已有212人学习,适合希望系统掌握NLP与机器学习流程、积累项目经验的读者参考。
1. 拆开这个电商评论情感分析包:34000 条真实评论能跑出什么
电商评论的情感分析,很多人第一反应是调个 API 或者套个预训练模型就完事。但真到毕业设计答辩或者课程设计验收的时候,老师一句「你这模型为什么用朴素贝叶斯不用 SVM」就能把人问住。这个资源包的价值不在于模型多先进,而在于它给了一条从原始 CSV 到可解释结果的完整链路——里面有spider_comments_34000.csv这种量级的数据集,有带注释的源码,还有虚拟环境配置文件。拿到手你至少能跑通三件事:把中文评论清洗成模型能吃的格式、用 TF-IDF 加朴素贝叶斯做一个基线、再用 SVM 或 LSTM 对比效果。适合正在做 NLP 课程设计的学生,也适合想补一下传统文本分类流程的 Python 开发者。下面按我实际拆包的顺序讲。
2. 环境与数据摸底:先搞清楚包里有什么再动手
2.1 虚拟环境文件说明了什么
包里有activate、activate.bat、deactivate.bat、sysconfig.cfg、pyvenv.cfg这几个文件,这是典型的 venv 虚拟环境目录被一起打包了。pyvenv.cfg里通常记录了 Python 解释器路径和版本,activate.bat是 Windows 下的激活脚本。这意味着原作者是在 Windows 上开发的,而且把整个虚拟环境目录塞进了压缩包。
我一般不建议直接用别人的 venv,因为路径对不上会报一堆错。正确做法是自己建一个干净环境,然后根据源码里的 import 反推依赖。常见做法是:
python -m venv venv venv\Scripts\activate pip install pandas numpy scikit-learn nltk textblob matplotlib seaborn jieba这里jieba是中文分词的关键,资源摘要里提了 NLTK 分词,但 NLTK 对中文支持一般,实际处理中文评论时jieba更顺手。textblob做英文情感极性可以,中文场景下它基本只能用来做主观性参考,别指望它直接给中文评论打标签。
提示:如果
pyvenv.cfg里写的 Python 版本和你本机差太多(比如 3.7 vs 3.11),直接放弃复用,重建环境更快。
2.2 四个 CSV 文件的分工
包里的 CSV 文件有spider_comments.csv、spider_comments_34000.csv、spider_meidi_comments2020.csv、spider_meidi_comments2019.csv。从命名看,spider_comments_34000.csv是主数据集,34000 条评论足够跑出一个像样的分类器。spider_meidi_comments系列应该是按年份抓的美的品牌评论,可以用来做时间维度的对比分析。
先别急着建模,用 pandas 把数据摸一遍:
import pandas as pd df = pd.read_csv('spider_comments_34000.csv', encoding='utf-8') print(df.shape) print(df.columns.tolist()) print(df.head(10)) print(df['label'].value_counts() if 'label' in df.columns else '无标签列')逻辑说明:shape看数据量,columns看字段名,head看评论内容长什么样,value_counts看正负样本是否均衡。如果发现没有label列,说明这是无监督数据,需要自己做标注或者用情感词典打弱标签。参数上,encoding试utf-8和gbk两种,中文 CSV 经常在这翻车。
2.3 评论数据的典型脏乱差
电商评论的脏数据比想象中严重。我拆过的包里,评论字段常见的问题包括:HTML 标签残留(<br>、<p>)、表情符号转义([微笑]、[差评])、重复评论、纯数字或纯符号、以及「此用户没有填写评价」这种系统默认文本。
清洗步骤我一般这么写:
import re import jieba def clean_text(text): if not isinstance(text, str): return '' text = re.sub(r'<[^>]+>', '', text) # 去HTML标签 text = re.sub(r'\[.*?\]', '', text) # 去表情转义 text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9]', ' ', text) # 只留中英文数字 text = re.sub(r'\s+', ' ', text).strip() # 合并空格 return text def tokenize(text): return ' '.join(jieba.cut(text)) df['clean'] = df['comment'].apply(clean_text) df['tokens'] = df['clean'].apply(tokenize) df = df[df['clean'].str.len() > 2] # 去掉过短评论逻辑说明:re.sub三步分别处理标签、表情和特殊字符,jieba.cut做中文分词后用空格连接,方便后续 TF-IDF 向量化。df['clean'].str.len() > 2这行是过滤掉「好」「差」这种单字评论,它们对模型训练贡献不大还容易造成噪声。参数上,[^\u4e00-\u9fa5a-zA-Z0-9]这个正则范围覆盖了常用汉字和英文数字,如果你数据里有日文或韩文评论,需要额外加范围。
3. 从词袋到 TF-IDF:特征工程决定天花板
3.1 词袋模型和 TF-IDF 的选型理由
资源摘要里提了词袋模型、TF-IDF、Word2Vec、GloVe 几种特征工程方法。实际做电商评论情感分析,我建议先用 TF-IDF 跑基线,原因有三个:第一,TF-IDF 对短文本效果稳定,电商评论普遍不长;第二,计算速度快,34000 条数据在普通笔记本上几分钟就能跑完;第三,可解释性强,答辩时能直接看哪些词权重高。
词袋模型的问题是高频词会淹没信息,「好」「不错」这种词在正负样本里都出现,权重却一样大。TF-IDF 通过 IDF 部分压制了这种词的权重,让「垃圾」「退货」这种区分度高的词浮出来。
from sklearn.feature_extraction.text import TfidfVectorizer tfidf = TfidfVectorizer( max_features=5000, ngram_range=(1, 2), min_df=3, max_df=0.8 ) X = tfidf.fit_transform(df['tokens']) print(X.shape)逻辑说明:max_features=5000限制特征维度,防止稀疏矩阵过大;ngram_range=(1, 2)同时考虑单字和双字词组,「不 好」和「不好」是两回事;min_df=3过滤掉出现少于 3 次的词,降噪;max_df=0.8过滤掉在 80% 以上文档都出现的词,通常是「的」「了」这类停用词。这四个参数是 TF-IDF 最常调的,建议先跑一遍看X.shape,如果维度超过 10000 就调小max_features。
3.2 停用词表的中文适配
NLTK 自带的停用词表是英文的,中文停用词需要自己准备。常见做法是搜一份「中文停用词表」txt 文件,加载后传给TfidfVectorizer的stop_words参数。但注意,stop_words参数接收的是列表,而且它会在分词之后做过滤,所以你的tokens列必须是空格分隔的字符串。
with open('stopwords.txt', 'r', encoding='utf-8') as f: stopwords = [line.strip() for line in f] tfidf = TfidfVectorizer( max_features=5000, ngram_range=(1, 2), min_df=3, max_df=0.8, stop_words=stopwords )逻辑说明:停用词表里通常包含「的」「是」「在」「了」这些词,但电商评论里「不」「没」「差」这些否定词千万别加进去,否则情感极性直接反转。我见过有人把「不」当停用词删了,结果「不好」变成「好」,模型准确率掉到 50% 以下,这种坑后面还会细说。
3.3 标签怎么来:弱监督与人工标注的折中
如果 CSV 里没有label列,你需要自己造标签。常见做法有两种:一是用情感词典给每条评论打一个弱标签,比如知网 HowNet 情感词典或 BosonNLP 情感词典;二是人工标注 2000 条左右作为训练集,剩下的用半监督学习扩展。
我一般会先用词典打标,然后人工抽检 200 条看准确率。如果词典打标准确率能到 75% 以上,就可以直接用来训练基线模型。如果低于 60%,说明词典和你的数据领域不匹配,得换词典或者改人工标注。
positive_words = set(['好', '不错', '满意', '喜欢', '推荐']) negative_words = set(['差', '垃圾', '退货', '失望', '慢']) def weak_label(text): pos = sum(1 for w in positive_words if w in text) neg = sum(1 for w in negative_words if w in text) if pos > neg: return 1 elif neg > pos: return 0 else: return -1 # 中性或无法判断 df['label'] = df['clean'].apply(weak_label) df_labeled = df[df['label'] != -1]逻辑说明:weak_label函数统计正负情感词的出现次数,谁多取谁。返回 -1 的样本直接丢掉,避免噪声。参数上,情感词典的大小直接决定弱标签质量,建议至少各准备 50 个词。这个方法的局限是处理不了反讽和双重否定,但作为基线够用了。
4. 模型训练与评估:朴素贝叶斯、SVM 和 LSTM 怎么选
4.1 朴素贝叶斯:最快的基线
朴素贝叶斯在文本分类里是经典基线,优点是训练快、对小数据集友好、可解释性强。sklearn里的MultinomialNB适合 TF-IDF 输出的稀疏矩阵。
from sklearn.model_selection import train_test_split from sklearn.naive_bayes import MultinomialNB from sklearn.metrics import classification_report X_train, X_test, y_train, y_test = train_test_split( X, df_labeled['label'], test_size=0.2, random_state=42, stratify=df_labeled['label'] ) nb = MultinomialNB(alpha=1.0) nb.fit(X_train, y_train) y_pred = nb.predict(X_test) print(classification_report(y_test, y_pred))逻辑说明:stratify参数保证训练集和测试集的正负样本比例一致,避免某类样本全跑一边去。alpha=1.0是拉普拉斯平滑参数,数据量大时可以调小到 0.1,数据量小就保持 1.0。classification_report输出精确率、召回率和 F1,重点看 F1 的宏平均,因为电商评论正负样本通常不均衡。
4.2 SVM:小样本下的边界构造
SVM 在文本分类里表现通常比朴素贝叶斯好,尤其是样本量在几千到几万之间时。LinearSVC比SVC更适合文本数据,因为文本特征维度高,线性核效率更高。
from sklearn.svm import LinearSVC svm = LinearSVC(C=1.0, max_iter=2000) svm.fit(X_train, y_train) y_pred_svm = svm.predict(X_test) print(classification_report(y_test, y_pred_svm))逻辑说明:C是正则化参数,越大越容易过拟合,越小越容易欠拟合。文本分类一般从 1.0 开始试,如果训练集准确率远高于测试集就调小 C。max_iter=2000是迭代上限,数据量大时可能不收敛,可以加到 5000 或者换SGDClassifier。
4.3 LSTM:什么时候值得上深度学习
资源摘要里提了 LSTM 和 CNN,但我要泼一盆冷水:34000 条数据用 LSTM 不一定比 SVM 好多少,而且训练时间长、调参复杂。LSTM 的优势在于捕捉长距离依赖和词序信息,比如「不是不好用,是根本没法用」这种句子,TF-IDF 会把它拆成词袋,LSTM 能学到「不是」和「根本」的递进关系。
如果你坚持要跑 LSTM,用 Keras 或 PyTorch 都行。关键参数是embedding_dim(词向量维度,一般 100 或 200)、hidden_dim(LSTM 隐藏层维度,128 或 256)、num_layers(层数,1 到 2 层足够)。训练时用Adam优化器,学习率 1e-3,batch size 64 或 128。
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout model = Sequential([ Embedding(input_dim=5000, output_dim=128, input_length=100), LSTM(128, dropout=0.3, recurrent_dropout=0.3), Dense(1, activation='sigmoid') ]) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) model.summary()逻辑说明:input_dim=5000要和 TF-IDF 的max_features一致,input_length=100是每条评论截断或填充后的长度。dropout=0.3和recurrent_dropout=0.3是防过拟合的,数据量小的时候可以调到 0.5。Dense(1, activation='sigmoid')输出二分类概率,如果是三分类(正/中/负)就改成Dense(3, activation='softmax')。
4.4 评估指标:别只看准确率
电商评论数据通常正负不均衡,比如 80% 好评 20% 差评。这时候准确率会骗人——全猜好评也有 80% 准确率。必须看精确率、召回率和 F1。
| 指标 | 含义 | 关注场景 |
|---|---|---|
| 精确率 | 预测为正的样本里真正为正的比例 | 差评误判为好评的代价高时 |
| 召回率 | 真正为正的样本里被预测出来的比例 | 漏掉差评的代价高时 |
| F1 | 精确率和召回率的调和平均 | 综合衡量,答辩常用 |
| AUC | ROC 曲线下面积 | 模型排序能力,不依赖阈值 |
我一般会在classification_report之外再画一个混淆矩阵,直观看到哪类错得多。如果差评召回率低,说明模型把很多差评漏成了好评,这时候要检查训练集里差评样本是不是太少,或者情感词典里负面词覆盖不够。
5. 避坑与排查:那些让我返工三次的细节
5.1 中文编码问题导致读取失败
现象:pd.read_csv报UnicodeDecodeError,或者读出来的评论全是乱码。
原因:CSV 文件可能是 GBK 或 GB2312 编码,而 pandas 默认用 UTF-8。
解决:先试encoding='gbk',再试encoding='gb18030'。如果还不行,用chardet库检测编码:
import chardet with open('spider_comments_34000.csv', 'rb') as f: result = chardet.detect(f.read(10000)) print(result)5.2 分词后特征维度爆炸
现象:TfidfVectorizer跑完X.shape显示几万甚至十几万维,内存直接爆掉。
原因:max_features没设或者设太大,min_df太小,导致大量低频词进入特征空间。
解决:max_features控制在 5000 到 10000,min_df至少设 2 或 3。如果还大,用SelectKBest做特征选择,保留卡方检验得分最高的 3000 个特征。
5.3 否定词被当停用词删掉
现象:模型把「不好」预测成好评,「不推荐」预测成推荐。
原因:停用词表里包含了「不」「没」「别」这些否定词,分词后被过滤掉了。
解决:检查停用词表,把否定词全部移除。更稳妥的做法是构建 n-gram 特征时保留「不+形容词」的组合,ngram_range=(1, 2)已经能覆盖一部分,但前提是否定词没被删。
5.4 训练集和测试集分布不一致
现象:训练集准确率 95%,测试集准确率 60%,差距巨大。
原因:train_test_split没加stratify,或者数据本身有时间顺序(比如 2019 年评论和 2020 年评论情感分布不同),随机划分导致分布偏移。
解决:分类任务加stratify=y;如果是时间序列数据,按时间切分,用 2019 年数据训练、2020 年数据测试,更贴近真实场景。
5.5 虚拟环境路径写死导致 import 失败
现象:激活包里的 venv 后,import pandas报ModuleNotFoundError或者路径错误。
原因:pyvenv.cfg和activate.bat里记录了原作者的绝对路径,换机器后路径不存在。
解决:别用包里的 venv,自己重建。如果非要修,打开pyvenv.cfg把home和executable改成你本机的 Python 路径,但activate.bat里的VIRTUAL_ENV也要改,麻烦且容易漏。重建环境是更省事的做法。
6. 把模型跑成可交付结果:从混淆矩阵到错误样本分析
模型训练完不是终点,答辩或者验收时老师最常问的是「你这个模型错在哪儿了」。我一般会做两件事:画混淆矩阵和导出错误样本。
混淆矩阵用sklearn.metrics.confusion_matrix加seaborn.heatmap就能画:
from sklearn.metrics import confusion_matrix import seaborn as sns import matplotlib.pyplot as plt cm = confusion_matrix(y_test, y_pred_svm) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=['负向', '正向'], yticklabels=['负向', '正向']) plt.xlabel('预测') plt.ylabel('真实') plt.title('SVM 混淆矩阵') plt.show()逻辑说明:annot=True在格子里显示数字,fmt='d'保证显示整数。重点看对角线外的数字,如果「真实负向预测为正向」的数量大,说明模型对差评不敏感,需要检查负面词特征权重或者增加差评样本。
导出错误样本更直接:
errors = df_labeled.iloc[y_test.index][y_test != y_pred_svm] errors[['clean', 'label']].to_csv('error_samples.csv', index=False, encoding='utf-8-sig')逻辑说明:y_test.index对应原始 DataFrame 的行号,y_test != y_pred_svm筛出预测错误的样本。encoding='utf-8-sig'保证 Excel 打开不乱码。拿到错误样本后,人工看 50 条,归类错误原因:是反讽?是双重否定?还是标注本身就错了?这一步做完,你对模型边界的理解会比看任何指标都深。
最后说一个我自己的习惯:每次跑完模型,我都会把TfidfVectorizer的get_feature_names_out()和模型系数拿出来看一遍,找出权重最高的 20 个正向词和 20 个负向词。如果负向词里出现「好」「喜欢」这种词,说明标签或者数据有问题,得回去查。这个检查花不了五分钟,但能拦住大部分低级错误。从那以后我每次交付情感分析模型前都强制走一遍特征词审查,希望帮到你。
本文还有配套的精品资源,点击获取