☰
基于Hadoop与Spark的大数据信贷风控系统:从数据链路到模型验证
2026/10/14 15:42:38 网站建设 项目流程

简介:这是一套基于Hadoop与Spark构建的金融信贷风险控制系统源码,面向大数据开发、金融风控算法及数据工程方向的开发者,可用于理解海量借贷数据下的信用评估与欺诈检测全流程。资源包共69个文件,大小约72KB,以36个Java文件、8个Scala文件组成核心计算逻辑,配套12个XML配置及5个properties参数文件,便于调整Spark/Hive运行环境与模型参数;另有SQL、JSON、JS等文件辅助数据集成与可视化展示。项目覆盖数据接入、预处理、逻辑回归/随机森林等模型训练、风险评分及实时批处理结合等模块,源码分层清晰,适合以此为基础扩展实时风控、特征工程或监控告警等实践。已有1219人学习下载,对于希望掌握大数据风控项目架构与技术选型的开发者来说,是一份可直接研读和二次开发的参考实现。

1. 基于Hadoop、Spark的大数据金融信贷风险控系统,先看懂它的数据账

信贷风控这个场景有个反直觉的结论:真正决定系统能不能跑起来的,不是模型有多先进,而是从申请信息、还款流水、外部征信到最终风险评分的这条数据链路是否通畅。我拿到一套大数据金融信贷风险控系统源码,第一件事从来不是翻模型代码,而是先看它的数据处理链路是怎么组织的。基于Hadoop、Spark的大数据金融信贷风险控系统,本质是用HDFS做全量数据底座、用Hadoop生态做离线批处理、用Spark做高时效计算,把信贷审批和贷后监控变成一条可审计、可回溯的数据流水线。它能解决的是数据量大、特征维度多、审批时效要求高的信贷风控场景。适合谁读?正在做风控平台技术选型、准备把这类源码落地成生产系统的工程师,或者想系统理解数据链路与模型链路如何衔接的新手。

2. 从源数据到风险分:Hadoop 做数据底座、Spark 做计算引擎的架构拆分

2.1 分层设计:离线层、近线层、在线层各管哪一段

一套完整的信贷风控系统,数据链路一定不是一条直线,而是按时效性拆成三个层次。离线层跑在Hadoop之上,数据落在HDFS,表结构放在Hive里,负责T+1的全量批处理:历史申请数据归档、月度特征重算、模型训练样本生成。近线层用Spark的微批或Structured Streaming,负责分钟级到小时级的增量特征刷新,比如贷后行为监控、额度复评需要用到的近几小时交易汇总。在线层则是规则引擎和模型服务,需要在毫秒级返回风险分,这部分通常不直接跑Spark,而是读取预计算好的特征。

这里有个常见误解:既然Spark既能做批处理又能做流处理,为什么还要保留Hadoop?答案是两者根本不是替代关系。Hadoop提供的是分布式存储HDFS和资源调度YARN,Spark是运行在YARN之上的计算框架。换句话说,Hadoop是底座,Spark是引擎。源码里如果看到MRv2的作业或Hive UDF,别急着说“这技术过时了”,它们负责的是数据归档、全量重算这类对实时性不敏感但对稳定性要求极高的任务。技术选型不是追新,是让每类任务落在它最合适的位置上。

三层之间的流转也很关键。离线层产出的特征宽表,会被同步到在线层的缓存或特征库中供实时查询;在线层产生的审批结果和贷后表现,又会回流到Hive表里成为下一轮模型训练的新样本。这个回环结构是信贷风控系统和一般推荐系统最大的不同:风控系统的数据是自我进化的,每放一笔款、每产生一笔逾期,都会成为未来模型的训练素材。

2.2 特征加工链路:Spark 做宽表 join,Hadoop 做归档沉淀

特征加工是风控系统最重的一块计算。申请人的基本信息、流水、多头借贷记录、历史逾期记录分散在几十张表里,必须拉到一起才能计算特征。常见做法是先用Spark SQL做多层宽表加工,把明细数据聚合成以申请为粒度的特征表,宽表里一行就是一个申请单对应的几百个特征,这是后续模型训练和规则判断的输入。加工过程中Spark的优势体现得很明显:内存计算避免了反复读写磁盘,同一个口径下,Spark跑一个多表join的耗时通常只有MapReduce的几分之一,这对于需要按天重跑的全量特征任务来说,直接决定了能不能在业务开门前跑完。

