ARTICLE DETAIL

资讯详情

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

诈骗电话识别机器学习项目复盘:从29779名到可落地的反诈模型

诈骗电话识别机器学习项目复盘:从29779名到可落地的反诈模型 简介机器学习在处理极端不平衡的分类任务时常面临样本分布稀疏、指标失效、时间漂移等挑战。基于通话详单的特征工程是区分正常通信与诈骗行为的关键——频次、时序、关系网络等维度能有效刻画异常模式。然而若特征统计跨越未来时间线就会引发特征泄漏导致线下验证虚高、线上效果崩盘。合理划分时序验证集、采用代价敏感学习或阈值后处理是提升模型鲁棒性的有效手段。这类技术不仅适用于电信反诈也广泛用于金融风控、异常检测等领域。本文基于一个排名落后的比赛项目复盘系统梳理了从数据清洗、特征构建、模型选型到避坑的完整流程为同类风控建模提供可复用的工程经验。1. 项目回顾我为什么捡起这个第29779名的“失败项目”2020年数字四川创新大赛里我提交了一个诈骗电话识别系统的机器学习模型最终排名定格在第29779名。说实话这个名次摆出来并不好看甚至有点丢人。但前两天整理硬盘时翻到了当时的项目压缩包“诈骗电话识别系统2020数字四川创新大赛-Rank29779-机器学习模型.zip”解压之后重新读了一遍自己的代码和笔记反而觉得这个项目值得好好写一篇复盘。原因很简单名次低不等于没有价值。29779这个数字背后藏着我对这个赛题全部的错误理解、踩过的坑、以及后来才想明白的改进方向。如果你也在做类似的电信诈骗识别、异常通话检测、风控模型或者只是想看看一个机器学习比赛项目从0到1的完整流程应该怎么走这篇文章也许能帮你少走几个月的弯路。项目本身不复杂主办方提供了脱敏后的通话详单数据要求参赛者构建模型预测某条通话记录是否为诈骗电话。文件包里包含了数据处理脚本、特征工程代码、LightGBM训练脚本、预测结果CSV以及我当时的模型说明文档。当时我的技术栈是Python pandas scikit-learn LightGBM属于非常标准的机器学习流程。后面我会把每一步展开讲清楚。先说结论这个项目最大的问题不在模型而在对业务场景的理解。我用了大量篇幅调参却没有花足够时间研究数据的时间分布导致模型在本地验证集上表现还行提交后分数一落千丈。这一点我后面会仔细讲。2. 赛题拆解诈骗电话识别到底在解决什么问题2.1 把业务问题翻译成机器学习问题诈骗电话识别本质上是电信运营商、公安反诈中心、金融风控系统都在做的异常通信检测。放到比赛语境里问题被简化成一个有监督的二分类任务给定一通电话的主被叫号码、通话时间、通话时长、基站信息等脱敏字段预测该通话是否属于诈骗电话。数据层面主办方提供了训练集和测试集。训练集里每条样本都有一个标签0表示正常1表示诈骗测试集没有标签需要参赛者输出预测概率。评估指标我记得当时采用的是AUC也就是ROC曲线下面积这也是这类不平衡二分类问题最常用的指标之一。但把“识别诈骗电话”翻译成“二分类预测”只是第一步。真正的难点在于诈骗电话的分布不是均匀的它们通常只占全部通话的极小比例。如果直接用原始数据训练模型哪怕把所有样本都预测为正常准确率也能高达99%以上但模型实际上是废的。所以问题的本质是不平衡学习而不是普通的分类。2.2 这个赛题真正难在哪里我后来复盘时总结了四个难点基本覆盖了这个赛题和同类真实风控场景的核心挑战。第一样本极度不平衡。诈骗电话在所有通话中的占比可能只有千分之几甚至更低。用常规的准确率评估模型会完全失效必须用AUC、PR-AUC这类对不平衡不敏感的指标。第二特征工程难度大。原始通话详单一般只有号码、时间、时长、位置等字段但真正能区分诈骗电话的信号往往需要从这些字段里“造”出来。比如一个号码在短时间内对外呼出几百次大概率是骚扰/诈骗凌晨两三点还在高频外呼嫌疑也很大主叫号码的被叫用户分布如果特别分散也可能是批量拨打的特征。第三时间维度上的分布漂移。这是我认为最关键的难点也是我这次比赛翻车的主要原因。诈骗电话的行为模式会随事件变化比如某个时段诈骗团伙集中使用某类号段过段时间又换了。如果训练集和测试集的时间跨度不同模型很容易过拟合训练期内的特定模式导致线上成绩和线下验证差异巨大。第四业务可解释性要求高。在真实反诈场景中模型预测一个号码是诈骗电话后风控人员需要知道“为什么”否则无法人工复核也无法向用户解释停机/拦截的原因。所以纯黑盒模型在实际落地时会有很大阻力比赛中虽然不强制要求但好的特征设计本身就能提升可解释性。3. 数据处理与特征工程从通话记录里挖出“诈骗的味道”3.1 数据清洗与样本构造拿到解压后的zip数据包第一件事不是建模而是把数据吃透。我当时的数据集大概是这样的每行一条通话记录包含主叫号码脱敏ID、被叫号码脱敏ID、通话开始时间、通话时长、主叫归属地、被叫归属地、主叫基站、被叫基站等字段。部分字段存在缺失比如基站信息在某些样本里是空值。数据清洗阶段我主要做了三件事。第一处理缺失值基站信息缺失率较高的字段我直接保留为“未知”类别而不是粗暴地删除整行因为缺失本身可能包含信息比如诈骗分子可能处于移动状态导致基站信息异常。第二去除异常值通话时长为0或超过24小时的记录明显不合理直接过滤。第三时间字段转换将通话开始时间拆成年、月、日、小时、星期几等特征方便后续做周期分析。样本构造上我没有直接让模型用单条通话记录作为输入而是先按主叫号码聚合再按时间段切分构造了“某个号码在某段时间窗口内的通话行为画像”。这么做是因为单条通话记录几乎没有区分度——诈骗电话和正常电话在单条记录上长得一模一样必须从行为模式中找差异。3.2 特征体系的搭建特征工程是整个项目里投入时间最多、也最值得展开讲的部分。我根据业务直觉把特征分成了四类每一类都有它独特的作用。第一类是频次与密度特征。核心思想是一个号码在短时间内产生大量通话大概率不是正常人的使用习惯。我按1小时、6小时、24小时三个窗口统计了每个主叫号码的外呼次数、被叫去重数量、被叫号码重合度等。短时间窗口内的高频外呼是诈骗电话最典型的行为特征之一。第二类是时序规律特征。正常人的通话习惯有昼夜节律而自动外呼系统没有。我从主叫号码的通话记录中提取了夜间0点至6点通话占比、每小时通话次数方差、工作日与周末通话比例等。诈骗号码往往作息“全天无休”或者集中在某个特定时段狂打这些特征能有效捕捉异常。第三类是关系网络特征。这是我认为区分度最高、但也是我当时做得最不充分的一类。如果两个号码频繁互相通话并且共同外呼了一批被叫号码它们之间可能存在团伙关系。我当时受限于时间和算力只做了简单的聚合比如号码的通话对象种类数、重复通话比例、是否出现“一对多”辐射式结构。后来复盘时发现如果能做更深入的图特征分析比如PageRank、连通分量、社区发现模型效果大概率能提升一截。第四类是号码静态特征。包括主叫号码的号段分布、归属地集中度、被叫号码在空间上的分散程度等。例如一个主叫号码的被叫用户分布在全国十几个省份明显不符合个人社交关系这就是一个强特征。需要特别提醒的是特征工程的每一步都要时刻警惕特征泄漏。我当时就踩过这个坑后面在踩坑实录里会详细讲。4. 模型选型与训练细节为什么选择GBDT而不是深度学习4.1 模型选择逻辑表格数据优先考虑GBDT在特征工程完成后我面临的模型选择问题是用深度学习还是用梯度提升树我的结论是对于这种以结构化表格数据为主、特征维度几百维、样本量在十万到百万级别的任务GBDT梯度提升决策树系列模型是最稳妥、最高效的选择。深度学习在处理图像、文本、语音等非结构化数据上有绝对优势但在表格数据上GBDT的泛化能力、训练速度和可解释性通常都优于深度模型。LightGBM和XGBoost是GBDT的两大代表。我当时在LightGBM和XGBoost之间做了对比最终选择了LightGBM。原因有三第一LightGBM使用基于直方图的算法训练速度快内存占用低适合快速迭代实验第二它对类别特征有原生支持省去了大量的One-Hot编码工作第三Leaf-wise的叶子生长策略在调整好参数的情况下往往能比XGBoost的Level-wise策略获得更好的精度。当然XGBoost也有它的优势比如正则化更强、对缺失值处理更成熟但在当时的时间压力和算力条件下LightGBM的性价比更高。参数调优上我使用了Optuna框架做贝叶斯搜索重点调优的参数包括n_estimators树的数量、learning_rate学习率、num_leaves叶子节点数、max_depth最大深度、min_child_samples叶子节点最小样本数、feature_fraction和bagging_fraction特征和样本采样比例等。核心思路是学习率设小一点树的数量相应增多同时通过限制叶子节点数和最小样本数来防止过拟合。4.2 不平衡样本的处理策略不是简单加权重就完事对于诈骗电话这种极端不平衡场景我想重点展开讲因为这里面有太多细节容易被忽略。第一个方案是采样法。常见的有过采样比如SMOTE合成少数类样本、欠采样从多数类样本里随机剔除和混合采样。我实验下来发现SMOTE在这个数据集上效果并不理想因为它基于K近邻做线性插值容易在特征空间的稀疏区域生成无意义的样本反而引入了噪声。欠采样则会导致信息损失严重模型欠拟合。最终我放弃了采样法主用方案是调整类别权重。第二个方案是代价敏感学习。LightGBM的scale_pos_weight参数可以给少数类样本赋予更高的权重让模型更加关注诈骗电话。具体做法是统计训练集中正负样本比例然后设置scale_pos_weight 负样本数 / 正样本数。这个参数我试过从1到20的多个取值发现并不是越大越好过高的权重会让模型把大量正常通话误判为诈骗导致AUC下降。最终选定的值在5左右效果最优。第三个方案是阈值后处理。模型输出的概率本身没有直接意义需要选择一个阈值进行二分类。在不平衡场景下默认的0.5阈值几乎总是错误的。我通过PR曲线和KS统计量来选择最优阈值让召回率抓到诈骗电话的比例和精确率判为诈骗的样本中真的是诈骗的比例取得一个平衡。在风控场景中通常宁可误杀也不漏杀所以阈值会适当降低。还需要强调的是评价指标和验证方式在建模前的第一步就要定下来。AUC计算简单且对不平衡不敏感但它更关心排序能力不关心概率校准。如果业务场景需要输出精确的概率还需要做Platt Scaling或Isotonic Regression进行概率校准。5. 踩坑实录从29779名复盘这场比赛5.1 最大的坑时间穿越与特征泄漏这是我最想分享的教训也是导致排名低的最关键原因。我在做特征工程时用了整个训练期间的数据来统计号码的历史行为特征却在预测测试集时也按同样的方式去算。问题在于如果我用来构造特征的时间窗口包含了未来信息比如用某号码在训练期内所有时间的通话记录来统计自然包含了测试期之后才发生的事件就构成了特征泄漏。这种泄漏会让模型在训练时表现异常优秀但一旦应用到新数据上就直接崩盘。当时我犯的具体错误是用主叫号码在整个训练集的全部记录来统计“平均通话时长”“每小时外呼次数”等特征然后把这个特征用于训练集和测试集。表面上看起来没问题但实际上当训练集和测试集来自不同时间区间时用全量统计的特征等价于让模型偷看了未来数据。我在本地交叉验证时得到的AUC高达0.95左右提交后成绩却天差地别根本原因就在这里。正确的做法是特征统计必须严格限制在预测时刻之前的已知信息范围内。比如预测某条通话是否为诈骗只能用该条通话发生时刻之前的历史记录来构造特征绝不能用整个时间窗的全局数据。这就像股票预测模型不能用未来的K线数据去算均线一样。5.2 验证集划分错误导致的结果虚高我在比赛中用的是随机划分训练集和验证集也就是把全部训练样本按比例随机打散。这种划分方式在普通分类问题上没问题但在时间序列相关的场景中是有缺陷的。诈骗电话的分布随时间变化显著随机划分会让训练集和验证集都包含完整的各个时间段的数据模型相当于“见过了”所有时间段的模式验证集上的表现自然虚高。真实场景中我们永远是用过去的数据预测未来所以更严谨的做法是按时序划分用前70%的时间段做训练后30%的时间段做验证模拟真实上线情况。我当时在交叉验证阶段反复调参始终觉得模型效果不错直到提交后才恍然验证集本身就不合理调参再努力也白费。这是所有时序相关机器学习项目中最常见的陷阱之一。5.3 无效特征和缺失值处理不当我的特征工程最初做了将近300个特征但并没有系统性地做特征筛选。很多特征之间存在高度相关性比如“24小时内呼出次数”和“24小时内呼出去重次数”就高度相关。冗余特征不仅增加了训练时间还会让模型更容易过拟合。后来我用了两种方式做特征筛选。一是基于LightGBM的特征重要性排序只保留重要性排名前80%的特征。二是计算特征之间的相关系数矩阵去除相关性高于0.9的冗余特征。最终特征数量从300降到了170左右模型效果反而提升了。缺失值方面的坑也值得一提。我的数据里电话号码的归属地字段有约30%的缺失我当时简单填充了“未知”。但后来发现诈骗号码的归属地缺失率远高于正常号码这本身就是一个强信号。正确的做法是把“是否缺失”也作为一个特征而不是简单填充后丢掉信息。缺失值处理的核心原则是让模型意识到“这个字段缺失了”而不只是遮盖缺失。5.4 我踩过的坑汇总表整理了一张表记录这个项目中最典型的几个问题、原因和解决方案方便你对照检查自己的项目。问题原因正确做法本地AUC高但线上分数差特征统计包含了未来信息造成特征泄漏用“预测时刻之前”的滑动窗口构造特征随机划分训练/验证集导致结果虚高忽略了诈骗模式的时间漂移按时序切分用过去预测未来SMOTE过采样效果差在高维稀疏空间插值产生噪声使用类别权重或代价敏感学习特征维度过高、冗余严重没有做系统性的特征筛选重要性排序相关性去重缺失值简单填充丢弃了“缺失本身是信号”的信息将“是否缺失”作为独立特征默认0.5分类阈值不平衡场景下0.5不是最优分界点根据PR曲线/K-S选择最优阈值6. 如果重来一次一套可复用的完整改进路线6.1 先用规则引擎捉漏网之鱼再做模型精细判断复盘之后我意识到诈骗电话识别这类场景最好的方案往往不是单一的机器学习模型而是“规则引擎 机器学习模型”的分层架构。规则引擎负责抓取那些极端明显、不需要模型就能判断的诈骗特征。比如一个号码在5分钟内呼出超过50次或者单日呼出超过500次这种号码即使模型没有识别出来规则引擎也应该直接拦截。这样做有两个好处一是模型不需要把所有精力都放在这些极端样本上可以更专注地学习那些“看起来正常但实际可疑”的模式二是规则引擎可解释性极强方便风控团队理解。然后模型作为第二层判断。规则引擎覆盖不了的、需要综合多维度行为特征判断的样本交给模型打分。模型输出的概率还可以结合风险等级划分成多个档位不同档位采取不同策略如通知确认、限制呼出、直接拦截而不是简单的二分类一刀切。6.2 用半监督和伪标签解决标注稀疏问题真实场景里最大的痛点往往是标注数据不足。运营商每天产生的通话数据以亿计但真正被标记为诈骗的样本占比极低人工标注又非常昂贵。比赛数据中训练集是现成的但真实业务中往往训练集都很难获取。一种可行的思路是使用半监督学习和伪标签技术。先用已有的少量标注数据训练一个初始模型用它对未标注的大规模数据进行预测将高置信度的预测结果作为伪标签加入训练集再迭代训练。这个过程中关键是要控制伪标签的可信度只选择概率极高或极低的样本避免噪声被放大。从业务角度讲诈骗团伙的行为模式会不断演化模型必须持续迭代。可以建立一套“打标-训练-上线-反馈-再训练”的闭环机制被模型判为诈骗但用户投诉说不是的回退到正常样本被规则引擎拦截但事后证明是诈骗的作为正样本强化训练。6.3 模型融合与Stacking单模型的上限终究有限当时我只用了单独一个LightGBM模型这在上分上是有瓶颈的。不同模型擅长捕捉的模式不同融合多个模型通常能获得更好的性能和更强的鲁棒性。可选方案有两个。第一个是简单平均/加权平均融合分别训练LightGBM、XGBoost和CatBoost三个模型对预测概率做加权融合。第二个是Stacking用第一层模型的输出作为第二层模型比如逻辑回归的输入特征让第二层模型学习如何组合第一层模型的预测结果。Stacking效果通常更好但实现复杂度更高也更容易过拟合需要额外的验证集来约束。另外同一模型的多折交叉验证预测结果取平均也是一个简单有效的思路。我当时用的是5折交叉验证每折训练一个模型最终把5个模型的预测结果取平均。这种方式既利用了全部数据又减少了单折数据划分带来的方差。6.4 部署与实时识别的考量从离线走向在线比赛做完拿到名次就结束了但真实的反诈系统要部署到生产环境里这完全是另一套逻辑。离线训练阶段模型对单条样本的推理耗时是毫秒级还是秒级根本不影响整体流程但上线后如果要求实时响应就完全不同了。诈骗电话识别通常需要在呼叫过程中或呼叫后很短时间内完成判断推理延迟必须控制在几百毫秒以内。这意味着模型不能太复杂特征计算要尽量轻量很多离线场景允许的重型特征比如全网关系图谱的PageRank在线上根本算不出来。我当时思考过一个可落地的方案是把模型部署成独立的推理服务输入是实时计算好的特征向量输出是诈骗概率。特征计算部分放在流式计算框架如Flink里在线实时计算最近时间窗口内的通话频次、被叫分散度等轻量特征那些耗时较重的关系网络特征则用离线批处理任务定时更新缓存成KV表供在线服务查询。模型推理则可以用ONNX、PM2.5或专门的推理框架加速。特征一致性是另一个容易被忽略的问题。离线训练时用的特征和在线推理时用的特征必须完全一致否则模型表现会断崖式下跌。有时候连版本管理、特征口径、缺失值填充方式这种小细节都会导致线上和线下数据分布不一致。所以上线前一定要做详细的特征一致性验证避免“训练时AUC 0.95上线后AUC 0.6”的尴尬。另外模型的监控和回滚机制也很重要。上线后不能撒手不管要持续监控预测概率的分布变化、特征分布漂移PSI指标以及业务反馈指标拦截准确率、误杀率等。一旦发现指标异常可以快速回滚到上一个稳定版本而不是在线上debug。我在实际踩过这一圈坑之后再回头看这个项目最大的感受是一个机器学习比赛项目的价值不在排名本身而在于你从失败中学到了多少。29779名确实不好看但它逼着我把时间穿越、验证集划分、不平衡处理、特征一致性这些问题彻底想明白了。如果当时我直接提交的版本运气好拿了个不错的名次大概率会误以为自己那套做法是对的反而不会去深挖这些细节。最后再分享一个小建议如果你也在做类似的比赛或项目拿到数据的第一天先花一整天时间只做数据分析和业务理解不要急着写特征工程代码。把时间分布、样本构成、缺失模式、正负样本的差异都摸清楚后面建模会轻松很多。这个习惯让我在后面的项目里省了无数时间和精力也推荐你试一试。本文还有配套的精品资源点击获取
返回列表