ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

多维特征融合驱动的互联网贷款客户聚类与风险预警实战

多维特征融合驱动的互联网贷款客户聚类与风险预警实战 这两天帮一位27届的学弟整理计算机毕设源码题目正好是《基于多维特征融合的互联网贷款客户聚类与风险预警研究》和配套的《基于数据挖掘的互联网贷款用途与风险定价可视化研究》。乍一看像是两个题目实际是一个完整的风控数据挖掘闭环先把客户按照多维特征聚成不同群体再做风险预警最后把贷款用途、利率、逾期率之间的关系用可视化大屏呈现出来。拿到这类题目容易发懵范围太大不知道该先做什么。我把从数据清洗到出具整套源码的全过程拆一遍包括选型思路、参数调优、踩过的坑尽量把能直接复现的细节都交代清楚。做过金融风控类毕设的人都知道这类项目最容易被卡住的不是模型而是“业务逻辑与数据特征对不对得上”。比如为什么要把聚类和风险预警放一起为什么贷款用途要和风险定价关联。先把这个题目的设计思路讲明白后续每一步才走得顺。1. 项目整体设计与拆解1.1 题目到底在考什么“多维特征融合”这个词听起来很高端其实就是把客户画像里分散的信息整合起来形成一个能同时反映还款能力、还款意愿、资金需求紧急度的特征集。传统做法可能只盯着收入、负债或者历史逾期某一个字段效果往往不稳定。一个用户可能在单一维度上表现正常但把收入负债比、近3个月征信查询次数、当前逾期天数放在一起看风险特征立刻暴露出来。客户聚类解决的是“客群分层”问题。不同客群的风险驱动因素不一样比如刚毕业的学生群体负债低但收入稳定性差经营类客群收入高但资金周转波动大把这些人混在一起训练模型预警准头会大打折扣。所以题目先要求聚类再要求预警本质上是希望用无监督方法先找出结构性差异再针对不同分群搭建有监督的风险判断。风险定价可视化的核心逻辑也很简单风险越高的客户贷款利率理应越高。但这还不够还需要回答“贷款用途对违约风险有没有显著影响”这个问题。买房装修、日常消费、生意周转、教育医疗不同用途背后的资金去向和真实还款来源不同定价策略也应该有差异。把这个差异通过散点图、漏斗图、热力图展示出来就是可视化研究的价值。1.2 整体技术架构与代码组织我的技术选型以Python为主原因很简单数据挖掘生态最成熟sklearn直接提供聚类和分类模型pandas处理表格数据非常顺手可视化用ECharts做前端展示后端用Flask暴露接口。整份源码分成四个模块data_process/原始数据导入、缺失值处理、异常值截断、特征工程最终输出标准化后的建模宽表。cluster_model/PCA降维、K-Means聚类、层次聚类辅助验证、轮廓系数评估、分群画像输出。risk_model/预警标签构造、训练集/测试集划分、逻辑回归与随机森林对比、AUC与KS指标计算。visualization/Flask后端接口ECharts前端大屏包括聚类散点图、风险漏斗图、用途利率散点图。这个组织方式不是随便拍的而是按照“数据 - 人群 - 风险 - 表达”的顺序来安排。每个模块的产物会成为下一个模块的输入。比如聚类结果作为一个新字段喂给风控模型风控模型的输出结果再作为可视化大屏的数据源。这样各模块之间耦合度低论文里也容易画出层次清晰的架构图。选择Flask而不直接用Django是因为毕设场景下不需要用户登录、后台管理这些偏工程的能力Flask轻量、接口逻辑简单前端用原生HTMLECharts就能跑。如果你已经装了Anaconda直接跑pip install flask pandas scikit-learn matplotlib就能把环境凑齐不需要额外折腾数据库。数据量在十万级以下时CSV文件配合pandas足够支撑整个项目了。2. 数据预处理与多维特征融合2.1 数据来源与核心字段梳理这个题目没有公开的标准数据集需要自己构造或合拼。常见的做法是参考Lending Club的公开字段结构自己构造一份包含客户基本信息、历史行为、贷款字段的模拟数据。字段设计要贴合国内互联网贷款的业务习惯至少要覆盖下面几类特征类别代表字段用途说明客户基本盘年龄、性别、收入、学历、职业类型刻画还款能力的基础信息负债与需求负债总额、贷款金额、贷款期限、申请用途判断资金压力与资金去向历史行为征信查询次数、历史逾期次数、当前逾期天数、历史贷款笔数判断还款意愿与信用历史行为融合特征收入负债比、近6个月平均查询次数、额度使用率、收入稳定性多维特征融合后的核心变量我在做数据准备时特别注意“贷款用途”字段题目里专门提到用途与风险定价可视化所以这个字段必须保留而且不能只做成哑变量还要在后续聚类和预警里作为分群的特征之一。用途一般包含经营周转、日常消费、装修、教育、医疗、购车、旅游等建议统一编码成类别再结合数值特征做Embedding式融合。如果你用的是自己爬来的数据格式肯定很乱。清洗的第一步是把所有字段统一命名、统一单位金额统一换算为元时间字段统一为YYYY-MM-DD。第二步是检查取值范围比如年龄不可能超过100岁贷款金额不能为负数征信查询次数必须是整数这些约束都要写进清洗函数里而不是靠肉眼核对。2.2 清洗、标准化与特征构造数据清洗这一块最容易出问题是缺失值比例高的时候。贷款数据里收入字段经常缺失直接删除会损失大量样本。我的做法是先用缺失比例排序低于5%的字段用中位数填充高于30%的字段直接丢弃中间的字段用“是否缺失”作为一个二值特征加入模型。这样既保住了样本量又把缺失信息本身变成了特征。异常值不能只靠describe()看一眼就完事。收入字段的99分位可能高达几十万直接参与聚类会让K-Means的重心计算被极值带偏。稳妥的截断方法是取1%到99%分位小于下分位或大于上分位的值统一压到边界。实际操作里可以这样写def winsorize_series(s, lower0.01, upper0.99): q_low s.quantile(lower) q_high s.quantile(upper) return s.clip(q_low, q_high)特征工程是提高模型效果最划算的一步。原始字段只能提供单点信息真正的风险信号往往藏在比值、频次、趋势里。我做过的项目中下面这几个手工衍生特征对聚类和预警的帮助最大debt_to_income 总负债 / 年收入直接衡量还款压力数值越高风险越大。recent_query_6m 近6个月征信查询次数查询次数暴增通常代表多头借贷。overdue_ratio 历史逾期次数 / 历史贷款笔数衡量用户还款习惯的稳定性。usage_rate 贷款金额 / 信用卡额度代表资金紧张程度。income_stability 近6个月收入标准差 / 平均收入收入波动越大越可能断供。这些衍生特征加进去之后你会明显感觉到客户之间的差异性被拉出来了。后面K-Means分出来的人群不再只是“年龄大的人”和“年龄小的人”而是“高负债、高查询、经营周转型”和“低负债、稳定收入、日常消费型”业务解释性强得多。2.3 多维特征融合策略特征融合在工程上的常见做法不是把变量随便拼在一起而是区分不同类型采用不同策略。数值特征用标准化类别特征用独热编码或目标编码时间特征拆成年、月、是否工作日等周期项再把这些处理后的特征一起拼成宽表。拼接之后通常会有几十维如果直接拿去聚类稀疏性和噪声会干扰距离计算。这时就需要降维。最常用的方案是PCA但PCA本身是无监督方法它只保留方差最大的方向不一定对业务解释友好。我会同时保留两个层面的结果聚类散点图用PCA的前两个主成分做展示建模数据用用标准化后的全量特征不强行压到极低维度。这样既照顾了可视化需求又不损失建模信息。真正意义上的多维融合我还会做一步“特征选择”。通过随机森林的特征重要性排序把重要性低于阈值的字段剔除只保留对风险区分度最高的二十个左右特征。这样做的好处是防止聚类被无关变量带偏比如“客户姓名长度”这种字段如果存在一定会干扰距离计算但它和风险没有关系。把所有特征无脑拼进去是新手最常见的错误。3. 客户聚类从K值选择到分群画像3.1 为什么先聚类再做风险预警一个很容易被忽视的问题是既然我们已经有监督模型可以做预警为什么还要先花力气聚类我的理解是互联网贷款客户天生就是异质的不同人群的风险驱动因素完全不同。单一模型用一个全局阈值判断所有客户会牺牲不同群体的差异。举个例子一个年收入50万的个体户偶尔逾期5天但贷款用途是备货周转还款来源清晰一个刚入职月收入6000元的白领从未逾期但贷款用途是分期买手机负债率已经超过60%。如果用全局模型打分很可能个体户被标记为风险更高但实际爆雷概率也许低于那个负债已经到顶的白领。聚类能帮我们先识别出这种结构性差异再在每个客群内部分别评估风险。同时聚类结果本身就是很好的业务标签。银行和消金公司非常看重客群分层不同层级用不同营销策略、不同利率、不同额度。毕设里能把这层意思讲透评委通常会觉得你对业务有理解而不是只会调包。3.2 K-Means与层次聚类的实践聚类算法我用的是K-Means主方案原因是它在样本量几万到几十万时效率高结果也相对好解释。但K-Means对初始中心敏感对离群点敏感所以前面提到的异常值截断和标准化必须做干净。为了避免随机种子带来的不稳定性我固定了random_state42并且跑了多组K值取轮廓系数的平均值再选K。K值的选择我同时用了两种方法。第一种是肘部法则画出簇内误差平方和SSE随K值变化的曲线找拐点第二种是轮廓系数直接计算不同K下的平均轮廓系数选最高的K。两者结论不一致时优先参考轮廓系数因为SSE曲线很多时候没有明显拐点。from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score scores [] for k in range(2, 9): model KMeans(n_clustersk, random_state42, n_init10) labels model.fit_predict(X_scaled) scores.append(silhouette_score(X_scaled, labels)) best_k range(2, 9)[scores.index(max(scores))]除了K-Means我还会跑一次层次聚类作为交叉验证。层次聚类可以画出树状图直观看出每个簇的分裂过程特别适合在论文里展示聚类逻辑。但层次聚类的计算复杂度偏高样本超过五万就慢得难受所以只用它在一个小规模抽样子集上验证。两个算法得出的簇数一致时结论会更有说服力。聚类散点图一定是用PCA降维后画的不然高维空间只能看两两组合无法呈现群体边界。实操中可以先PCA降到二维再以散点图着色展示聚类结果。每个簇的中心点可以用红色X标记这样即使客户样本点重叠严重也能看出分群重心的大致位置。3.3 聚类结果画像与业务解读聚类完成后我习惯做一张“分群画像表”把每个簇在各个特征上的均值列出来然后根据均值特征给簇起一个有业务含义的名字。这一步很关键论文里的分析深度全靠这里体现。比如做出来的结果可能是这样分群收入均值负债率均值近6月查询次数逾期率主要用途业务标签01.2万18%2.10.8%装修、购车优质稳健型1800055%6.84.2%日常消费、教育资金紧张型22.5万65%9.37.5%经营周转高杠杆经营型“优质稳健型”客户收入不低、负债不高逾期率极低适合推低息大额产品“资金紧张型”客户收入偏低但查询次数多存在多头借贷迹象应该控制额度并提高利率“高杠杆经营型”客户收入高但负债率也高风险缺口在于现金流波动需要结合经营流水数据做二次核验。这些画像结论看起来不复杂但需要你真的去统计均值并观察差异。我通常会把每个簇的特征均值做成横向柱状对比图再用雷达图展示三四个核心特征的相对水平。这些图放在毕设里比一堆数字直观得多评委扫一眼就知道聚类是有效的。4. 风险预警与风险定价模型4.1 预警标签怎么打风险预警属于有监督学习第一步是确定“什么是坏用户”。公开数据不会直接给你一个违约标签所以需要自己根据业务含义定义。我的定义规则是当前逾期天数30天标记为高风险同时结合未来三个月的还款表现如果用户后续逾期超过60天也标记为坏样本。这句话听起来简单但要注意标签泄漏。如果直接用“当前是否逾期”作为标签再把“当前逾期天数”作为特征模型会学到一个恒等映射看起来AUC接近1.0实际毫无意义。正确做法是删除“当前逾期天数”这个字段只保留历史行为特征比如过去12个月的逾期次数、查询次数、负债变化等。对于没有未来还款表现的数据也可以用无监督的异常检测思路来辅助打标签。把异常分数排名前10%的用户视为潜在风险客户再结合规则标签获得一个半监督训练集。但毕设场景下我更推荐直接构造模拟坏客户在正常客户中随机注入高逾期特征再标记为高风险。这样模型学的是特征之间的组合关系而不是某一列的复制粘贴。4.2 预警模型的选择与训练我同时跑了逻辑回归和随机森林原因是两者各有优势论文里可以做对比。逻辑回归是风控场景的基准模型系数正负和大小能直接解释为“特征每增加一个单位风险对数几率的变化”可解释性强随机森林能自动捕捉非线性关系AUC通常更高但黑盒感强。训练时要特别注意类别不平衡。正常情况下好客户可能占90%以上坏客户不到10%。如果直接训练模型会倾向于把所有样本预测为好客户虽然准确率很高AUC却并不一定差但坏客户一个也捞不上来。解决思路有三个一是用class_weightbalanced让模型自动调整类别权重二是对训练集做SMOTE过采样三是预测时把阈值从0.5下调到0.3或0.2按业务需求在召回率与精确率之间找平衡。评估指标不要只看准确率风控场景看AUC、KS、混淆矩阵。KS值能刻画模型区分好坏样本的能力大于0.3就基本可用了AUC和KS一起看比单看准确率靠谱得多。额外多说一句做特征重要性分析时随机森林能直接输出Gini importance逻辑回归可以用系数绝对值排序如果两边的top特征高度重合那说明特征工程做得很扎实。4.3 风险定价与贷款用途的关系风险定价这块很多人不知道怎么做量化。我的处理方式是把预警概率映射为风险溢价再与利率建立线性关系。假设基准资金成本是5%客户的违约概率是p预期损失率约等于p乘以一个固定损失回收系数那么风险定价利率可以近似为base_rate 0.05 loss_given_default 0.7 pred_prob model.predict_proba(X_test)[:, 1] risk_premium pred_prob * loss_given_default loan_rate base_rate risk_premium operation_cost这里面的loss_given_default设为0.7表示一旦违约本金损失七成这个数字可以按业务场景调整。最终算出的利率会自然地按预警概率高低排序高风险客户利率更高。为了可视化我们用贷款用途分组统计每组客户的平均预警概率、平均利率、实际逾期率然后画散点图和柱状图。从业务角度看经营周转类客户的金额通常较大、期限较短但利率跨度也比较大日常消费类客户金额小、利率相对稳定。如果发现某个用途组的实际逾期率和模型预测概率差距过大就说明该组客户的还款行为特殊需要单独调整定价策略。这种“用途-利率-逾期率”的三角关系正好是题目副标题要求研究的内容。5. 可视化大屏与数据呈现5.1 可视化指标框架与页面结构可视化部分不是随便扔几张统计图就完事而是要有清晰的看板逻辑。我的大屏分四个核心区域顶部放整体业务概览包括客户总数、贷款总额、整体风险等级占比、平均利率中间区域放聚类结果散点图和每个簇的用户画像指标右侧放风险预警概览包括高风险客户占比、风险等级漏斗图、逾期率走势底部区域放贷款用途与利率、逾期率的关系图。页面结构上我参考了现在的企业级数据可视化大屏风格以深色科技风为主用ECharts自带的深色主题配合红黄绿三色做风险等级提示。背景用深蓝灰色卡片标题统一左上角基础单位统一显示避免图表数据量纲不同导致误读。也可以用现有的大屏框架类项目作为模板但自己写HTML布局其实不复杂四张卡片加四个图表容器就够了。一屏放不下所有内容就采用“总览筛选同页切换”的方式。顶部放一个贷款用途的下拉筛选器选择不同用途时下面的聚类散点图、风险漏斗、利率分布全部联动刷新。交互式分析能体现你做了前端联动方案在评审时也是加分项。5.2 用ECharts实现核心图表ECharts的图形渲染质量高社区资料多出图效果非常适合毕设。我在项目里用了三种核心图PCA聚类散点图、风险等级漏斗图、用途-利率-逾期率散点图。聚类散点图的实现逻辑是把后端返回的每个样本的x、y、cluster三个字段拿出来遍历成ECharts的scatter数据。每个簇单独一个series颜色用调好的色板区分symbolSize按样本量做缩放避免太多点重叠成一团糊。这样的图放在大屏中间一眼就能看出人群分裂的边界。用途-利率-逾期率散点图更讲究一点横轴是平均利率纵轴是实际逾期率点的大小代表贷款金额颜色代表用途类型。这张图最直观地回应了“风险定价可视化研究”这个题目当看到某一个用途的点在右上方说明利率高、逾期率也高定价是合理的如果某个用途的点在左上方说明利率不高但逾期率不低就存在定价偏低的嫌疑。前端调用后端接口时我的Flask路由统一返回jsonify({status: 200, data: list_dict})前端用fetch拉数据后直接setOption。需要注意ECharts实例要在数据返回后再初始化否则容器宽度还没稳定很容易出现图表发虚的毛病。5.3 大屏的交互与部署注意交互设计方面我会给每个图表加上tooltip提示框、legend图例、dataZoom缩放条。尤其聚类散点图样本量大不带缩放条的话局部细节完全看不清楚。筛选器不只是装饰要真的绑定到后端的查询参数上比如选择“经营周转”后接口返回的数据全部按该用途过滤前端图表刷新。部署时我把Flask服务跑在本地9100端口大屏页面通过相对路径访问避免跨域问题。如果你要把项目打包交给老师不要给MySQL依赖最好把处理好的数据缓存在pickle或JSON文件里前端直接读静态JSON。这样免去老师配数据库的麻烦项目打开即用。数据口径的一致性是可视化最容易翻车的地方。很多人在后端写统计SQL时只写了客户总数到前端图表又是另一个总数两张图对不上答辩时会被一眼看穿。我的经验是全局只保留一份汇总好的JSON数据所有图表都从这份数据里取值不再各自计算。6. 实操过程遇到的高频坑6.1 标签泄漏预警模型看起来效果爆表的原因我一开始直接把“当前逾期天数”当作特征保留了结果逻辑回归AUC直接冲到0.97心里还挺高兴。后来用特征重要性一看排名第一的特征就是当前逾期天数这明显不对。因为“当前逾期天数”和标签的定义本就是一回事模型根本不需要学习直接记住这个字段就能预测。解决办法是把标签相关的字段从特征里全部剔除包括当前逾期天数、是否逾期这些直接变量。保留的只能是与历史行为有关的时间滞后特征比如过去6个月内的逾期次数、过去12个月的最高逾期天数。我反复提醒自己凡是“到了今天才能知道”的信息都不能用来预测“今天是否会发生风险”。6.2 K-Means结果不稳定换了几次数据都不一样K-Means对初始中心敏感换一套数据或者换一个随机种子聚类结果可能面目全非。哪怕逻辑上没有错答辩时老师让你复现一遍结果对不上就很尴尬。我做了两件事第一固定所有随机种子包括train_test_split、KMeans、模型初始化全部传random_state42第二增加n_init参数让KMeans跑十次取最优结果。再加上前面说的异常值截断和标准化聚类结果基本稳定。如果样本量大还可以对数据做mini-batch K-Means但毕设数据量一般不大直接跑标准版就行层次聚类验证也够用了。6.3 类别不平衡让预警模型变成“全好客户分类器”这个问题在训练初期折磨了我很久。当时模型预测的结果几乎全是“好客户”因为好客户占了92%哪怕全部预测为好准确率也有92%。但风险预警完全失效。后面我改用class_weightbalanced并把决策阈值下调才让坏客户召回率从不到10%升到65%左右。阈值调节时需要同时看混淆矩阵不能只看AUC。AUC只反映排序能力阈值决定实际判断结果这两个不是一回事。我还试过用SMOTE过采样效果也不错但对高维稀疏数据容易生成一些离群样本。偶尔遇到数据和模型没问题的场景加上SMOTE反而不如调阈值稳。所以我现在默认先用class_weight再调阈值过采样只是备选方案。6.4 可视化大屏图表错位和数据对不上图表容器在小屏端容易挤压变形我是在setOption之前等容器完成布局并且在初始化时给每个图表设置固定宽高百分比不写死像素。数据对不上的问题核心是统一数据源。我的方案是后端只生成一份dashboard_data.json前端四个图表引用同一个对象个别图表需要聚合数据就在前端临时计算不做两次后端查询。还有一个低级的坑ECharts的formatter里拼接变量时如果数值是None或者NaN整个tooltip会显示undefined。我一度以为自己数据有问题排查半天才发现是字符串模板里没做类型转换。所有输出数据在返给前端前我都强制round(..., 2)或str()避免这类玄学报错。7. 写在最后的一点建议这套源码做下来我最大的体会是数据分析类的毕设重点不全在模型调参而在于能不能把每一步决策背后的业务逻辑说通。为什么选K-Means而不是DBSCAN为什么把贷款用途放进聚类特征为什么风险定价要由预警概率驱动这些问题比AUC多0.03更能体现你的工作量和理解深度。源码交付时我习惯把环境依赖写在一个requirements.txt里附上一份“快速运行说明”从数据生成到启动Flask服务三步跑通。任何工具链都经不起折腾能一键演示的项目在评审时天然占优势。最后再分享一个小技巧如果你的时间仓促聚类部分只保留K-Means轮廓系数预警部分只保留逻辑回归阈值调节可视化只保留聚类散点图和风险漏斗图也能形成一个完整闭环。演示时按“数据预处理 - 分群 - 预警 - 定价 - 可视化”的顺序讲既有逻辑又有视觉冲击比盲目堆模型靠谱得多。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表