Hadoop在这一环节的职责更多是数据沉淀。经过Spark加工后的结果会写回Hive表,以分区形式组织,按业务日期分区存放。这样设计的价值在于:一旦发现某个特征口径算错了,可以只重跑对应分区而不需要动全表;另外Hive表配合HDFS的副本机制,天然承担了数据备份和灾难恢复的职责。生产系统里我一般会在离线层保留最近三年的原始明细数据,压缩后存放在HDFS上,为后续的模型回溯分析留足弹药。

用Spark接增量明细、加工特征,再落回Hive分区表的骨架大概长这样:

from pyspark.sql import SparkSession from pyspark.sql.functions import col, to_date, current_timestamp spark = SparkSession.builder \ .appName("customer_feature_etl") \ .enableHiveSupport() \ .getOrCreate() biz_date = "2025-01-15" df = spark.read.format("parquet") \ .load("hdfs:///data/ods/customer_trans/dt={}".format(biz_date)) df_clean = df.filter(col("trans_amt") >= 0) \ .withColumn("etl_time", current_timestamp()) \ .withColumn("feature_dt", to_date(col("trans_time"))) df_clean.createOrReplaceTempView("trans_detail") spark.sql(""" SELECT customer_id, SUM(trans_amt) AS amt_total, COUNT(*) AS cnt_total, AVG(trans_amt) AS amt_avg FROM trans_detail GROUP BY customer_id """).write.mode("overwrite") \ .format("parquet") \ .partitionBy("feature_dt") \ .saveAsTable("dws.customer_feature_agg")

代码逻辑分四步:先按业务日期读取ODS层的客户交易明细,读取时直接定位到当天分区,避免全表扫描;然后做基础清洗,过滤掉金额为负的异常流水,同时补上ETL时间和特征日期字段;接着注册成临时视图,用Spark SQL做聚合;最后按feature_dt分区写入DWS层特征聚合表。这里有两个参数值得留意:一是read.load的路径中dt={},这个日期参数应当由调度平台传入,保证任务重跑时能指定正确分区;二是write.mode("overwrite"),对于按分区写入的场景,overwrite模式只覆盖对应分区,不会影响其他日期的数据,远比先删表再重建安全。

2.3 样本与标签:风控建模里最容易翻车的时点对齐问题

信贷风控训练数据和普通机器学习训练数据最大的区别在于:标签不是立刻能拿到的。某笔申请在1月15日通过审批,它是不是坏样本,要看放款后90天甚至180天有没有逾期,这就是风控里常说的表现期。因此源码里样本表的设计通常包含三个时间字段:申请时间、放款时间、表现期截止时间。切样本的时候只能取那些表现期已经走完的历史申请,还在表现期内的不能进训练集。

特征时点对齐是另一个重灾区。训练样本里某个特征,比如近三个月消费金额,计算窗口的截止点必须严格卡在申请时间点,不能把申请之后的流水也算进去。这个坑如果踩了,模型在回测时的AUC会虚高,上线后却立刻失效。典型的错误写法是直接拿当前时间作为窗口终点去算历史样本的特征,这在源代码评审时是最先要揪出来的问题。正确的做法是把申请时间作为参数传入特征计算任务,让每一条样本的特征都严格截取在它自己申请时间之前。

还有一种常见做法是在数据表设计上做约束:申请事实表和特征表都用申请单号做主键,观察期、表现期作为字段存下来。这样不管是训练还是推理,都从同一个宽表里取数,避免训练和上线两套口径。宽表生成后,模型训练只读这张表,不再回源明细,既快又不容易出错。

3. 读懂这类源码的四块核心:数据接入、特征计算、规则与模型、任务调度

3.1 数据接入:把申请信息与还款流水接进 Hive 的 Spark 作业骨架

拿到源码包,先别急着跑,先按目录结构把模块划分认清楚。这类系统常见的模块拆法是:数据接入层、特征加工层、决策引擎层、管理后台层。数据接入层负责把业务库、消息队列、外部数据源里的数据搬运到HDFS和Hive里,是整个链条的起点。信贷场景里的数据源有几种典型形态:业务申请数据存在业务库中,常用Sqoop或DataX同步;还款流水和行为埋点走消息队列;外部征信数据以文件或接口方式提供。源码里这一层通常是最稳定的,因为模式相对固定,但也是最先出问题的地方。

以还款流水接入为例,用Spark接消息队列再落Hive分区的骨架代码如下:

