简介这是基于神经网络与支持向量机的保险风险预测项目使用保诚公司真实数据构建适合保险行业数据分析师、机器学习初学者及希望动手实战分类模型的读者可同时用于学习经典算法与业务落地。资源共7个文件、压缩包37.1MB包含3个Python脚本、3个CSV数据文件及1个说明文档脚本分别对应数据预处理、支持向量机风险评估和神经网络风险评估数据文件提供训练集、测试集与预测输出便于完整复现。目前已有43人学习下载。通过该资源读者能掌握两种主流算法处理高维保险数据的完整流程包括特征工程、模型训练、参数调优与性能评估理解混淆矩阵、精确率、召回率、F1分数等关键指标还可将相关方法迁移到客户细分、欺诈识别、个性化定价等业务场景对提升保险风险评估能力有直接帮助。1. 保诚保险风险预测为什么准确率不是这个项目的第一指标做保险风险预测尤其是面对保诚公司的这份公开数据时第一个要认清的现实是这个比赛和常规分类任务不一样官方评价指标不是准确率而是 QWKQuadratic 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(ascendingFalse) 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 万条样本特征维度 127SVM 正好可以处理这样的规模而且决策边界清晰不会像深度模型那样需要大量调参。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_size0.2, random_state42, stratifyy ) pipe Pipeline([ (scaler, StandardScaler()), (svm, SVC(kernelrbf, C1.0, gammascale, class_weightbalanced, probabilityTrue)) ]) pipe.fit(X_train, y_train) val_pred pipe.predict(X_val)参数说明C是惩罚系数控制误分类的容忍度C 越大惩罚越大决策边界越复杂容易过拟合C 越小越平滑但可能欠拟合。gammascale表示 gamma 由特征数量自动计算公式是 1/(特征数 * 方差)比手动设置更稳妥。probabilityTrue会启用 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_weightbalanced这行参数值得专门讲保诚数据中 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, activationrelu, input_shape(X_train.shape[1],)), BatchNormalization(), Dropout(0.3), Dense(128, activationrelu), Dropout(0.3), Dense(64, activationrelu), Dropout(0.2), Dense(8, activationsoftmax) ]) model.compile( optimizeradam, losssparse_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( monitorval_loss, patience10, restore_best_weightsTrue ) history model.fit( X_train, y_train_0, validation_data(X_val, y_val_0), epochs80, batch_size256, callbacks[early_stop], verbose1 ) # 预测时先 argmax 再加 1 prob model.predict(X_val) val_pred_nn np.argmax(prob, axis1) 1batch_size256是表格数据的常用设置显存占用小梯度更新稳定。patience10表示如果验证损失连续 10 轮没有下降就终止训练并恢复最佳权重这是防止过拟合的后悔药比手动调 epochs 靠谱得多。restore_best_weightsTrue保证你拿到的模型是验证集上最优的那一版而不是最后一步的权重。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 到 8sklearn 要求标签是连续的 0 到 7否则权重矩阵会错位。cohen_kappa_score的weightsquadratic参数虽然能算二次加权但标签值本身不合法时结果没有任何意义。解决先把预测值和真实值都减 1再送入函数。另一个细节是QWK 的计算要求两个序列长度一致且预测值必须被裁剪到合法范围内如果模型输出了 0 或者 9QWK 会直接崩。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, weightsquadratic) print(QWK:, qwk)5.2 SVM 训练时间失控几小时不出结果现象直接用完整 6 万训练集跑 RBF 核 SVM等了很久没有输出。原因SVM 核矩阵计算复杂度在样本量上接近 O(n^2) 甚至更高6 万样本已经非常勉强加上probabilityTrue会额外做交叉验证来校准概率时间成倍增长。解决先在大样本上使用线性核或采样跑通流程再用小样本交叉验证选参数。RBF 核先抽 1 万到 1.5 万样本调参最终提交前才在更大样本上重训并且关闭probabilityTrue只在保存概率时才开启。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 开启probabilityTrue后可以得到概率矩阵神经网络本身输出概率矩阵对两者加权求和再取 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, axis1) 1 qwk_final cohen_kappa_score(y_val - 1, np.clip(final_pred - 1, 0, 7), weightsquadratic) 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 各有适用边界一次好的落地往往靠的是两者互补而不是单一模型炫技。希望这些参数和踩坑记录能帮到你。本文还有配套的精品资源点击获取
