
简介这份毕业设计资源面向计算机、数据挖掘或 NLP 方向的本科学生聚焦社交媒体文本情感分析采用情感字典与机器学习相结合的技术路线覆盖从数据清洗、特征构建、模型训练到算法评估的完整流程。资源包共 87 个文件以 Python 脚本为主46 个 py 源码与 33 个 pyc 编译文件另含 4 个项目配置 xml、3 个说明 txt 及 1 个 iml 工程文件压缩包大小约 52KB目录结构按功能模块清晰划分。目前已有 61 人学习下载。内容按 sentimentdictionary、bayesian、neuralnetwork、utils 等模块组织分别实现了情感字典、朴素贝叶斯与神经网络含 CNN、RNN、LSTM等算法同时涵盖词袋模型、TF-IDF 等特征提取方法以及支持向量机对比实验可帮助理解不同模型在情感分类任务上的效果差异。项目基于 Python 3.5 开发附有 readme 说明与数据读取脚本便于直接运行或二次扩展。针对预处理阶段 voca_dict.csv 第 43123 行出现空值的问题资源给出了替换为“保留”的临时处理方案能帮助后来者避开同类数据清洗隐患。适合需要快速上手情感分析项目、参考完整代码结构与排错思路的毕业设计者。1. 一个.zip里的毕设选题社交媒体情感分析为什么同时需要情感字典和机器学习导师把项目文档打包成.zip发到群里标题就这几个字社交媒体文本中的情感分析情感字典机器学习。这是本科毕设里最高频的选题之一也是很多学生第一次完整跑通自然语言处理流程的机会。它要解决的问题很具体从一条微博、一条短评论里判断用户态度是正向、负向还是中性这是舆情监测、电商评价分析里最基础的需求。情感字典是拿现成的词表给句子打分通俗讲就是查一张“正负词表”机器学习则是用标注好的文本训练分类器让模型自己找规律。两条路单独用都有明显短板做在一起就有意思了字典给出可解释的依据机器学习补上字典覆盖不到的语境。这篇文章把这套方案拆开讲——数据清洗怎么处理噪声、词典怎么构建和打分、分类器怎么选型训练、哪些地方容易翻车。适合正在做毕设的学生也适合需要快速搭建情感分析原型的从业者。2. 情感字典与机器学习两套方案的选型为什么毕设常把两者做对比2.1 情感字典的原理词汇级先验知识如何变成可解释的分数情感字典也叫情感词表的思路很直接人工整理一批带有情感倾向的词每个词记录极性和强度比如“喜欢”是正向强度1“热爱”是正向强度2“失望”是负向强度-2。分析一句话时把句子分词、逐词匹配词表把命中的词按权重加起来得到整句话的分数。核心是这套流程完全不需要训练数据靠的是人对语言情感的先验知识。这个原理在社交媒体短文本上特别适用原因在于短句子的情感表达很大比重落在几个关键词上。比如“这家店服务太差了再也不来”真正带情感的只有“差”和“再也不来”里的否定结构字典方法只要命中“差”就能给一个负向基准分。相比深度学习的端到端模型字典方法的优势是每一步都可解释你可以从最终分数逆推是哪个词贡献了多少分。答辩时老师问“为什么判成负向”直接把命中的词列表展示出来就行这是机器学习模型很难做到的也是常被人吐槽的黑匣子。但字典方法的边界同样清晰。词表是静态的覆盖不了“绝绝子”“yyds”这类网络新词反讽和反语在词汇层面无解比如“服务真是太好了微笑”和正面句子共享大量词汇字典会不加区分地判正向否定和程度词还要额外写逻辑不是简单相加就能搞定。2.2 机器学习模型的思路把情感分类看成文本特征到标签的映射机器学习路线的本质是把情感分析转成一个分类任务准备好一批已经标好正负标签的文本抽取特征训练分类器再用分类器预测新文本。应用流程一般是这样清洗和分词把原始文本变成词序列。用 TF-IDF、n-gram 或词向量把文本表示成数值矩阵。切分训练集与测试集选择模型常用朴素贝叶斯、逻辑回归、线性 SVM。交叉验证调整超参数评估模型在测试集上的效果。对新文本走同样的特征抽取流程后预测。为什么本科毕设在这个任务里首选线性模型因为中文社交媒体文本的训练集规模通常只有几万条在这样的规模下线性模型稳定、训练快、效果好且超参数少。相比之下深度学习模型虽然在新词和上下文理解上有优势但在小数据上容易过拟合调参成本高而且特征不可解释。机器学习模型还有另一个问题噪声数据。社交文本的标签来自人工标注不同人对同一条文本的标签可能不一致“这东西也就那样吧”有人标中性有人标负向这类冲突如果不做清洗模型会学到噪声表现为训练集上分数高但测试集上不稳定。2.3 选型决策给没有标注数据的场景一个判断框架我一般把选型拆成四个问题按顺序回答就能确定路线有没有标注数据没有就只能走字典路线或者先做半自动标注再训练。如果有才谈得上机器学习。结果要不要向非技术人员解释要字典路线的词表命中记录天然可解释机器学习配合特征权重也可以给出部分解释但没有字典那么直观。文本里网络新词和反讽占比高不高高字典会大量失配机器学习至少能从数据里学到一部分固定搭配。走毕设要不要对比实验很多学校要求毕设必须有“已有方法 vs 改进方法”的对比情感字典作为baseline、机器学习作为提升路线正好凑成完整的对比逻辑。把这个框架落实到实际选择可以先跑通字典打分作为基线再训练一个逻辑回归模型做对比最后分析两者的互补情况。这个组合也是标题里“情感字典机器学习”并列的原因单靠哪个都有死角对比着用才有论文素材。对比维度情感字典机器学习标注数据需求不需要需要千条以上可解释性高逐词可回溯中依赖特征权重新词覆盖差较好反讽处理几乎无效部分有效训练成本无低线性模型实现复杂度低中毕设适合度做baseline做主力模型3. 数据准备把社交媒体文本清洗成特征四类噪声怎么处理3.1 数据来源与标注公开语料、自标注和标签噪声控制数据从哪来是每个做这个题的人第一个卡住的地方。常见做法去学术共享渠道找公开的中文情感语料微博、电商评论都有现成的标注数据这类数据集通常已经分好正负标签缺点是有年份限制网络新词覆盖少。另一个做法是自己爬社交媒体文本再人工标注先按关键词或账号采样过滤广告和无关内容再找两三个人按统一规则标注。自标注的好处是标签贴近当前语言环境代价是人力成本高而且容易引入标签噪声。不管走哪条路都要面对噪声数据控制问题。社交文本里“哈哈哈哈”“无语子”“这不是欺负老实人吗”这类表达词面意思和情感倾向经常不一致。我见过有人用一条规则“表情符号为正面就标正”来批量标注结果“”出现的负面文本全被标反了。控制办法标注规范里明确冲突规则比如文字正向但表情负向按负向处理标注完成后抽样做一致性校验两人不一致的样本单独清洗或丢弃。这步做不好后面机器学习模型学到的基本是随机规律。3.2 清洗流程URL、用户、emoji 和重复标点的处理代码公开数据集的具体路径没有统一答案但清洗逻辑对所有数据集都通用。我的习惯是写一个函数一次处理四类噪声URL 和 用户直接删除因为它们在分类里基本没有情感信息连续重复的标点压缩成单个避免出现“”这种对情感权重无意义的干扰符号emoji 不删除但替换成占位符。为什么保留 emoji“”“”在社交媒体文本里本身就是强情感信号删掉等于主动丢特征。import re import jieba def clean_weibo_text(text: str) - str: # 1. 去掉 URL 和 用户 text re.sub(rhttps?://\S|www\.\S, , text) text re.sub(r[\w\u4e00-\u9fa5_-], , text) # 2. 压缩连续重复的中英文标点保留一个 text re.sub(r([!?。~])\1, r\1, text) # 3. emoji 转成占位符方便后续单独处理或作为特征 text re.sub(r[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF], [emoji] , text) # 4. 去掉多余空白 return re.sub(r\s, , text).strip() sample 这手机小米 真不错http://t.cn/xxx print(clean_weibo_text(sample)) # 输出示例: 这手机 真不错 [emoji]第 1 步的 URL 正则要覆盖 http 和 https 两种同时把 www 开头的也算进去否则漏掉一批裸链接。第 2 步压缩标点不只是格式需要更是为了下一步词典打分时避免“”被当成一个无意义词。第 3 步的 emoji 占位符在线性模型里可以和其他词一样参与 TF-IDF 计算在字典路线里则不会因为占位符扣分。一个细节正则里的 emoji 范围只覆盖了常见表情不足以覆盖全部 Unicode 表情符号如果语料里 emoji 形式很多先跑一个频率统计把高频表情符收集齐再做替换。3.3 分词jieba 的默认切分和情感词失配问题分词的坑往往在细节里。jieba 默认词典把“不高兴”切成“不/高兴”这在常规文本分析里没有任何问题但在情感分析里会把否定词和情感词拆散后面字典打分时的“否定翻转”逻辑就完全失效了。我一般会针对情感任务维护一个自定义词表把常见的高频“否定词情感词”组合加进去。def tokenize_for_sentiment(text: str): # 让 jieba 优先把“不高兴”“不怎么样”切成整词 jieba.suggest_freq(不高兴, True) jieba.suggest_freq(不怎么样, True) jieba.suggest_freq(不是很好, True) return list(jieba.cut(text)) print(tokenize_for_sentiment(今天不高兴不怎么样。)) # 习惯输出: [今天, 不高兴, , 不怎么样, 。]suggest_freq 的作用是给分词器一个词频先验强制它把指定词作为单独结果输出。参数 True 表示用指定词频句子越长、组合词越多效果越重要。注意这个操作是针对整个进程的全局状态所以不要在循环里去调用 jieba.suggest_freq应该在启动时一次性配置好。如果发现还有别的组合词被切碎跑一个“分词后匹配情感词表失败”的统计把失败样本里出现多次的片段手动加进自定义词表比盲目堆 suggest_freq 更有效。分词结果要不要去停用词要谨慎。通用停用词表里往往包含“不”“很”“太”这类对情感强度有决定性影响的词一旦过滤掉字典打分和机器学习特征都会损失重要信号。我的做法是“去停用词但排除否定词和程度副词”这个坑后面还有一条专门的排错记录。4. 情感字典的实现词典构建、否定翻转和程度加权的完整打分逻辑4.1 词典来源与数据结构情感词表、否定词表、程度副词表三件套字典路线的第一步是把词典资源组织成统一结构。公开渠道常见的资源有大连理工大学情感词汇本体、知网情感分析用词集等这些资源大多以 Excel 或文本形式提供字段各不相同需要清洗成统一的格式。我的经验是先不管原始格式全部转成三个 Python 数据结构情感词表用“词到强度”的字典否定词表和程度副词表各用一个字典。强度值的归一化很关键。有的词典极性是正向/负向两个值有的是 -5 到 5 分强度直接混合用会导致打分尺度不一。我一般把正负向强度统一映射到 [-3, -1, 1, 3] 这样的档位负面强情感是 -3弱负面是 -1弱正面是 1强正面是 3。程度副词则定义成权重比如“稍微”0.5、“很”1.5、“非常”2.0。否定词不用权重用布尔逻辑因为中文否定词的语义就是翻转极性。# 统一后的词典结构示例 sentiment_dict { 喜欢: 1, 热爱: 3, 失望: -1, 糟糕: -3, 开心: 2, 难过: -2, 差: -2, 好: 1, 不错: 2, } neg_words {不, 没, 没有, 别, 无, 非, 莫} degree_words {稍微: 0.5, 很: 1.5, 太: 1.8, 非常: 2.0, 特别: 2.0} def load_lexicon(score_file, neg_file, degree_file): # 实际读取逻辑取决于原始文件格式这里只演示统一接口 pass4.2 打分函数否定翻转和程度加权怎么配合实现打分函数时最容易写错的是“否定词的影响范围”。我的实现里否定词采用计数器而不是单纯取反目的是支持“不是不好”这种双重否定。算法逻辑遍历分词结果遇到程度副词先记录权重但不立即生效遇到否定词就计数加一遇到情感词时根据否定计数的奇偶决定是否翻转极性再用当前程度权重放大最后累加分数并复位否定计数和程度权重。def lexicon_score(tokens, sentiment_dict, neg_words, degree_words, default_degree1.0): score 0.0 neg_cnt 0 degree default_degree for tok in tokens: if tok in neg_words: neg_cnt 1 elif tok in degree_words: degree degree_words[tok] elif tok in sentiment_dict: s sentiment_dict[tok] if neg_cnt % 2 1: # 奇数个否定词则翻转极性 s -s score degree * s neg_cnt 0 # 一个情感词结算完复位否定与程度 degree default_degree return score print(lexicon_score(list(jieba.cut(这手机很不错)), sentiment_dict, neg_words, degree_words)) print(lexicon_score(list(jieba.cut(这手机不是很差)), sentiment_dict, neg_words, degree_words))default_degree1.0 表示没有程度副词时的默认权重它必须在函数外部统一否则每次调用都开一个 1.0。neg_cnt 的取模判断是这段代码的核心一个否定词翻转一次两个否定词等于没翻转三个又翻转符合中文口语习惯。degree 的复位时机放在情感词结算后避免程度副词跨情感词持续生效比如“很不开心”里“很”只作用于“开心”不会影响到下一个情感词。“这手机很不错”分词为“这/手机/很/不错”“很不错”会被切出来“很”和“不错”是两个词所以结果是 1.5 × 2 3.0“这手机不是很差”里“是”不在词典也不在否定词表忽略“不”翻转“差”最终也是正分符合“不是很差”表示中等偏上的语感。4.3 效果边界新词召回不了怎么办阈值如何定字典打分跑完一轮你会发现大量文本的分数是 0或者正负分数集中在几个词上。这不是 bug是边界。网络新词“绝绝子”“yyds”“家人们谁懂啊”根本不在情感词表里这些文本判不出来反讽文本打分经常和真实情感完全相反“还行”“一般”这种弱倾向词分数很低导致中性类最吃亏。处理办法分三个层面第一层是阈值设置不要用 score 0 判正、score 0 判负直接设定 |score| 0.5 为中性能减少一部分误判第二层是词典扩容对训练集里分数为 0 的样本做词频统计人工把高频情感词补进 sentiment_dict第三层是承认边界把“反讽、新词”作为已知局限写进论文讨论。这一步做完字典路线作为 baseline 的价值就完全展示了。5. 注意与避坑噪声数据、标签泄漏、否定词失效的排错记录5.1 数据清洗侧停用词误删、标签不一致、特征泄漏三个事故事故一清洗后情感词全没了。现象做完停用词过滤再跑字典打分发现语料里情感词的命中率骤降几乎分数全是 0。原因网上现成的中文停用词表里包含了“不”“很”“太”这类词。它们在通用检索场景下是噪音但在情感分析里恰恰是情感强度的核心修饰成分。字典打分时“很喜欢”被清成“喜欢”还好更惨的是“不喜欢”被清成“喜欢”正负直接反转。解决停用词表不是拿来就用先做一步“从停用词表里剔除否定词和程度副词”的过滤更稳妥的方案是用分词结果的词性标注过滤去掉代词、助词、语气词保留形容词、副词、动词。事故二同一条数据在两个批次里标成相反情感。现象训练集来自多人标注正负比例看起来正常但模型交叉验证分数忽高忽低训练时反复震荡。原因标注标准不统一噪声数据比例过高。“挺好的就是有点贵”有人标正、有人标负、有人标中性三种标签都对但互相冲突。解决清洗标签而不是只清洗文本。把多人标注不一致的样本单独抽出来按投票决定最终标签平票样本直接丢弃验证集抽样时也要检查标注一致性否则模型过拟合的都是噪声。事故三测试集上 F1 高达 0.95一换成新数据就跌到 0.7 以下。现象训练和测试都来自同一批数据按 8:2 切分后效果极好但部署到新爬取的文本上效果暴跌。原因最典型的原因是特征泄漏。有人把 TF-IDF 向量化器在“切分前的全部数据”上做了 fit再切分训练测试。测试集文本的 IDF 权重包含测试文本自身的统计信息等于测试答案提前泄露给了模型。解决先切分再 fit。写代码时始终遵循“fit 只发生在训练集上验证和测试集只走 transform”。提示先切分再 fit_transform验证集和测试集永远只走 transform。这是防数据泄漏的底线规则也是面试和答辩爱问的点。5.2 模型与特征侧分词把情感词切碎、n-gram 覆盖不了否定结构事故四字典打分把“不难吃”判成负向。现象口语里“不难吃”是中性偏正字典打分却得到负分“不是很好”又被判成正向。原因分词把“不难吃”切成“不/难吃”“难吃”命中负向情感词否定逻辑还没来得及生效“不是很好”分词后是“不是/很好”如果词典里只有“好”没有“很好”否定词计数又对不上。解决第 3 章用 suggest_freq 把“不高兴”“不怎么样”加进分词这里再补充一步把否定词和情感词的连续组合在词典层手动注册。比如在处理“不难吃”时先检查“不情感词”前缀命中后直接按翻转逻辑走同时给分词器补充“不难吃”“不是很好”这类高频短语从源头避免切碎。真正跑线上系统时这一步往往决定字典路线的良心。事故五机器学习模型对否定结构不敏感“不是很好”被判正向。现象逻辑回归在验证集上表现正常抽检发现含“不是”的句子被系统性地判为正。原因特征抽取用 ngram_range(1,1)模型只能看到单词特征“不是很好”被拆成“不是”和“很好”两个独立特征权重学不到组合的反转含义。这在文本分类里是经典问题也叫组合特征缺失。解决把 ngram_range 打开到 (1,2) 或 (1,3)让“不是很好”“不是特别差”这类二元或三元组合成为独立特征。代价是特征矩阵爆炸需要用 min_df2 或 max_features 限量。更强的做法是手工构造“否定词情感词”组合特征列与 TF-IDF 拼接模型会显著变稳。6. 进阶把字典打分拼进机器学习特征再补一个错误分析6.1 让线性模型吃到词典先验字典分数作为特征列一个小技巧字典打分本身可以当作一维特征拼进 TF-IDF 矩阵。逻辑回归会学到这个特征的权重等于在特征层面把“人工先验”和“数据统计”合流。这种做法经常是精度提升有限但对“字典明显判错”的样本有纠正作用而且这一列特征的贡献可以输出成权重答辩时很好讲。import scipy.sparse as sp from sklearn.linear_model import LogisticRegression # dict_scores 是 lexicon_score 在全部文本上的结果 # 注意 fit 前对训练集和测试集分别做 min-max 归一化避免数据泄漏 def lr_score_normalize(scores, train_idx): lo min(scores[i] for i in train_idx) hi max(scores[i] for i in train_idx) return [(s - lo) / (hi - lo 1e-9) for s in scores] X_dict sp.csr_matrix(norm_scores).T # (n_samples, 1) X_comb sp.hstack([X_tfidf, X_dict]) # (n_samples, n_features 1) lr LogisticRegression(max_iter1000, C1.0) lr.fit(X_comb[train_idx], y_train)C1.0 是逻辑回归的正则化强度C 越小正则越强。文本特征场景下 C 在 0.5 到 2 之间扫描一下即可不必过分调优。归一化是为了让字典分数和 TF-IDF 的数值尺度接近避免这一列因为数量级问题被正则误解。6.2 错误分析答辩老师真正会问的是错在哪一类多数毕设汇报只会放一个结果矩阵但如果想拿高分要再做一步错误分析。我在测试集预测后把样本按“字典对但模型错”和“字典错但模型对”分组各看 30 条。你会发现一个规律字典对但模型错的常常是流行语出现频率低的少量样本模型对但字典错的往往是词典未登录的新词。这两类正好构成一个对比结论——字典覆盖先验知识模型覆盖数据分布两者互补。当年我做这个题时把 F1 刷到 0.87 就收了答辩老师一句“错在哪类”直接把我问住。后来补了错误分析才发现模型对“反讽程度副词”的组合是系统性失误而这恰好是中文情感分析的研究难点。把这些边界写清楚比刷那 0.01 的 F1 更能体现对任务的理解。希望这个方案和这些坑能帮到你。本文还有配套的精品资源点击获取