from pyspark.sql import SparkSession from pyspark.sql.functions import col, from_json, to_date from pyspark.sql.types import StructType, StringType, LongType, DoubleType spark = SparkSession.builder \ .appName("repay_flow_ingest") \ .enableHiveSupport() \ .getOrCreate() schema = StructType() \ .add("loan_id", StringType()) \ .add("repay_amt", DoubleType()) \ .add("repay_time", LongType()) \ .add("status", StringType()) raw = spark.readStream.format("kafka") \ .option("kafka.bootstrap.servers", "node01:9092,node02:9092") \ .option("subscribe", "repay_flow") \ .option("startingOffsets", "latest") \ .load() parsed = raw.select( from_json(col("value").cast("string"), schema).alias("data") ).select("data.*") parsed = parsed.withColumn("dt", to_date(col("repay_time").cast("timestamp"))) def write_batch(batch_df, batch_id): batch_df.write.mode("append") \ .format("parquet") \ .partitionBy("dt") \ .saveAsTable("dwd.repay_flow_daily") parsed.writeStream \ .foreachBatch(write_batch) \ .outputMode("append") \ .trigger(processingTime="5 minutes") \ .start()

这里用了Structured Streaming的foreachBatch模式,每5分钟把攒下的一批数据写入Hive分区表。foreachBatch比直接writeStream到Hive更可靠,因为可以在这里加入去重、格式校验、甚至调用其他依赖该批数据的任务。参数上重点关注两处:startingOffsets设为latest表示只消费新数据,如果要做历史补数,需要改成earliest或指定offset;trigger的processingTime决定数据可见延迟,5分钟一刷对贷后监控够用,但需要按业务对时效的要求动态调整。

3.2 特征计算:窗口函数与时间滑窗的典型写法

特征加工是风控源码里代码量最大的部分,也是最值得逐行读的部分。常见特征按窗口分为近1个月、近3个月、近6个月、近12个月,甚至包含节假日在内的特殊时段特征。这类时间滑窗特征在Spark SQL里非常适合用窗口函数加条件聚合来实现。

近3个月还款行为特征的典型SQL写法如下:

SELECT loan_id, customer_id, SUM(CASE WHEN repay_status = 'overdue' THEN 1 ELSE 0 END) AS overdue_cnt_3m, COUNT(*) AS repay_cnt_3m, SUM(repay_amt) AS repay_amt_3m, AVG(repay_amt) AS repay_avg_amt_3m, MAX(repay_amt) AS repay_max_amt_3m FROM dwd.repay_flow_daily WHERE repay_time >= TO_DATE('2025-01-15') - INTERVAL 3 MONTHS AND repay_time < TO_DATE('2025-01-15') GROUP BY loan_id, customer_id

这条SQL把还款明细按申请单聚合,输出逾期次数、还款笔数、总额、均值、最大值五个特征。需要注意的是WHERE条件里窗口终点的写法:用TO_DATE('2025-01-15')而不是CURRENT_DATE。如果用CURRENT_DATE,跑历史样本的补数任务时就会把未来数据也算进去,导致训练集和线上特征口径不一致。这个细节在多人协作的项目里反复出现,也是绝大多数特征口径事故的根源。

另外,特征计算任务最好做成配置驱动:窗口长度、聚合字段、时间字段都放在配置表或配置项里,代码里不写死。好处有两个,一是新增特征时不用改代码,改配置加一行就行;二是模型迭代时方便对照特征清单审查口径。源码里如果特征都是硬编码在一个大SQL里,后续维护成本会很高,拿到手后第一步建议就是把它重构为配置化。

3.3 规则引擎与模型打分:规则表、PMML、原生 Pipeline 的取舍

信贷风控决策不是纯模型,而是规则与模型融合的结果。硬规则负责拦截监管红线和极端风险,比如年龄超限、黑名单命中、身份证格式非法;模型负责对剩余申请做精细化风险排序。源码里面向这一块的代码通常分两部分:规则引擎和模型推理服务。

规则引擎的设计常见做法是规则配置化,规则不写死在代码里,而是放在数据库表或配置文件中,支持热更新。规则表的结构一般包含这些字段:

CREATE TABLE risk_rule_set ( rule_id STRING, rule_name STRING, rule_expr STRING, -- 例如 score_model < 600 AND apply_amt > 50000 action STRING, -- PASS / MANUAL / REJECT priority INT, status STRING, update_time TIMESTAMP );

