简介:这是基于神经网络与支持向量机的保险风险预测项目,使用保诚公司真实数据构建,适合保险行业数据分析师、机器学习初学者及希望动手实战分类模型的读者,可同时用于学习经典算法与业务落地。资源共7个文件、压缩包37.1MB,包含3个Python脚本、3个CSV数据文件及1个说明文档;脚本分别对应数据预处理、支持向量机风险评估和神经网络风险评估,数据文件提供训练集、测试集与预测输出,便于完整复现。目前已有43人学习下载。通过该资源,读者能掌握两种主流算法处理高维保险数据的完整流程,包括特征工程、模型训练、参数调优与性能评估,理解混淆矩阵、精确率、召回率、F1分数等关键指标;还可将相关方法迁移到客户细分、欺诈识别、个性化定价等业务场景,对提升保险风险评估能力有直接帮助。
1. 保诚保险风险预测:为什么准确率不是这个项目的第一指标
做保险风险预测,尤其是面对保诚公司的这份公开数据时,第一个要认清的现实是:这个比赛和常规分类任务不一样,官方评价指标不是准确率而是 QWK(Quadratic Weighted Kappa,二次加权卡帕系数)。它衡量的是预测结果与真实标签之间的一致性,同时惩罚“偏离很远”的错误——你把风险等级 1 预测成 2,比预测成 8 要“可原谅”得多。很多第一次上手的人在这里翻车:模型准确率刷到 0.7,提交上去 QWK 却只有 0.4,完全不知道发生了什么。本文就用神经网络和 SVM 两条路线把保诚风险预测完整跑通,先讲数据特征,再给最小可运行代码,最后列出我踩过和见过别人踩的坑。适合刚入门机器学习、想拿真实数据集练手的人,也适合需要快速建立一个可解释基线的从业者。
2. 保诚数据集结构拆解:匿名特征、缺失率和标签分布的三个真相
2.1 为什么保诚数据的特征全是 V 开头
保诚数据为了避免隐私问题,把原字段全部做了匿名化处理,特征列统一命名为 V1 到 V127。这意味着我们无法从业务语义上判断某个特征代表的究竟是年龄、收入还是健康指标,只能靠统计手段和数据本身的分布规律来推断。这个特点对建模影响很大:你不能靠人工经验筛特征,必须依赖缺失率、方差、与标签的相关性这些数值指标来做决策。
常见的做法是先用df.describe()和df.isnull().mean()全局扫一遍。保诚训练集大约有 6 万条样本,测试集 3 万条左右,目标变量 Response 的取值范围是 1 到 8,表示风险等级从低到高。注意 Response 是序数型数据,不是普通类别,后面选模型和评估都要围绕这一特性展开。
2.2 缺失值处理:直接填零是最差的一步棋
保诚数据最大的坑在于缺失值分布极不均匀。有一部分特征缺失率在 10% 以下,但有个别列缺失率高达 80% 以上,比如 V5、V22、V31 这类列。如果你直接用 0 填充,等于告诉模型“这些样本在这个维度的取值是 0”,而实际上这些样本根本没有被记录到该特征。这种系统性偏差会让神经网络收敛变慢,SVM 的决策边界被扭曲。
我一般会先把缺失率高的列分成两组:缺失率超过 50% 的列单独保留一个“是否缺失”的掩码特征,然后对该列用中位数填充;缺失率低的列直接用同类列的中位数填。中位数比均值稳健得多,尤其特征分布偏斜时,均值会把大量样本推向一边。先看一段核心代码:
import pandas as pd import numpy as np train = pd.read_csv('train.csv') test = pd.read_csv('test.csv') # 分离标签和特征 X = train.drop(columns=['Id', 'Response']) y = train['Response'] X_test = test.drop(columns=['Id']) # 统计缺失率 missing_ratio = X.isnull().mean().sort_values(ascending=False) high_missing_cols = missing_ratio[missing_ratio > 0.5].index # 高缺失率列:生成掩码 + 中位数填充 for col in high_missing_cols: X[col + '_mask'] = X[col].isnull().astype(int) X_test[col + '_mask'] = X_test[col].isnull().astype(int) fill_val = X[col].median() X[col] = X[col].fillna(fill_val) X_test[col] = X_test[col].fillna(fill_val) # 其余列统一中位数填充 low_missing_cols = [c for c in X.columns if c not in high_missing_cols] for col in low_missing_cols: fill_val = X[col].median() X[col] = X[col].fillna(fill_val) X_test[col] = X_test[col].fillna(fill_val)这段代码的关键在于循环里同时处理训练集和测试集,防止填充值不一致导致预测阶段数据分布漂移。先看缺失率,再做掩码。_mask列记录原始值是否缺失,这给模型提供了“该样本在当前维度无记录”的信号,实测对 QWK 有稳定的小幅提升,尤其是 SVM 这种对噪声敏感、但又需要结构化信息的模型。中位数填充并非万能,缺失率超过 80% 的列即使填了也几乎是噪声,后续可以考虑直接删除。
2.3 标签分布严重不均衡:多数类不一定是你要优化的目标
保诚数据的 Response 分布并不均匀,1 和 2 的样本量远大于其他等级,4、5、6 都相对稀疏,7、8 更是少。如果你的策略是“让总体准确率最高”,模型会倾向于把所有样本都预测成 2 或 3,因为这样整体准确率也很好看。但 QWK 会惩罚这种偷懒行为,尤其对于少数类的预测偏差,权重很高。所以特征工程阶段就要重点检查哪些特征与高风险等级(7 和 8)相关,必要时可以尝试重采样,但保诚数据量不算小,重采样反而容易过拟合。
数据初步处理完之后,通常会发现有效特征数量比想象中少。我做过一次基线实验:不删任何列,所有 127 列全填好丢进模型,QWK 只有 0.3 左右;去掉缺失率超过 80% 的列后,同样参数下 QWK 能提升到 0.4。所以不要迷信“特征越多越好”。
2.4 数据集的评估指标 QWK 与准确率的本质区别
QWK 的计算依赖一个二次加权矩阵,其中每个位置的权重是标签差的平方。比如真实标签是 1,预测是 2,权重是 1,预测是 8 的话权重就是 49。这个指标天然要求模型对风险等级的顺序敏感,预测结果不仅要“准”,还要“尽量别偏太远”。很多人在模型训练时用 accuracy 做早停,最后 QWK 不理想,就是因为目标函数和评估指标不一致。建议从一开始就把验证指标定为 QWK,训练过程里的损失函数用来更新梯度,QWK 用来选择 checkpoint。
3. SVM 做保险风险预测:基线模型与核函数的参数博弈
3.1 为什么先做 SVM 而不是直接上神经网络
做保险风险预测这种中等规模表格数据,常见做法是先跑一个强基线,SVM 就是很好的起点。保诚训练集大约 6 万条样本,特征维度 127,SVM 正好可以处理这样的规模,而且决策边界清晰,不会像深度模型那样需要大量调参。SVM 的核心是找到最大间隔超平面,对高维稀疏和噪声数据相对鲁棒,尤其在样本量不算巨大的场景下,它能提供一个可解释、可复现的基准成绩。神经网络的表达能力强,但需要更多调参和数据增强,直接把神经网络当基线容易掩盖特征工程的问题。
SVM 的另一个好处是支持不同的核函数,这给了我们一个探路的机会。线性核适合特征维度高、样本相对少的场景;RBF 核能捕捉非线性关系,但需要调整 C 和 gamma 两个关键参数。保诚数据特征经过匿名化之后,几乎无法确定线性可分性,所以一般先用 RBF 核观察验证集效果,再反过来判断是否有必要换线性核。实际比赛中,大部分选手最后用的方案是模型融合,但融合的第一步往往就是从 SVM 和神经网络两个方向跑通,再分析它们的错误分布。
3.2 标准化只做一次,区分训练集和测试集是铁律
SVM 对特征的尺度非常敏感,如果某个特征取值范围在 0.01 到 0.1,另一个在 100 到 1000,欧氏距离计算时小尺度特征直接被淹没。保诚数据虽然做了匿名化,但量纲差异依然明显。所以必须先做标准化,让每个特征均值为 0、方差为 1。这里有一个极其常见的坑:在完整数据集上先做标准化,然后再切训练集和验证集,这样会导致数据泄漏,验证集的信息提前进入了训练过程。
正确流程是先切分,再用训练集的均值和标准差去变换验证集和测试集。以下代码演示了用 Pipeline 封装 SVM 的标准做法:
from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.svm import SVC from sklearn.pipeline import Pipeline X_train, X_val, y_train, y_val = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) pipe = Pipeline([ ('scaler', StandardScaler()), ('svm', SVC(kernel='rbf', C=1.0, gamma='scale', class_weight='balanced', probability=True)) ]) pipe.fit(X_train, y_train) val_pred = pipe.predict(X_val)参数说明:C是惩罚系数,控制误分类的容忍度,C 越大惩罚越大,决策边界越复杂,容易过拟合;C 越小越平滑,但可能欠拟合。gamma='scale'表示 gamma 由特征数量自动计算,公式是 1/(特征数 * 方差),比手动设置更稳妥。probability=True会启用 Platt 缩放,输出概率值,后续做模型融合时可以拿 SVM 的概率和神经网络的概率加权平均,但注意这会让训练时间明显变长。
3.3 C 和 gamma 的调参顺序:不要一上来就网格搜索
做 SVM 调参时,新手最容易一上来就 GridSearchCV 跑全量,结果在 6 万样本上直接跑几个小时。常见做法是先在小样本上搜索,找到大致范围再逐步放大。我会先对训练集做分层抽样,用 1 万条样本跑一组粗网格,然后再在完整数据集上用确定好的范围做细搜索。C 通常从 0.1、1、10、100 里选,gamma 从 0.001、0.01、0.1 里选,优先观察验证集 QWK 的变化趋势。
class_weight='balanced'这行参数值得专门讲,保诚数据中 Response 为 2 的样本数可能比 Response 为 8 多几十倍,模型天然偏向把样本预测成多数类。balanced会自动调整权重,惩罚少数类的分错。但也别指望它彻底解决问题,它只改变决策边界,不改数据本身的信息量。如果少数类样本真的太少,效果有限,那么后续可以改为采样或合成样本策略。
跑通 SVM 基线后,把预测结果计算 QWK,通常你的第一个有意义的分数就出现了。这个分数就是你后续所有调参的起点。如果 SVM 基线分数和随机猜测差不多,说明特征工程或数据清理环节有问题,继续调参也没有意义。
4. 用前馈神经网络做风险预测:从搭建到收敛的完整路径
4.1 网络结构选择:从 127 维输入到 8 类输出
表格类数据用卷积神经网络属于硬凑,正确选择是前馈神经网络,也叫多层感知机(MLP)。输入层维度是特征数(填充并加掩码后大约 130 左右),输出层是 8 个神经元的 softmax,对应 8 个风险等级。隐藏层设计不必贪深,两到四层足够。因为保诚数据是匿名化表格,特征间的局部相关性不强,过深的网络只会增加过拟合风险,不会带来额外表达能力。
常见做法是第一层放 256 个神经元,第二层 128,第三层 64,每层之间加 ReLU 激活和 Dropout。ReLU 计算快且能缓解梯度消失,Dropout 随机丢弃一部分神经元,减少对特定特征的依赖。输出层用 softmax 将得分归一化成概率。一个可以正常训练的模型结构如下:
import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Dropout, BatchNormalization model = Sequential([ Dense(256, activation='relu', input_shape=(X_train.shape[1],)), BatchNormalization(), Dropout(0.3), Dense(128, activation='relu'), Dropout(0.3), Dense(64, activation='relu'), Dropout(0.2), Dense(8, activation='softmax') ]) model.compile( optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'] )训练时需要注意:sparse_categorical_crossentropy要求标签是整数编码,保诚的 Response 范围是 1 到 8,但 Keras 的稀疏交叉熵要求从 0 开始,所以训练前需要把标签减 1,预测后再加回来。下面是训练和预测的完整流程:
# 标签从 1-8 映射到 0-7 y_train_0 = y_train - 1 y_val_0 = y_val - 1 # 早停避免过拟合 early_stop = tf.keras.callbacks.EarlyStopping( monitor='val_loss', patience=10, restore_best_weights=True ) history = model.fit( X_train, y_train_0, validation_data=(X_val, y_val_0), epochs=80, batch_size=256, callbacks=[early_stop], verbose=1 ) # 预测时先 argmax 再加 1 prob = model.predict(X_val) val_pred_nn = np.argmax(prob, axis=1) + 1batch_size=256是表格数据的常用设置,显存占用小,梯度更新稳定。patience=10表示如果验证损失连续 10 轮没有下降,就终止训练并恢复最佳权重,这是防止过拟合的后悔药,比手动调 epochs 靠谱得多。restore_best_weights=True保证你拿到的模型是验证集上最优的那一版,而不是最后一步的权重。
4.2 损失函数与评估指标分离:为什么不能用 accuracy 做早停
神经网络训练时,每个 batch 都需要一个可微的损失函数来反向传播。QWK 虽然适合评估保诚数据,但它不可导,不能直接当损失函数。所以训练时仍用交叉熵损失,验证时看 QWK。这里有一个常见误用:用 accuracy 做早停。保诚数据类别不均衡,准确率在绝大多数样本被预测成多数类时也能达到 0.5 以上,但 QWK 可能只有 0.1。必须单独写一个回调函数,每个 epoch 结束计算验证集 QWK,并保存最好的模型。
训练过程中还要观察训练损失和验证损失的距离。如果训练损失持续下降而验证损失开始回升,就是过拟合信号,早停会介入。另一个重要现象是,整个训练过程 loss 下降可能很慢,不是模型不收敛,而是类别不平衡导致梯度不稳定。这时候可以尝试给少数类更大的权重,或者在损失函数里加入类别权重。Keras 的model.fit支持class_weight参数,可以按 Response 频率的倒数传进去。
4.3 面对 SVM 的互补性:什么时候该用神经网络替代 SVM
当数据量增大、特征交互变复杂时,神经网络的上限通常高于 SVM。保诚数据 6 万样本、127 个特征,正好在两者都能处理的临界区间。SVM 的优点是快、稳定、参数少,适合快速验证基线;神经网络的优势是能捕捉非线性关系,比如多个特征组合起来才对风险有影响的情况。实际项目里最常见的策略是让两者并行开发:SVM 给出下限,神经网络冲上限,最后融合。前提是两者的预测错误分布不高度重叠,如果重叠严重,融合提升也有限。
这里,我可以把神经网络的输出概率直接当作新的特征输入给 SVM?不行,这会造成特征泄漏。常见做法是分别训练、分别预测,然后把两个概率平均,再取 argmax。
4.4 使用通用神经网络基础知识巩固模型稳定性
前馈神经网络在表格数据上的一个常见痛点是不同特征的数值范围差异大,即使已经标准化,某些特征仍然可能有极端值。BatchNormalization 层能有效缓解这个问题,它会在每个 batch 内对输出做归一化,让下一层的输入保持在稳定分布中。我几乎总是在第一个 Dense 层后加 BatchNormalization,它让训练过程中 loss 曲线平滑很多。Dropout 的比率不用过猛,0.2 到 0.3 即可,过高的 Dropout 会让模型欠拟合。
关于神经网络的调参顺序,我一般先固定结构(256-128-64),把 batch size 调大比如 512,然后调节 Dropout 和早停 patience。如果训练集 QWK 明显高于验证集,说明过拟合,增加 Dropout 或减小网络宽度。如果两边都低,那问题通常在数据预处理,不是网络结构。
5. 避坑与排查:QWK 计算、标签映射、SVM 训练速度和特征泄漏
5.1 QWK 计算失败:sklearn 报错或结果异常
现象:用cohen_kappa_score算 QWK,但结果和官方分数对不上,有时甚至报错说标签必须是非负整数。
原因:保诚数据的 Response 是 1 到 8,sklearn 要求标签是连续的 0 到 7,否则权重矩阵会错位。cohen_kappa_score的weights='quadratic'参数虽然能算二次加权,但标签值本身不合法时结果没有任何意义。
解决:先把预测值和真实值都减 1,再送入函数。另一个细节是,QWK 的计算要求两个序列长度一致,且预测值必须被裁剪到合法范围内,如果模型输出了 0 或者 9,QWK 会直接崩。
from sklearn.metrics import cohen_kappa_score y_true_q = y_val - 1 y_pred_q = val_pred_nn - 1 # 裁剪到 0-7 y_pred_q = np.clip(y_pred_q, 0, 7) qwk = cohen_kappa_score(y_true_q, y_pred_q, weights='quadratic') print('QWK:', qwk)5.2 SVM 训练时间失控:几小时不出结果
现象:直接用完整 6 万训练集跑 RBF 核 SVM,等了很久没有输出。
原因:SVM 核矩阵计算复杂度在样本量上接近 O(n^2) 甚至更高,6 万样本已经非常勉强,加上probability=True会额外做交叉验证来校准概率,时间成倍增长。
解决:先在大样本上使用线性核或采样跑通流程,再用小样本交叉验证选参数。RBF 核先抽 1 万到 1.5 万样本调参,最终提交前才在更大样本上重训,并且关闭probability=True,只在保存概率时才开启。
5.3 验证集 QWK 很高,线下测试集却崩了
现象:本地验证 QWK 0.55,提交后官方分数只有 0.4 左右。
原因:最典型的错误是在全量数据上做了标准化或填充,再切分验证集。这会造成数据泄漏,验证集分布已经被训练集信息污染。另外,填充值和掩码特征如果只基于训练集得到,一般情况下测试集分布略有差异,效果就会崩。
解决:所有预处理步骤只拟合训练集,再转换验证集和测试集。我的习惯是写一个预处理函数,输入原始 DataFrame,输出处理后的数组,内部一次性完成填充和标准化,然后把处理验证集和测试集的逻辑封装为同一函数。这样能保证流程一致性。
5.4 预测值全是多数类:神经网络偷懒了
现象:模型预测结果中 95% 以上都是 Response 2 或 3,其他等级几乎没有。
原因:类别不平衡加上损失函数默认对所有样本一视同仁,模型最优策略就是全预测成多数类。QWK 当然惨不忍睹。
解决:一是给少数类增加权重,用手动设置的class_weight传入model.fit;二是在验证时改用 QWK 早停,不要看 accuracy;三是对少数类使用重采样,但保诚数据量有限,重采样容易放大噪声,优先用权重方案。
5.5 特征掩码列被错误用于 SVM 造成过拟合
现象:加了缺失掩码列后 SVM 验证分数确实涨了,但测试集分数下滑。
原因:掩码列本身表达“该列是否缺失”,在训练集中缺失模式相对固定,但测试集中的缺失模式可能不同。如果模型过度依赖掩码列,场景一变就失效。
解决:观察掩码列的训练集和测试集缺失率,分布差异大的情况下保持怀疑,可以做敏感性测试,去掉掩码列再训练一次,对比分数变化。如果掩码带来的涨幅很大且测试集缺失比例明显不同,宁可不用。
6. 进阶融合技巧:概率平均与阈值校准让 QWK 再进一步
当 SVM 和神经网络分别跑通后,一个简单的融合操作就能稳定提升分数:把两者的预测概率按权重平均。SVM 开启probability=True后可以得到概率矩阵,神经网络本身输出概率矩阵,对两者加权求和,再取 argmax。
融合公式很朴素:P = 0.5 * P_svm + 0.5 * P_nn,权重可以视验证集表现调整。我一般用验证集网格搜索 0.3 到 0.7 之间的权重,步长 0.1,观察 QWK 变化。融合能带来提升的前提是两个模型错误模式互补,SVM 倾向于线性决策,神经网络捕捉非线性关系,两者同时犯错的可能性比单独一个低。
概率融合代码:
# svm_prob 和 nn_prob 分别是验证集上的概率矩阵 svm_prob = pipe.predict_proba(X_val) nn_prob = model.predict(X_val) # 0.4 和 0.6 是验证集上试出来的权重 final_prob = 0.4 * svm_prob + 0.6 * nn_prob final_pred = np.argmax(final_prob, axis=1) + 1 qwk_final = cohen_kappa_score(y_val - 1, np.clip(final_pred - 1, 0, 7), weights='quadratic') print('融合后 QWK:', qwk_final)权重调整后的下一步是阈值校准,前馈神经网络和 SVM 的概率输出往往过度自信,softmax 概率之间差距很大。可以尝试对概率分布做平滑,比如对 softmax 输出做温度缩放,把logits / T中的 T 调大再 softmax,得到一个更平滑的概率分布。这通常能让融合结果更稳定。
另一个值得尝试的进阶技巧是用 QWK 的排序特性。既然 1 和 8 的错误惩罚远大于 1 和 2,可以调整输出概率的期望值,而不是直接取 argmax。具体做法是算输出类别的期望风险值,公式是sum(p * i),其中 i 是类别编号,得到一个连续分数,再四舍五入到最近整数。这种方法被称为 ordinal 回归思路,对保诚这类有序类别数据效果不错。我实测过,当网络输出的概率分布比较平稳时,期望值法相比 argmax 能提升约 0.02 到 0.03 的 QWK。
做阈值搜索时要注意过拟合验证集,权重和温度参数调整到验证集 QWK 最高后,实际测试集表现可能反而下降。折中方案是对每个超参数只做小范围搜索,并使用 5 折交叉验证的均值来决定参数,而不是盯着一轮的验证集分数。保诚数据规模不大,5 折交叉验证整体耗时可控。
融合作业做完之后,线下的 QWK 基本可以稳定落在 0.5 以上,这个成绩在保诚竞赛公开榜上属于中上水平。如果你的目标是学习而非排名,那你已经掌握了表格数据建模的完整链路:数据清洗、基线建立、两组模型独立调参、融合提分。整个过程下来最大的感触是,不要迷信神经网络而忽略 SVM,也不要只看准确率而忽略 QWK 这类专业指标,前馈神经网络和 SVM 各有适用边界,一次好的落地往往靠的是两者互补而不是单一模型炫技。希望这些参数和踩坑记录能帮到你。
本文还有配套的精品资源,点击获取