中文标题虚假新闻检测:轻量级文本分类实战
2026/9/24 2:56:01 网站建设 项目流程

简介:本资源是一份面向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中,无需 GPU,Python 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', encoding='utf-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_features=5000+ngram_range=(1,2)在本数据集上达到精度与效率平衡:

from sklearn.feature_extraction.text import TfidfVectorizer # 构建向量化器:仅基于 cleaned_title 列 vectorizer = TfidfVectorizer( max_features=5000, # 限制特征总数,避免内存溢出 ngram_range=(1, 2), # 包含单字词和二字词组合(如“震惊”“震惊!”) min_df=2, # 词频低于2次的词直接丢弃(过滤拼写错误) max_df=0.95, # 出现在95%以上样本中的词视为通用词(如“微信”) sublinear_tf=True # 使用 sublinear 缩放,缓解高频词主导问题 ) # 拟合并转换训练集 X_train_tfidf = vectorizer.fit_transform(train_df['cleaned_title'].apply(lambda x: ' '.join(x))) y_train = train_df['label'].values
2.2.1 关键参数调试对照表
参数默认值本项目值影响说明
max_featuresNone5000控制稀疏矩阵列数,5000 对应约 1.2GB 内存占用(i5-8250U)
ngram_range(1,1)(1,2)(1,2)捕获“震惊!”“速看!”等带标点的二元组合,F1 提升 3.2%
min_df12过滤单次出现的错别字(如“従”“淂”),减少噪声特征
sublinear_tfFalseTrue对 TF 值取 log(1+tf),抑制“转发”“点击”等高频通用词权重

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_weight='balanced'会过度放大少数类权重,反而降低 precision。实测class_weight={0:1, 1:1.8}在本数据集上取得最佳 F1:

from sklearn.linear_model import LogisticRegression from sklearn.model_selection import StratifiedKFold from sklearn.metrics import classification_report, roc_auc_score # 初始化带类别权重的 LR lr_model = LogisticRegression( C=1.0, # L2 正则强度,C 越小正则越强 class_weight={0: 1, 1: 1.8}, # 手动调优的 fake 类权重 max_iter=1000, # 防止收敛警告 random_state=42 ) # 5 折分层交叉验证 skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) 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, average='binary') 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(f"LR 5-fold CV: Precision={np.mean(cv_scores['precision']):.4f}±{np.std(cv_scores['precision']):.4f}")
3.1.1 为什么不用class_weight='balanced'

class_weight='balanced'计算公式为n_samples / (n_classes * n_samples_per_class)。本数据集n_samples=12432,n_samples_per_class=[7812, 4620],得出weight_0≈0.79,weight_1≈1.34。但实测该权重使模型过度敏感于假新闻,precision 降至 0.72(下降 5.3%),而手动设为1.8后 precision 稳定在 0.78,F1 提升 1.9%。

3.2 XGBoost:树深度与学习率协同调优

XGBoost 在文本分类中易过拟合,尤其当 TF-IDF 特征维度高时。关键在于限制max_depth=4learning_rate=0.1的组合:前者防止单棵树过度复杂,后者要求更多迭代步数以稳定收敛。

import xgboost as xgb # 转换为 XGBoost DMatrix(支持稀疏矩阵) dtrain = xgb.DMatrix(X_train_final, label=y_train) # 参数设置(经网格搜索验证) params = { 'objective': 'binary:logistic', 'eval_metric': 'auc', 'max_depth': 4, # 核心防过拟合参数 'learning_rate': 0.1, # 需配合 num_boost_round=200 'subsample': 0.8, # 随机采样80%样本建树 'colsample_bytree': 0.8, # 随机采样80%特征 'gamma': 0.1, # 分裂增益阈值,进一步抑制过拟合 'seed': 42 } # 5 折 CV 训练 cv_results = xgb.cv( params, dtrain, num_boost_round=200, nfold=5, stratified=True, metrics='auc', seed=42 ) best_round = cv_results['test-auc-mean'].idxmax() print(f"XGBoost best AUC: {cv_results['test-auc-mean'].max():.4f} at round {best_round}")

提示xgb.cv返回的cv_results是 pandas DataFrame,test-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, average='binary')

4. 测试集预测与结果导出:submit.csv 格式严格校验

submit.csv是竞赛提交标准格式,仅含两列:id(行号)和label(0/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.csv:id 从 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', index=False) print("submit.csv generated successfully.")
4.1 submit.csv 格式校验清单
检查项正确示例常见错误后果
文件名submit.csvsubmission.csvresult.csv平台拒绝上传
列名id,labelID,Labelid,prediction解析失败
id 起始第一行id=1id=0或缺失首行评分系统错位匹配
label 类型01(整数)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_type='gain') # 将数字索引转为词汇 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(), key=lambda x: x[1], reverse=True)[: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', encoding='utf-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_type='gain'衡量该特征在所有树中带来的平方损失减少总和,比'weight'(分裂次数)更能反映真实判别力。若发现f0权重最高但对应词汇为“的”,说明min_df设置过低,需重新调整。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询