rule_expr里存的是可解析的表达式,规则引擎启动时加载全表,把表达式编译成内存对象,逐条对申请数据做匹配。action表示命中后干什么,priority决定多条规则冲突时听谁的。把规则从代码里抽出来放到表里,最大的价值是风控业务人员可以直接配置、即时生效,不需要等开发排期上线。

模型打分这部分,选型的核心矛盾是跨语言部署和性能。常见做法有三种:把模型导出为PMML格式,用Java加载打分;导出为ONNX格式,用推理引擎加载;或者直接用Spark ML库的Pipeline保存和加载。PMML在信贷风控里用得仍然很广,因为它对特征顺序敏感,字段名一一对应,部署方不需要关心训练方的代码细节。用Spark训练完的模型保存并加载做打分,流程最顺,但需要保证运行环境里有匹配的Spark版本依赖。

3.4 任务调度与血缘:离线任务为什么必须配调度

信贷系统的离线任务数量通常几十上百个,靠crontab或人工触发根本管理不过来。源码包里如果有部署文档,调度配置一定是重点章节。调度平台要做的事是两件:一是按依赖关系编排任务,上游产出分区数据后下游才能启动;二是失败重试和告警,任务挂了自动重试几次,再不行就通知值班的人。

任务依赖编排的核心是数据就绪检测。一个Spark任务启动前,要先确认它依赖的Hive分区已经生成,而不是用sleep硬等。常见做法是调度平台支持前置检查节点,检查SQL返回的分区数据量是否大于0。离线跑批链路里,ODS同步决定了今天有没有原材料,DWD清洗决定了加工质量,DWS特征宽表决定了模型和报表能否产出。任何一环挂掉,后面的任务即使跑成功,产出的也是脏数据。源码里这一块的工程价值最容易被低估,但生产事故往往就出在这——任务报SUCCESS但数据是空的,比任务失败更难排查。

血缘管理同样值得提。每个特征字段要能追溯到它来自哪张源表、经过了哪些加工步骤,否则排查问题时只能靠翻代码。实际项目里我会要求所有特征加工脚本都登记一张源表-目标表映射表,调度平台跑完任务后就更新血缘信息,半年下来这套元数据比代码注释可靠得多。

4. 让源码落地:集群规划、打包提交与必调参数

4.1 环境选型:物理机、云主机还是容器化的判断标准

拿到源码后的第一个决定是在什么环境上跑。判断标准其实就三条:数据量、任务节奏、运维能力。数据量在几TB到几十TB、任务以T+1批处理为主的,用几台物理机构建小型Hadoop集群最经济。数据量在百TB级、还需要弹性扩容应对大促高峰的,云主机会更合适,按需扩容、用完释放。想跑在容器里的也可以,但Hadoop生态的容器化方案对网络和存储要求比较高,运维能力不够的话不建议一步到位。

规模参考按节点数给个常见范围:5台以内属于入门规模,适合跑通源码和开发环境;10到20台能支撑千万级用户的风控跑批;再往上就要考虑业务拆分了。机器规格方面,NameNode节点对内存敏感,建议64GB起步;DataNode节点磁盘容量优先,计算密集时CPU核数多给一些。Spark的executor和HDFS的DataNode通常混部在同一批节点上,这样数据本地性最好——Spark读数据时直接从本机磁盘拿,省去网络传输。

4.2 从源码到运行:打包、上传、提交 Spark 作业

源码工程一般是Maven或Gradle组织的多模块工程,拿到后先编译打包,再提交到集群运行。打包和提交的命令如下:

mvn clean package -DskipTests -Pdist hdfs dfs -mkdir -p /apps/credit-risk hdfs dfs -put credit-risk-1.0.jar /apps/credit-risk/ spark-submit \ --master yarn \ --deploy-mode cluster \ --class com.example.credit.CreditBatchMain \ --files hdfs:///apps/credit/risk_rule.conf \ --conf spark.executor.extraJavaOptions="-Dconfig.file=risk_rule.conf" \ --queue risk \ --executor-memory 16g \ --executor-cores 4 \ --num-executors 20 \ /apps/credit-risk/credit-risk-1.0.jar \ --env prod \ --dt 2025-01-15

各参数作用拆开看:--master yarn表示交给集群资源调度,--deploy-mode cluster让Driver也跑在集群里,适合离线批任务;--class指定要运行的入口类;--files把外部配置文件分发到每个执行节点;--queue指定YARN队列,把风控任务单独放一个队列可以避免和其他业务互相抢占资源;最后两个参数--env和--dt是业务参数,--dt就是第2章里提到的业务日期,特征计算任务都依赖它来定位分区。

