简介:这份资源是面向计算机相关专业学生与项目实战学习者的Python人工智能大作业完整资料包,聚焦网络舆情分析系统的设计与实现,适合正在准备期末大作业、毕业设计或需要积累AI项目经验的人群。压缩包共118个文件,约45.2MB,以27个py源码文件为核心,辅以36个txt说明、12个jar与10个class等Java组件、7个java与6个xml配置,以及ipynb实验笔记、json数据、xlsx表格和少量md文档,覆盖从数据采集、中文NLP处理到可视化展示的完整链路。内容预览中可见AipNLP、BarChart、PieChart、InitialUIDesign等类,说明系统包含情感倾向分析与图表界面模块。该资源经导师指导并获评审98分,源码均本地编译调试可运行,已有144人学习。读者可获得可复用的舆情分析代码框架、界面与算法实现思路及配套资料,便于快速搭建与二次开发。
1. 舆情分析大作业为什么总在“情感分类”这一步翻车
做课程大作业时,很多同学把网络舆情分析系统理解成“爬虫 + 词云 + 情感饼图”三件套,结果代码跑通了,报告却拿不到高分。问题不在工具,而在链路:原始文本没清洗、情感标签靠拍脑袋、时间序列只画一条折线,最后系统只能演示,不能解释。基于 Python 的人工智能大作业网络舆情分析系统,真正要解决的是从非结构化文本里提取可验证的舆情信号,并让每一步都有参数可调、有指标可看。它适合正在赶大作业的本科生、需要快速搭原型的开发者,以及想用一套完整源码理解 NLP 落地流程的从业者。下面按“数据进来、模型跑通、结果可信”的顺序拆开讲,中间会给出可直接抄的代码和参数表。
2. 先定数据管道:从采集到存储的四个关键字段
2.1 为什么不能直接拿原始评论喂模型
舆情系统的输入通常来自公开讨论区、新闻评论或模拟生成的 JSON 文件。原始文本里混着表情符号、URL、重复刷屏和广告,直接送进模型会让准确率掉 10 到 20 个百分点。常见做法是先把每条记录压成四个字段:doc_id、publish_time、raw_text、source。doc_id用哈希或自增整数都行,但必须唯一;publish_time统一转成YYYY-MM-DD HH:MM:SS,后面做时间窗口聚合全靠它;raw_text保留原文,方便回溯;source标记渠道,用于分组对比。清洗阶段只做三件事:去掉 URL 和 @ 提及、把连续空格压成一个、过滤长度小于 5 的短文本。不要在这一步做分词或去停用词,因为后续可能换模型,保留原始字符更安全。
import re import pandas as pd def clean_text(text: str) -> str: # 去掉 URL 和 @ 提及,保留中文、英文、数字和基本标点 text = re.sub(r'https?://\S+|www\.\S+', '', text) text = re.sub(r'@\w+', '', text) text = re.sub(r'\s+', ' ', text).strip() return text def build_pipeline(raw_df: pd.DataFrame) -> pd.DataFrame: df = raw_df.copy() df['clean_text'] = df['raw_text'].astype(str).apply(clean_text) df['publish_time'] = pd.to_datetime(df['publish_time'], errors='coerce') # 丢弃时间解析失败和过短文本 df = df.dropna(subset=['publish_time']) df = df[df['clean_text'].str.len() >= 5] df['date'] = df['publish_time'].dt.date return df[['doc_id', 'publish_time', 'date', 'clean_text', 'source']]这段代码里errors='coerce'会把无法解析的时间变成NaT,避免后续聚合报错;长度阈值 5 是经验值,低于 5 个字符的文本通常是“顶”“赞”这类无信息内容。date字段单独抽出来,是为了按天做舆情热度曲线,不用每次重算。
2.2 存储选型:SQLite 够用,别急着上 MongoDB
大作业的数据量一般在几千到几万条,SQLite 完全能扛住,而且单文件方便打包进源码。建表时把doc_id设为主键,publish_time和source建索引,查询速度足够。如果非要用 MongoDB,注意默认端口和认证配置,否则演示时连不上会非常尴尬。下面是一个最小建表语句:
CREATE TABLE IF NOT EXISTS posts ( doc_id TEXT PRIMARY KEY, publish_time TEXT NOT NULL, clean_text TEXT NOT NULL, source TEXT, sentiment_label INTEGER, sentiment_score REAL ); CREATE INDEX IF NOT EXISTS idx_publish_time ON posts(publish_time); CREATE INDEX IF NOT EXISTS idx_source ON posts(source);sentiment_label和sentiment_score先留空,等模型跑完再回填。这样数据管道和模型解耦,换模型不用重建表。
2.3 参数怎么设:清洗阈值与时间窗口
清洗阶段有两个参数容易拍脑袋:最小文本长度和去重策略。最小长度建议 5 到 8 个字符,太短没信息,太长会误删有效短评。去重按clean_text的 MD5 哈希做,保留最早出现的那条,避免刷屏影响热度统计。时间窗口按小时或按天聚合,取决于数据密度:一天超过 500 条就按小时,否则按天。这些参数没有绝对最优,但要在报告里写清楚,否则复现时会被质疑。
3. 情感分类模型:从朴素贝叶斯到预训练模型的取舍
3.1 三条技术路线的适用边界
做舆情情感分类,常见三条路:朴素贝叶斯 + TF-IDF、LSTM + 词向量、预训练模型微调。朴素贝叶斯训练快,几千条数据几分钟出结果,适合作为基线;LSTM 能捕捉语序,但需要更多数据和调参时间;预训练模型准确率最高,但显存和时间成本也最高。大作业如果只有 CPU,建议先跑朴素贝叶斯,再用预训练模型做对比实验,报告里放两张混淆矩阵,分数立刻上去。选型时看三个指标:准确率、训练时间、可解释性。舆情场景往往需要解释“为什么判为负面”,朴素贝叶斯的特征权重反而比黑盒模型更好写分析。
3.2 用 TF-IDF + 朴素贝叶斯跑通最小闭环
下面这段代码假设你已经有一个带clean_text和sentiment_label的 DataFrame,标签 0 为负面、1 为中性、2 为正面。先划分训练集和测试集,再建管道。
from sklearn.model_selection import train_test_split from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.pipeline import Pipeline from sklearn.metrics import classification_report, confusion_matrix # 假设 df 已包含 clean_text 和 sentiment_label X_train, X_test, y_train, y_test = train_test_split( df['clean_text'], df['sentiment_label'], test_size=0.2, random_state=42, stratify=df['sentiment_label'] ) pipe = Pipeline([ ('tfidf', TfidfVectorizer( max_features=5000, # 控制特征维度,防止过拟合 ngram_range=(1, 2), # 一元和二元词组,捕捉“不 满意”这类否定 min_df=2, # 至少出现两次才保留,过滤噪声 max_df=0.9 # 出现在 90% 以上文档的词直接丢弃 )), ('clf', MultinomialNB(alpha=0.1)) # alpha 是平滑系数,越小越依赖数据 ]) pipe.fit(X_train, y_train) y_pred = pipe.predict(X_test) print(classification_report(y_test, y_pred, digits=4)) print(confusion_matrix(y_test, y_pred))max_features=5000是平衡内存和效果的常用值,数据量超过 5 万条可以提到 10000。ngram_range=(1,2)能抓到“不 满意”“很 好”这类组合,对舆情情感很关键。alpha=0.1比默认的 1.0 更激进,如果数据干净且标注一致,效果更好;如果噪声大,调回 0.5 到 1.0。跑完看classification_report,如果中性类 F1 明显低,说明中性样本定义模糊,需要回头检查标注规则。
3.3 预训练模型微调时的显存与学习率
如果要用预训练模型,先确认环境有 GPU。常见做法是加载中文预训练权重,接一个分类头,用 AdamW 优化器,学习率设 2e-5 到 5e-5,批次大小 16 或 32。显存不够就减小批次并开启梯度累积。训练轮数 3 到 5 轮足够,太多会过拟合。下面是一个训练循环的关键参数片段:
from transformers import AdamW, get_linear_schedule_with_warmup optimizer = AdamW(model.parameters(), lr=3e-5, weight_decay=0.01) epochs = 4 total_steps = len(train_loader) * epochs scheduler = get_linear_schedule_with_warmup( optimizer, num_warmup_steps=int(0.1 * total_steps), # 前 10% 步数做 warmup num_training_steps=total_steps )weight_decay=0.01是常规正则化,warmup防止初期梯度爆炸。如果训练损失震荡,先把学习率降到 1e-5;如果损失下降太慢,检查数据标签是否平衡。预训练模型跑完后,把预测结果回填到 SQLite 的sentiment_label和sentiment_score字段,后续分析直接查表。
4. 舆情热度与趋势:时间序列聚合的三种口径
4.1 按小时聚合还是按天聚合
聚合口径决定趋势图能不能看出突发点。数据密度高时按小时,能捕捉“某事件两小时内爆发”;数据稀疏时按天,否则曲线全是零。计算热度可以用三个指标:发帖量、负面占比、平均情感得分。发帖量反映关注度,负面占比反映风险,平均情感得分反映整体倾向。下面是一个按天聚合的示例:
def daily_aggregate(df: pd.DataFrame) -> pd.DataFrame: grouped = df.groupby('date').agg( post_count=('doc_id', 'count'), neg_ratio=('sentiment_label', lambda x: (x == 0).mean()), avg_score=('sentiment_score', 'mean') ).reset_index() # 补全缺失日期,避免折线断裂 full_range = pd.date_range(grouped['date'].min(), grouped['date'].max(), freq='D') grouped = grouped.set_index('date').reindex(full_range.date, fill_value=0).reset_index() grouped.columns = ['date', 'post_count', 'neg_ratio', 'avg_score'] return groupedreindex补全缺失日期很重要,否则折线图会跳过没有数据的日期,视觉上误导。neg_ratio用(x == 0).mean()计算,前提是标签 0 代表负面。如果标签编码不同,改成对应值即可。
4.2 突发检测:滑动窗口与阈值报警
舆情系统的高分点往往在“突发检测”。简单做法是计算过去 7 天同一时段的均值和标准差,当天发帖量超过均值加 2 倍标准差就标记为突发。这个逻辑可以用 pandas 滚动窗口实现:
grouped['rolling_mean'] = grouped['post_count'].rolling(window=7, min_periods=3).mean() grouped['rolling_std'] = grouped['post_count'].rolling(window=7, min_periods=3).std() grouped['is_burst'] = grouped['post_count'] > (grouped['rolling_mean'] + 2 * grouped['rolling_std'])window=7是经验值,数据少于 14 天可以改成 3。min_periods=3保证前三天也有值。is_burst为 True 的日期在报告里单独列出来,配上当天高频词,分析说服力会强很多。
4.3 高频词与情感词的交叉分析
只放词云不够,要把高频词和情感标签交叉。比如“退款”这个词在负面样本里出现频率是正面的 5 倍,那它就是风险信号。实现时先分词,再按标签分组统计词频,最后算每个词在不同标签下的频率比。分词工具选常见的中文分词库即可,停用词表用公开的通用表,再手动加几个领域词。交叉分析的结果用表格呈现,比词云更硬核。
5. 避坑与排查:大作业里最容易翻车的五个点
5.1 标签泄露:测试集里混进了训练集文本
现象:测试准确率 0.98,换一批数据掉到 0.6。原因:去重没做好,同一条文本既在训练集又在测试集。解决:在划分数据集之前,先按clean_text的哈希去重,确保每个文本只出现一次。如果同一条文本有多个标签,保留多数投票结果或直接丢弃。
5.2 时间解析失败导致聚合为空
现象:按天聚合后所有日期都是NaT,折线图画不出来。原因:原始时间格式不统一,比如混了“2024/01/01”和“2024-01-01”。解决:在pd.to_datetime里加format='mixed'或先用正则统一分隔符。更稳妥的做法是写一个解析函数,依次尝试几种常见格式,全部失败再丢弃。
5.3 类别不平衡让模型只会猜多数类
现象:负面样本占 80%,模型把所有文本都判为负面,准确率 0.8 但 F1 很低。原因:训练时没有处理不平衡。解决:在MultinomialNB里设class_prior,或在划分时用stratify保证每类比例一致。更直接的办法是对少数类过采样,但要注意过采样只在训练集做,测试集保持原始分布。
5.4 预训练模型显存溢出
现象:训练到一半报CUDA out of memory。原因:批次太大或序列太长。解决:把批次从 32 降到 16 或 8,同时开启梯度累积,累积步数设为 2 或 4。序列长度截断到 128 或 256,不要直接设 512。如果还是溢出,换用更小的预训练模型。
5.5 报告里只放准确率,不放混淆矩阵
现象:老师问“中性类识别怎么样”,答不上来。原因:只看了整体准确率。解决:每次实验都输出混淆矩阵和分类报告,重点看每一类的精确率和召回率。舆情场景里,负面类的召回率比整体准确率更重要,漏掉一条负面舆情可能比误判一条正面更严重。
6. 把系统跑成可演示的闭环:一个最小 Web 界面与验证习惯
最后一章落到怎么把上面这些串成一个能演示的东西。不需要复杂前端,用 Streamlit 或 Flask 写一个单页就够。页面分三块:顶部输入框粘贴一条新文本,中间显示情感预测结果和置信度,底部展示历史趋势图。Streamlit 的好处是代码量少,适合大作业演示。下面是一个最小示例:
import streamlit as st import pandas as pd import joblib # 加载训练好的管道 pipe = joblib.load('sentiment_pipe.pkl') st.title('舆情分析演示') text = st.text_area('输入一条评论文本') if st.button('分析'): pred = pipe.predict([text])[0] proba = pipe.predict_proba([text]).max() label_map = {0: '负面', 1: '中性', 2: '正面'} st.write(f'情感倾向:{label_map[pred]},置信度:{proba:.2f}') # 展示历史趋势 df = pd.read_csv('daily_aggregate.csv') st.line_chart(df.set_index('date')['post_count'])joblib.load加载的是之前训练好的管道,注意保存时用joblib.dump(pipe, 'sentiment_pipe.pkl')。predict_proba返回最大概率作为置信度,低于 0.6 的可以标为“不确定”,避免误导。趋势图直接读聚合后的 CSV,不用每次重算。
验证习惯方面,我一般会做三件事:第一,每次改完清洗规则或模型参数,重新跑一遍测试集,记录准确率和负面类召回率;第二,把预测错误的样本单独导出,人工看 20 条,找规律;第三,在报告里写清楚每个参数的取值理由,而不是只贴结果。这套流程跑下来,大作业的分数不会低,更重要的是你真正理解了从文本到决策的每一步。希望帮到你。
本文还有配套的精品资源,点击获取