简介:这份资源是面向计算机相关专业学生与从业者的毕业设计项目包,聚焦电商淘宝商品评论的情感分析,采用Python与机器学习技术实现完整流程。项目从淘宝评论爬取入手,借助Selenium模拟真实登录行为获取数据,随后进行文本清理,剔除“666”“好好好”等无意义词汇并去除标点,再用jieba精确模式分词并构造词典,完成词汇向量化与词语索引映射,最后对比SVM与LSTM两类分类模型的效果。压缩包共43个文件,约66.68MB,包含14个py源码、7个csv数据集、4个npy与3个pkl模型文件、2个h5权重、2个xls样本以及yml配置、vector词向量、model模型等,覆盖爬虫、预处理、训练与测试各环节。该毕设评审分达97分,代码经过严格调试可稳定运行,已有382人学习。读者可据此获得一套可直接复用的情感分析方案,理解从数据采集到模型对比的完整链路,并作为课程设计或毕业设计的参考模板。
1. 淘宝评论情感分析毕设:从一份 zip 到能答辩的完整链路
做毕业设计最怕的不是不会写代码,而是拿到一份「Python 基于机器学习的电商淘宝商品评论情感分析项目源码+数据」的压缩包,解压之后不知道从哪一行开始看。这个标题背后其实是一条很清晰的技术链路:用爬虫或现成数据集拿到淘宝商品评论,做中文分词和清洗,抽成 TF-IDF 或词向量特征,喂给朴素贝叶斯、SVM、逻辑回归这类经典机器学习模型,输出好评/差评的二分类结果,最后用准确率、F1 值和混淆矩阵证明这套东西真的能用。它解决的是电商场景里「几万条评论人工看不完」的问题,适合计算机、大数据、电商相关专业的毕业生,也适合想入门 NLP 但不想一上来就啃深度学习的同学。我见过太多人卡在环境配置和中文编码上,其实真正跑通一遍,你会发现核心代码不到 300 行,难点全在数据清洗和特征工程这些「脏活」里。
2. 先搞清楚数据长什么样:淘宝评论的字段、噪声与标注逻辑
2.1 一份典型评论数据集包含哪些列
拿到项目里的数据文件,第一件事不是急着建模,而是把数据读进 pandas 看一眼。淘宝评论数据通常长这样:review(评论正文)、label(情感标签,一般 1 表示好评、0 表示差评)、有时还有rating(星级)、date(评论时间)、product_id(商品 ID)。不同来源的数据集列名会变,有的叫content、sentiment,所以第一步永远是df.columns确认字段。
import pandas as pd # 读取评论数据,注意中文编码常见为 utf-8 或 gbk df = pd.read_csv('taobao_comments.csv', encoding='utf-8') # 先看形状和前几行,确认列名和数据结构 print(df.shape) print(df.head()) print(df.columns.tolist()) # 检查标签分布,判断是否类别不平衡 print(df['label'].value_counts())这段代码的逻辑是先建立对数据的整体认知。shape告诉你样本量,head()让你看到真实评论文本的样子,value_counts()则暴露一个关键问题:好评往往远多于差评,如果比例超过 4:1,后面建模就必须处理类别不平衡,否则模型全预测成好评也能有 80% 准确率,这种「虚高」在答辩时一问就露馅。
参数上,encoding是最容易翻车的地方。Windows 下用 Excel 另存过的 CSV 经常是gbk,直接读会报UnicodeDecodeError。我的习惯是先试utf-8,报错就换gbk,再不行用utf-8-sig(带 BOM 的情况)。
2.2 中文评论的噪声类型和清洗优先级
淘宝评论的脏是出了名的:有「此用户没有填写评价」这种系统默认文本,有「好评!好评!好评!」的重复刷屏,有大量 emoji 和颜文字,还有「宝贝收到啦,物流很快,下次还来」这种和情感弱相关的客套话。清洗要有优先级,不能一把梭。
常见做法是分四步:第一步去重和去空值,第二步过滤掉系统默认评论和长度小于 4 个字的短文本,第三步用正则去掉 URL、数字、英文字母和特殊符号,第四步才是分词和去停用词。顺序不能反,因为如果先分词再去重,重复文本会被切得面目全非,去重就失效了。
import re # 去重、去空 df = df.dropna(subset=['review']) df = df.drop_duplicates(subset=['review']) # 过滤系统默认评论和过短文本 df = df[df['review'] != '此用户没有填写评价'] df = df[df['review'].str.len() >= 4] # 正则清洗:去掉 URL、数字、字母、多余符号 def clean_text(text): text = re.sub(r'http[s]?://\S+', '', text) # 去 URL text = re.sub(r'[a-zA-Z0-9]', '', text) # 去字母数字 text = re.sub(r'[^\u4e00-\u9fa5,。!?]', '', text) # 只保留中文和常用标点 return text.strip() df['clean_review'] = df['review'].apply(clean_text) df = df[df['clean_review'].str.len() >= 4] print(df.shape)这里\u4e00-\u9fa5是中文 Unicode 区间,保留它意味着把 emoji、日文、乱码全部干掉。有人会舍不得 emoji,觉得「😊」也是情感信号,但在传统机器学习里,emoji 进不了 TF-IDF 的词表,留着反而增加噪声,不如直接去掉。清洗完记得再打印一次shape,对比清洗前后掉了多少数据,如果掉了超过 30%,说明过滤条件太狠,要回头调len >= 4这个阈值。
3. 用 jieba + TF-IDF 把中文评论变成模型能吃的数字
3.1 jieba 分词的三种模式和停用词表怎么配
中文和英文最大的区别是词与词之间没有空格,所以必须先分词。jieba 是毕设里最稳的选择,安装就一句pip install jieba。它有三种模式:精确模式(cut)、全模式(cut_all=True)、搜索引擎模式(cut_for_search)。情感分析用精确模式就够了,全模式会把「不错」切成「不」「错」,直接毁掉情感极性。
import jieba # 加载停用词表,网上通用的中文停用词表即可 with open('stopwords.txt', 'r', encoding='utf-8') as f: stopwords = set([line.strip() for line in f]) def segment(text): # 精确模式分词,过滤停用词和单字 words = jieba.cut(text, cut_all=False) return ' '.join([w for w in words if w not in stopwords and len(w) > 1]) df['seg_review'] = df['clean_review'].apply(segment) print(df[['clean_review', 'seg_review']].head())停用词表是情感分析里被低估的一环。通用停用词表(哈工大、百度、川大版都行)会去掉「的」「了」「是」这类无意义词,但要注意它可能误删情感词。比如「不」在很多停用词表里,可「不好」和「好」的极性完全相反。我的处理是:分词后保留「不」「没」「很」「太」这几个程度和否定词,手动从停用词表里删掉它们。len(w) > 1是为了去掉单字噪声,但同样会误伤「差」「烂」这种单字情感词,如果数据集里这类词多,就把条件放宽。
3.2 TF-IDF 向量化的参数怎么调
分词完得到的是空格分隔的字符串,模型还不认识,要用 TF-IDF 转成数值矩阵。TF-IDF 的核心思想是:一个词在当前评论里出现越多(TF 高),但在所有评论里出现越少(IDF 高),它就越有区分度。「物流」「质量」这种词区分度就比「东西」高。
from sklearn.feature_extraction.text import TfidfVectorizer # TF-IDF 向量化 tfidf = TfidfVectorizer( max_features=5000, # 只保留权重最高的 5000 个词 ngram_range=(1, 2), # 同时考虑单字词和双字词组合 min_df=3, # 至少出现在 3 条评论里才保留 max_df=0.8 # 出现在超过 80% 评论里的词丢掉 ) X = tfidf.fit_transform(df['seg_review']) y = df['label'] print(X.shape)四个参数各有讲究。max_features=5000是控制维度的,词表太大模型训练慢还容易过拟合,5000 对几万条评论的数据集够用。ngram_range=(1,2)是关键,因为「不 好」被切成两个词后,单看「好」是正面,加上 bigram「不 好」才能捕捉否定。min_df=3过滤掉只出现一两次的罕见词,max_df=0.8过滤掉几乎每条评论都有的词。这四个值没有标准答案,但ngram_range一定要设成(1,2),这是中文情感分析里提升 F1 最明显的一步。
注意:
fit_transform只能用在训练集上。测试集必须用训练集 fit 好的tfidf去transform,否则会造成数据泄漏,答辩时被问到「你怎么保证测试集没参与训练」会很难解释。
4. 三个经典模型跑对比:朴素贝叶斯、SVM、逻辑回归谁更适合毕设
4.1 训练集测试集划分与基线模型搭建
毕设里最讨巧也最稳的做法是「多模型对比」,既显得工作量足,又能用表格展示效果差异。先划分数据,stratify=y保证训练集和测试集的正负样本比例一致,random_state固定住让结果可复现。
from sklearn.model_selection import train_test_split from sklearn.naive_bayes import MultinomialNB from sklearn.svm import LinearSVC from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, f1_score, classification_report # 划分数据集,stratify 保证标签比例一致 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # 三个模型依次训练和评估 models = { '朴素贝叶斯': MultinomialNB(alpha=1.0), '线性SVM': LinearSVC(C=1.0, max_iter=2000), '逻辑回归': LogisticRegression(C=1.0, max_iter=1000) } for name, model in models.items(): model.fit(X_train, y_train) y_pred = model.predict(X_test) print(f'{name} 准确率: {accuracy_score(y_test, y_pred):.4f} F1: {f1_score(y_test, y_pred):.4f}')MultinomialNB的alpha是平滑系数,防止某个词在训练集没出现导致概率为 0,默认 1.0 一般够用。LinearSVC的C是正则强度,越大越容易过拟合,文本分类里 0.5 到 2 之间调。LogisticRegression的max_iter要调大,因为 TF-IDF 是稀疏高维矩阵,默认 100 次迭代经常不收敛,会报警告。
4.2 模型对比结果怎么读、怎么选
跑完你会看到类似这样的结果(具体数值取决于数据集):
| 模型 | 准确率 | F1 值 | 训练速度 | 适合场景 |
|---|---|---|---|---|
| 朴素贝叶斯 | 0.86 | 0.85 | 最快 | 数据量小、要快速出结果 |
| 线性 SVM | 0.89 | 0.88 | 中等 | 追求精度、特征维度高 |
| 逻辑回归 | 0.88 | 0.87 | 中等 | 需要输出概率、可解释性 |
选哪个写进毕设?我的建议是主推线性 SVM,因为它在高维稀疏文本上表现最稳,然后把逻辑回归作为「可解释性对比」——逻辑回归能输出每个词的权重系数,你可以展示「差」「垃圾」「退货」是权重最高的负面词,这在答辩时是个加分项。朴素贝叶斯作为基线,说明「简单模型也能到 85%」,体现你对模型选型的思考。
不要只报准确率。如果数据不平衡,准确率会骗人。一定要打印classification_report,看差评那一类的 recall。如果差评 recall 只有 0.6,说明模型漏掉了很多差评,这在电商场景里是致命的——漏掉一个差评比误判一个好评代价大得多。
# 看详细的分类报告,重点关注少数类的 recall best_model = models['线性SVM'] y_pred = best_model.predict(X_test) print(classification_report(y_test, y_pred, target_names=['差评', '好评']))5. 避坑与排查:中文情感分析毕设里最容易翻车的 5 个地方
5.1 现象:模型准确率 95% 但一测真实评论全错
原因:数据泄漏。最常见的是在划分训练测试集之前就做了 TF-IDF,或者把测试集也拿去fit了。另一个隐蔽原因是数据集本身有重复评论,同一条评论既在训练集又在测试集。
解决:严格按「先划分、再 fit 训练集、transform 测试集」的顺序。划分前先drop_duplicates。如果准确率高得离谱(比如 98%),先怀疑数据泄漏,再怀疑标签泄漏(比如rating列和label完全对应,等于把答案喂给了模型)。
5.2 现象:jieba 分词后「不好」被拆成「不」「好」,情感判断反了
原因:jieba 默认词典里没有「不好」这个词,精确模式会按单字切。
解决:用jieba.add_word('不好')手动添加领域词,或者加载自定义词典jieba.load_userdict('userdict.txt')。把「不好」「不推荐」「退货」「差评」这类情感强相关的词加进去。这一步在毕设里叫「领域词典优化」,写进论文是个正经的创新点。
5.3 现象:训练时警告ConvergenceWarning: lbfgs failed to converge
原因:逻辑回归默认迭代次数不够,TF-IDF 矩阵维度高,梯度下降需要更多轮。
解决:把max_iter调到 1000 甚至 2000。如果还警告,考虑换solver='liblinear',它对小数据集更友好。这个警告不影响结果,但答辩时屏幕上飘红不好看,提前消掉。
5.4 现象:预测新评论时报错ValueError: X has 3 features, but TfidfVectorizer is expecting 5000
原因:预测时用了新的TfidfVectorizer去fit_transform单条评论,词表对不上。
解决:把训练好的tfidf对象用joblib.dump存下来,预测时load回来,只调transform不调fit。
import joblib # 保存模型和向量化器 joblib.dump(best_model, 'svm_model.pkl') joblib.dump(tfidf, 'tfidf.pkl') # 预测新评论 model = joblib.load('svm_model.pkl') tfidf = joblib.load('tfidf.pkl') new_text = segment(clean_text('质量太差了,用两天就坏')) pred = model.predict(tfidf.transform([new_text])) print('好评' if pred[0] == 1 else '差评')5.5 现象:换台电脑跑,ModuleNotFoundError: No module named 'jieba'
原因:环境没迁移。毕设答辩经常要换机器演示。
解决:用pip freeze > requirements.txt导出依赖,新机器pip install -r requirements.txt。更稳的做法是用 conda 建虚拟环境,把环境一起打包。Python 版本建议锁 3.8 到 3.10,太新的版本有些库还没适配。
6. 让毕设从「能跑」到「能拿高分」:可视化、调参与答辩话术
到这一步模型已经能跑了,但毕设评分不只看代码能不能运行,还看你会不会「讲故事」。我一般会加三样东西:词云图、混淆矩阵热力图、模型对比柱状图。词云用wordcloud库,把好评和差评分别画,好评里「质量」「物流」「喜欢」最大,差评里「退货」「垃圾」「失望」最大,这张图往论文里一放,评委一眼就懂。
from wordcloud import WordCloud import matplotlib.pyplot as plt # 差评词云 neg_text = ' '.join(df[df['label'] == 0]['seg_review']) wc = WordCloud(font_path='simhei.ttf', width=800, height=400, background_color='white').generate(neg_text) plt.imshow(wc) plt.axis('off') plt.savefig('negative_wordcloud.png', dpi=150)font_path必须指定中文字体,否则词云全是方块,这是新手最常踩的坑。Windows 用simhei.ttf,Mac 用/System/Library/Fonts/PingFang.ttc。
调参方面,如果时间充裕,用GridSearchCV对 SVM 的C和 TF-IDF 的max_features做网格搜索,把最优参数写进论文。但要注意,网格搜索很慢,cv=3就够,别设cv=10,几万条数据能跑一下午。
from sklearn.model_selection import GridSearchCV from sklearn.pipeline import Pipeline # 用 Pipeline 把向量化和模型串起来,避免数据泄漏 pipe = Pipeline([ ('tfidf', TfidfVectorizer(ngram_range=(1, 2))), ('svm', LinearSVC(max_iter=2000)) ]) params = { 'tfidf__max_features': [3000, 5000], 'svm__C': [0.5, 1.0, 2.0] } grid = GridSearchCV(pipe, params, cv=3, scoring='f1', n_jobs=-1) grid.fit(df['seg_review'], df['label']) print(grid.best_params_, grid.best_score_)用Pipeline的好处是它把向量化和训练绑在一起,交叉验证时每一折都会重新 fit 向量化器,彻底杜绝数据泄漏。scoring='f1'而不是默认的准确率,是因为我们更关心差评的识别效果。n_jobs=-1用满所有 CPU 核心加速。
答辩话术上,我踩过的坑是:评委问「你为什么不用 BERT」,如果你答「BERT 太复杂」,会显得偷懒。正确的答法是「本数据集规模在万级,经典机器学习在 TF-IDF 特征下已经能达到 88% 的 F1,BERT 需要 GPU 且训练成本高,在毕设的时间和数据条件下性价比不高,但我预留了接口,后续可以替换成预训练模型做对比」。这样既承认了边界,又展示了技术判断力。
最后一个习惯:所有随机过程都固定random_state,所有实验都记录参数和结果到 CSV,答辩前把最优模型和向量化器用joblib存好,现场演示直接load预测,不要现场重新训练。我见过太多人现场训练卡住、报错、超时,血泪经验就是——演示用的模型一定是提前跑好存下来的。希望帮到你。
本文还有配套的精品资源,点击获取