初次启动时建议先不用--num-executors,改用Spark的动态资源分配,让集群按负载自动伸缩。等稳定运行一段时间后,再根据监控数据固定executor数量或直接保留动态策略。源码里如果自带启动脚本,重点看脚本里的参数值——不同公司集群规模差异很大,直接照搬的后果通常不是OOM就是资源浪费。

4.3 必调参数:executor 内存、分区数与动态资源

Spark作业调参是玄学,但有几条基本逻辑不是玄学。Executor内存、CPU核数、分区数三者必须放在一起看,单独调哪一个都可能翻车。常见参数配置参考如下:

参数常见取值调整依据
spark.executor.memory8g ~ 32g每个任务的数据处理量、是否有大聚合
spark.executor.cores2 ~ 4越高并行度越高,但要控制单节点总核数
spark.sql.shuffle.partitions200 ~ 2000和shuffle数据量挂钩,过少导致单分区压力过大,过多导致小文件膨胀
spark.dynamicAllocation.enabledtrue / false生产环境建议开启,让集群弹性伸缩
spark.yarn.executor.memoryOverhead1g ~ 4g存放JVM堆外内存,频繁OOM时优先调这里
spark.sql.adaptive.enabledtrue动态调整reduce分区数,解决数据倾斜

第一次跑源码任务时,我一般先把shuffle分区分母设成executor总核数的2到3倍,比如20个executor、每个4核、共80核,分区数设160到240,然后看Spark UI里每个stage的耗时分布。如果某个stage最后几个task明显比别的慢很多,大概率是数据倾斜。解决数据倾斜优先开spark.sql.adaptive.enabled;还不行再考虑加盐或者拆广播变量。executor内存溢出则优先调memoryOverhead,它不是堆内存,但堆外内存不够一样会OOM。

还有一个容易被忽略的参数是spark.speculation,开启后Spark会重跑那些明显滞后的task,取最先完成的那个结果。对大数据量的批处理任务能兜底,但也可能带来重复计算,稳妥做法是在跑的慢的节点上临时开启,稳定后关掉。参数调整后必须观察Spark UI上的GC时间、shuffle落地量、单task处理时长三个指标,不要只看任务成功还是失败。

5. 风控系统落地避坑:五条值得记住的排查记录

5.1 现象:夜间批处理任务堆积,Spark 作业反复失败

某次生产环境凌晨的T+1批处理任务在3点后开始堆积,Spark作业提交成功但运行几分钟后就被YARN杀掉。现象是任务反复失败,日志里出现大量ExecutorLostFailure和Container killed messages。

原因是executor申请的内存超过节点可用内存,加上多个任务在同一批节点上同时运行,物理内存被打满后触发系统OOM,容器直接被回收。这不是单次任务的问题,而是所有夜间批任务挤在同一时间窗口、资源池没有隔离的必然结果。

解决分两步:先给风控任务单独划分YARN队列,限制队列最大资源占用,防止批任务之间互相争抢;再调小单executor内存,把16g降到8g,同时增加executor数量保住总计算能力。更根本的办法是把大任务拆成多个有依赖关系的小任务错峰执行,让峰值内存需求错开。

5.2 现象:实时评分接口延迟抖动,从 200ms 涨到 2s

风控实时评分接口平时稳定在200ms左右,某段时间开始下午高峰期涨到2s甚至超时。现象没有规律,不是每次都慢,但慢的时候集中在流量大的时间段。

抓线程和GC日志后发现,在线评分服务读取特征缓存时,每次都用同步方式请求远程缓存,一旦缓存服务出现网络抖动或者慢查询,调用方线程全部阻塞在等待上。另外服务本身的JVM堆设置偏小,高峰期Full GC频繁,也会放大延迟。

解决是在评分服务本地加一层二级缓存,把热点特征缓存到本地内存,设置2分钟的过期时间,远程缓存只在本地未命中时回源。同时把远程缓存查询改成异步刷新:请求进来先查本地缓存,查不到就直接回源一次,后台再异步更新。改动后接口延迟稳定在100ms到300ms之间,抖动问题没有再出现。

5.3 现象:训练集和线上特征口径对不上,模型 AUC 虚高

模型在回测时AUC到了0.82,上线后实际区分能力明显达不到回测水平,调阈值也没用。排查后发现训练特征和上线特征不是一个源头。

