ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

伯努利朴素贝叶斯实战:保险客户购买预测与Python实现

伯努利朴素贝叶斯实战:保险客户购买预测与Python实现 简介在机器学习分类任务中二分类问题广泛存在于营销响应预测、信用评估等场景。贝叶斯定理通过先验概率与条件概率计算后验概率为分类提供了坚实的概率基础。伯努利朴素贝叶斯是朴素贝叶斯家族中专门处理二值特征的高效算法它假设特征独立且取值为0或1凭借训练速度快、参数少、可解释性强等优势成为快速建立基线的理想选择。在保险交叉销售场景中针对存量客户是否购买房车险的预测伯努利朴素贝叶斯能够对客户进行概率排序帮助销售团队聚焦高潜名单显著提升转化效率。本文从数据预处理、模型训练到结果评估完整展示了基于Python和伯努利朴素贝叶斯的保险客户购买预测实现过程。 月初我拿到一批保险存量客户名单业务方的需求很直接这几万个客户里哪些人最有可能为房车单独购买一份保险。销售人力有限不可能挨个电话拜访只能从数据里筛出高概率人群优先跟进。这就是典型的二分类营销预测问题而我选的方案是Python、源码加数据集一条龙搞定核心模型用伯努利朴素贝叶斯。训练集里每个客户有几十个多维度的属性特征目标列就一个0表示不买1表示购买。跑完以后按预测概率倒序排把名单前10%的客户交给销售实测下来这个名单的转化率比随机名单高出好几倍。这篇文章就把整个项目完整拆开从为什么选伯努利朴素贝叶斯到数据集长什么样再到源码怎么写、预测结果怎么解读最后把我在实际跑数据时踩过的坑和调优方向一次性说清楚。1. 为什么用伯努利朴素贝叶斯这个模型和保险预测场景的匹配逻辑1.1 房车险客户预测到底在预测什么房车险不属于高频刚需险种买的人少客单价却高。业务上的本质是存量客户交叉销售客户已经在保险公司买过车险、家财险或寿险现在要判断他会不会再买一份专门保障房车露营车的保险。这类预测有几个共同特点样本量足够大正样本比例很低特征几乎都是离散化的人口统计信息和历史投保行为可解释性必须要强因为销售顾问需要知道“为什么推荐这个客户”。用模型术语说这就是一个二分类监督学习问题。输入是客户的静态画像特征输出是购买概率。问题本身不复杂但难就难在正样本太稀薄。我处理过的这批数据里购买房车险的客户占比只有6%左右用朴素准确率评估毫无意义后面我会专门讲怎么应对。为什么选择贝叶斯类模型而不是直接上深度网络原因也简单这是结构化表格数据不是图像文本样本量大约只有几万条深度模型在这里既容易过拟合又不好解释。朴素贝叶斯刚好相反训练极快参数极少而且能给出每个特征“倾向于买”还是“倾向于不买”的直观证据。1.2 伯努利朴素贝叶斯的核心假设和适用条件朴素贝叶斯的核心就是贝叶斯定理外加一条“特征条件独立”的简化假设。用公式表达就是P(购买 | 特征) ∝ P(购买) × P(特征1 | 购买) × P(特征2 | 购买) × … × P(特征n | 购买)这里的“朴素”指的是假设所有特征在给定标签的条件下互相独立。现实中这个假设几乎不可能完全成立但贝叶斯模型在实践中经常表现得不错尤其当特征经过精心选择后相关性没有严重到让排序失真。伯努利朴素贝叶斯是朴素贝叶斯家族里专门处理二值特征的一个变种。它假设每个特征只取0或1比如“家庭年收入是否超过40万”“是否拥有自己的住房”“是否有过车险理赔记录”。模型要估计的是两类概率类先验P(购买)、P(不购买)以及每个特征在某个类别下的条件概率P(特征1 | 购买)和P(特征1 | 不购买)。这些概率直接通过训练集里的频次统计得到完全不涉及梯度下降。把它和另一种常见变种多项式朴素贝叶斯区分开很重要。多项式朴素贝叶斯通常用于文本分类里“单词出现多少次”的计数场景而伯努利版本只关心“出现还是没出现”。放在房车险场景里我们其实不需要知道客户打过多少次理赔电话只需要知道“今年是否发生过理赔”这个二值开关信息量已经足够。打个比方如果你要判断一个人今天是否去健身房你只需要知道他有没有穿运动鞋、有没有带健身包这几个0/1信号就够了不必统计他走了多少步。伯努利朴素贝叶斯就是这种“只看开关不看刻度”的思路。1.3 伯努利朴素贝叶斯和其它常见模型的横向对比这个项目里我一开始也纠结过要不要直接用逻辑回归或者随机森林后来专门做了一轮对比才确定伯努利朴素贝叶斯作为先发模型最合适。三类模型的特点我用表格列一下。模型训练速度可解释性对高维稀疏二值特征的处理主要风险伯努利朴素贝叶斯极快几乎瞬时完成强每个特征有独立概率贡献天然适配无需额外编码特征独立性假设过强逻辑回归较快需要迭代收敛较强权重可解释可用但特征共线性会影响权重稳定性需要特征标准化和共线性处理随机森林中等树的数量影响耗时一般可用特征重要性解释可以用但二值化后会损失部分信息容易过拟合调参成本高从项目落地角度看伯努利朴素贝叶斯最大的优势是“快速建立基线”。在保险交叉销售这种业务里第一版模型通常不是追求极致精度而是要把客户排序能力跑出来证明数据价值。伯努利朴素贝叶斯从读数据到出第一版预测结果半小时之内就能完成后面再上更复杂的模型也有基线和参照。当然它的缺点也非常明确。一旦特征之间存在强相关比如“有房”和“月供1万”这两个高度相关的特征模型会把同一个证据重复计算导致对购买概率的高估。这个我在第五部分会专门讲遇到这个问题怎么处理。2. 数据集长什么样字段结构、预处理和二值化2.1 数据字段的整体设计逻辑这批数据集的原始结构可以理解成一张宽表每一行是一个客户每一列是一个特征最后一列是标签。特征数量在80到90个之间因此经常被称作“86个客户特征”。这些特征大致可以分为四类人口统计类如年龄区间、收入等级、婚姻状况、家庭成员数资产类如是否有房、是否有船、是否有露营车、是否有第二套住房历史投保类如是否持有车险、是否持有寿险、历史理赔次数是否大于0营销渠道类如是否通过邮件营销接触过、是否有过电话沟通记录。这个字段设计逻辑和保险行业本身的数据沉淀方式高度一致。保险公司手里最值钱的不是单个字段而是客户在不同产品线之间的交叉持有情况。例如一个客户既有车险又有家财险他购买房车险的概率通常高于只买过意外险的客户。这类强业务规则虽然朴素但经过贝叶斯模型统计后就能变成可量化的条件概率。这里需要提醒一点原始数据里有些字段是数值型连续变量比如“收入”和“年龄”。在伯努利朴素贝叶斯里不能直接把连续数值扔进模型需要先做二值化处理。2.2 连续特征怎么转成伯努利需要的0/1伯努利朴素贝叶斯要求每个特征都是二值的所以预处理是整个项目里最影响效果的一步。我常被问到一个问题“为什么非要把收入这种连续变量变成0和1直接用原始数值不好吗”答案是不好因为BernoulliNB的底层概率计算公式只统计“特征出现”的频次喂进连续数值它也要先做内部的二值化否则计算出的概率没有实际含义。转二值化的核心是阈值选择。最常用的思路是按经验阈值切分比如收入按“是否超过中位数”切年龄按“是否超过40岁”切。但更稳妥的做法是把一个连续变量拆成多个二值变量比如把年龄拆成“是否大于30岁”“是否大于40岁”“是否大于50岁”三个字段这样能保留更多分布信息。具体操作上如果数据字段是0/1保持原样如果是连续数值用以下逻辑处理选择业务上可解释的阈值例如收入大于40万记为1如果没有业务阈值取中位数作为切分点缺失值不直接丢弃单独生成一个“是否缺失”的二值字段。下面这个示例展示了一个客户从原始记录到二值特征的转换过程。原始字段原始值转换后的特征转换结果Income550000Income_over_400k1Age36Age_over_301Age36Age_over_400Has_BoatNoHas_Boat0Missing_FieldNaNMissing_Field12.3 类别不平衡问题的初步摸底拿到数据后第一件事不是建模而是统计标签分布。房车险购买场景里正样本比例通常非常低我这次跑的数据购买客户占比约6%也就是说如果模型把所有客户都预测为“不买”准确率也能有94%。但这样的模型毫无业务价值因为它一个高潜客户都挑不出来。统计标签分布只用一行代码就能完成用pandas的value_counts方法顺便算一下百分比。这一步的意义在于提前确定评估策略不能只用准确率后面必须看召回率、精确率以及AUC。同样也决定了训练集测试集划分时要使用分层抽样确保测试集里的正样本占比和全量数据一致否则评估结果会失真。3. 完整实现过程从数据加载到预测落地3.1 数据读取与标签构建项目源码的第一步是读取CSV文件并区分特征矩阵和标签向量。假设文件里最后一列是“Caravan”它表示是否购买房车险1代表购买0代表不购买。读取代码非常直接。import pandas as pd data pd.read_csv(caravan_data.csv) print(data.shape) print(data[Caravan].value_counts()) X data.drop(Caravan, axis1) y data[Caravan]数据读取后务必检查两点特征列是否全部是数值型目标列是否只有0和1两个取值。如果发现原始文件里有字符串类型的分类列比如“职业”是“工程师”“教师”这类文本需要先用独热编码转换成多个0/1列。X pd.get_dummies(X, columns[occupation], drop_firstTrue)这里的drop_first参数是为了避免虚拟变量陷阱比如职业有5个类别只用4个0/1列就能完整表达多出来的一列会和其它特征形成多重共线性。3.2 训练集和测试集的划分划分数据时必须使用分层抽样。因为正样本只有6%随机切分很容易把正样本都切到某一侧导致训练集学不到购买模式或者测试集里正样本太少无法评估。from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) print(y_train.value_counts(normalizeTrue)) print(y_test.value_counts(normalizeTrue))stratifyy的意思是按照y的比例分层切分这样训练集和测试集里的购买占比都会保持在6%附近。random_state固定为42是为了复现结果不然每次运行划分都会变模型评估就没有可比性。3.3 用BernoulliNB完成训练与预测模型训练本身只有几行代码。这里最关键的是搞清楚binarize参数的作用如果你希望模型在训练前自动把特征二值化就设置binarize0.0意思是“大于0的数值全部记为1小于等于0的记为0”如果你已经在预处理阶段手动把所有特征都转成了严格的0/1那可以不设置这个参数或者显式设置binarizeNone。from sklearn.naive_bayes import BernoulliNB from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score model BernoulliNB(alpha1.0, binarize0.0) model.fit(X_train, y_train) y_pred model.predict(X_test) y_scores model.predict_proba(X_test)[:, 1] print(classification_report(y_test, y_pred)) print(confusion_matrix(y_test, y_pred)) print(AUC:, roc_auc_score(y_test, y_scores))predict_proba返回的是一个二维数组第一列是“不购买”的概率第二列是“购买”的概率我们只需要取第二列作为客户得分。这个得分是后面做销售名单排序的核心依据。3.4 核心参数到底怎么调BernoulliNB的主要参数就三个alpha、binarize、class_prior。alpha是拉普拉斯平滑参数默认是1.0。它解决的问题是如果某个特征在训练集的“购买”样本里从未出现过1那P(特征1 | 购买)会被算成0一旦这个因子乘进去整个概率直接归零。平滑参数的作用就是给这些未出现的组合一个很小的非零概率。alpha越大所有概率估计越向均匀分布靠拢对噪声的容忍度越高但也可能抹掉真实信号。class_prior参数用于手动指定先验概率。如果业务上认为全市场房车险购买率是3%而数据里面是6%你可以传一份更贴近真实的比例进去。如果不设模型会直接用训练集里的标签频率通常也没问题。实际项目中我很少刻意去搜索alpha的最优值因为伯努利朴素贝叶斯对alpha不敏感在0.1到5之间变化对排序结果影响都不大。真正影响结果的是特征二值化的阈值选择。4. 评估结果怎么解读准确率之外更要看的指标4.1 混淆矩阵里藏着业务真相第一次跑完代码分类报告里准确率可能高达94%但千万不要高兴得太早这个数字几乎完全由“预测不买”贡献。真正要看的是一张4x4的混淆矩阵四个格子对应四种情况真负例、假正例、假负例、真正例。真正例数量决定了销售名单里能被转化的人数假正例数量意味着名单里有多少人其实不会买但这些人依然要占用销售的时间成本。假负例代表被我们漏掉的潜在客户这部分人如果没被跟进就是直接损失的业务机会。用混淆矩阵去看问题才发现准确率根本回答不了“名单质量怎么样”这个业务问题。我这次跑的结果里直接使用默认阈值0.5时真正例数量非常少因为模型预测出的购买概率普遍不高正样本比例低导致后验概率天然被压低。这是朴素贝叶斯在类别不平衡数据上的典型表现不是你代码写错了而是阈值设置不合理。4.2 精确率和召回率的选择取决于销售成本分类报告里的precision和recall在保险营销场景下需要结合业务成本来权衡。精确率的含义是“预测会买的人里真正买了的比例”召回率的含义是“所有真正购买的人里被我们预测到的比例”。如果销售跟进一个客户的电话成本是50元而一单房车险的佣金是2000元那精确率和召回率的价值就完全不一样。精确率太低意味着大量电话打给了不会买的人成本浪费召回率太低意味着很多高潜客户无人跟进机会流失。实际操作中我不会只盯着某一个指标而是以AUC为主结合不同阈值下的精确率和召回率做综合判断。AUC衡量的是模型对所有客户的排序能力只要排序正确后面怎么选阈值都可以由业务成本决定。4.3 概率不等于真实转化率但可以用于排序伯努利朴素贝叶斯输出的概率常常存在偏差因为它基于条件独立的强假设会把特征之间的重叠证据重复计算导致概率要么偏大要么偏小。这一点在业务上容易被误用比如业务方看到模型说某个客户购买概率是80%就以为转化率就是80%实际可能只有20%。因此我在项目里反复强调一个原则不要直接用模型概率做绝对判断要用概率做相对排序。把客户按得分从高到低排取前1000名给销售然后统计这批人的实际转化率这个转化率才是可信的。模型概率高只是说“这个客户比后面的客户更有可能买”不是说“他一定会买”。更精细的做法是概率校准把模型的输出分数映射到真实转化率区间。可以通过等频分箱把测试集客户按预测得分分成10组统计每一组的真实购买率如果第10组的真实购买率明显高于第1组说明模型的排序能力是有效的。5. 实测中的坑和调优方向5.1 类别不平衡的实战处理思路直接跑原始数据后最常见的现象是模型几乎把所有样本都判成0。这并不一定代表模型没用而是默认阈值0.5在正样本只有6%的情况下根本不适用。解决方案有两个方向。第一个方向是改阈值但不改模型。这个方法最简单。模型输出的是购买概率后不用0.5做判断而是取全量客户中得分最高的固定比例比如前10%标记为1。这相当于把阈值重新设为一个只跟客户得分分布有关的百分位数绕开了先验不平衡的影响。实际业务里销售能跟进的客户数量本来就是固定的所以这个方案最贴合现实。第二个方向是调整class_prior让模型相信正样本比例更高从而输出更大的后验概率。但这个方法有个副作用它会同时改变所有客户的概率排序结果变化不大纯粹是让概率数值整体抬高。所以如果最终目标只是做名单排序调先验意义不大如果业务方必须要看到一个“更像概率”的分数那才值得调整。5.2 特征强相关会让概率失真伯努利朴素贝叶斯的阿喀琉斯之踵就是独立性假设。当两个特征高度相关比如“拥有露营车”和“过去有过房车租赁记录”这两个特征其实在表达同一件事但模型会把它当成两次独立证据把购买概率叠加上去导致高估。我实测中遇到过一组客户他们同时满足三个高度相关的资产类特征模型给出的概率直接逼近0.9但实际上这些人的真实购买率远没有这么高。发现问题后我用了一个比较实用的方法先算特征之间的相关系数矩阵把相关系数超过0.7的特征对挑出来二选一保留业务解释性更强的那一个。也可以做一次降维用主成分分析或截断奇异值分解把特征压缩后再喂给模型但可解释性会变差销售团队会看不懂。我的建议是如果项目看重可解释性优先做手动的相关特征筛选如果只看排序效果可以保留所有特征因为即使概率数值偏高排序通常依然可用。5.3 阈值选择是落地前的最后一道坎模型训练完成不代表项目结束阈值选择才是直接决定业务效果的那一步。这里有一个很好用的实操方法把测试集的预测得分排序后取几个候选比例比如前5%、10%、20%分别计算每个候选名单里的真实购买率。假设全量客户购买率是6%随机抽取10%名单的真实购买率也接近6%。如果按模型得分取前10%的名单真实购买率能到18%那说明模型把关键客户成功集中到了名单头部模型的业务价值就体现出来了。这比看任何统计指标都更能说服业务方。如果候选名单的真实购买率依然接近全量平均水平就要回头检查特征是否有效或者考虑换模型而不是继续在BernoulliNB上调参。5.4 后续可以怎么扩展伯努利朴素贝叶斯在这个项目里最合适的定位是基线模型后续扩展空间其实很大。可以直接替换成逻辑回归看看同样特征下线性模型的排序能力有没有提升可以尝试梯度提升树类模型它对特征交互的捕捉能力更强但可解释性会打折扣还可以把连续变量变换为多阈值二值组合再重跑贝叶斯模型有时候效果提升比换模型还明显。我个人的习惯是先跑通伯努利朴素贝叶斯建立基线再逐步叠加复杂模型。这样每一步改进都能量化不至于一上来就上一个黑盒模型出了问题根本不知道是特征的问题还是模型的问题。最后再分享一个小技巧模型落地后定期用新数据重训很重要。保险客户的消费习惯会随时间变化半年前训练好的模型放到今天名单质量大概率会下滑。把这个重训流程脚本化每个月自动跑一次比任何花哨的模型优化都更稳。本文还有配套的精品资源点击获取
返回列表