尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

R语言非参数模型在保险费率厘定中的应用:从GLM到GAM的进阶实践

R语言非参数模型在保险费率厘定中的应用:从GLM到GAM的进阶实践 1. 项目概述为什么费率厘定需要“非参数”这把手术刀干了这么多年精算和数据科学我发现一个挺有意思的现象一提到用统计模型厘定保险费率很多人的第一反应就是上广义线性模型GLM。这没错GLM是车险、健康险等领域的行业标准结构清晰解释性强监管也认。但GLM有个很强的假设就是预测变量比如年龄、车辆价值和响应变量比如索赔频率、索赔强度之间的关系必须通过一个预设的“连接函数”来表达并且通常是线性的或者多项式形式的。这就好比我们非得用直尺或固定弧度的曲线板去描摹一个复杂物体的轮廓很多时候是描不准的。这就是“R语言非参数模型厘定保险费率”这个项目的核心价值所在。它探讨的不是替代GLM而是补全工具箱。当我们的数据呈现出明显的非线性、交互效应或者我们根本不清楚变量间确切的关系形态时非参数模型就像一把灵活的手术刀能更精细地“雕刻”出数据背后的真实风险曲面。局部回归、广义相加模型GAM和样条回归正是三把不同特性但都极其好用的“手术刀”。这个项目的目的就是带你亲手操作这三把刀理解它们各自的原理、适用场景以及在R语言环境下的完整实现流程最终得到一个比传统参数模型更贴合实际、预测更准确的费率厘定方案。简单说这适合所有已经熟悉基础GLM但渴望模型能力更上一层楼的精算师、保险数据分析师和风险建模人员。它能帮你解决那些“用线性项拟合总觉得差点意思”的难题比如年轻司机和老年司机的风险并非随年龄线性变化或者车辆价格与风险的关系在某个区间存在突变。2. 核心思路从“假设关系”到“让数据自己说话”传统的参数模型如GLM需要我们事先指定模型形式比如log(预期索赔频率) β0 β1 * 年龄 β2 * 年龄^2。这里的“参数”β0, β1, β2是我们需要估计的但模型的结构有线性的年龄项和二次的年龄项是人为设定的。非参数模型的核心思想恰恰是放松甚至取消这种预设的结构。它不假定一个全局的、固定的数学关系式而是允许数据在局部范围内决定关系的形态。这就好比参数模型给你一个二次函数模板y ax² bx c你去调整a, b, c来拟合数据。非参数模型给你一盒橡皮泥你可以根据数据点的分布自由地捏出最贴合的曲线形状。在这个项目中我们重点实践三种主流非参数/半参数方法局部回归Locally Weighted Regression, LOESS它的思路非常直观。要预测某个点x的值并不用全部数据而是只关注x附近的一个“邻域”内的数据点给这些近邻点更高的权重然后在这个小窗口内用一个简单的多项式比如一次或二次进行拟合。相当于用一个灵活的“滑动窗口”在数据上移动逐段拟合。优点是极度灵活能捕捉非常局部的特征缺点是对参数带宽敏感计算量大且不易产生一个整体的解析表达式更多用于探索性分析。广义相加模型Generalized Additive Models, GAM这是本项目的主角可以看作是GLM的强大升级版。它把GLM的线性预测项β1*X1 β2*X2替换成了平滑函数项s(X1) s(X2)。这里的s()就是一个平滑函数通常由样条基函数构成。GAM的核心优势在于可加性它允许每个变量以非线性的方式影响响应变量但效应仍然是可加的。这既保留了GLM易于解释和分解风险贡献的优点又引入了非线性的灵活性。在保险定价中我们可以轻松得到“年龄的风险曲线”、“车价的风险曲线”这些曲线就是s(年龄)、s(车价)的函数图像。样条回归Spline Regression可以理解为GAM的“发动机”之一。样条是通过连接多个低阶多项式通常是三次片段来构造平滑曲线的数学工具连接点称为“节点”。通过选择节点的数量和位置我们可以控制曲线的灵活度。样条为GAM提供了构建平滑函数s()的基础能力。我们也可以直接使用回归样条如B样条、自然样条作为预测变量放入线性模型或GLM中这本身也是一种半参数方法。项目整体设计思路是递进的先用局部回归快速探索单个变量与风险之间的非线性关系形成直观认知然后系统性地构建GAM将其作为核心定价模型并深入理解其平滑函数、自由度选择等关键概念最后剖析支撑GAM的样条基础原理知其然更知其所以然。整个流程在R中实现会用到mgcvGAM建模的核心包、ggplot2可视化等强大工具。3. 工具选型与数据准备为什么是R和mgcv在数据科学和统计建模领域Python和R是两大主流。对于这个专注于统计模型、尤其是指数族分布和加性模型的保险费率厘定项目R语言是更自然、更强大的选择。原因有三第一R生态拥有最全面、最成熟的统计建模包mgcv包由GAM理论的重要贡献者Simon Wood维护其算法实现如惩罚迭代重加权最小二乘P-IRLS、广义交叉验证GCV经过千锤百炼可靠性和效率极高。第二精算领域本身有大量基于R的传统和工具如actuar包社区支持好。第三R在模型诊断、结果可视化ggplot2方面表现极其优雅便于我们分析模型效果。核心工具栈如下R 4.3项目运行的基础环境。tidyverse包含dplyr,tidyr,ggplot2等用于数据操作和可视化是现代R数据分析的“标配”。mgcv广义加性模型建模的绝对核心包。功能全面支持多种分布、连接函数、平滑类型和选择方法。modelr/broom辅助进行模型评估、交叉验证和结果整理。数据集为了贴近实际我们使用一个模拟的汽车保险数据集。它包含以下关键字段policy_id: 保单唯一标识exposure: 风险暴露车年数n_claims: 索赔次数claim_amount: 总索赔金额用于严重程度建模age: 驾驶员年龄vehicle_age: 车辆年龄vehicle_value: 车辆价值万power: 发动机功率千瓦region: 地区分类城市/郊区/农村首先我们进行数据准备。费率厘定通常分为频率Frequency和严重程度Severity两步建模这里我们以索赔频率建模为例。# 加载必要的库 library(tidyverse) library(mgcv) library(modelr) # 假设数据已加载为 data.frame claims_data # 1. 数据概览与清洗 glimpse(claims_data) summary(claims_data) # 检查缺失值 colSums(is.na(claims_data)) # 处理缺失值示例对于数值型变量用中位数填充分类变量用众数 claims_clean - claims_data %% mutate(across(where(is.numeric), ~ifelse(is.na(.), median(., na.rm TRUE), .)), across(where(is.factor), ~fct_explicit_na(., na_level Missing))) # 2. 创建建模所需变量 # 计算索赔频率作为响应变量 claims_clean - claims_clean %% mutate(claim_freq n_claims / exposure) # 通常 exposure 0 # 将分类变量转为因子 claims_clean - claims_clean %% mutate(region as.factor(region)) # 查看响应变量分布 ggplot(claims_clean, aes(x claim_freq)) geom_histogram(bins 30, fill steelblue, alpha 0.7) labs(title 索赔频率分布, x 索赔频率 (次/车年), y 频数)注意在实际操作中对于exposure为0或极小的观测值需要谨慎处理可能需剔除或进行特殊调整。此外claim_freq作为比率其分布通常过度离散方差大于均值这提示我们在后续GAM中应使用泊松分布或负二项分布并设置offset(log(exposure))来校正暴露差异。4. 探索性分析用局部回归LOESS窥见非线性端倪在构建复杂模型前先用局部回归探索单变量关系是非常有价值的。它能给我们一个“第一印象”判断哪些变量可能存在非线性效应为后续GAM中平滑项的选择提供依据。我们以驾驶员age与claim_freq的关系为例。# 使用ggplot2的geom_smooth快速绘制LOESS曲线 p_age - ggplot(claims_clean, aes(x age, y claim_freq)) geom_point(alpha 0.1, size 0.5) # 原始数据点设置透明度避免重叠 geom_smooth(method loess, span 0.3, se TRUE, color red) labs(title 局部回归(LOESS)探索年龄与索赔频率, x 驾驶员年龄, y 索赔频率) theme_minimal() print(p_age) # 如果想获取LOESS拟合的详细数据可以使用loess()函数 loess_fit - loess(claim_freq ~ age, data claims_clean, span 0.3) age_grid - data.frame(age seq(min(claims_clean$age), max(claims_clean$age), length.out 100)) loess_pred - predict(loess_fit, newdata age_grid, se TRUE) age_grid$fit - loess_pred$fit age_grid$se - loess_pred$se.fit # 绘制带置信区间的曲线 ggplot() geom_ribbon(data age_grid, aes(x age, ymin fit - 1.96*se, ymax fit 1.96*se), fill grey80) geom_line(data age_grid, aes(x age, y fit), color red, size 1) geom_point(data claims_clean, aes(x age, y claim_freq), alpha 0.05) labs(title 手动LOESS拟合与置信带, x 年龄, y 拟合值)关键参数解析与实操心得span这是LOESS最重要的参数控制平滑度。span越小使用的数据比例越小曲线越“崎岖”捕捉局部细节能力越强但也更容易过拟合span越大曲线越平滑。通常通过交叉验证选择但0.3到0.5是一个常见的经验起始范围。实操心得1在保险数据中由于个体风险差异大数据点会非常分散。直接画散点图可能是一片“毛球”。此时先对数据进行聚合能更清晰地揭示趋势。例如按年龄组计算平均索赔频率。claims_clean %% mutate(age_group cut(age, breaks seq(18, 80, by 5))) %% group_by(age_group) %% summarise(mean_freq mean(claim_freq, na.rm TRUE), exposure_sum sum(exposure)) %% ggplot(aes(x age_group, y mean_freq, size exposure_sum)) geom_point() geom_smooth(aes(group 1), method loess, span 0.5, se FALSE) labs(title 按年龄组聚合后的LOESS趋势, x 年龄组, y 平均索赔频率)实操心得2LOESS曲线两端的波动需要特别警惕。由于边界处数据较少拟合可能不稳定置信区间变宽。解读时应更关注数据密集区域的中段趋势。用同样的方法我们可以快速探索vehicle_age,vehicle_value等连续变量与风险的关系。如果LOESS曲线明显不是一条直线那就强烈暗示我们在GAM中需要对该变量使用平滑项。5. 核心模型构建广义相加模型GAM的实战与调优探索性分析后我们进入正题——构建GAM。我们将使用mgcv包它默认使用惩罚回归样条薄板样条、立方回归样条等并通过广义交叉验证GCV或限制性最大似然REML自动估计平滑度惩罚参数这大大简化了模型选择过程。5.1 基础GAM模型构建我们首先构建一个包含所有可能非线性效应的GAM。假设我们怀疑age,vehicle_age,vehicle_value,power都存在非线性效应而region是分类变量保持线性因子形式。响应变量是索赔次数n_claims服从泊松分布并使用offset(log(exposure))。# 构建基础GAM模型 gam_poisson - gam(n_claims ~ s(age, k 20) # 对年龄使用平滑项k设置基函数维度上限 s(vehicle_age, k 15) # 车辆年龄平滑项 s(vehicle_value, k 15) # 车辆价值平滑项 s(power, k 15) # 功率平滑项 region, # 地区作为因子线性效应 data claims_clean, family poisson(link log), # 泊松分布对数连接函数 offset log(exposure), # 偏移量校正暴露 method REML) # 使用REML进行平滑参数估计通常比GCV更稳定 # 查看模型摘要 summary(gam_poisson)模型摘要解读关键点平滑项显著性查看s(age)等行的p-value。一个很小的p值如0.05表明该变量的非线性效应是显著的。edf有效自由度是一个非常重要的指标。edf 1意味着该平滑项退化为线性关系edf越大说明关系越复杂、非线性程度越高。k是我们设置的上限edf应显著小于k否则可能需要增大k。参数项显著性region各水平的系数和p值解读方式同GLM。模型拟合优度Deviance explained表示模型解释的偏差百分比类似于R²。REML score或GCV score用于模型比较值越小越好。分布检查Family: poisson。对于索赔次数泊松分布假设均值等于方差。如果数据存在过度离散方差远大于均值deviance / df_residual会远大于1。此时应考虑使用负二项分布family nb或拟泊松分布。5.2 处理过度离散切换到负二项GAM保险索赔数据过度离散是常态。mgcv支持负二项分布其中方差 μ μ²/θθ是离散参数。# 构建负二项GAM gam_nb - gam(n_claims ~ s(age, k 20) s(vehicle_age, k 15) s(vehicle_value, k 15) s(power, k 15) region, data claims_clean, family nb(link log), # 负二项分布 offset log(exposure), method REML) summary(gam_nb) # 注意查看输出的离散参数(theta)估计值5.3 模型诊断与可视化模型拟合后必须进行诊断。mgcv提供了强大的诊断函数gam.check()。# 模型诊断 gam.check(gam_nb)这个函数会输出四张图残差 vs. 拟合值图检查方差齐性、正态Q-Q图检查残差正态性、残差 vs. 线性预测变量图、响应 vs. 拟合值图。同时它还会检查每个平滑项的基函数维度k是否足够k-index接近1且p-value不显著则通常认为足够。更重要的是可视化平滑效应这是GAM的精髓。# 绘制各个平滑项的效果图 plot(gam_nb, pages 1, residuals TRUE, shade TRUE, shade.col lightblue, cex 1.2) # residuals TRUE会将部分残差叠加在图上shade TRUE显示置信带。 # 使用visreg或gratia包进行更美观的绘图 # install.packages(gratia) library(gratia) draw(gam_nb, residuals TRUE) # gratia的绘图函数效果更佳平滑效应图解读Y轴表示该变量对线性预测器对数索赔频率的贡献。曲线展示了在保持其他变量不变时该变量变化带来的非线性影响。置信带反映了不确定性。例如s(age)的图可能显示年轻司机风险极高随后迅速下降在中年段趋于平缓老年段可能略有上升完美捕捉了U型或J型风险曲线。5.4 关键参数调优与实操陷阱k的选择k是基函数数量的上限决定了平滑项的最大灵活度。设置太小会导致曲线过于平滑无法捕捉真实模式欠拟合设置太大虽能提高灵活性但会增加计算负担且可能过拟合。gam.check()会提示哪个项的k可能不足。一个经验法则是从k10-20开始如果edf接近k-1因为有一个自由度被用于截距并且诊断图显示残差仍有模式就增大k。# 如果gam.check()提示age的k不足可以重新拟合 gam_nb_k - gam(n_claims ~ s(age, k 30) ..., family nb, ...)平滑类型选择mgcv默认使用tp薄板回归样条。对于单变量cr立方回归样条或cs收缩立方回归样条可将不重要的项收缩至线性也是好选择。bs参数指定。s(age, bs cr, k 20) # 使用立方回归样条 s(age, bs cs, k 20) # 使用收缩立方样条可自动降级为线性method选择REML或ML估计通常比默认的GCV更稳定尤其是在数据量不是特别大或模型复杂度高时能更好地避免过拟合。我个人的经验是在保险定价这种强调稳定性和可解释性的场景下优先使用method REML。包含线性项与平滑项的交互有时我们想探究一个变量的效应是否随另一个变量变化。GAM可以处理这种交互。# 张量积平滑用于不同尺度的变量交互如 age 和 vehicle_value s(age, vehicle_value, k c(10, 10)) # 因子-平滑交互例如不同region的风险年龄曲线不同 s(age, by region, k 15) # 这会为每个region生成一个独立的平滑曲线注意交互项会极大增加模型复杂度和解释难度且需要大量数据支持。在费率厘定中引入前务必通过似然比检验或AIC/BIC确认其必要性。6. 模型比较、验证与费率因子提取6.1 与传统GLM比较为了凸显GAM的价值我们拟合一个可比的泊松/负二项GLM其中连续变量被处理为线性项或多项式项。# 拟合一个包含年龄二次项的GLM作为基准 glm_nb - glm.nb(n_claims ~ age I(age^2) vehicle_age vehicle_value power region offset(log(exposure)), data claims_clean) # 比较AIC AIC(gam_nb, glm_nb) # 比较偏差解释度 data.frame(Model c(GAM-NB, GLM-NB), Dev_Explained c(summary(gam_nb)$dev.expl, 1 - glm_nb$deviance/glm_nb$null.deviance))通常GAM的AIC会更低偏差解释度更高因为它更灵活地拟合了数据。6.2 交叉验证与过拟合防范尽管REML估计本身有防止过拟合的惩罚但在样本外预测能力至关重要。我们可以进行k折交叉验证。set.seed(123) cv_folds - crossv_kfold(claims_clean, k 5) # 使用modelr创建5折 cv_results - cv_folds %% mutate( # 在每份训练集上拟合GAM model map(train, ~gam(n_claims ~ s(age) s(vehicle_age) s(vehicle_value) s(power) region, data ., family nb(link log), offset log(exposure), method REML)), # 在测试集上预测并计算误差例如泊松偏差 pred map2(model, test, ~predict(.x, newdata .y, type response)), actual map(test, ~.x$n_claims), exposure_test map(test, ~.x$exposure), # 计算测试集偏差 deviance pmap_dbl(list(pred, actual, exposure_test), function(p, a, e) { # 计算泊松偏差 2 * sum(ifelse(a 0, 0, a * log(a/(p*e))) - (a - p*e)) }) ) mean_cv_deviance - mean(cv_results$deviance) print(paste(平均交叉验证偏差:, mean_cv_deviance))6.3 提取费率因子最终目的是得到可用于定价的费率因子。对于GAM我们需要计算每个风险等级如每个年龄、每个车型的相对风险系数。# 1. 创建所有风险因子的组合网格示例 rating_grid - expand.grid( age seq(18, 75, by 1), vehicle_age c(1, 3, 5, 7), vehicle_value c(5, 15, 30, 50), power c(70, 120, 180), region factor(c(Urban, Suburban, Rural), levels levels(claims_clean$region)), exposure 1 # 设为1用于计算单位暴露下的预测 ) # 2. 使用训练好的GAM模型进行预测在log尺度上 rating_grid$log_pred - predict(gam_nb, newdata rating_grid, type link) # typelink 得到的是线性预测器 η log(μ) # 3. 选择一个基准组计算相对风险因子 # 例如选择年龄30车龄3车价15功率120地区Suburban作为基准 base_conditions - rating_grid$age 30 rating_grid$vehicle_age 3 rating_grid$vehicle_value 15 rating_grid$power 120 rating_grid$region Suburban base_log_pred - rating_grid$log_pred[base_conditions] # 4. 计算相对风险因子 (Relativity) rating_grid$relativity - exp(rating_grid$log_pred - base_log_pred) # 查看年龄因子的示例 age_rel - rating_grid %% filter(vehicle_age 3, vehicle_value 15, power 120, region Suburban) %% select(age, relativity) ggplot(age_rel, aes(x age, y relativity)) geom_line(size 1) geom_hline(yintercept 1, linetype dashed, color grey) labs(title 基于GAM的年龄相对风险因子, x 驾驶员年龄, y 相对风险系数 (基准1)) theme_minimal()这张图就是最终可用于定价的、非线性的年龄风险曲线。相比GLM给出的一个固定斜率或抛物线GAM产生的曲线能更精准地反映不同年龄段的风险差异。7. 常见问题、排查技巧与高级话题7.1 常见问题速查表问题现象可能原因排查与解决思路gam.check()提示k不足设置的基函数维度k太小无法捕捉复杂的波动。增大该平滑项的k值例如从k10增加到k20或k30重新拟合模型。模型拟合速度极慢数据量过大10万行或平滑项过多、k设置过大特别是使用了张量积交互平滑。1. 尝试使用bam()函数替代gam()它对大数据集进行了优化。2. 检查并酌情减少不必要的平滑项或降低k值。3. 对于分类变量确保已转为factor类型而不是作为数值平滑。平滑效应图置信带非常宽数据在该变量取值范围内稀疏或变量与响应变量关系很弱。1. 检查该变量的数据分布。2. 考虑是否真的需要将该变量纳入模型或者是否可以将其转换为分类变量。3. 对于区间两端置信带变宽是正常现象。预测时出现NA值新数据中存在模型未见过的因子水平或数值范围超出训练数据范围。1. 使用predict(..., typeresponse, exclude...)或predict(..., newdata.guaranteedTRUE)时需谨慎。2. 确保预测数据框中的因子水平与训练集完全一致。mgcv的predict对因子水平非常敏感。过度离散依然存在即使使用了负二项分布残差图仍显示模式或偏差仍很高。1. 考虑使用零膨胀模型如零膨胀泊松/负二项如果数据中零索赔比例异常高。mgcv可通过familyziP()或ziplss()实现。2. 检查是否遗漏了重要的预测变量或交互效应。3. 考虑使用更灵活的Tweedie分布familytw它统一处理频率和强度。模型结果不稳定每次拟合的平滑曲线形状略有不同。1. 设置随机种子set.seed()因为某些平滑基的构造涉及随机初始化。2. 尝试使用bscr立方回归样条等确定性更强的基函数。3. 增加数据量。7.2 高级话题选择性与收缩性平滑mgcv的强大之处在于其惩罚机制。除了默认平滑还有两种特别有用的类型双惩罚平滑 (bsts)在惩罚曲线摆动的同时额外施加一个惩罚可以将整个平滑项收缩至零。如果某个变量的效应可能根本不存在这个选项非常有用它能够自动进行变量选择。s(power, bsts, k10) # 如果power不重要效应可被收缩掉自适应平滑 (bsad)允许平滑的灵活度带宽随预测变量取值而变化。适用于某些区域变化剧烈、某些区域变化平缓的情况。但计算成本更高。7.3 从GAM到纯样条回归最后理解样条回归有助于深化对GAM的认识。我们可以手动构建B样条基函数并将其放入GLM中这本质上就是一个回归样条模型。library(splines) # 为年龄变量创建自然三次样条基函数具有2个内部节点 ns_basis - ns(claims_clean$age, df 4) # df 截距 节点数 1 这里df指自由度 # ns() 生成的是基函数矩阵 # 将其放入GLM glm_ns - glm(n_claims ~ ns_basis vehicle_age vehicle_value ..., family poisson, offset log(exposure), data claims_clean) # 预测并绘图 age_seq - seq(min(claims_clean$age), max(claims_clean$age), length.out 100) ns_seq - predict(ns_basis, newx age_seq) # 注意这里需要正确生成新数据的基函数 # ... 后续预测和绘图代码略复杂旨在展示原理 # 相比之下GAM的s(age)自动完成了基函数构建、节点放置和平滑惩罚省心且通常效果更好。核心体会GAM中的平滑项可以看作是一种“智能的”、“带惩罚的”样条回归。它自动化了节点选择和平滑度控制这个最棘手的过程让我们能更专注于业务逻辑和模型解释。在保险费率厘定这个领域GAM在精度和可解释性之间取得了绝佳的平衡它既不像局部回归那样难以形成全局模型又不像黑盒机器学习模型那样难以向业务和监管方解释。当你下次再遇到用直线或简单曲线无法拟合的风险因子时GAM应该是你工具箱里的首选利器。
返回列表