根因在特征计算任务的日期参数上。训练样本的特征是历史某一天跑的批任务产出的,当时用的窗口终点是当天;线上评分时特征服务用的窗口终点是当前时刻。对同一个申请单,如果特征引用的是未来一段时间的流水,训练出来的特征分布和线上完全不同。更隐蔽的是缺失值处理逻辑不一致,训练时用了全表均值填充,线上实时服务没有这个均值表,填的0,分布直接偏移。

解决是整个团队统一用特征生成脚本的同一份代码,训练和线上共用一套特征逻辑。新增一个特征校验任务,每天对比训练特征表和线上特征表的同名字段的均值、标准差、分布差异,PSI超阈值就报警。这个机制上线后,口径不一致的问题基本做到了早发现早处理。

5.4 现象:Hive 小文件暴涨,NameNode 内存吃紧

集群NameNode堆内存使用率持续攀升,Hive表查询越来越慢。查看后发现DWS层特征表的文件数量从上万个涨到了十几万个,大量几KB到几百KB的小文件占满了NameNode内存。

原因是特征计算任务用了动态分区写Hive,同时shuffle分区数设得过大,Spark写出的文件数量等于shuffle分区数乘以动态分区数,数据量没涨,文件数涨了好几倍。小文件多不仅拖垮NameNode,还会让后续读取任务启动大量task,每个task都在做元数据操作,整个集群变慢。

解决分两层:Spark写入前先做一次repartition,把输出分区数控制在合理范围内;写入后跑一次合并任务,用Hive的MERGE或Spark repartition按新分区数重写一遍。参数方面把spark.sql.shuffle.partitions从2000降到400,匹配实际数据规模而不是简单照搬。关键是建立一条小文件巡检任务,每天统计各层表的文件数变化,超过阈值就自动触发合并。

5.5 现象:模型上线后坏账率不降反升

新模型上线一个月后,审批通过率没有明显变化,坏账率却比旧模型时期高了一截。团队第一反应是模型不行,但要区分是模型本身的问题还是客群结构变化的问题。

排查后发现新模型确实更准,但它会把一批旧模型认为高风险、拒绝掉的人群放进来。这批人其实是旧模型时期被拒绝、没有真实贷后表现、只能靠拒绝推断去估计其风险的人。新模型使用了更宽泛的特征,给了部分原本该拒绝的人通过,而这些人的实际表现比训练时预估的更差。

解决思路是引入拒绝推断的修正样本,把被拒绝人群的风险标签做权重调整后放进训练集,同时上线前做严格的回测和影子评分。更稳妥的做法是新模型分阶段放量:先放行新模型判定为低风险的申请,观察几周真实贷后表现,再逐步放开阈值。这样即使模型有偏差,损失也可控。

6. 拿来即用的进阶验证:回测与影子评分,给上线前的模型兜底

回测和影子评分是我在信贷风控项目里最看重的两道验证工序。回测的核心是时间切分,把历史数据按时间切两段:前一段用来训练,后一段用来验证。和一般机器学习随机切分不同,风控回测必须按时间切,因为信贷样本天然有时序关系,随机切分会把未来信息泄漏进训练集。切分点一般选在表现期之前,比如验证段要留出90天表现期,那就至少把最后90天的申请排除出训练集。回测指标要同时看区分度、排序稳定性和群体稳定性三类:区分度看KS和AUC,排序稳定性看每个分数段通过率是否和训练期一致,群体稳定性看PSI,PSI超过0.25说明推理时的特征分布和训练期差异已经很大,模型需要重训。

影子评分是把新模型旁路部署在线上但不参与实际决策:生产请求进来,旧模型打分决定审批结果,新模型同时打分但结果只落库不生效。跑上一到两个星期,拿新模型的分和旧模型的分做对比,看排序一致性、各分数段通过率差异、特征分布偏移情况。影子评分能验证一件事回测验证不了的事:特征在真实线上环境里取数是否稳定。我在一个项目上吃过亏,回测时AUC很漂亮,影子跑了一周发现新模型的评分均值比旧模型低了将近30分,原因是线上特征服务对某些长尾特征取到的值大量为0,和训练时的分布完全脱节。从那次以后,我养成一个习惯:任何模型上线前必须跑至少一周影子评分,并对比分位数、PSI和特征覆盖率三个报表。这个习惯虽然让上线周期拉长了几天,但拦下了不少看着好看、落地就翻车的模型。这套验证链路值得每一个做信贷风控的团队坚持,希望帮到你。

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

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

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

立即咨询