KS 和 LIFT 是风控、营销等策略模型中最核心的两个评估指标用于衡量模型的排序能力和业务提升效果。一、KSKolmogorov-Smirnov1.1 定义KS 衡量的是模型区分好样本和坏样本的能力即模型能把好人和坏人分开的程度。KSmax(∣TPR−FPR∣)KSmax(∣TPR−FPR∣)TPR真正率坏样本被正确识别的比例召回率FPR假正率好样本被误判为坏样本的比例1.2 直观理解import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.metrics import roc_curve # 示例模型预测分数 np.random.seed(42) n_bad 300 # 坏人 n_good 700 # 好人 # 坏人分数较高模型认为坏人概率高好人分数较低 bad_scores np.random.beta(5, 1, n_bad) * 0.5 0.5 # 坏人分数 0.5-1.0 good_scores np.random.beta(1, 5, n_good) * 0.5 # 好人分数 0-0.5 scores np.concatenate([bad_scores, good_scores]) labels np.concatenate([np.ones(n_bad), np.zeros(n_good)]) # 计算 KS fpr, tpr, thresholds roc_curve(labels, scores) ks_value max(tpr - fpr) ks_threshold thresholds[np.argmax(tpr - fpr)] print(fKS 值: {ks_value:.4f}) print(f最佳阈值: {ks_threshold:.4f}) # 绘制 KS 曲线 plt.figure(figsize(10, 6)) plt.plot(fpr, tpr, b-, linewidth2, labelfROC (KS{ks_value:.4f})) plt.plot([0, 1], [0, 1], r--, label随机) plt.fill_between(fpr, tpr, fpr, alpha0.3) plt.xlabel(FPR (好人误判率)) plt.ylabel(TPR (坏人识别率)) plt.title(KS 曲线) plt.legend() plt.show()1.3 KS 评估标准KS 范围模型质量说明 0.2不可用几乎没有区分能力0.2 - 0.3勉强可用需要优化0.3 - 0.5良好工业界常用标准0.5 - 0.6优秀非常好的模型 0.75过高可能过拟合风控行业标准信贷评分卡KS 0.3 可上线反欺诈模型KS 0.4 较好二、LIFT提升度2.1 定义LIFT 衡量的是用模型筛选出来的目标群体比随机选择好多少倍。LIFT模型选中范围内的坏样本率整体坏样本率LIFT整体坏样本率模型选中范围内的坏样本率2.2 计算示例def calculate_lift(y_true, y_pred_proba, top_percent10): 计算 LIFT 值 Parameters ---------- y_true : array 真实标签 y_pred_proba : array 预测为正类的概率 top_percent : float 取前百分之几的用户 Returns ------- lift : float 提升度 # 按预测概率降序排序 df pd.DataFrame({label: y_true, proba: y_pred_proba}) df df.sort_values(proba, ascendingFalse).reset_index(dropTrue) # 整体坏样本率 overall_bad_rate df[label].mean() # 前 top_percent% 的坏样本率 top_n int(len(df) * top_percent / 100) top_bad_rate df.iloc[:top_n][label].mean() # LIFT lift top_bad_rate / overall_bad_rate if overall_bad_rate 0 else 0 return lift, top_bad_rate, overall_bad_rate # 示例 lift_10, top_rate, overall_rate calculate_lift(labels, scores, top_percent10) print(f整体坏样本率: {overall_rate:.4f}) print(f前10%坏样本率: {top_rate:.4f}) print(fLIFT10%: {lift_10:.2f}倍) # 计算不同百分位的 LIFT for pct in [5, 10, 20, 30]: lift, _, _ calculate_lift(labels, scores, top_percentpct) print(fLIFT{pct:2d}%: {lift:.2f}倍)2.3 LIFT 曲线def plot_lift_curve(y_true, y_pred_proba): 绘制 LIFT 曲线 df pd.DataFrame({label: y_true, proba: y_pred_proba}) df df.sort_values(proba, ascendingFalse).reset_index(dropTrue) overall_bad_rate df[label].mean() lift_values [] percentiles range(1, 101) for p in percentiles: top_n int(len(df) * p / 100) top_bad_rate df.iloc[:top_n][label].mean() lift top_bad_rate / overall_bad_rate if overall_bad_rate 0 else 0 lift_values.append(lift) plt.figure(figsize(10, 6)) plt.plot(percentiles, lift_values, g-, linewidth2) plt.axhline(y1, colorr, linestyle--, label基准线 (LIFT1)) plt.fill_between(percentiles, 1, lift_values, alpha0.3) plt.xlabel(用户百分比 (%)) plt.ylabel(LIFT 值) plt.title(LIFT 曲线) plt.legend() plt.grid(alpha0.3) plt.show() plot_lift_curve(labels, scores)三、在你的分类模型中添加 KS 和 LIFTimport pandas as pd import numpy as np from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import roc_curve, roc_auc_score import joblib import sys def calculate_ks(y_true, y_pred_proba): 计算 KS 值 fpr, tpr, thresholds roc_curve(y_true, y_pred_proba) ks max(tpr - fpr) # 获取 KS 对应的阈值 ks_threshold thresholds[np.argmax(tpr - fpr)] return ks, ks_threshold def calculate_lift(y_true, y_pred_proba, top_percent10): 计算 LIFT 值 df pd.DataFrame({label: y_true, proba: y_pred_proba}) df df.sort_values(proba, ascendingFalse).reset_index(dropTrue) overall_bad_rate df[label].mean() top_n int(len(df) * top_percent / 100) top_bad_rate df.iloc[:top_n][label].mean() lift top_bad_rate / overall_bad_rate if overall_bad_rate 0 else 0 return lift, top_bad_rate, overall_bad_rate def calculate_lift_curve(y_true, y_pred_proba, percentiles[5, 10, 20, 30, 50]): 计算多个百分位的 LIFT results {} for p in percentiles: lift, top_rate, overall calculate_lift(y_true, y_pred_proba, p) results[p] { lift: lift, top_bad_rate: top_rate, overall_bad_rate: overall } return results def main(): # 读取数据 input_file sys.argv[1] if len(sys.argv) 1 else data.csv output_file sys.argv[2] if len(sys.argv) 2 else model.pkl df pd.read_csv(input_file) X df.drop(target, axis1) y df[target] # 划分数据 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 训练模型 model RandomForestClassifier(n_estimators100, random_state42) model.fit(X_train, y_train) # 预测概率 y_pred_proba model.predict_proba(X_test)[:, 1] # 计算 KS ks, ks_threshold calculate_ks(y_test, y_pred_proba) # 计算 AUC auc roc_auc_score(y_test, y_pred_proba) # 计算 LIFT lift_10, _, _ calculate_lift(y_test, y_pred_proba, top_percent10) lift_curve calculate_lift_curve(y_test, y_pred_proba) # 输出结果 print(f\n{*50}) print(f策略模型评估指标) print(f{*50}) print(fAUC: {auc:.4f}) print(fKS: {ks:.4f}) print(fKS阈值: {ks_threshold:.4f}) print(fLIFT10: {lift_10:.2f}倍) print(f\nLIFT 曲线:) for p, v in lift_curve.items(): print(f LIFT{p:2d}%: {v[lift]:.2f}倍 (坏样本率: {v[top_bad_rate]:.2%})) print(f{*50}\n) # 保存模型 joblib.dump(model, output_file) print(f模型已保存到 {output_file}) return ks, lift_10 if __name__ __main__: main()四、KS 和 LIFT 的业务应用4.1 确定拒绝阈值风控场景def find_optimal_threshold_by_ks(y_true, y_pred_proba, target_ksNone): 根据 KS 找到最优阈值 Parameters ---------- y_true : array 真实标签 y_pred_proba : array 预测概率 target_ks : float, optional 目标 KS 值如果指定则找到达到该 KS 的最小阈值 Returns ------- optimal_threshold : float 最优阈值 fpr, tpr, thresholds roc_curve(y_true, y_pred_proba) ks_values tpr - fpr if target_ks: # 找到达到目标 KS 的最小阈值 idx np.where(ks_values target_ks)[0] if len(idx) 0: optimal_idx idx[0] else: optimal_idx np.argmax(ks_values) else: # 找到最大 KS 对应的阈值 optimal_idx np.argmax(ks_values) optimal_threshold thresholds[optimal_idx] # 计算拒绝率和通过率 reject_rate (y_pred_proba optimal_threshold).mean() pass_rate 1 - reject_rate # 计算拒绝样本中的坏样本率 rejected_mask y_pred_proba optimal_threshold rejected_bad_rate y_true[rejected_mask].mean() if rejected_mask.any() else 0 print(f最优阈值: {optimal_threshold:.4f}) print(f拒绝率: {reject_rate:.2%}) print(f通过率: {pass_rate:.2%}) print(f拒绝样本坏样本率: {rejected_bad_rate:.2%}) return optimal_threshold # 使用 threshold find_optimal_threshold_by_ks(y_test, y_pred_proba)4.2 根据 LIFT 确定营销目标人群def find_target_population_by_lift(y_true, y_pred_proba, min_lift2.0): 根据最小 LIFT 确定目标人群比例 Parameters ---------- y_true : array 真实标签 y_pred_proba : array 预测概率 min_lift : float 最小期望提升度 Returns ------- target_percent : float 目标人群比例 df pd.DataFrame({label: y_true, proba: y_pred_proba}) df df.sort_values(proba, ascendingFalse).reset_index(dropTrue) overall_bad_rate df[label].mean() for p in range(1, 101): top_n int(len(df) * p / 100) top_bad_rate df.iloc[:top_n][label].mean() lift top_bad_rate / overall_bad_rate if overall_bad_rate 0 else 0 if lift min_lift: target_percent p - 1 break else: target_percent 100 print(f要达到 {min_lift} 倍提升应筛选前 {target_percent}% 的用户) return target_percent # 使用 target_pct find_target_population_by_lift(y_test, y_pred_proba, min_lift2.0)五、KS vs LIFT 对比总结维度KSLIFT核心问题模型能把好坏分开吗模型比随机选择好多少计算公式max(TPR - FPR)(选中群体坏率) / (整体坏率)取值范围0 ~ 10 ~ ∞通常 1~3判断标准 0.3 可用 1.5 有价值业务含义模型区分能力业务提升倍数主要用途评估模型质量确定营销/拒绝策略关注点模型整体性能前百分之几的用户一句话记忆KS风控模型能不能把坏客户揪出来LIFT用模型筛人比瞎蒙高效多少倍风控策略应用KS 0.3模型可以上线LIFT10% 2.0拒绝前10%的用户是划算的