ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

中文谣言检测实战:从TF-IDF到逻辑回归的完整NLP流程

中文谣言检测实战:从TF-IDF到逻辑回归的完整NLP流程 简介面向自然语言处理方向学生的本科毕业设计论文资源针对中文网络谣言识别问题提供从数据清洗、分词、特征提取到文本分类建模的完整实现方案。项目基于清华大学开源中文谣言数据集将谣言检测定义为二分类任务覆盖词袋模型、TF-IDF、主题模型等特征构建方法以及逻辑回归、SVM、随机森林等分类算法并包含准确率、精确率、召回率与F1等评估指标。资源共28个文件以12个Python脚本为主体辅以11个文本文件、4个JSON数据和1个Markdown说明文档分别对应模型代码、预处理后的语料与中间结果、项目使用说明压缩包整体仅4.93MB。已有59人学习下载。借助该资源可以快速复现中文谣言检测实验了解自然语言处理在虚假信息治理中的落地流程也可作为毕业设计、课程项目或算法对比研究的参考基线。1. 中文谣言检测为什么文本二分类不够用中文谣言检测在工程上并不是一个“喂数据出模型”的简单二分类任务。同样一句“某地发生事故”换一个时间戳、换一个发布账号传播路径完全不同而且谣言文本往往在事件早期和辟谣信息高度相似仅仅靠词频统计很难拉开差距。做毕业设计时最容易被卡住的地方不是选什么算法而是数据怎么洗、特征怎么对齐、标签从哪来——这套中文谣言检测项目恰好把整条链路都拆开了从清华开源的中文谣言数据集 Chinese_Rumor_Dataset 出发依次完成数据合并、标签化、分词、停用词过滤、TF-IDF 向量化、主题建模、逻辑回归分类和聚类分析。对于想复现一个完整 NLP 文本分类流程的从业者或者准备把“文本分类”作为毕设主题的学生这份资源的价值在于它是一份可以逐文件对照执行的全过程代码包。2. 从原始语料到干净特征谣言库的预处理链路2.1 数据集长什么样Chinese_Rumor_Dataset 与 datap 目录项目的数据源头是清华大学开源的中文谣言数据集仓库地址为 https://github.com/thunlp/Chinese_Rumor_Dataset.git 。这个数据集的最大特点不是“量大”而是字段结构完整每条记录包含原始文本、事件标签、时间戳、发布者信息等。实际训练时并不会直接使用 GitHub 上的原始 json 文件而是先在本地做一轮清洗把散落在不同目录下的数据统一合并成alldata.json再进一步拆出用于监督学习的data.json和data_1.json。这里要理解datap目录的作用。它存放的是经过二次处理的中间产物包括classList.txt类别标签列表、textList.txt文本列表、logPro.txt概率对数结果、tfidfPro.txtTF-IDF 特征结果、scoreList.txt得分列表、tagList.txt标签列表等。这些 .txt 文件是 Python 脚本运行时生成的检查点不是人工维护的静态资源。文本分类项目最容易犯的错是把中间产物当成训练数据正确做法是先搞清每个文件的写入方和消费方。本项目中生成方是1_wholedataProcess.py、2_addtag.py、3_1_rumorProcess.py消费方是5_x系列和6_x系列脚本。2.2 数据处理的执行管线1_wholedataProcess.py 到 3_2_featureProcess.py整个项目的脚本命名有清晰的主线编号执行顺序从 1 到 8。对应数据处理的脚本关系如下脚本职责主要输出1_wholedataProcess.py合并原始 json统一字段alldata.json2_addtag.py为每条文本打谣言/非谣言标签data.json、标签列表3_1_rumorProcess.py提取谣言正样本并格式化清洗后的谣言样本集3_2_featureProcess.py文本特征初筛、统计特征词文件与统计信息5_1_preTM.py/5_2_stopwordTM.py分词前准备、停用词加载分词中间语料5_3_jiebaTM.pyjieba 分词jieba.txt5_4_tfidfTM.pyTF-IDF 向量化tfidfPro.txt6_1_preFM.py特征矩阵预处理可直接训练的特征矩阵6_2_logisticFM.py逻辑回归训练与预测logPro.txt7_clusterModel.py聚类分析聚类标签与中心8_finalModel.py最终模型评估评估结果与最终输出按这个顺序执行前四个脚本起着“数据入口”的作用。1_wholedataProcess.py里最常见的操作是把原始数据集中的多个 json 块拼成一个数组再统一键名。以实际数据为例原始记录中谣言和非谣言字段的命名可能不一致直接读入会报 KeyError。处理这类问题的代码模式如下import json raw_data [] for line in open(original.json, r, encodingutf-8): line line.strip() if not line: continue try: item json.loads(line) # 统一字段名原始数据里可能叫 text 也可能叫 content text item.get(text) or item.get(content) or label item.get(label) or item.get(isRumor) or unknown raw_data.append({text: text, label: label}) except json.JSONDecodeError: continue with open(alldata.json, w, encodingutf-8) as f: json.dump(raw_data, f, ensure_asciiFalse, indent2)这里的关键点是 json 解析时的健壮性。很多入门实现直接json.load()整个文件遇到一个坏行就整体崩溃。更稳妥的做法是逐行读取并捕获异常坏数据跳过不是“偷懒”而是谣言文本本身噪声极大——网页抓取的文本里混着表情符号、URL、用户等噪声不能指望数据源是干净 JSON。脚本整体对文本类型采用ensure_asciiFalse避免中文被转成\uXXXX造成下游读取不便。2.3 停用词表与 jieba 分词中文分词的边界中文谣言检测里分词的结果直接影响特征质量。5_2_stopwordTM.py会读取stopword.txt和stop_word.txt两个停用词文件。保留两份不同命名的文件说明来源不同——一份来自公开停用词表一份是自定义补充。常见做法是将两者合并去重形成一套“基础停用词 领域自定义停用词”。jieba 分词在这个项目中承担主力。默认jieba.cut(text)采用精确模式适合文本分类场景。但谣言文本容易混入“用户”“#话题#”等半结构化内容直接分词会产生大量无效 token。一个合适的分词与清洗过程如下import jieba import re stopwords set() for w in open(stopword.txt, r, encodingutf-8): w w.strip() if w: stopwords.add(w) def clean_and_tokenize(text): # 去掉 URL、用户、#话题# 等半结构化噪声 text re.sub(rhttp\S, , text) text re.sub(r\S, , text) text re.sub(r#\S#, , text) # 只保留中文、英文和数字中文标点直接丢弃 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9], , text) words jieba.lcut(text) return [w for w in words if w.strip() and w not in stopwords]停用词过滤不能做得太激进。“不”“没”“难道”这类否定词在谣言文本中往往是关键线索例如“医院辟谣称没有死人”和“医院确认有人死亡”去掉否定词后语义完全翻转。因此用通用停用词表时长词要保留短词要谨慎。实践中的经验是自定义停用词表主要覆盖无效虚词和人称代词而不是所有两个字的词。分词完成后每一条文本对应一串词列表写入jieba.txt供后续 TF-IDF 使用。3. TF-IDF 与主题特征谣言分类的特征工程3.1 为什么词袋模型之后还要 TF-IDF文本不能直接输入逻辑回归需要先转成数值向量。最简单的词袋模型统计每个词在文档中出现的次数但这样做有两个问题高频无意义词权重过大长文档天然更容易获得高分。TF-IDF 通过“词频 × 逆文档频率”来削弱常见词的干扰突出在少数文档中出现但能区分类别的词。设想谣言文本里经常出现“紧急”“扩散”“千万别转”这类情绪性词汇同时文章正文里“记者”“来源”等词频繁出现TF-IDF 可以让这两类词的权重明显分开。项目里5_4_tfidfTM.py承担了 TF-IDF 向量化的任务tfidfPro.txt保存计算结果。用 scikit-learn 实现时TfidfVectorizer本身就支持最大特征数、ngram 范围等参数控制。具体实现可以写成from sklearn.feature_extraction.text import TfidfVectorizer import joblib corpus [] with open(jieba.txt, r, encodingutf-8) as f: for line in f: line line.strip() if line: corpus.append(line.replace( , )) vectorizer TfidfVectorizer( max_features10000, # 只保留 TF-IDF 值最大的 1 万个词控制维度 ngram_range(1, 2), # 保留单个词和相邻双词捕捉“不实消息”这类短语 min_df2, # 词至少在 2 篇文档中出现过滤垃圾 token max_df0.9, # 出现在 90% 以上文档中的词视为通用词丢弃 sublinear_tfTrue # tf 取 1log(tf)降低热门词的绝对优势 ) X vectorizer.fit_transform(corpus) joblib.dump(vectorizer, tfidf_vectorizer.pkl)关键参数值得展开解释。max_features10000是维度上限原始词典规模可能在 5 万以上但低频词对逻辑回归几乎没有贡献反而增加过拟合风险。min_df2表示只在一条文本中出现过的词直接丢弃。max_df0.9是反向过滤比如“我们”“大家”这类在大多数文档中都出现的词对区分谣言没有意义。sublinear_tf用对数平滑削弱高词频差异是一种在短文本分类中效果稳定的常用配置。ngram_range(1, 2)在谣言检测中尤其重要。单字词“谣”没有意义双字词“辟谣”才有区分度“有”“人”分开看无意义合在一起“有人”在谣言文本里往往连接“看到”“听到”等传闻性表达。双词组的引入代价是维度显著增加因此配合max_features一起使用。3.2 主题模型把文档折叠成话题分布TF-IDF 给每个词一个权重但词与词之间的关联没有被利用。主题模型Topic Modeling则把文档视为多个话题的混合体输出的是“这篇文本有多大比例在谈卫生事件、多大比例在谈政策回应”。项目中5_1_preTM.py到5_4_tfidfTM.py的主题建模部分用 LDA 为每条文本额外生成一组主题分布特征再拼接到 TF-IDF 特征矩阵后面。LDA 是生成式模型对输入格式有要求——它希望输入是“词袋”形式而不是 TF-IDF 权重矩阵。gensim 的实现方式很直接from gensim import corpora, models # dictionary_corpus 是上一步分词结果格式为 list[list[str]] dictionary corpora.Dictionary(doc_list) # 过滤低频词和超高频词保持主题独立性 dictionary.filter_extremes(no_below3, no_above0.8) bow_corpus [dictionary.doc2bow(doc) for doc in doc_list] lda_model models.LdaModel( corpusbow_corpus, id2worddictionary, num_topics20, # 主题数量语料量小就少设 iterations50, passes10 ) topic_features lda_model[bow_corpus]num_topics20是经验起点。中文谣言数据集规模通常在几万条量级20 个主题足够覆盖突发事件、医疗健康、食品安全等常见类别。主题数太多会出现一个主题只对应少数文档逻辑回归反而学到噪声。no_above0.8和 TF-IDF 里的max_df目的一致都是丢弃出现在绝大多数文档中的通用词否则 LDA 会把它们单独抽成一个“废话主题”。主题特征可以和 TF-IDF 特征横向拼接也可以在分类后做概率融合。简单做法是把 topic_features 转为稠密矩阵用scipy.sparse.hstack与 TF-IDF 稀疏矩阵拼接后喂给逻辑回归。3.3 特征融合的维度管理与实际效果把 TF-IDF 和主题特征拼接后特征矩阵从稀疏变半稀疏逻辑回归训练速度下降但准确率通常会提升。这是因为谣言文本的判别线索往往同时依赖词汇级别和话题级别的信息——词汇级别抓“造谣”“求证”这样的关键词话题级别抓“文本属于哪个事件背景”。类不平衡问题在这里开始显现非谣言样本通常远多于谣言样本模型会倾向于把所有样本判成多数类。特征来源特征含义对区分谣言的典型贡献TF-IDF 词权重词汇层面的判别性抓住“辟谣”“紧急”“求证”等高频词TF-IDF 双词组短语层面的共现模式识别“经核实”“不实消息”等固定搭配LDA 主题分布文档级话题归属结合事件背景判断文本是否属于官方通报文本长度特征字数、句子数谣言文本往往短且情绪化符号特征感叹号、问号数量情绪表达强度是辅助信号这里有一个容易被忽略的实现细节拼接特征后必须记录特征名列表。如果后续要输出“哪些词对谣言判断影响最大”特征名映射一旦丢失权重系数就无法对应到具体词。因此训练前保存一份feature_names vectorizer.get_feature_names_out()拼接后维护一个完整的特征名列表这对答辩展示和后续调参都很关键。4. 分类模型、评估与调参逻辑回归的实际表现4.1 为什么选逻辑回归而不是 SVM 或随机森林项目最终分类器用的是逻辑回归而不是 SVM 或随机森林。逻辑回归在文本分类任务里的优势很明确高维稀疏特征下训练快模型输出是概率值可以直接用于置信度过滤和阈值调整。SVM 适合小样本高维场景但训练复杂度随样本量增长明显变慢随机森林对稀疏特征不敏感很容易在小样本下过拟合。逻辑回归还有一个附加价值——权重系数可解释每个词的权重直接反映它对谣言倾向的贡献方向。对于毕业设计答辩来说能说清楚“模型为什么判断这条文本是谣言”非常重要。6_1_preFM.py的作用是把前面得到的tfidfPro.txt、主题特征、标签文件整合为一个X_train, y_train可直接训练的数据结构。这里常见的坑是训练集和测试集特征维度不一致原因是特征矩阵的构建过程没有先 fit 再 transform而是对全量数据一起 fit 了。正确做法是先用训练集 fit 向量化器再用训练好的向量化器 transform 测试集。4.2 逻辑回归训练与概率输出6_2_logisticFM.py 和 8_finalModel.py6_2_logisticFM.py里的核心逻辑可以用以下代码表示from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, classification_report import joblib X_train, X_test, y_train, y_test train_test_split( X_combined, y_labels, test_size0.2, random_state42, stratifyy_labels ) model LogisticRegression( C1.0, # 正则化强度的倒数越小正则越强 class_weightbalanced, # 自动根据类别占比调整权重 solverliblinear, # 适合中小规模稀疏数据L1/L2 均可 max_iter1000 ) model.fit(X_train, y_train) y_pred model.predict(X_test) y_prob model.predict_proba(X_test)[:, 1] print(classification_report(y_test, y_pred)) joblib.dump(model, logistic_rumor_model.pkl)class_weightbalanced是处理谣言负样本占比过大的常用手段。它按照n_samples / (n_classes * np.bincount(y))为每个类自动分配权重让少数类样本在损失函数中获得更高权重。solverliblinear适合几万维特征、几万条样本的场景如果样本量到百万级可以换saga。C1.0是默认值在文本分类里往往需要调小到 0.1 甚至 0.01因为一万维以上的特征矩阵非常稀疏逻辑回归很容易在少量强特征上过度自信更强的 L2 正则能压低这种风险。4.3 评估指标与阈值调整的细节项目在评估阶段输出准确率、精确率、召回率和 F1 分数这些指标在类别不平衡时的含义需要准确理解。准确率高不代表模型好——如果谣言样本只占 10%全预测为“非谣言”也有 90% 准确率。真正需要关注的是谣言类正类的召回率和精确率。指标含义谣言检测场景下的优先级Accuracy全部样本中预测正确的比例仅供参考容易虚高Precision预测为谣言中真实谣言的比例高避免误伤正常发言Recall真实谣言中被模型找出的比例最高漏检比误报后果更严重F1精确率和召回率的调和平均平衡指标模型选型时用通常的做法是直接使用class_weightbalanced之后再观察 F1 值。如果谣言类召回率仍然太低可以考虑调整决策阈值逻辑回归默认以 0.5 为分界但在不平衡场景下应改为在验证集上搜索最优阈值import numpy as np best_threshold 0.5 best_f1 0.0 for threshold in np.arange(0.3, 0.7, 0.05): y_pred_adj (y_prob threshold).astype(int) f1 f1_score(y_test, y_pred_adj, pos_label1) if f1 best_f1: best_f1 f1 best_threshold threshold print(fbest threshold {best_threshold:.2f}, F1 {best_f1:.4f})阈值搜索必须在验证集进行不能用测试集。先划分训练集、验证集、测试集用训练集拟合参数用验证集选择阈值最后才能用测试集报告最终效果。毕业设计里常见的问题是直接拿全部数据求最优阈值导致结果虚高真实泛化能力无从知晓。5. 混淆矩阵之外谣言检测落地时的四类典型毛病最后一章不讲新模型聊四个在复现这套代码时最容易翻车的工程问题。第一个是特征泄漏。2_addtag.py在合并标签时如果处理的是带时间顺序的事件数据直接用train_test_split(random_state42)随机划分会把同一条事件的爆发期和辟谣期文本分别放进训练集和测试集。谣言检测任务是时间敏感的正确做法是按事件 ID 或时间戳分组划分保证同一条事件的文本不会跨集合出现。当年做复现时被这一点坑过模型评估 F1 有 0.9换到新时间段的语料后直接掉到 0.6。第二个是分词歧义处理。谣言文本里有很多网络新词和缩写例如“yyds”“u1s1”jieba 默认词典不认识会拆成单个字母数字。解决方法是在分词前加载自定义词典把网络热词强制视为一个 token。jieba.add_word(yyds)之后该词在 TF-IDF 里成为一个独立特征否则“y”“yd”“ds”这类碎片会成为噪声特征。第三个是类别标记的置信度问题。tagList.txt里的标签来自原始数据集的标注规则但标注本身存在不确定性——同一事件的文本发布时间和语境变化可能导致标注结果不同。训练前应当对标签分布做一次可视化和统计如果某个事件下谣言与非谣言比例极端失衡建议单独分析而不是直接纳入训练。这是“数据质量优先于模型复杂度”最典型的表现。第四个是特征文件的重新生成策略。tfidfPro.txt、logPro.txt这类中间文件一旦生成重新运行后不会自动更新。如果改动分词或停用词逻辑必须删除旧的中间文件再从 5_x 系列脚本开始重新执行。项目中脚本按编号依次执行但编号之间的文件依赖关系没有用 Makefile 或 shell 脚本固化实际运行时容易漏跑。复现时最省心的方式是按照编号顺序完整跑一遍观察每个 .txt 是否生成成功再进入下一阶段。整个项目最后可以通过8_finalModel.py输出分类报告和若干条谣言样本的预测概率用这些概率分布反推模型在哪类文本上犹豫不决再针对性地补充训练样本或调整特征。这才是中文谣言检测项目里真正值得沉淀的部分——特征和模型都能抄而排错和迭代的思路只能在实际运行中积累。本文还有配套的精品资源点击获取
返回列表