保险定价的核心是把未来的赔付成本合理地分摊到不同风险的保单上。传统精算使用广义线性模型,以年龄、性别、地区、职业、历史出险记录等特征估计期望损失。AI模型可以捕捉非线性关系和交互项,提高风险区分能力,但也更容易放大数据中的群体差异。风险分类本身不是歧视,但当价格差异与种族、性别、宗教等受保护属性高度相关时,即使模型没有直接使用这些属性,也可能产生算法歧视。接下来需要从目标函数、数据生成机制和公平性约束三个层面理解这种张力。

一、算法歧视在保险定价中是如何形成的
保险模型的训练目标通常是最小化预测赔付金额与实际赔付金额之间的误差,例如均方误差或泊松损失。假如某个群体的历史平均出险率较高,模型为了降低整体误差,自然会给该群体赋予更高的风险分数。即使个体从未出险,只要他身上带有该群体的统计特征,保费就可能上升。这种现象在统计学上称为统计性歧视,与直接以受保护属性作为定价因子的行为不同,但结果可能一样。
更隐蔽的是代理变量问题。邮编、职业、信用评分、车型、甚至手机品牌都可能与种族、性别或收入高度相关。AI模型会主动挖掘这些变量的组合来逼近受保护属性,从而间接恢复敏感信息。例如某地区历史上理赔率较高,模型可能给该地区所有保单加价,而居住在该地区的低收入群体和少数族裔会不成比例地受到影响。下面这段代码演示一个简化场景:模型没有使用性别变量,但使用信用评分和车型后,预测保费在性别间的差异反而被放大。
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
# 构造模拟数据:性别未直接进入模型,但信用评分和车型与性别相关
np.random.seed(42)
n = 2000
gender = np.random.binomial(1, 0.5, n) # 1 表示女性,0 表示男性
credit_score = 600 + gender * 60 + np.random.normal(0, 50, n)
car_type = np.random.binomial(1, 0.3 + gender * 0.2, n) # 与性别相关
claims = 3000 - 3 * credit_score + 800 * car_type + np.random.normal(0, 2000, n)
df = pd.DataFrame({
"gender": gender,
"credit_score": credit_score,
"car_type": car_type,
"claims": claims
})
# 模型输入故意不包含 gender
X = df[["credit_score", "car_type"]]
y = df["claims"]
model = LinearRegression().fit(X, y)
df["pred"] = model.predict(X)
# 比较不同性别群体的平均预测保费
print(df.groupby("gender")["pred"].mean())
从输出可以看到,即使性别没有出现在特征集中,平均预测保费仍然可能在性别间出现显著差异。原因是信用评分和车型充当了性别的代理变量。对保险机构来说,这种差异可能被解释为风险差异;但对监管者和消费者来说,这可能是算法歧视。
二、风险分类精度与群体公平性的核心矛盾
保险精算中有一个基本原则:纯保费应等于期望赔付成本。如果模型对所有群体都做到校准,即预测赔付概率等于实际出险频率,那么不同风险群体自然会有不同价格。从这个角度看,基于真实风险差异的定价是公平的,甚至是保险制度可持续的前提。但群体公平性指标通常要求不同受保护群体在价格、接受率或结果上具有某种统计均等,例如群体间平均保费差异不能过大,或者相同风险分数的个体在不同群体中获得相同价格。
矛盾在于,真实风险分布确实存在差异时,风险分类精度和群体统计均等无法同时满足。例如A群体历史出险率是B群体的两倍,若要求平均保费完全一致,那么A群体每份保单的赔付将超过所收保费,B群体则补贴A群体。如果强制约束群体间平均预测保费相等,模型必须压低A群体高风险个体的保费、抬高B群体低风险个体的保费,整体损失预测误差会上升,逆选择和定价失准风险也会增加。下面用代码计算后处理调整前后的平均保费差异和均方误差变化。
from sklearn.metrics import mean_squared_error
# 使用上文模型预测结果
baseline_mse = mean_squared_error(df["claims"], df["pred"])
gender_gap = df.loc[df.gender == 1, "pred"].mean() - df.loc[df.gender == 0, "pred"].mean()
# 简单后处理:给女性群体统一加一个常数,使平均预测保费接近男性
female_mean = df.loc[df.gender == 1, "pred"].mean()
male_mean = df.loc[df.gender == 0, "pred"].mean()
adjustment = male_mean - female_mean
df.loc[df.gender == 1, "pred_adj"] = df.loc[df.gender == 1, "pred"] + adjustment
df.loc[df.gender == 0, "pred_adj"] = df.loc[df.gender == 0, "pred"]
adjusted_mse = mean_squared_error(df["claims"], df["pred_adj"].fillna(df["pred"]))
adjusted_gap = df.loc[df.gender == 1, "pred_adj"].mean() - df.loc[df.gender == 0, "pred_adj"].mean()
print("原始均方误差:", baseline_mse)
print("调整后均方误差:", adjusted_mse)
print("原始平均预测差异:", gender_gap)
print("调整后平均预测差异:", adjusted_gap)
上述调整消除了群体平均差异,但调整后均方误差上升,说明为了满足统计均等牺牲了部分风险区分能力。实际业务中更常用的折中方案不是强制平均保费相等,而是限制群体间费率差异的上限,或者要求相同风险水平下不同群体价格一致,这对应机会均等和校准等更精细的公平性准则。
三、在模型中嵌入公平性约束的可行方法
如果希望在训练阶段就平衡风险分类和群体公平,可以把公平性要求写成优化问题中的约束项。常见做法包括预处理、训练中约束优化和后处理调整。预处理方法通过重加权或重采样改变训练数据分布,使不同群体在标签或特征上的差异减弱;后处理方法在不修改模型的情况下调整预测结果,实现简单但可能破坏校准。训练中约束优化则直接修改目标函数,例如在损失函数中加入群体间平均预测差异的惩罚项。
下例展示一个简化的约束优化思路:训练线性回归时,在损失函数中加入男性和女性平均预测保费之差的平方作为公平惩罚项。代码使用PyTorch手动计算梯度,展示如何将公平约束融入参数更新过程。
import torch
import torch.nn as nn
import torch.optim as optim
# 将数据转为张量
X_t = torch.tensor(X.values, dtype=torch.float32)
y_t = torch.tensor(y.values, dtype=torch.float32).view(-1, 1)
g_t = torch.tensor(gender, dtype=torch.float32).view(-1, 1)
class FairLinearModel(nn.Module):
def __init__(self, input_dim):
super().__init__()
self.linear = nn.Linear(input_dim, 1)
def forward(self, x):
return self.linear(x)
def fair_loss(pred, target, group, lambda_fair):
mse = torch.mean((pred - target) ** 2)
female_mean = pred[group == 1].mean()
male_mean = pred[group == 0].mean()
fairness_penalty = lambda_fair * (female_mean - male_mean) ** 2
return mse + fairness_penalty
model = FairLinearModel(X_t.shape[1])
optimizer = optim.Adam(model.parameters(), lr=0.01)
for epoch in range(500):
optimizer.zero_grad()
pred = model(X_t)
loss = fair_loss(pred, y_t, g_t, lambda_fair=0.8)
loss.backward()
optimizer.step()
# 评估性别间平均预测差异
with torch.no_grad():
final_pred = model(X_t).squeeze()
final_gap = final_pred[g_t.squeeze() == 1].mean().item() - final_pred[g_t.squeeze() == 0].mean().item()
print("公平约束训练后的性别平均预测差异:", final_gap)
lambda_fair控制公平项与精度项的相对权重。当该权重增大,模型会更努力缩小群体平均差异,但预测误差也会上升;当权重趋近于零,则退化为普通精度优先模型。实际项目中可以通过交叉验证和业务指标选择合适权重,也可以使用更严格的约束优化算法,例如将公平性作为硬约束而非惩罚项。
在保险场景中还可以对特征做公平性处理。例如删除明显代理变量,但注意仅删除敏感属性通常不够,因为模型会寻找替代变量。更稳健的做法是使用对抗训练或分布匹配,让模型学到的表示难以被用来推断受保护属性。这类方法实现成本较高,适合特征维度大、监管要求严格的场景。
四、用可解释性与审计机制守住公平底线
无论采用哪种技术手段,公平性都不能只在模型训练阶段一次性完成。保险产品生命周期长、监管严格,需要建立持续的算法审计和可解释性机制。可解释性工具可以识别哪些特征对保费预测贡献最大,如果发现某些特征在不同群体中的影响方向或强度存在异常,就需要评估是否构成间接歧视。例如SHAP值可以展示某个特征对个体保费的正负贡献,帮助解释为什么某位消费者被收取较高保费。
审计指标方面,除了平均保费差异,还应关注分位数差异、拒保率差异、相同风险分数下的价格差异,以及模型校准度在不同群体间是否一致。模型校准度尤其重要:如果模型对A群体预测的出险概率系统性低于实际值,那么即使平均价格相同,也可能造成不公平。下面代码计算分组校准误差,帮助识别这类问题。
# 将预测值分箱,比较不同性别群体在每个箱内的平均实际出险与平均预测出险
df["pred_bin"] = pd.qcut(df["pred"], q=5, labels=False)
calibration_table = []
for gender_value in [0, 1]:
subset = df[df.gender == gender_value]
row = subset.groupby("pred_bin").agg(
avg_pred=("pred", "mean"),
avg_actual=("claims", "mean"),
count=("claims", "size")
)
row["gender"] = gender_value
calibration_table.append(row)
print(pd.concat(calibration_table))
审计结果需要与业务和法务团队共同解读。保险机构可以建立模型风险管理流程,在模型上线前进行公平性评估,上线后定期监控不同群体的赔付率、投诉率和续保率变化。如果发现某个群体的实际赔付率与预测赔付率持续偏离,说明模型可能在该群体上失效,需要重新训练或施加额外约束。
此外,向消费者解释保费构成是降低算法歧视感知的重要方式。监管机构越来越倾向于要求保险公司提供算法决策的透明说明,包括使用了哪些类别特征、是否使用敏感属性或代理变量、以及公平性测试结果。即使模型内部复杂,也可以通过局部代理模型或规则提取给出近似解释。这有助于在风险分类精度与群体公平性之间建立可持续的信任基础。