多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

为什么最优欺诈率不是零?成本权衡下的风控决策

为什么最优欺诈率不是零?成本权衡下的风控决策 很多人做风控时第一反应是把欺诈率压到 0甚至写进 KPI欺诈率必须为零、风险单必须清零。但 2022 年海外一篇讨论文章《The optimal amount of fraud is non-zero》提出了一个非常反直觉的观点最优的欺诈数量不是 0而是一个大于 0 的值。这个观点如果只停留在口号层面很容易被误读成“风控可以放水”。实际上它是在讲一个成本决策问题欺诈检测系统不是一个“抓坏人越多越好”的分类器而是一个需要在“拦截损失”和“误杀成本”之间做权衡的业务系统。把欺诈率压到 0 的代价往往是误杀大量真实用户损失的收入和用户体验远高于那点欺诈损失。这篇文章我会从概念、数学推导、Python 模拟、实时风控 API 设计、批量扫描、指标验证和排查思路几个维度展开。适合支付、电商、广告反作弊、账号安全、信贷风控的同学也适合想理解“为什么反欺诈系统要留余量”的产品和技术负责人。1. 核心概念什么是最优欺诈率1.1 从“零容忍”到“总成本最小”欺诈检测在本质上是一个二分类决策问题。每一条交易、每一次登录、每一笔支付系统都要输出一个“放行 / 拦截 / 人工审核”的决策。如果只看欺诈这一侧目标很清晰把欺诈交易全部拦下来。但任何决策系统都有代价决策结果含义真实代价正确放行TN正常用户通过无代价正确拦截TP欺诈行为被拦截无代价但占用风控资源漏过欺诈FN欺诈交易放行成功直接资金损失、舆情损失误杀正常用户FP正常交易被拦截用户流失、客服成本、收入损失、品牌损失“最优欺诈率非零”的核心就是不要把 FN 和 FP 看作对称的错误而是把它们的代价量化再去找一个让“总代价最小”的决策边界。1.2 数学表达最优阈值不是一个拍脑袋的值假设模型给某条交易输出的欺诈概率是 p。我们做两种决策放行期望代价 p × C_FN拦截期望代价 (1 - p) × C_FP其中C_FN 是漏过一笔欺诈的损失包含资金、赔付、舆情等。C_FP 是误杀一个真实用户的损失包含收入损失、客服成本、用户流失等。当拦截代价小于放行代价时我们应该拦截(1 - p) × C_FP p × C_FN化简得到最优阈值p* C_FP / (C_FP C_FN)这个公式非常重要。它说明最优阈值不是由模型精度单独决定的而是由业务成本决定的。如果误杀一个真实用户损失 500 元漏过一笔欺诈损失 50 元那么最优阈值是p* 500 / (500 50) 0.909也就是说只有当模型认为这笔交易的欺诈概率超过 90.9% 时才拦截。这时候欺诈率一定不是 0因为大量欺诈概率在 50% 到 90% 之间的交易会被放行。1.3 为什么不是零三个直觉理由第一欺诈检测不是一个完美可分的分类问题。正常用户和欺诈者的行为特征有重叠把阈值拉得越低误杀的正常用户就越多而且增长速度非常快。第二真实用户的价值通常远大于单笔欺诈损失。一个用户可能持续消费五年复购、口碑、推荐都会带来收入。误杀一个优质用户损失的往往不是一单而是整个生命周期价值。第三追求零欺诈率会导致系统过度拒绝。如果业务层面要求“欺诈率必须为零”那唯一的做法就是所有高风险动作一律拦截哪怕概率只有 1%。这会让大量正常用户无法下单、无法登录、无法提现业务收入和用户体验会以更快的速度崩塌。所以最优欺诈率不是 0而是“再多拦一笔欺诈带来的边际成本刚好等于再放走一笔欺诈带来的边际损失”的那个位置。2. 适用场景与使用边界2.1 这种思路适合什么场景这套成本权衡思路最适合商业决策类风控场景支付风控决定一笔交易是否放行可以算清资金损失和挽留成本。电商平台识别虚假订单、恶意退款、黄牛抢购但不能把正常买家误杀。广告反作弊过滤无效点击和刷量流量但也要保证正常推广流量不被误判。交易平台与内容社区识别刷单、虚假评价、垃圾账号但需要保证正常创作者和买家体验。在这些场景里“误杀”和“漏过”都有明确的业务代价可以用金额量化也可以转化为业务 KPI 来跟踪。2.2 什么场景不能用“非零欺诈”有一些场景欺诈率必须趋近于零不是由成本模型决定的而是由法律合规和资金安全底线决定的洗钱、赌博、诈骗资金链路监管明确要求止付和上报。盗卡交易涉及卡片组织的强制规则和赔付要求必须拦截。未成年人保护、金融适老化等合规场景不能只算经济账。批量盗号、撞库攻击即使拦截的真实用户比例高也必须先止损。在这些场景里成本模型只能作为辅助不能覆盖规则底线。正确做法是把强制规则放在最前端成本决策放在规则之后。2.3 隐私和合规边界风控系统会大量使用用户基础信息、设备信息、行为轨迹。使用时必须注意数据合规数据采集必须基于业务必要性和用户授权。涉及人脸、声纹、指纹等生物特征数据的需要按法律要求做单独授权和告知。风控模型训练和测试使用的数据要脱敏不能直接暴露原始敏感字段。用户被误杀时要有申诉通道和人工复核机制不能完全交给模型一票否决。3. 技术落地把欺诈检测做成一个决策系统3.1 一个典型的实时风控链路实时欺诈检测并不只是“训练一个模型”那么简单工程上通常是一条完整链路客户端埋点 / 服务端事件上报 ↓ 特征平台实时特征 离线特征 ↓ 规则引擎强规则、黑名单、频控 ↓ 模型服务欺诈概率打分 ↓ 决策引擎结合阈值、成本、策略输出结果 ↓ 处置动作放行 / 拦截 / 人工审核 / 二次验证 ↓ 监控与复盘指标回溯、特征漂移、策略回滚在这个链路里模型输出的不是“是或否”而是“欺诈概率”。真正的最终决策由决策引擎结合业务成本、阈值、规则等级、用户维度、资源情况共同完成。3.2 用 Python 模拟成本最优决策为了理解上面那个阈值公式我用 Python 做一个小型模拟。数据是构造的不代表真实业务但流程可以复用。import numpy as np def simulate_decisions(y_true, y_prob, threshold): 模拟决策结果返回误杀数和漏过数。 y_true: 真实标签1 为欺诈 y_prob: 模型输出的欺诈概率 threshold: 拦截阈值 y_pred (y_prob threshold).astype(int) fp int(((y_pred 1) (y_true 0)).sum()) # 误杀 fn int(((y_pred 0) (y_true 1)).sum()) # 漏过 return fp, fn def search_threshold(y_true, y_prob, cost_fp, cost_fn, grid): 在给定成本参数下搜索总代价最小的阈值。 best_cost float(inf) best_result None for t in grid: fp, fn simulate_decisions(y_true, y_prob, t) total_cost fp * cost_fp fn * cost_fn if total_cost best_cost: best_cost total_cost best_result (t, fp, fn, total_cost) return best_result再用一份构造数据跑一下搜索rng np.random.default_rng(42) n 100000 # 构造正常样本风险分数低欺诈样本风险分数高 y_true np.concatenate([ np.zeros(n - 500), np.ones(500) ]).astype(int) y_prob np.concatenate([ rng.beta(2, 8, sizen - 500), # 正常样本 rng.beta(7, 3, size500) # 欺诈样本 ]) # 假设误杀一个真实用户损失 50 元漏过一笔欺诈损失 200 元 cost_fp 50.0 cost_fn 200.0 grid np.round(np.arange(0.05, 0.96, 0.01), 2) result search_threshold(y_true, y_prob, cost_fp, cost_fn, grid) print(最优阈值:, result[0]) print(误杀数量:, result[1]) print(漏过数量:, result[2]) print(最小总代价:, result[3])运行结果会随着构造数据的分布变化但思路是清晰的当 cost_fp 相对 cost_fn 更大时最优阈值会上升系统会容忍更多欺诈当 cost_fn 更大时最优阈值会下降系统会更激进地拦截。这就是“最优欺诈率非零”在代码层面的体现最优阈值不是让欺诈率最低的那个值而是让总业务成本最小的那个值。3.3 把成本敏感逻辑接入决策引擎真实系统里模型分数和阈值不会只有一个而是分层决策def decide(score, amount, user_level, cost_fp, cost_fn): 简化的决策函数。 实际生产中还会有多条规则、多个模型分数和人工审核兜底。 threshold cost_fp / (cost_fp cost_fn) # 强规则优先 if amount 50000 and score 0.8: return reject_high_risk, high_amount_high_score # 成本最优阈值 if score threshold: return reject, cost_based # 中风险进入人工审核 if score threshold * 0.7: return review, medium_risk return approve, normal注意这里 threshold 会随业务场景变化。大额交易的 cost_fn 更大阈值应该更低小额交易 cost_fn 小阈值可以更高。同一个模型分数在不同业务场景下应该映射到不同决策。4. 效果验证与指标设计4.1 不要只看欺诈率很多团队上线风控模型时只看“拦截了多少欺诈”这是不够的。要判断系统是否处于“最优区域”至少要看一组指标指标计算方式说明欺诈率欺诈交易 / 总交易衡量风险水平拦截率被拦截的欺诈交易 / 欺诈交易衡量模型能力误杀率被拦截的正常交易 / 正常交易衡量误伤程度可避免损失成功拦截的欺诈金额量化止损效果用户申诉率申诉用户 / 被拦截用户反映误杀的显性压力收入影响被误杀用户的预期消费金额量化 FP 代价风险覆盖率被模型覆盖的交易量 / 总交易量判断策略是否全面4.2 用实验验证阈值是否合理阈值不是上线就固定不变的。比较稳妥的做法是先做离线回测用历史交易数据把不同阈值下的欺诈率、误杀率、总成本拉出来画出曲线。再小流量实验线上切 5% 流量对比新阈值和旧阈值下的指标差异。观察一个完整业务周期至少覆盖一次正常交易高峰和一次营销活动因为这两个阶段的行为特征差异很大。确认收益如果新阈值下总成本下降、用户申诉没有明显上升再逐步放开流量。这里的关键思维是不要单独以一个“阈值”或一个“分数”为目标而是以业务总代价为目标。模型分数是一个中间变量不是最终指标。5. 接口与批量任务设计5.1 实时决策接口风控系统通常以 HTTP API 的方式对外提供服务。一个最简单的实时风险决策接口是这样的curl -X POST http://127.0.0.1:8080/v1/risk/check \ -H Content-Type: application/json \ -d { request_id: req_20250101_001, user_id: user_10086, biz_type: payment, amount: 299.00, ip: 203.0.113.8, device_id: device_8f2a1c, scene: order_pay }返回结果{ request_id: req_20250101_001, decision: approve, score: 0.31, threshold: 0.41, reason_codes: [], cost_fp_ref: 1.0, cost_fn_ref: 0.6 }Python 调用示例import requests url http://127.0.0.1:8080/v1/risk/check payload { request_id: req_20250101_002, user_id: user_10087, biz_type: payment, amount: 199.00, ip: 203.0.113.9, device_id: device_aabb, scene: order_pay } response requests.post(url, jsonpayload, timeout500) result response.json() if result[decision] approve: print(放行) elif result[decision] review: print(转人工审核) else: print(拦截)接口设计时要注意所有参数必须带 request_id方便全链路追踪。返回 deciion 的同时要把 score、threshold、reason_codes 一起返回方便业务方定位问题。服务要做好超时和降级不能因为风控服务超时把支付主流程拖死。5.2 批量离线扫描除了实时决策风控系统还需要支持离线批量任务比如每天扫描全量历史订单、批量识别异常退款、给存量用户打风险分层标签。批量任务通常走异步队列不能直接同步返回结果POST /v1/risk/scan/batch { task_id: task_20250101_001, biz_type: order_refund, date_range: [2024-12-01, 2024-12-31], threshold_override: 0.45 }任务提交后服务端返回任务状态业务方轮询或通过回调获取结果{ task_id: task_20250101_001, status: running, total_items: 120000, processed_items: 45000 }批量任务要特别注意几点任务可以重跑结果要幂等。每个子任务失败要记录错误并重试不要因为个别数据异常导致整个任务中断。输出结果按风险等级分目录保存方便不同团队取用。批量任务最好支持“试运行”模式先输出统计结果不实际触发处置动作。6. 性能与资源占用观察6.1 实时风控的性能指标风控接口的性能直接影响业务主流程核心观察指标包括平均响应时间通常要在 100ms 到 300ms 以内取决于特征计算量。P99 响应时间比平均值更能反映长尾问题。QPS接口最大处理能力。特征平台命中率缓存命中率越高响应越快。外部数据源耗时三方数据、黑名单查询、IP 库查询容易成为瓶颈。如果发现实时接口变慢优先排查这些点特征计算链路里有没有慢 SQL。外部数据源有没有设置超时时间和熔断。Redis 缓存是否过期集中导致缓存击穿。模型推理时有没有做批量加速还是逐条请求。6.2 批量任务对资源的影响批量扫描如果和实时服务共用同一套计算资源很容易把实时服务的响应时间拉高。推荐的策略是批量任务部署在单独的进程或容器中。实时服务优先使用低延迟资源离线扫描使用独立的弹性资源。批量任务限制并发数比如设置最大 20 个并发避免打爆数据库和特征平台。批量任务运行期间要关注 CPU、内存、磁盘 IO尤其是大量读取订单表时容易造成主库压力。6.3 如何降低计算压力特征结果做缓存重复特征不重复计算。模型做个轻量版本先粗筛再对高分样本用全量特征重算。规则引擎和模型分阶段处理黑名单命中直接拦截不进入模型推理。批次扫描时按用户维度做聚合减少重复计算。7. 常见问题与排查方法问题现象可能原因排查方式解决方向欺诈率明显上升但拦截率没变化模型或特征漂移欺诈模式变化对比近期样本分数分布查看模型 PSI重新训练模型、补充新特征误杀率突然升高最优阈值被调低或特征异常检查阈值配置、特征值分布回滚阈值检查上游特征所有风险决策都变成放行模型服务未加载或强规则未生效检查模型服务日志、规则配置重新加载模型检查规则版本风控接口响应变慢特征计算慢、外部数据源超时、缓存失效查看链路追踪查看慢查询日志加缓存、设置外部接口超时、预计算特征批量任务卡住消息队列堆积、下游依赖不可用查看队列堆积数、任务日志提高消费者并发、重试失败任务指标口径对不上不同团队对“欺诈率”定义不一致统一分母、分子口径说明建立指标字典统一计算口径阈值调优后整体负向成本参数没同步更新或实验观察时间过短检查 cost_fp / cost_fn 配置查看实验周期重新核算成本参数延长实验周期人工审核队列堆积中风险阈值设置过高审核资源不足查看队列长度和平均处理时长调整中风险阈值增加审核人力或优化审核工具8. 最佳实践与合规红线8.1 工程最佳实践第一第一次上线不要追求复杂。先用一个规则引擎加一个模型固定一套最小可运行配置跑通整条链路后再逐步加策略。第二模型、规则、阈值、成本参数全部做版本管理。每次变更要能回滚最好做到一键回滚到上一个稳定版本。第三分目录管理好模型文件、特征配置、样本数据、输出结果。离线实验目录和线上服务目录要分开避免误用。第四批量任务必须加日志和失败重试机制。批量任务涉及的数据量通常很大如果中间断掉需要能断点续跑而不是从头再来。第五接口服务要加鉴权和限流。风控接口暴露的是核心决策能力不能让任意调用方随意访问也要防止业务高峰期把风控服务打垮。第六给真实用户留“逃生通道”。被误杀的用户需要有申诉入口申诉后要能人工复核或者二次验证而不是永远无法恢复。8.2 合规红线“最优欺诈率非零”是一个成本决策工具不是放松监管的理由。涉及洗钱、赌博、诈骗、盗卡等监管明确禁止的资金链路必须先按法律法规拦截并上报涉及用户隐私数据的采集和处理必须获得合法授权并做脱敏涉及人脸、声纹等敏感生物数据必须有单独授权和严格的使用边界任何风控策略都不应该让用户陷入不可申诉、不可复核的局面。如果成本模型算出来“放行更好”但合规规则要求“必须拦截”合规规则优先。成本模型只能影响业务层面的阈值不能突破法律和安全底线。8.3 跨团队协作机制风控不是一个纯技术问题需要业务、算法、研发、客服、法务共同参与业务侧提供成本参数比如真实用户的生命周期价值、流失率、客单价。算法侧提供模型分数和特征解释。研发侧保证决策链路稳定和可回滚。客服侧反馈误杀申诉数据反向驱动阈值调整。法务和合规侧划定不能触碰的红线。这套机制建立起来之后风控系统的目标就不再是“零欺诈”而是“在合规前提下让业务总成本最低”。9. 总结与下一步回到《The optimal amount of fraud is non-zero》这个观点它真正的价值是给出了一个可操作的工程判断框架最优欺诈率不是 0最优阈值也不是模型精度的产物而是成本参数的产物。建议你先做三件事第一把当前风控决策的四个结果——正确放行、正确拦截、漏过欺诈、误杀正常用户——用成本量化出来。哪怕一开始只是拍一个粗略数值都比完全没有强。第二用 Python 脚本把你历史交易数据做成一张“阈值 vs 总代价”的曲线图找到当前系统的最优阈值在哪里再对比现行的阈值差多少。第三把成本参数配置化。不要写在代码里而是做成配置项这样业务侧调整成本参数时不需要重新发版模型和代码。最容易踩的坑是把“最优欺诈率非零”理解成“欺诈可以放着不管”。它恰恰要求你更精细地管理风险只是管理目标从“最低欺诈率”换成了“最低业务总代价”。后续可以继续扩展的方向包括成本敏感学习、强化学习动态调阈值、基于用户分层差异化决策、批量扫描报告自动化、以及风控模型上线后的漂移监控体系。先把成本这个维度引入系统后续每一步优化会顺很多。
返回列表