Python微博情感分析系统毕业设计:从数据清洗到BERT微调与Flask部署

发布时间:2026/10/12 14:24:57
Python微博情感分析系统毕业设计:从数据清洗到BERT微调与Flask部署 简介这份资源是面向计算机、人工智能、通信等专业学生与教师的Python毕业设计项目主题为基于自然语言处理的微博用户情感分析系统适合作为毕业设计、课程大作业或期末课设的参考方案也便于基础较好的学习者在此基础上二次开发。压缩包共734个文件约85MB涵盖131个Python源码文件、166个JavaScript脚本、86个HTML页面、35个CSS样式表以及Vue组件、模型文件、数据库与多语言资源等前后端结构完整便于按模块阅读与调试。项目已通过调试测试答辩评审分达98分代码质量与可运行性有保障。目前已有244人学习下载。读者可获得完整的情感分析系统实现思路、数据预处理与模型调用逻辑、前端展示页面及配套文档说明既能用于快速搭建毕设原型也能帮助理解自然语言处理在微博文本场景中的落地方式具备较高的学习借鉴与改造价值。1. 微博情感分析系统从一条吐槽到一份可交付的毕业设计刷微博时看到一条“这破快递三天没动客服还装死”你大概能立刻判断这是负面情绪。但要让程序自动判断并且把几十万条微博按正负中性分好类这就是自然语言处理里的情感分析任务。毕业设计选这个题好处是数据公开、场景直观、技术栈成熟坏处是很容易做成“调个库跑个demo”然后被答辩老师问穿。这篇笔记面向正在做 python 基于自然语言处理的微博用户情感分析系统源码加文档说明的同学把从环境搭建、数据清洗、模型选型到文档撰写和答辩防守的完整路径拆开讲。读完你应该能自己搭出一套能跑通、能解释、能写进论文的系统而不是只会复制一份看不懂的源码。2. 微博语料怎么来、怎么洗数据管道决定模型上限2.1 微博文本的三个脏点与清洗策略微博语料和标准影评数据集不一样它有三个绕不开的脏点。第一是噪声符号话题标签#话题#、提及、短链接、表情符号混在一起直接喂给模型会引入大量无意义token。第二是口语化表达“绝绝子”“yyds”“栓Q”这类网络用语在通用词典里查不到分词器容易切碎。第三是长度极短大量微博在10到30字之间上下文信息少模型很难靠单条文本判断情感。清洗策略我一般分四步走。先做正则替换把URL、用户、话题符号统一替换成占位符而不是直接删掉因为“某人”本身可能携带情绪指向。再做表情符号映射把常见emoji转成中文情感词比如“”映射为“大哭”“”映射为“赞同”这一步对短文本的情感信号补充非常明显。然后处理重复字符“哈哈哈哈”压缩成“哈哈”“太好好好了”压缩成“太好”避免分词后产生大量重复token。最后做停用词过滤但要注意情感词不能进停用词表像“不”“没”“别”这些否定词一旦被过滤情感极性直接反转。import re def clean_weibo_text(text): # 统一转为字符串处理NaN text str(text) # URL替换为占位符保留存在链接这一信息 text re.sub(rhttp[s]?://\S, [链接] , text) # 用户替换为占位符 text re.sub(r[\w\u4e00-\u9fa5], [提及] , text) # 话题标签去掉#但保留内容 text re.sub(r#(.?)#, r\1, text) # 连续重复字符压缩超过2次的压缩为2次 text re.sub(r(.)\1{2,}, r\1\1, text) # 去除多余空白 text re.sub(r\s, , text).strip() return text这段代码的关键在于“替换而非删除”。URL和提及被替换成占位符后分词器会把它们当作独立token处理模型可以学到“带链接的微博情感分布是否不同”这类特征。重复字符压缩的正则(.)\1{2,}表示同一字符连续出现3次及以上时压缩为2次保留一定的强调语气又不至于产生噪声。停用词表建议用哈工大停用词表做基础然后手动把“不”“没”“无”“非”等否定词从表中移除。2.2 标注方案三分类还是五分类人工标多少条够用毕业设计里最常见的标注方案是三分类正面、负面、中性。五分类会加入“偏正面”“偏负面”但标注一致性会明显下降两个标注者很容易在“偏正面”和“正面”之间产生分歧。我的建议是本科毕设做三分类就够了标注一致性Kappa系数能到0.75以上就算合格。人工标注量的问题很多同学上来就想标一万条结果标到两千条就崩溃了。实际经验是如果只做课程设计级别的系统3000条标注数据足够训练出一个能看的模型。分配上正面1200条、负面1200条、中性600条中性少一些是因为中性样本本身区分度低标多了对模型提升有限。标注时用Excel或CSV两列text和labellabel用0/1/2表示负面/中性/正面。标完随机抽10%做交叉验证如果两个标注者一致率低于80%说明标注规范需要重新对齐。提示标注规范要写成文档明确“反讽算负面”“疑问句看上下文”“纯事实陈述算中性”否则后期返工成本极高。2.3 用pandas做数据划分与类别平衡标注完的数据不能直接扔进模型要先做划分和平衡。微博情感数据天然不均衡负面和正面多、中性少直接训练会导致模型偏向多数类。常见做法是先用train_test_split按8:2划分训练集和测试集再对训练集做随机过采样或SMOTE。文本数据用SMOTE效果一般因为它是基于数值特征插值的对高维稀疏的文本特征不太适用。更稳妥的做法是随机过采样少数类或者用class_weight参数让模型自动加权。import pandas as pd from sklearn.model_selection import train_test_split # 读取标注数据 df pd.read_csv(weibo_labeled.csv, encodingutf-8) # 清洗 df[clean_text] df[text].apply(clean_weibo_text) # 去除清洗后为空的样本 df df[df[clean_text].str.len() 0] # 分层划分保证训练集和测试集类别比例一致 train_df, test_df train_test_split( df, test_size0.2, random_state42, stratifydf[label] ) # 对训练集做随机过采样让三个类别数量一致 max_count train_df[label].value_counts().max() balanced_parts [] for label in train_df[label].unique(): part train_df[train_df[label] label] # 有放回抽样到最大类别数量 part_oversampled part.sample(max_count, replaceTrue, random_state42) balanced_parts.append(part_oversampled) train_balanced pd.concat(balanced_parts).sample(frac1, random_state42) print(f训练集原始分布:\n{train_df[label].value_counts()}) print(f过采样后分布:\n{train_balanced[label].value_counts()})stratifydf[label]这个参数很关键它保证划分后训练集和测试集的类别比例与原始数据一致避免测试集里某个类别一条都没有。过采样用sample(replaceTrue)有放回抽样虽然会引入重复样本但在数据量不大时比SMOTE更稳定。random_state42固定随机种子保证每次运行结果可复现写论文时这一点会被问到。3. 模型选型从TF-IDF加朴素贝叶斯到BERT微调3.1 传统方案TF-IDF加朴素贝叶斯的基线怎么搭传统方案不是用来凑数的它是你论文里的对照组也是答辩时证明“为什么选深度学习”的依据。TF-IDF加朴素贝叶斯在微博三分类上通常能到75%到82%的准确率训练时间不到一分钟非常适合快速验证数据质量。from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.pipeline import Pipeline from sklearn.metrics import classification_report import jieba # 自定义分词器用jieba做中文分词 def jieba_tokenizer(text): return jieba.lcut(text) # 构建pipeline pipeline Pipeline([ (tfidf, TfidfVectorizer( tokenizerjieba_tokenizer, max_features5000, # 保留词频最高的5000个词 ngram_range(1, 2), # 同时考虑单字和双字组合 min_df3, # 至少在3个文档中出现才保留 max_df0.9 # 出现在90%以上文档中的词丢弃 )), (clf, MultinomialNB(alpha0.1)) # 拉普拉斯平滑系数 ]) pipeline.fit(train_balanced[clean_text], train_balanced[label]) pred pipeline.predict(test_df[clean_text]) print(classification_report(test_df[label], pred, target_names[负面,中性,正面]))max_features5000控制特征维度微博短文本用5000已经足够再大容易过拟合。ngram_range(1,2)让模型能捕捉“不 开心”这种二元组合对否定句识别有帮助。min_df3过滤掉只出现一两次的生僻词max_df0.9过滤掉“的”“了”这类几乎每篇都有的词。朴素贝叶斯的alpha0.1是平滑系数值越小越依赖训练数据微博数据噪声大时适当调大可以提升泛化。3.2 BERT微调用预训练模型把准确率拉到90%以上如果答辩要求有深度学习成分BERT微调是当前最稳的选择。用bert-base-chinese或hfl/chinese-roberta-wwm-ext做三分类微调3000条数据训练3到5个epoch准确率通常能到88%到93%。关键是学习率要小2e-5到5e-5之间大了会灾难性遗忘预训练知识。import torch from torch.utils.data import Dataset, DataLoader from transformers import BertTokenizer, BertForSequenceClassification, AdamW class WeiboDataset(Dataset): def __init__(self, texts, labels, tokenizer, max_len128): self.texts texts self.labels labels self.tokenizer tokenizer self.max_len max_len def __len__(self): return len(self.texts) def __getitem__(self, idx): encoding self.tokenizer( self.texts[idx], max_lengthself.max_len, paddingmax_length, truncationTrue, return_tensorspt ) return { input_ids: encoding[input_ids].squeeze(), attention_mask: encoding[attention_mask].squeeze(), label: torch.tensor(self.labels[idx], dtypetorch.long) } # 初始化 tokenizer BertTokenizer.from_pretrained(bert-base-chinese) model BertForSequenceClassification.from_pretrained(bert-base-chinese, num_labels3) device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device) # 优化器学习率设小 optimizer AdamW(model.parameters(), lr3e-5, weight_decay0.01) # 训练循环 train_dataset WeiboDataset(train_balanced[clean_text].tolist(), train_balanced[label].tolist(), tokenizer) train_loader DataLoader(train_dataset, batch_size16, shuffleTrue) model.train() for epoch in range(3): total_loss 0 for batch in train_loader: input_ids batch[input_ids].to(device) attention_mask batch[attention_mask].to(device) labels batch[label].to(device) optimizer.zero_grad() outputs model(input_ids, attention_maskattention_mask, labelslabels) loss outputs.loss loss.backward() optimizer.step() total_loss loss.item() print(fEpoch {epoch1}, Loss: {total_loss/len(train_loader):.4f})max_len128对微博足够超过128的微博占比不到5%截断损失可以接受。batch_size16是显存和训练稳定性的折中显存够可以调到32。weight_decay0.01是BERT微调的常规正则化设置。训练3个epoch通常就够了再多容易过拟合判断依据是验证集loss开始上升。如果显存不够可以把max_len降到64batch_size降到8但准确率可能掉1到2个点。3.3 传统方案和BERT的对比论文里怎么写选型理由论文里不能只写“BERT效果好所以选BERT”要给出对比数据。建议在测试集上跑三组实验TF-IDF加朴素贝叶斯、TF-IDF加SVM、BERT微调。对比维度包括准确率、F1值、训练时间、推理速度。通常BERT准确率高8到12个点但训练时间是传统方案的几十倍推理速度也慢很多。如果你的系统要求实时分析大量微博传统方案反而更合适。选型理由要结合应用场景写比如“本系统面向离线批量分析对推理速度要求不高优先保证准确率因此选择BERT”。4. 系统落地从模型文件到可交互的情感分析服务4.1 用Flask搭一个最小可用的分析接口毕业设计通常要求有可视化界面或API接口。Flask足够轻量半天能搭完。核心逻辑是加载训练好的模型接收前端传来的微博文本返回情感标签和置信度。from flask import Flask, request, jsonify import torch from transformers import BertTokenizer, BertForSequenceClassification app Flask(__name__) # 启动时加载模型避免每次请求都加载 tokenizer BertTokenizer.from_pretrained(./saved_model) model BertForSequenceClassification.from_pretrained(./saved_model, num_labels3) model.eval() device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device) label_map {0: 负面, 1: 中性, 2: 正面} app.route(/predict, methods[POST]) def predict(): data request.get_json() text data.get(text, ) if not text: return jsonify({error: 文本为空}), 400 # 清洗 text clean_weibo_text(text) # 编码 encoding tokenizer(text, max_length128, paddingmax_length, truncationTrue, return_tensorspt) input_ids encoding[input_ids].to(device) attention_mask encoding[attention_mask].to(device) # 推理 with torch.no_grad(): outputs model(input_ids, attention_maskattention_mask) probs torch.softmax(outputs.logits, dim1) pred torch.argmax(probs, dim1).item() confidence probs[0][pred].item() return jsonify({ text: text, label: label_map[pred], confidence: round(confidence, 4) }) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)模型在启动时加载一次不要放在predict函数里否则每次请求都重新加载模型响应时间会从几十毫秒变成几秒。torch.no_grad()关闭梯度计算推理时能省显存并提速。debugFalse在生产环境必须关掉否则有安全风险。返回的置信度用round保留四位小数前端展示更友好。4.2 批量分析微博数据集的脚本怎么写除了单条分析毕业设计通常还要展示“对一批微博数据的情感分布统计”。写一个批量处理脚本读CSV、逐条推理、输出统计结果。import pandas as pd from tqdm import tqdm def batch_analyze(input_csv, output_csv): df pd.read_csv(input_csv, encodingutf-8) results [] for text in tqdm(df[text].tolist(), desc分析中): cleaned clean_weibo_text(text) encoding tokenizer(cleaned, max_length128, paddingmax_length, truncationTrue, return_tensorspt) input_ids encoding[input_ids].to(device) attention_mask encoding[attention_mask].to(device) with torch.no_grad(): outputs model(input_ids, attention_maskattention_mask) probs torch.softmax(outputs.logits, dim1) pred torch.argmax(probs, dim1).item() confidence probs[0][pred].item() results.append({ text: text, label: label_map[pred], confidence: round(confidence, 4) }) result_df pd.DataFrame(results) result_df.to_csv(output_csv, indexFalse, encodingutf-8-sig) # 输出分布统计 print(result_df[label].value_counts()) return result_dftqdm显示进度条批量处理几千条时能直观看到进度。输出用utf-8-sig编码Excel打开不乱码。batch_size这里没设逐条推理虽然慢但代码简单如果数据量大可以改成批量推理一次传16条速度能快5到8倍。4.3 可视化情感分布饼图和词云怎么做论文和答辩PPT里需要可视化。饼图用matplotlib词云用wordcloud。注意词云要分情感类别做正面词云和负面词云对比展示比一张混合词云有说服力。import matplotlib.pyplot as plt from wordcloud import WordCloud # 设置中文字体否则词云显示方块 plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False # 情感分布饼图 label_counts result_df[label].value_counts() plt.figure(figsize(6, 6)) plt.pie(label_counts.values, labelslabel_counts.index, autopct%1.1f%%) plt.title(微博情感分布) plt.savefig(sentiment_pie.png, dpi150, bbox_inchestight) # 负面微博词云 negative_texts .join(result_df[result_df[label] 负面][text].tolist()) wc WordCloud(font_pathsimhei.ttf, width800, height400, background_colorwhite, max_words100) wc.generate(negative_texts) wc.to_file(negative_wordcloud.png)font_path必须指定中文字体文件路径否则词云全是方块。max_words100控制词云显示100个词太多会挤在一起看不清。饼图的autopct%1.1f%%显示百分比保留一位小数。5. 避坑与排查毕业设计里最容易翻车的五个地方5.1 分词后准确率反而下降现象用jieba分词后训练准确率比不分词还低。原因微博里的网络新词被jieba切碎“绝绝子”被切成“绝绝”“子”语义完全丢失。解决加载自定义词典把微博高频新词加进去或者直接用字符级特征跳过分词这一步。BERT本身是基于字的不需要分词这也是它效果好的原因之一。5.2 模型在测试集上准确率95%实际用起来一塌糊涂现象测试集F1很高但拿几条新微博测试结果离谱。原因训练集和测试集来自同一批标注数据分布一致但实际微博的用词和标注数据差异大属于分布外样本。解决留出一部分标注数据不参与训练作为“真实测试集”或者从微博上重新爬一批新数据做验证。论文里要诚实报告这个差距答辩老师反而会觉得你严谨。5.3 显存不够BERT跑不起来现象报错CUDA out of memory。原因max_len设太大或batch_size设太大。解决先把max_len从128降到64再把batch_size从16降到8还不行就降到4。如果显卡只有4G显存可以用gradient_accumulation_steps模拟大batch或者改用ALBERT这类轻量模型。CPU训练虽然慢但3000条数据跑3个epoch也就一两个小时能接受。5.4 保存的模型加载后预测结果全一样现象训练时loss正常下降保存后重新加载所有输入都预测成同一类。原因保存模型时只保存了state_dict加载时没有重新创建模型结构或者num_labels对不上。解决用model.save_pretrained(./saved_model)和tokenizer.save_pretrained(./saved_model)保存完整模型和分词器加载时用from_pretrained并指定num_labels3。不要用torch.save(model, path)这种整模型保存方式版本不兼容时容易出问题。5.5 文档说明写成代码注释的堆砌现象文档说明里全是函数说明没有系统架构、没有数据流程、没有实验结果。原因把文档当成了代码说明书。解决文档说明按“系统概述→数据采集与标注→模型选型与训练→系统实现→实验结果与分析→部署说明”的结构写。每个部分配一张图数据流程图、模型结构图、系统架构图。实验结果要有表格对比不同模型的准确率、F1、训练时间。答辩老师看文档的时间比看代码多文档结构清晰能省很多解释成本。6. 让系统经得起追问验证方法与一个提分技巧6.1 交叉验证和混淆矩阵证明模型不是碰运气单次划分测试集的结果可能受随机性影响用5折交叉验证能给出更可靠的评估。把数据分成5份每次用4份训练1份验证跑5次取平均。代码上把train_test_split换成StratifiedKFold就行。混淆矩阵能看出模型在哪些类别上容易混淆微博情感分析里最常见的混淆是“中性”被误判为“正面”或“负面”因为中性文本本身情感信号弱。论文里放一张混淆矩阵热力图比只写准确率有说服力得多。from sklearn.model_selection import StratifiedKFold from sklearn.metrics import confusion_matrix import seaborn as sns skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) all_preds [] all_labels [] for train_idx, val_idx in skf.split(df[clean_text], df[label]): train_fold df.iloc[train_idx] val_fold df.iloc[val_idx] # 这里用传统方案做交叉验证BERT太慢 pipeline.fit(train_fold[clean_text], train_fold[label]) preds pipeline.predict(val_fold[clean_text]) all_preds.extend(preds) all_labels.extend(val_fold[label].tolist()) # 混淆矩阵 cm confusion_matrix(all_labels, all_preds) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabels[负面,中性,正面], yticklabels[负面,中性,正面]) plt.xlabel(预测) plt.ylabel(真实) plt.title(5折交叉验证混淆矩阵) plt.savefig(confusion_matrix.png, dpi150, bbox_inchestight)交叉验证用传统方案跑BERT跑5折时间太长论文里可以只对BERT做单次划分评估但要在文档里说明原因。混淆矩阵的annotTrue显示具体数值fmtd表示整数格式。6.2 一个提分技巧错误分析比调参更有效模型准确率卡在88%上不去的时候不要盲目调学习率或加数据。把验证集里预测错误的样本导出来逐条看。通常会发现几类典型错误反讽句被误判、否定句被误判、带表情符号的短文本被误判。针对反讽句可以加规则后处理检测到“呵呵”“真棒”“太好了”等词且上下文有负面词时强制修正为负面。针对否定句检查停用词表里是否误删了否定词。针对表情符号确认emoji映射表是否覆盖了常见表情。我做过一次错误分析发现30%的错误来自反讽加了一条简单规则后准确率直接涨了3个点。这个技巧写进论文的“优化与分析”章节比堆模型结构更有说服力。做毕业设计最深的体会是不要等到代码全写完才开始写文档边做边记把每次实验的参数和结果都存下来。我见过太多同学最后一周通宵补文档结果实验数据对不上被答辩老师问得下不来台。另外源码要能跑通文档要能自洽这两件事比模型准确率高两个点重要得多。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询