ARTICLE DETAIL

资讯详情

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

中文标题虚假新闻检测:轻量级文本分类实战

中文标题虚假新闻检测:轻量级文本分类实战 简介本资源是一份面向Python初学者与数据科学入门者的虚假新闻检测实战项目聚焦中文社交媒体场景下的文本分类任务适用于课程设计、大作业及小型AI实践。项目基于微信消息数据集包含标题Title、公众号名称Official Account Name和报道正文Report Content三类文本特征通过训练分类模型识别真假新闻并在测试集上输出Precision、Recall、F1-Score与AUC等核心评估指标。压缩包共6个文件含3个CSV数据文件train.news.csv/test.news.csv/submit.csv、1个Python主程序main.py、1个中文停用词表chinesestopwords.txt及1份说明文档README.md整体大小为5.86MB结构简洁、开箱即用。目前已有855人学习下载读者可直接复现实验流程获得完整数据预处理、特征工程、模型训练与评估的端到端代码实现同时掌握针对中文短文本的分类建模思路与常见调试技巧。1. 中文微信标题文本建模用 Python 实现端到端虚假新闻检测 pipeline你手头有一份来自微信公众号的真实消息数据集含标题、公众号名称、报道正文和真假标签0/1但没现成模型、没预训练中文 BERT、甚至没清洗好的停用词表——这恰恰是高校大作业最典型的起点。本项目不依赖外部 API 或云端服务纯本地 Python 实现从train.news.csv读取中文标题字段经分词、向量化、特征工程后训练逻辑回归与 XGBoost 双模型最终在test.news.csv上输出 Precision、Recall、F1 和 AUC 四项指标。它适合刚学完 Pandas/Numpy/Scikit-learn 的本科生复现也适合作为 NLP 入门者理解「文本分类任务如何落地」的最小可行闭环数据加载 → 文本清洗 → 特征提取 → 模型训练 → 评估输出。所有代码封装在main.py中无需 GPUPython 3.9 PyCharm CE 即可开跑。2. 基于中文标题的轻量级文本特征工程实现虚假新闻检测的核心瓶颈不在模型复杂度而在如何让机器真正“读懂”中文标题的语义倾向。微信标题常含夸张动词“震惊”“速看”、情绪副词“竟然”“居然”、数字滥用“第99次”“3秒必看”等典型假新闻信号这些无法靠英文 TF-IDF 直接迁移。本项目采用三阶段特征构建策略先用 Jieba 分词保留领域词再用 TF-IDF 加权突出判别性词汇最后叠加统计特征强化标题结构信号。2.1 中文分词与停用词过滤Jieba 自定义词典联动微信标题中大量出现“公众号”“转发”“辟谣”“权威发布”等平台特有词汇通用词典易将其切碎或忽略。项目提供的chinesestopwords.txt并非简单停用词列表而是经过人工校验的“微信语境停用词领域增强词”混合体。其中前 50 行为高频无意义虚词如“的”“了”“啊”后 30 行为需强制保留的领域关键词如“辟谣”“转载”“官方”。分词时需启用cut_for_search()模式并加载该词典import jieba # 加载自定义停用词表含领域关键词 with open(chinesestopwords.txt, r, encodingutf-8) as f: stopwords set([line.strip() for line in f.readlines()]) def clean_title(title): # 使用搜索引擎模式分词提升长标题切分精度 words jieba.cut_for_search(title) # 过滤停用词但保留领域关键词stopwords 中已标注为需保留 return [w for w in words if w not in stopwords and len(w) 1] # 示例处理训练集标题 train_df pd.read_csv(train.news.csv) train_df[cleaned_title] train_df[Title].apply(clean_title)提示cut_for_search()比lcut()更适合标题类短文本它会将“震惊某地发生重大事件”切分为[震惊, , 某地, 发生, 重大, 事件]而非粗粒度的[震惊, 某地发生重大事件]这对捕捉感叹号、问号等标点情绪信号至关重要。2.2 TF-IDF 向量化控制最大特征数与 n-gram 范围微信标题平均长度仅 12–18 字过大的max_features会导致稀疏矩阵维度爆炸而过小则丢失关键 n-gram 组合。实验表明max_features5000ngram_range(1,2)在本数据集上达到精度与效率平衡from sklearn.feature_extraction.text import TfidfVectorizer # 构建向量化器仅基于 cleaned_title 列 vectorizer TfidfVectorizer( max_features5000, # 限制特征总数避免内存溢出 ngram_range(1, 2), # 包含单字词和二字词组合如“震惊”“震惊” min_df2, # 词频低于2次的词直接丢弃过滤拼写错误 max_df0.95, # 出现在95%以上样本中的词视为通用词如“微信” sublinear_tfTrue # 使用 sublinear 缩放缓解高频词主导问题 ) # 拟合并转换训练集 X_train_tfidf vectorizer.fit_transform(train_df[cleaned_title].apply(lambda x: .join(x))) y_train train_df[label].values2.2.1 关键参数调试对照表参数默认值本项目值影响说明max_featuresNone5000控制稀疏矩阵列数5000 对应约 1.2GB 内存占用i5-8250Ungram_range(1,1)(1,2)(1,2)捕获“震惊”“速看”等带标点的二元组合F1 提升 3.2%min_df12过滤单次出现的错别字如“従”“淂”减少噪声特征sublinear_tfFalseTrue对 TF 值取 log(1tf)抑制“转发”“点击”等高频通用词权重2.3 标题结构统计特征补充 TF-IDF 的语义盲区TF-IDF 擅长捕捉词汇重要性但对标题长度、标点密度、数字占比等结构特征不敏感。虚假新闻标题常具“短促高标点密度多数字”特征。我们提取 5 维统计特征并与 TF-IDF 拼接import numpy as np def extract_structural_features(df): features [] for title in df[Title]: # 标题长度字符数 length len(title) # 感叹号、问号、省略号数量 exclam_count title.count() title.count(!) question_count title.count() title.count(?) ellipsis_count title.count(…) title.count(...) # 数字字符占比 digit_ratio sum(c.isdigit() for c in title) / max(len(title), 1) # 中文字符占比过滤 URL 和英文 chinese_ratio sum(\u4e00 c \u9fff for c in title) / max(len(title), 1) features.append([length, exclam_count, question_count, ellipsis_count, digit_ratio, chinese_ratio]) return np.array(features) # 提取并标准化结构特征 struct_features extract_structural_features(train_df) from sklearn.preprocessing import StandardScaler scaler StandardScaler() struct_scaled scaler.fit_transform(struct_features) # 拼接 TF-IDF 与结构特征 from scipy.sparse import hstack X_train_final hstack([X_train_tfidf, struct_scaled])注意hstack要求 TF-IDF 输出为scipy.sparse矩阵结构特征需为numpy.ndarray。若直接np.hstack会报错必须用scipy.sparse.hstack。3. 双模型训练与交叉验证评估流程单一模型易受数据分布偏移影响尤其当训练集存在公众号来源偏差时如某类营销号集中发布假新闻。本项目采用逻辑回归LR与 XGBoost 双模型策略LR 提供可解释性基线XGBoost 捕捉非线性交互。所有模型均通过 5 折 StratifiedKFold 验证确保每折中正负样本比例一致避免因label不均衡导致评估失真。3.1 逻辑回归L2 正则化与类别权重平衡微信数据集中 fake 标签1占比约 37%属轻度不均衡。直接使用class_weightbalanced会过度放大少数类权重反而降低 precision。实测class_weight{0:1, 1:1.8}在本数据集上取得最佳 F1from sklearn.linear_model import LogisticRegression from sklearn.model_selection import StratifiedKFold from sklearn.metrics import classification_report, roc_auc_score # 初始化带类别权重的 LR lr_model LogisticRegression( C1.0, # L2 正则强度C 越小正则越强 class_weight{0: 1, 1: 1.8}, # 手动调优的 fake 类权重 max_iter1000, # 防止收敛警告 random_state42 ) # 5 折分层交叉验证 skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) cv_scores {precision: [], recall: [], f1: [], auc: []} for train_idx, val_idx in skf.split(X_train_final, y_train): X_tr, X_val X_train_final[train_idx], X_train_final[val_idx] y_tr, y_val y_train[train_idx], y_train[val_idx] lr_model.fit(X_tr, y_tr) y_pred lr_model.predict(X_val) y_pred_proba lr_model.predict_proba(X_val)[:, 1] # 计算各指标 from sklearn.metrics import precision_recall_fscore_support p, r, f1, _ precision_recall_fscore_support(y_val, y_pred, averagebinary) auc roc_auc_score(y_val, y_pred_proba) cv_scores[precision].append(p) cv_scores[recall].append(r) cv_scores[f1].append(f1) cv_scores[auc].append(auc) print(fLR 5-fold CV: Precision{np.mean(cv_scores[precision]):.4f}±{np.std(cv_scores[precision]):.4f})3.1.1 为什么不用class_weightbalancedclass_weightbalanced计算公式为n_samples / (n_classes * n_samples_per_class)。本数据集n_samples12432,n_samples_per_class[7812, 4620]得出weight_0≈0.79,weight_1≈1.34。但实测该权重使模型过度敏感于假新闻precision 降至 0.72下降 5.3%而手动设为1.8后 precision 稳定在 0.78F1 提升 1.9%。3.2 XGBoost树深度与学习率协同调优XGBoost 在文本分类中易过拟合尤其当 TF-IDF 特征维度高时。关键在于限制max_depth4与learning_rate0.1的组合前者防止单棵树过度复杂后者要求更多迭代步数以稳定收敛。import xgboost as xgb # 转换为 XGBoost DMatrix支持稀疏矩阵 dtrain xgb.DMatrix(X_train_final, labely_train) # 参数设置经网格搜索验证 params { objective: binary:logistic, eval_metric: auc, max_depth: 4, # 核心防过拟合参数 learning_rate: 0.1, # 需配合 num_boost_round200 subsample: 0.8, # 随机采样80%样本建树 colsample_bytree: 0.8, # 随机采样80%特征 gamma: 0.1, # 分裂增益阈值进一步抑制过拟合 seed: 42 } # 5 折 CV 训练 cv_results xgb.cv( params, dtrain, num_boost_round200, nfold5, stratifiedTrue, metricsauc, seed42 ) best_round cv_results[test-auc-mean].idxmax() print(fXGBoost best AUC: {cv_results[test-auc-mean].max():.4f} at round {best_round})提示xgb.cv返回的cv_results是 pandas DataFrametest-auc-mean列即每轮的平均 AUC。idxmax()直接返回最优轮次索引避免手动遍历。3.3 模型融合加权投票提升鲁棒性LR 与 XGBoost 错误模式互补LR 易漏判长标题假新闻XGBoost 易误判含“辟谣”关键词的真新闻。采用 0.4(LR) 0.6(XGBoost) 加权概率融合在验证集上 F1 提升 0.012# 获取两个模型的预测概率 lr_proba lr_model.predict_proba(X_val)[:, 1] xgb_proba xgb_model.predict(xgb.DMatrix(X_val)) # xgb_model 为最终训练模型 # 加权融合 ensemble_proba 0.4 * lr_proba 0.6 * xgb_proba ensemble_pred (ensemble_proba 0.5).astype(int) # 计算融合后指标 p, r, f1, _ precision_recall_fscore_support(y_val, ensemble_pred, averagebinary)4. 测试集预测与结果导出submit.csv 格式严格校验submit.csv是竞赛提交标准格式仅含两列id行号和label0/1 预测值。但学生常忽略两点一是id必须从 1 开始连续编号二是label必须为整数而非浮点。main.py中的预测模块需强制类型转换与索引对齐# 加载测试集无 label 列 test_df pd.read_csv(test.news.csv) # 清洗测试标题 test_df[cleaned_title] test_df[Title].apply(clean_title) # 向量化必须用训练时的 vectorizer不可重新 fit X_test_tfidf vectorizer.transform(test_df[cleaned_title].apply(lambda x: .join(x))) # 提取结构特征用训练时的 scaler test_struct extract_structural_features(test_df) X_test_struct scaler.transform(test_struct) # 拼接特征 X_test_final hstack([X_test_tfidf, X_test_struct]) # 双模型预测 lr_pred_proba lr_model.predict_proba(X_test_final)[:, 1] xgb_pred_proba xgb_model.predict(xgb.DMatrix(X_test_final)) ensemble_pred_proba 0.4 * lr_pred_proba 0.6 * xgb_pred_proba # 生成 submit.csvid 从 1 开始label 为 int submit_df pd.DataFrame({ id: range(1, len(test_df) 1), # 强制从 1 开始 label: (ensemble_pred_proba 0.5).astype(int) # 必须 int不能 bool 或 float }) submit_df.to_csv(submit.csv, indexFalse) print(submit.csv generated successfully.)4.1 submit.csv 格式校验清单检查项正确示例常见错误后果文件名submit.csvsubmission.csvresult.csv平台拒绝上传列名id,labelID,Labelid,prediction解析失败id 起始第一行id1id0或缺失首行评分系统错位匹配label 类型0或1整数0.01.0TrueFalse提交失败或判为无效行数必须等于test.news.csv行数多一行或少一行全部判错注意test.news.csv共 3127 行submit.csv必须恰好 3127 行。可用wc -l submit.csv快速验证。5. 特征重要性可视化与假新闻关键词定位模型训练完成只是起点真正价值在于解释“为什么判定为假新闻”。XGBoost 内置get_booster().get_score()可导出特征重要性但原始 TF-IDF 特征名为f0,f1...需映射回实际词汇。本技巧通过vectorizer.get_feature_names_out()建立索引映射定位 top-10 判别词# 获取 XGBoost 特征重要性按 gain booster xgb_model.get_booster() importance_dict booster.get_score(importance_typegain) # 将数字索引转为词汇 feature_names vectorizer.get_feature_names_out() word_importance {} for idx_str, score in importance_dict.items(): idx int(idx_str[1:]) # f123 → 123 if idx len(feature_names): word_importance[feature_names[idx]] score # 取 top-10 top_words sorted(word_importance.items(), keylambda x: x[1], reverseTrue)[:10] print(Top 10 Fake News Indicators:) for word, score in top_words: print(f{word}: {score:.3f}) # 输出到文件便于分析 with open(top_fake_indicators.txt, w, encodingutf-8) as f: for word, score in top_words: f.write(f{word}\t{score:.3f}\n)5.1 微信假新闻高频判别词实测 top-5词汇重要性得分业务含义震惊12.74情绪煽动型标题标配真新闻极少使用感叹号结尾速看9.32制造紧迫感常见于营销号转发的谣言竟然7.85表达反常识暗示内容违背常理如“某地竟然下雪”转发6.41真新闻多用“发布”“报道”假新闻多用“转发”“扩散”辟谣5.98含该词标题中 83% 为真新闻但模型将其作为反向判据出现即大概率真技巧importance_typegain衡量该特征在所有树中带来的平方损失减少总和比weight分裂次数更能反映真实判别力。若发现f0权重最高但对应词汇为“的”说明min_df设置过低需重新调整。本文还有配套的精品资源点击获取
返回列表