资讯动态

基于Python的微博情感分析:数据清洗、模型训练与部署

发布时间:2026/10/20 0:36:41 来源:尧图企业网站定制
简介一份基于自然语言处理的微博用户情感分析毕业设计围绕微博数据采集、文本清洗、中文分词、情感倾向判别与可视化看板展开适合计算机、通信、人工智能、自动化等专业学生作为毕设或课程设计参考。资源包共734个文件压缩后约85MB其中包含131个Python源码、109个pyc编译文件以及用Vue、HTML、CSS实现的前端展示页面还提供marshal格式的分词语料、情感标注数据与Scrapy爬虫配置目录按后端处理、前端界面和语料数据分层组织结构清晰。代码全部调试测试通过答辩评审分达98分并配有文档说明从环境搭建到结果分析均有交代可直接运行或二次开发如调整情感分类模型、扩展数据源等。目前已有244人学习下载适合希望快速搭建可运行的微博情感分析系统并理解NLP工程化流程的初学者和进阶开发者。1. 微博用户情感分析为什么模型跑得动、毕设毕不了提起基于 Python 自然语言处理的微博用户情感分析很多同学的第一反应是找个开源代码、配上公开数据集、把准确率跑到 90% 以上毕业设计就算交差了。但实际上这个题目最常见的翻车点不在模型而在数据——全网公开的微博语料混着表情、超话签到、转发口令和营销号文本情绪极性严重不平衡训练集上 95% 的准确率拿到新数据上一测直接掉到 70% 以下。更麻烦的是文档说明往往只写“加载数据、跑模型、出指标”丢了特征工程和阈值设定的依据答辩时老师一追问就露馅。这篇笔记按毕业设计交付物的顺序拆开讲从环境搭建、数据清洗、分词与向量化、模型选型到系统封装重点落在参数怎么设、指标怎么解释、错误样例怎么看以及最容易坑到你的那几个细节。内容面向两类人——需要从零搭出一套可演示系统的同学和已经写了部分代码但卡在效果调优上的熟手。目标是让你不只会跑通还能讲清楚每一步为什么这么做。2. 搭建情感分析环境与语料准备先把数据和依赖的坑填平2.1 运行时环境与依赖版本Python 3.8 到 3.10 是最省事的区间情感分析项目跑不起来八成不是算法问题而是依赖打架。文本处理链路涉及 Jieba 分词、Scikit-learn 特征提取、PyTorch 或 TensorFlow 深度学习框架再加上 Pandas、NumPy版本不匹配时最常见的问题就是 NumPy 1.24 之后移除了部分旧 API某个中间库直接 ImportError。我一般在项目根目录放一个requirements.txt按下面的组合锁定版本python3.9 pandas2.0.3 numpy1.24.4 scikit-learn1.3.2 jieba0.42.1 torch2.1.0 torchtext0.16.0说明一下Python 3.9 加 NumPy 1.24.4 是目前兼容面最宽的组合PyTorch 2.1.x 的预编译包对 3.8 到 3.10 都提供支持不会出现“装上了但 import 就崩”的情况。如果你用的是 Apple Silicon 的机器torch 要装对应芯片版本Windows 上则要留意 Microsoft Visual C Redistributable 是不是缺失缺了会在运行时报OSError: [WinError 126]。2.2 公开微博语料与标签分布先用带标注的数据集再决定要不要自采做毕业设计不一定要自己爬数据公开的微博情感标注语料足够支撑实验。常见做法是使用某公开的微博情感评测数据集一般在 10 万条量级标签分正面、负面两类少数版本带中性。拿到压缩包后第一件事不是解压看文本而是统计标签分布确认是不是二分类、类别是否均衡import pandas as pd df pd.read_csv(weibo_senti_100k.csv, encodingutf-8) print(df.shape) print(df[label].value_counts()) # 检查文本是否为空、是否重复 print(df[text].isnull().sum()) print(df[text].duplicated().sum())这段代码的作用是让你在动手建模前先看清语料的底细。df[label].value_counts()会输出正负样本的数量如果发现负样本明显多于正样本比如 6:4 甚至 7:3后面训练时就要考虑类别权重或欠采样。空值文本和完全重复的文本都建议直接剔除营销号在公开语料里经常几十条一模一样的转发内容不清掉会把模型带偏。3. 文本清洗与分词向量化决定情感分析效果的两个隐藏关节3.1 微博文本清洗规则不是单纯去停用词就完事微博文本跟新闻语料完全两回事。正文里夹杂着“#话题名#”、用户、URL、表情符号、“转发微博”等半结构化噪音。我做这个项目时总结了一套清洗顺序先去掉 URL 和 用户名再处理话题标签最后统一全半角符号。话题标签里的关键词对情感有作用比如 #今天好开心#我的做法是保留标签内的中文内容去掉两边的井号。import re def clean_weibo(text): text re.sub(rhttp\S|www\.\S, , text) # 去 URL text re.sub(r[\w\u4e00-\u9fa5\-], , text) # 去 用户名 text re.sub(r#(.?)#, r\1, text) # 保留话题词去井号 text re.sub(r\[.?\], , text) # 去表情描述 text re.sub(r\s, , text).strip() return text参数说明URL 和 用户名是纯噪音直接去掉不用犹豫表情描述形如 [嘻嘻]、[泪]在公开语料里是文本形式而非图片去掉是因为它们携带的情感极性权重太大会导致模型偷懒只认表情不认正文话题标签保留原文是为了不丢失“#疫情快点结束#”这类直接表达情绪的词。清洗完之后做一次人工抽检——随机打印 50 条清洗后的文本看有没有把“哈哈哈哈”拆散、有没有留下孤立的井号。这个步骤花不了十分钟却能避免后续特征工程全部建立在脏数据上。3.2 Jieba 分词与自定义词典网络新词是情感分析的第一个坑Jieba 默认词典对“绝绝子”“yyds”“破防”“躺平”这类微博高频词支持得并不好要么切碎成单字要么直接跳过。而“yyds”在语境里几乎总是正面“破防”要区分感动和愤怒不分词直接进 TF-IDF 是提不出这些特征的。我一般会在项目下建一个user_dict.txt每行一个词可以附带词频和词性比如yyds 100 nz 绝绝子 100 nz 破防 50 v 躺平 50 v然后这样加载import jieba jieba.load_userdict(user_dict.txt) def tokenize(text): return list(jieba.cut(text, cut_allFalse))注意cut_allFalse是精确模式也是文本分类任务的默认选择。全模式会把句子切出所有可能的词组合特征噪音太大搜索引擎模式同理。加载自定义词典的时机必须在第一次jieba.cut()之前否则分词结果里不会包含新词——这是 Jieba 的初始化机制决定的。3.3 TF-IDF 或 Word2Vec先上 TF-IDF 打底再决定要不要引入词向量文本向量化的路线有两条一条是统计派 TF-IDF另一条是分布派 Word2Vec / 预训练向量。作为毕业设计我建议两条都做但基线必须用 TF-IDF——它解释性强、训练快、答辩时好讲。核心参数如下from sklearn.feature_extraction.text import TfidfVectorizer vectorizer TfidfVectorizer( tokenizertokenize, ngram_range(1, 2), min_df5, max_features30000, sublinear_tfTrue ) X vectorizer.fit_transform(df[clean_text])参数说明ngram_range(1, 2)的意思是特征取单个词和相邻两个词的组合“非常 满意”会被拆成“非常”“满意”和“非常 满意”三个特征后者能抓住否定词的局部语义min_df5忽略在少于 5 条文档中出现的词去掉低频噪音max_features30000限制特征维度防止矩阵过大sublinear_tfTrue用 1log(tf) 替代原始词频防止“哈哈哈”这种高频符号词碾压其他特征。我踩过的坑是tokenizertokenize传进来的是 Python 函数对象不能写成tokenizertokenize()否则会在每一条文本上重复分词导致内存溢出。Word2Vec 路线适合作为对比实验用 Gensim 在全部训练语料上自训练 128 维向量然后对每条文本的词向量取平均作为句子向量。注意自训练要在清洗分词之后做语料量低于 5 万条时效果不稳定此时可以退回到 TF-IDF 基线。4. 模型训练与评估从朴素贝叶斯到 TextRNN 的进阶路线4.1 三套模型的适用边界选型依据比代码本身更重要微博情感分类任务毕业设计里做得最多的是三套模型朴素贝叶斯、逻辑回归、TextRNNBiLSTM。朴素贝叶斯的强项是训练极快、适合高维稀疏特征但对特征独立性假设太强微博里“虽然……但是……”这种转折句经常被它误判逻辑回归本质是线性分类器配合 TF-IDF 是情感分析最稳的基线一般能到 85% 左右TextRNN 能捕捉词序信息对“这部电影不错但太长了不算好看”这类转折句更友好但训练慢、需要调超参数语料小的容易过拟合。选择建议是这样的如果你只有 5 万条以内数据逻辑回归是性价比最高的如果数据量超过 10 万条且你能拿到 GPU再上 BiLSTM。不要一上来就套 BERT——微博文本短、口语化重BERT 类模型的收益会被训练成本和时间成本吃掉而且答辩时难以解释清楚。4.2 训练代码与超参数类别权重和早停是提升效果的两张牌逻辑回归和朴素贝叶斯直接走 Scikit-learn 接口重点在类别不平衡的处理和阈值的选定from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report train_texts, test_texts, train_labels, test_labels train_test_split( df[clean_text], df[label], test_size0.2, random_state42, stratifydf[label] ) X_train vectorizer.fit_transform(train_texts) X_test vectorizer.transform(test_texts) model LogisticRegression( C1.0, class_weightbalanced, max_iter1000, solverliblinear ) model.fit(X_train, train_labels) pred model.predict(X_test) print(classification_report(test_labels, pred, target_names[负面, 正面]))参数说明class_weightbalanced会根据类别频率自动调整权重当你发现语料里负样本是正样本两倍时不加这一条模型会全面倾向于预测负样本C1.0是正则化强度的倒数值越小正则化越强可以先用默认值如果测试集 F1 明显低于训练集再把 C 调到 0.5 试试solverliblinear适合小数据集和二分类问题处理 TF-IDF 这种高维稀疏特征最快。注意vectorizer.fit_transform(train_texts)和vectorizer.transform(test_texts)的区别——测试集只 transform 不能 fit_transform否则会把测试集的词表混进特征空间。分类报告里重点看正类的 recall 和 F1不是整体 accuracy。微博情感分析里负面误判成正面比正面误判成负面更伤所以答辩时强调 F1 曲线而不是单个数字。BiLSTM 模型用 PyTorch 实现多跑一个 epoch 看验证损失。以下是一个 TextRNN 网络的定义与训练主体import torch import torch.nn as nn class BiLSTMClassifier(nn.Module): def __init__(self, vocab_size, embed_dim, hidden_dim, num_layers, num_classes): super().__init__() self.embedding nn.Embedding(vocab_size, embed_dim, padding_idx0) self.lstm nn.LSTM(embed_dim, hidden_dim, num_layers, batch_firstTrue, bidirectionalTrue) self.fc nn.Linear(hidden_dim * 2, num_classes) def forward(self, x): emb self.embedding(x) out, _ self.lstm(emb) out out[:, -1, :] return self.fc(out)这里的网络结构不复杂但有两个参数最容易翻车。padding_idx0必须和你在构造词表时设定的 padding 下标一致否则模型会学到无意义的 padding 向量bidirectionalTrue时 LSTM 输出维度是hidden_dim * 2这是新手最常写错的地方——FC 层输入维度写hidden_dim跑起来必崩。训练时要加早停Early Stopping监控验证集损失连续 3 个 epoch 不下降就停同时给损失函数配置类别权重class_weights torch.tensor([1.0, 2.0]).to(device) loss_fn nn.CrossEntropyLoss(weightclass_weights)类别权重的数值来自训练集正负样本的比例如果负样本是正样本的两倍权重就设 [1.0, 2.0]。4.3 评估指标与混淆矩阵用一张矩阵说服答辩老师准确率在类别不平衡面前就是块遮羞布而混淆矩阵是解释模型缺陷最直观的素材。把测试集预测结果画成矩阵你能立刻看出模型把哪些负面样本错分到正面。典型的情况是“生气、无语”这种表达强烈的词识别没问题但“服了”“可以”这类反讽词全被分到正面。from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay cm confusion_matrix(test_labels, pred) disp ConfusionMatrixDisplay(cm, display_labels[负面, 正面]) disp.plot()画完矩阵后单独抽 20 条 FP 和 20 条 FN 的原文打印出来逐条分析误判原因这部分内容写进文档说明里能让整个毕业论文的“问题分析”章节饱满很多。5. 系统封装与避坑排查从训练脚本到可演示系统5.1 基于 Flask 的最小预测接口模型持久化与在线分词毕业设计最终要有一个能演示的东西常见做法是用 Flask 包装一个 HTTP 接口用户输入文本返回情感类别和置信度。模型训练完先持久化再写加载逻辑import joblib joblib.dump(model, model/logistic.pkl) joblib.dump(vectorizer, model/vectorizer.pkl)加载端注意两点。第一TfidfVectorizer持久化后再加载预测阶段必须使用transform()而不是fit_transform()否则词表不一致预测结果完全没有意义。第二Jieba 的自定义词典路径要写绝对路径或相对项目的固定路径因为 Web 服务的工作目录和训练脚本所在目录经常不一致导致运行时词典加载失败、分词结果和训练时完全对不上。Flask 接口代码骨架如下from flask import Flask, request, jsonify app Flask(__name__) model joblib.load(model/logistic.pkl) vectorizer joblib.load(model/vectorizer.pkl) app.route(/predict, methods[POST]) def predict(): data request.get_json() text data.get(text, ) cleaned clean_weibo(text) tokens tokenize(cleaned) vec vectorizer.transform([ .join(tokens)]) prob model.predict_proba(vec)[0] pred int(model.predict(vec)[0]) return jsonify({sentiment: pred, confidence: round(float(prob[pred]), 4)}) if __name__ __main__: app.run(host0.0.0.0, port5000)注意代码里先tokenize再 .join(tokens)是必须的因为TfidfVectorizer在训练时接收的是分词后以空格拼接的字符串。如果你直接传原始文本特征空间完全对不上。5.2 部署后响应慢与显存不足问题先看分词再查模型接口响应超过 3 秒通常不是模型推理慢而是 Jieba 首次加载词典和初始化前缀字典耗时。解决方式很直接在服务启动时预热一次调用jieba.initialize()强制加载。BiLSTM 模型在 CPU 上推理一条文本大约 100 毫秒但如果把 batch size 设得过大内存不足的问题就会在服务端暴露——部署时把batch_size1写死在推理函数里不要用训练时的设置。5.3 常见问题排查5 条血泪经验现象一验证集准确率很高但新数据预测结果一塌糊涂。 原因TfidfVectorizer在训练和测试时分别fit_transform()了特征词表不一致导致训练和预测根本不在同一个向量空间。 解决测试集只transform()这个错误在代码里几乎看不出来只能从流程上杜绝。现象二train_test_split报错Found input variables with inconsistent numbers of samples。 原因df[clean_text]里存在空值清洗后变成了空字符串数组和标签长度对不上或者之前删行之后 index 没重置。 解决训练前执行df df.dropna().reset_index(dropTrue)再取特征和标签。现象三Jieba 分词“yyds”被切成“y”“yd”“ds”。 原因自定义词典文件编码不是 UTF-8或者词频数字格式不对。 解决用 UTF-8 无 BOM 保存词典文件词和词频之间用空格分隔不要用 tab。现象四BiLSTM 训练 loss 不下降一直在 0.69 附近。 原因类别不平衡导致模型从一开始就倾向于预测多数类学习率设置不合理。 解决配置损失函数类别权重把学习率从 1e-3 调到 1e-4 再试。现象五模型把所有含“哈哈”的文本判为正面。 原因“哈哈”在正样本中出现频率过高模型把它当成强正面信号忽略了上下文里的负面词。 解决把“哈哈”以及“哈哈哈”加入停用词列表或者扩展 ngram_range 到 (1, 3)让模型能同时看到“哈哈 气死”这种组合。6. 进阶验证与落地技巧从单条预测到可解释的情感分析系统第 5 章的接口能演示但要让毕业设计上一个台阶还得给系统加一层“可解释性”。我用的是 LIME 局部解释它能在一条预测样本上给出哪些词把模型推向了正面、哪些推向负面并把结果渲染成一个概率贡献条形图。这是答辩现场最有说服力的功能模块之一。import lime import lime.lime_text explainer lime.lime_text.LimeTextExplainer( class_names[负面, 正面], splittertokenize ) def predict_proba(texts): vec vectorizer.transform(texts) return model.predict_proba(vec) exp explainer.explain_instance( 这家店服务太差了等了一个小时再也不来, predict_proba, num_features6 ) exp.show_in_notebook()参数说明splittertokenize必须和训练时用同一个分词函数否则 LIME 随机扰动生成的文本切片跟模型看到的文本对不上解释结果全是错的num_features6控制在图表里展示的特征数太少了看不出规律太多了图很乱。如果模型是 BiLSTMpredict_proba函数体里要加 batch 的维度变换并且把torch.no_grad()包在外面。除了 LIME误差分析脚本也是值得写进文档说明的模块。把预测错误的样本按“负面预测成正面”和“正面预测成负面”分组统计各自的高频词。你会发现“无语”“离谱”“呵呵”常被误判为正面这时把这三类词加进自定义情感词典给它们预设负向权重能立竿见影地改善错分。注意加词后必须重新分词、重新训练模型只改预测端的词表没有用。说一个我自己的教训第一次做这个题目时我把大量时间耗在调 BiLSTM 的 hidden size 上结果发现效果和逻辑回归差不多而文档里记不清每次实验用的是什么超参数整个复现过程乱成一团。后来养成了一个习惯——任何一次实验都先把random_state、ngram_range、C值、类别权重固定下来写进一个 JSON 配置模型文件名带上时间戳。这个习惯让后面的改版和论文复现省了非常多时间。微博情感分析这个方向真正拉开差距的地方从来不是模型架构而是对文本噪音的清洗深度、对类别不平衡的处理方式和对误判样本的归因分析。希望你做完之后不只是拿到一个跑得动的代码包还能对着任何一个预测结果说出“它为什么这样判”。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