
简介面向自然语言处理与网络欺诈检测研究者的中英文双语数据集汇总8,564个来自网络钓鱼诈骗、虚假招聘广告、社交媒体和新闻等场景的真实欺诈案例。数据包含基础集与升级集两部分基础集占25%、升级集占75%中英文案例均衡分布欺诈类型细分为欺诈服务、冒充、网络钓鱼、虚假招聘及网络关系等常见形式便于针对不同欺诈模式与多语种环境开展算法训练和效果评测。压缩包共4个文件均为JSON格式分别对应中英文基础集与中英文升级集四份独立数据文件可直接解析加载整体体积仅6.09MB轻量易用。目前已有156人学习下载。借助该数据集研究者可深入分析欺诈文本的词汇习惯、句式结构与语言风格识别紧急性措辞、虚假权威口吻等典型信号并据此构建跨语言欺诈识别模型应用于钓鱼邮件过滤、虚假招聘甄别等实际场景为提升互联网安全防护能力提供扎实的数据支撑。1. 8,564 条欺诈样本能做什么先看清这个数据集的边界做网络欺诈检测最稀缺的从来不是模型而是带可靠标注的文本数据。当你手头拿到一份包含 8,564 个欺诈案例的自然语言处理数据集先说结论这个规模放在深度学习里偏小放在真实业务里却刚好够用——因为大多数中小团队既没有人力标注海量数据也不需要跑一个千亿参数模型去识别一条钓鱼邮件。这个数据集的真正价值在于覆盖了四种典型场景网络钓鱼诈骗、虚假招聘广告、社交媒体垃圾信息和新闻类欺诈文本。它适合用来建立反欺诈文本分类的完整基线验证不同模型在短文本和长文本混搭场景下的表现差异也可以作为少样本学习、主动学习和模型上线前评估的起点数据。需要注意的是8,564 条意味着平均每个类别只有几千条样本直接硬怼深度模型容易过拟合真正的功夫在于数据拆解、特征设计和评估口径。2. 先拆数据集再动手四个文本来源的差异与标签设计2.1 四个来源的文本画像钓鱼邮件、假招聘、社媒短文本、新闻长文各吃哪一套特征不同来源的欺诈文本语言特征完全不是一回事。网络钓鱼诈骗通常包含紧迫性词汇、可疑 URL、索取凭证的指令性语句虚假招聘广告的典型特征是职位描述模板化、薪资异常偏高、联系方式指向免费邮箱社交媒体欺诈文本短、非正式、带大量表情符号和话题标签而且上下文信息稀薄新闻类欺诈文本则更长、更书面化常用权威口吻和伪科学措辞增加可信度。这种差异直接决定了特征工程和模型选型。钓鱼邮件适合加 URL 密度、域名年龄这类外部特征纯文本模型只能学到表面词汇社交媒体文本因为短需要依赖字符级特征和预训练模型的上下文理解能力假招聘广告则容易掉进模板陷阱模型可能学到的是“这段文字看起来像职位描述”而不是“这是虚假信息”。处理多来源混合数据第一步不是急着训练而是按来源统计文本长度分布、标点密度、URL 出现比例这些统计量能帮你看清楚每个类别的真实边界在哪。我在处理这类数据时有一个固定习惯先把 text 列拆出 train/validation/test然后对每个来源单独统计标签比例。如果某个来源的欺诈样本占比和其他来源差距过大这个来源要么需要单独建模要么需要重新审视标注口径。2.2 标签怎么定二分类欺诈还是多分类欺诈类型标题里的描述是“欺诈案例”落到标签设计上有两种走法把所有案例标成二分类的 fraud/legitimate或者把欺诈类型拆成 phishing、fake_job、social_spam、fake_news 四个细类。两种方案各有支撑理由。二分类的优势是任务简单、样本利用率高8,564 条数据按 80/10/10 切分后训练集还有近七千条对传统机器学习模型完全够用劣势是模型学到的决策边界是四种欺诈类型的混合体而且四个来源的文本风格差异巨大模型很容易学会“按文体分类”而不是“按欺诈性分类”——这是这类数据里最隐蔽的坑。多分类更适合做细粒度分析比如你想知道哪类欺诈最难被识别或者想让模型输出带类型的告警信息。但四分类会进一步摊薄每类的训练样本社交媒体短文本类尤其吃亏。我的建议是先跑二分类基线把整体 F1 拿到手再训练一个四分类模型做对比诊断。如果四分类模型在某个细类上显著拉胯这个细类单独拿出来做二分类通常能救回来。2.3 数据切分按来源切还是按样本随机切这个问题看起来琐碎实际决定了你的模型能不能上线。随机切分会让训练集和测试集来自同一分布模型容易拿到虚高的离线指标按来源切分则更接近真实上线场景——你训练时见过钓鱼邮件但上线时可能要处理从未见过的新型社交媒体诈骗文本。常见做法是同时做两套切分一套随机切分用来做模型选型和调参另一套按来源留出专门用来测试泛化能力。具体操作是先把四个来源按 7:3 分成大组训练时只喂前七成来源的数据验证时拿后三成来源的文本出来压测。这里有个前提条件四个来源的样本量本身就不均衡按来源切分后小类别的验证集会非常稀薄评估结果方差很大需要多看几个随机种子下的表现不要被单次跑分忽悠。3. 用 TF-IDF 逻辑回归打基线不调参先看翻车点在哪3.1 特征工程文本长度、URL 占比、数字密度与 TF-IDF 的组合在 8,564 条这种规模的数据上逻辑回归加 TF-IDF 的基线组合仍然是最值得先跑的方案。它训练快、可解释性强还能帮你在半小时内摸清数据的难度。纯词袋特征对这类反欺诈数据的覆盖度不够需要叠加几个手工特征来补盲区。我一般会加三类特征文本长度字符数和词数URL 相关特征URL 数量、URL 占比、是否包含 IP 地址以及特殊符号密度数字占比、大写字母占比、标点密度。钓鱼邮件里的 IP 直链、假招聘广告里的异常高薪数字都是纯词表学不到的强信号。把这些特征拼进 TF-IDF 矩阵逻辑回归的 F1 通常能提升三到五个点效果立竿见影。特征拼装时注意尺度问题。TF-IDF 矩阵的值已经做了归一化但文本长度是几百到几千这样的大数值直接拼接会让逻辑回归的梯度优化偏向大尺度特征。要么对统计特征做标准化要么干脆只用那些本身就是比例类型的特征比如 URL 占比、数字密度这类 0 到 1 之间的数值不会干扰权重学习。3.2 最小可行代码TF-IDF 向量化到逻辑回归训练一条龙下面是一段能直接跑通的最小代码数据文件按 CSV 组织包含 text 和 label 两列。import pandas as pd import re from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from scipy import sparse # 加载数据 df pd.read_csv(fraud_data.csv) X_raw df[text].fillna() y df[label].astype(int) # 统计特征长度、URL数量、数字密度 def build_stat_features(texts): urls [len(re.findall(rhttps?://[^\s]|www\.[^\s], t)) for t in texts] digits [sum(c.isdigit() for c in t) / max(len(t), 1) for t in texts] return pd.DataFrame({url_count: urls, digit_ratio: digits}) stat_feats build_stat_features(X_raw) # TF-IDF1-2 gram 对抓短语更有效 vectorizer TfidfVectorizer( ngram_range(1, 2), max_features20000, min_df2, sublinear_tfTrue, strip_accentsunicode ) tfidf vectorizer.fit_transform(X_raw) # 合并特征矩阵 X sparse.hstack([tfidf, sparse.csr_matrix(stat_feats.values)]) # 切分并训练 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) clf LogisticRegression(C1.0, class_weightbalanced, max_iter2000) clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(classification_report(y_test, y_pred))这段代码的核心逻辑分四层先构建统计特征来补充词袋模型看不见的信号然后做 TF-IDF 向量化并压缩到 20,000 维再把两组特征横向拼接成稀疏矩阵最后用带类别权重平衡的逻辑回归训练。class_weightbalanced是刻意加的因为欺诈文本数据集通常存在类别不均衡不处理的话模型会把多数类全猜对、少数类全错。C1.0是逻辑回归默认的正则化强度第一次跑基线保留默认值就好不要一上来就调参先看未调参的翻车点在哪。值得注意的参数是sublinear_tfTrue。它把词频做了对数压缩对长新闻文本和短社交媒体文本混搭的数据集很友好避免长文本里的高频词把短文本的稀有词信号淹没。min_df2过滤掉只出现一次的词汇八千多条数据里这种词大多是噪声。3.3 评估口径为什么准确率在这里是最没用的指标反欺诈数据集的标签分布通常不均衡。假设合法样本占 85%欺诈样本占 15%一个把全部样本都预测为合法的“聪明”分类器也能拿到 85% 的准确率但它对业务毫无意义——你的目标是找出欺诈案例不是判断大部分案例合法。这里必须盯住的指标是 Precision、Recall 和 F1-score尤其要关注少数类欺诈类的召回率。召回率太低意味着大量漏网之鱼精度太低则意味着人工审核团队要被大量假阳性淹没。真实业务中还要给误报设置代价权重比如金融场景中漏报一笔网络钓鱼的代价远超误报十笔。评估时还应该输出 ROC 曲线和 PR 曲线。类别不均衡场景下 PR 曲线比 ROC 更有参考价值因为 ROC 对多数类的表现过于乐观。如果你发现模型在 validation 上的 F1 上蹿下跳先怀疑切分方式而不是模型本身换几个随机种子重跑取均值才是靠谱的基线。4. 微调 BERT 类模型短文本欺诈分类的进阶路线4.1 为什么在小数据集上还要用预训练模型八千多条数据微调 BERT听起来像杀鸡用牛刀但在反欺诈领域这条路反而常见。原因在于欺诈文本的语言风格高度依赖于上下文钓鱼邮件里的“验证您的账户”和合法邮件的“验证您的账户”字面完全一样但前者链接指向可疑域名后者指向官方域名词袋模型永远看不到这个差异预训练模型却能从句法和上下文里捕捉到细微的语气差别。更重要的是社交媒体诈骗短文本几乎没有词频信号可言一条二十个字的诈骗推文和一条二十个字的正常推文TF-IDF 的向量可能高度相似但 BERT 的注意力机制能从措辞组合方式上找到破绽。预训练模型自带的通用语言知识在这种样本量有限的任务里相当于给模型注入了迁移学习的先验比从头训练效果稳好几个档次。代价是训练时间和推理成本大幅上升而且短文本上微调 BERT 很容易过拟合。这里的关键不是“用不用 BERT”而是怎么配置训练策略来压住过拟合同时保住预训练模型学到的通用范式。4.2 微调代码加载、训练、早停与结果保存用 Transformers 库在本地跑通 BERT 微调的流程如下代码可以直接保存为脚本执行。import pandas as pd from sklearn.model_selection import train_test_split from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer, EarlyStoppingCallback ) df pd.read_csv(fraud_data.csv) df[text] df[text].fillna() train_texts, val_texts, train_labels, val_labels train_test_split( df[text].tolist(), df[label].tolist(), test_size0.15, random_state42, stratifydf[label] ) # 加载模型和分词器9类之外加上二分类头 model_name bert-base-uncased tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForSequenceClassification.from_pretrained( model_name, num_labels2 ) # 预编码max_length64 对短文本足够 train_enc tokenizer( train_texts, truncationTrue, paddingTrue, max_length64 ) val_enc tokenizer( val_texts, truncationTrue, paddingTrue, max_length64 ) import torch class FraudDataset(torch.utils.data.Dataset): def __init__(self, encodings, labels): self.encodings encodings self.labels labels def __getitem__(self, idx): item {k: torch.tensor(v[idx]) for k, v in self.encodings.items()} item[labels] torch.tensor(self.labels[idx]) return item def __len__(self): return len(self.labels) train_dataset FraudDataset(train_enc, train_labels) val_dataset FraudDataset(val_enc, val_labels) args TrainingArguments( output_dir./fraud_bert, num_train_epochs3, per_device_train_batch_size16, per_device_eval_batch_size32, learning_rate2e-5, weight_decay0.01, warmup_steps100, evaluation_strategyepoch, save_strategyepoch, load_best_model_at_endTrue, metric_for_best_modeleval_f1, greater_is_betterTrue, fp16False ) trainer Trainer( modelmodel, argsargs, train_datasettrain_dataset, eval_datasetval_dataset, compute_metricscompute_metrics_fn, # 自定义函数返回 F1 callbacks[EarlyStoppingCallback(early_stopping_patience2)] ) trainer.train()这段代码的配置核心是防过拟合策略。load_best_model_at_endTrue配合EarlyStoppingCallback(patience2)会让模型在验证集 F1 连续两个 epoch 不再上升时自动停止训练并回滚到最优 checkpoint这是在小数据集上微调的必要手段。weight_decay0.01是给权重加 L2 正则warmup_steps100是为了让模型在训练初期学习率平缓爬升避免预训练权重被大步长更新冲坏。max_length64这个参数值得多说两句。这个数据集的文本来源跨度大社交媒体短文本可能不足二十个 token钓鱼邮件和新闻可能超过五百个 token。直接截断到 64 会丢掉新闻类长文的后半段信息拉长到 512 又会让 batch size 被迫缩小、训练时间暴涨。实际做法是先用分词器统计全量样本的 token 长度分布取 90 分位作为max_length而不是拍脑袋定值。4.3 三个必调参数学习率、max_length、batch size 与它们的连带关系小数据集上微调 BERT最重要的参数是学习率。2e-5 是全量微调的安全起点但这个值需要根据验证集的表现微调如果训练 loss 降不下去适当上调到 3e-5如果验证集 F1 在早期就停滞先检查是否过拟合再尝试降到 1e-5。学习率与 batch size 高度耦合更大的 batch 意味着更稳定的梯度估计可以适当提高学习率但小数据集上用大 batch 会加速过拟合16 是一个不容易出错的起点。max_length的选择直接影响训练速度和语义完整性。短文本任务用 64 到 128 足够长文任务至少要 256。注意max_length拉长后fp16可以考虑开启来缓解显存压力但带来的数值稳定性问题在少量样本场景下不容忽视如果训练 loss 出现 NaN先关掉 fp16 排查。batch size 与梯度累积是另一个连带关系。显存不够时常见的做法是减小 batch size 到 8同时设置gradient_accumulation_steps2来模拟 16 的等效 batch。但过小的真实 batch size 会让 BN 层的统计量不稳定在 Transformer 模型里影响相对小却仍然存在。我的建议是优先保证 batch size 不小于 8再靠梯度累积来凑等效大小。5. 在这类数据上翻过车的 5 个坑逐条给后悔药5.1 把 URL 当正文喂进去模型学到的只有长度一开始做特征时偷懒直接把 URL 串在正文里丢给 TF-IDF 和 BERT 训练结果验证集 F1 虚高但上线后一塌糊涂。后来观察模型对样本的注意力权重发现它根本没看正文语义而是在数 URL 的长度和数量——钓鱼邮件里动辄三四个超长 URL模型记住的是“URL 多就是欺诈”而不是“这个链接可疑”。原因很清楚URL 是高度非结构化的字符串词表里根本不会出现相同的 URLTF-IDF 只能把它当噪声BERT 的注意力也只能学到 URL 与标签的相关性。解决方法是做文本规范化用正则把 URL 替换成特殊标记[URL]把邮箱地址替换成[EMAIL]把数字串替换成[NUM]。这样模型学到的是“这段文本包含链接”这个结构性信号而不是某一个具体的链接字符串。同样的逻辑适用于手机号、银行卡号这类高价值实体。原始文本直接进模型这些实体是非重复的高基数字符串白白占用词汇表位置还会让模型记住训练集里的特定号码造成严重的过拟合假象。5.2 类别不均衡被准确率粉饰召回率高不起来第一次跑完基线准确率 91%看着挺像样。打印出 classification_report 之后发现欺诈类的 F1 只有 0.62召回率更是低到 0.48。所有看起来不错的指标全是多数类贡献的。原因在于多数类样本在训练中占据绝对主导模型只要学会把少数类倾向性分类为多数类就能把 loss 压得很低。解决方法是组合拳class_weightbalanced是最简单的一招如果效果不明显改用过采样/欠采样或者用 focal loss 代替交叉熵损失。在 8,564 条这种规模的数据上我更推荐先调class_weight因为它不需要改动训练流程逻辑回归几行代码就能试完。如果模型上线后的业务指标还是不达标检查你的评估指标是不是纸面指标——离线 F1 再高如果 PR 曲线的操作点不在合理范围内真实召回率照样难看。回归到业务上定义好坏样本的代价权重才是正路。5.3 短文本微调 BERT 直接过拟合现象、原因与处理用 BERT 跑这批数据的时候训练 loss 第一个 epoch 就降到 0.1 以下验证集 F1 却纹丝不动典型的过拟合前兆。短文本数据维度低、重复模式多模型很快记住了训练集里的措辞套路在验证集上却遇到没见过的表述就失效。尤其在社交媒体这一类短文本上很多 hashtag 和表情符号只出现在训练集里模型学到的是死记硬背。针对短文本场景我做过的有效处理是冻结底层参数只微调最后两三层。给 AutoModelForSequenceClassification 传入parameter.requires_grad False把前几层 BERT 参数冻住只更新分类层和最后两层 Transformer 块。这样模型保留更多通用语言知识同时减少可训练参数量过拟合压力大幅下降。另一招是文本增强。把训练文本里的词做同义词替换、随机删除副词、在句子中加噪每样本扩展成两三份。对这种小数据集数据增强对稳定性的贡献相当可观。注意这里不要改变标签语义——欺诈文本增强后必须还是欺诈这个前提在社交媒体垃圾信息上还容易满足在新闻类长文上就要小心了。5.4 按来源切分不彻底验证集得了高分模型在随机切分的验证集上 F1 是 0.87上线后掉到 0.70 以下。排查发现数据里的钓鱼邮件全部来自某一年的安全报告合集随机切分时同一篇报告衍生出的多条文本会被同时分进训练集和验证集标题变体、结尾签名都高度相似导致验证集虚胖。这正是我在前面强调按来源切分的原因。真实场景中欺诈者会不断变换手法你的模型迟早要面对训练分布之外的文本。建议至少留出一个来源的全部样本作为压力测试集比如训练时完全不喂社交媒体数据把模型在社交媒体上的表现当作可接受下限的参照。这个“下限指标”比随机切分得到的指标更能反映上线后的真实水平。5.5 模板文本带来的伪特征假招聘广告里全是“职位描述”虚假招聘广告数据集里有个隐蔽陷阱欺诈帖为了显得可信通常会套用大量正常职位描述的模板语句“负责日常运营管理工作”“要求五年以上相关经验”这类内容几乎每条都有。模型如果学到的特征是“这段文本属于职位描述文体”它自然会判断这很像欺诈——不是因为它识别出了薪资异常而是因为它见过太多同样措辞的欺诈样本。这个问题的根源是欺诈样本的正样本共享了大量模板文本缺乏多样性。解决方法是做数据清洗在训练前先做一次去重删除结构完全相同的模板片段保留差异化的部分。也可以尝试在训练时屏蔽高频无信息词把职位描述里的通用动词加进停用词表。更稳健的做法还是回到标注质量上——标注人员在采集样本时应该有意识地覆盖多个模板变体避免大量抓取同一来源的相似文本。6. 把基线做成能用的系统置信度阈值校准与主动学习闭环6.1 用预测概率而非标签来做业务决策离线评估完成后模型返回的二值标签不能直接用于业务。欺诈检测的成本远不对称漏过一个真实的网络钓鱼攻击可能造成数万元损失误杀一个正常客户的账户验证邮件则只会带来一次客服投诉。正确做法是取预测概率按业务代价选定阈值。from sklearn.metrics import precision_recall_curve import numpy as np # 用验证集预测概率来找阈值 val_probs clf.predict_proba(X_test)[:, 1] precision, recall, thresholds precision_recall_curve(y_test, val_probs) # 场景漏报代价是误报的5倍偏向高召回一侧 cost_fp, cost_fn 1.0, 5.0 total_cost np.inf best_threshold 0.5 for i, thr in enumerate(thresholds): # 计算此阈值下总代价 p, r precision[i], recall[i] fp_rate 1 - p # 粗略近似 fn_rate 1 - r cost fp_rate * cost_fp fn_rate * cost_fn if cost total_cost: total_cost cost best_threshold thr print(f最优阈值: {best_threshold:.3f}, 近似最小代价: {total_cost:.3f})这个片段干的事情是把概率转成业务成本曲线然后挑出最省钱的那个阈值。实际业务中建议保守设定阈值偏低让更多可疑样本进入人工复核队列而不是直接拦截。毕竟人工复核可以纠正模型的误判而漏掉的欺诈是不可逆的。6.2 主动学习把模型不确定的样本送人工复核模型迭代到瓶颈期后单纯堆数据未必有效因为新采样的数据可能大多是模型已经能判准的简单样本。主动学习的思路是让模型自己挑出最不确定的样本先请人工标注再加入训练集迭代。这一步对落地价值极大能显著降低标注成本。具体操作可以这样走每批次推理全量待标注数据取预测概率靠近 0.5 的样本作为不确定样本人工标注后增量训练同时每隔几个批次重新校准一次阈值。这套闭环跑几轮模型对冷门欺诈手法的覆盖会肉眼可见地提升。我的个人习惯是永远保留一个“不确定池”把预测概率在 0.3 到 0.7 之间的样本全部沉淀下来。这个池子的意义不只是用来训练更是用来发现欺诈者的新套路——当池子里突然涌入一批聚集出现的相似文本通常意味着一种新型欺诈正在蔓延赶在标注前先人工瞄一眼经常能提前拦截一轮攻击。做这类项目守住评估口径和样本分布比追逐 SOTA 更值钱。希望这份笔记能帮你少走几段弯路。本文还有配套的精品资源点击获取