Souscription IA : ingénierie des fonctionnalités et notation des risques dans l'assurance moderne
La souscription est le cœur de toutes les compagnies d’assurance : et le processus pour elles décidez si vous acceptez un risque, à ce prix et dans ces conditions. Pendant des décennies, ce processus cela dépend de l'expertise des comptables humains analysant les documents, papiers et appliqués Règles réelles codées dans les tableaux. Les résultats? Décisions disponibles dans 3 à 5 jours, coûts Les niveaux élevés d'exploitation et la variabilité subjective me semblent être les souscripteurs.
L’intelligence artificielle réécrit radicalement ces règles. Selon McKinsey, le les investissements mondiaux dans les solutions d’IA pour l’assurance dépasseront les 6 milliards de dollars en 2025, le BCG estimant que le 36 % de la valeur totale de l’assurance IA se concentre précisément dans la fonction de souscription. Les chiffres opérationnels sont tout aussi impressionnants : le délai moyen de décision de souscription est passé de 3 à 5 jours à 12,4 minutes pour eux politiques standard, maintenant un taux d'exactitude dans l'évaluation des risques de 99,3%.
Mais comment fonctionne réellement un système de souscription IA ? Ce guide déconstruit le tout pile technique : de la collecte et de l'ingénierie des fonctionnalités aux modèles de notation des risques, jusqu'à à l'interprétabilité et à la gestion des biais - avec des exemples de code réels prêts pour la production.
Ce que Vous Apprendrez
- Architecture d'un système d'abonnement IA de bout en bout
- Ingénierie de fonctionnalités spécifiques au domaine de l'assurance
- Modèles ML pour la notation des risques : XGBoost, deux étapes de fréquence/gravité
- Interprétabilité avec SHAP pour des décisions vérifiables et prêtes à être conformes
- Détection des biais et atténuation de l’équité dans le contexte réglementaire de l’UE
- MLOps pour les modèles de souscription en production avec MLflow
- Surveillance de l'orientation des femmes avec l'indice de stabilité de la population (PSI)
Le processus de souscription : de l’héritage à l’AI-Native
Avant de concevoir un système d’IA, il est essentiel de comprendre le déroulement du travail traditionnel dans notre nouvelle activité. automatiseur. Le processus de souscription est divisé en quatre phases fondamentales :
- Recueillir des informations : Le demandeur fournit des femmes sur lui et même sur le risque (questionnaire, documents, éventuelle inspection physique du bien)
- Analyser les risques : Le souscripteur apprécie la probabilité et la gravité de toutes les réclamations futures
- Pricing: Determinazione del premio in base al rischio valutato e agli obiettivi di combined ratio del portfolio
- Décision: Acceptation, refus ou acceptation sous conditions (exclusions, franchise, prime)
Un système d'IA natif ne supprime pas ces phases, mais les transformations sont profondes : la collection de femmes L'analyse des risques est automatique à partir de sources génétiques (vue d'ensemble, télévision, bureau de crédit). et créé par les modèles ML en millisecondes, le calibrage est dynamique et personnalisé pour chaque individu demandeur, et la décision est automatisée pour les standards avec supervision humaine pour moi cas complexes ou limités.
Quadro Normativo: AI Act EU e Underwriting
La loi européenne sur l'IA (entièrement en vigueur à partir d'août 2027) classe les systèmes notation crédit et assurance comment IA à haut risque (Annexe III). Pois chiches comporte des obligations spécifiques : transparence des décisions automatisées, droit de contrôle humain, technique de documentation détaillée et évaluation de la conformité avant commercialisation. La conception de Les systèmes de souscription IA doivent intégrer les besoins de l'architecture, et ne commentent pas poursuite de la rénovation.
Ingénierie de fonctions pour la souscription d'assurance
La qualité de l'ingénierie des fonctions est le fait qui diffère d'un modèle d'abonnement à l'autre excellent à médiocre. Contrairement aux domaines vus par l'utilisateur, où les fonctions sont extraction automatique des feuilles convolutives, les femmes ont besoin d'onglets d'assurance ingénierie manuelle approfondie basée sur la connaissance du domaine réel.
Les fonctionnalités destinées au secteur automobile sont regroupées en cinq catégories principales :
- Caractéristiques démographiques : âge, état civil, type de résidence
- Caractéristiques de conduite : années de permis de conduire, âge à la première obtention, antécédents d'accidents et d'infractions
- Caractéristiques du véhicule : marque, modèle, année, valeur, puissance, année d'immatriculation
- Caractéristiques géographiques : densité urbaine, indice de criminalité de la zone, risque météorologique
- Caractéristiques économiques : pointage de crédit, type de contrat d’assurance requis
import pandas as pd
import numpy as np
from typing import Dict, Optional
from dataclasses import dataclass
from datetime import date
@dataclass
class PolicyApplicant:
"""Rappresenta i dati grezzi di un richiedente polizza auto."""
applicant_id: str
birth_date: date
license_date: date
zip_code: str
vehicle_make: str
vehicle_year: int
vehicle_value: float
annual_mileage: int
claims_3yr: int
violations_3yr: int
credit_score: Optional[int] = None
marital_status: str = "single"
housing_type: str = "tenant"
class AutoInsuranceFeatureEngineer:
"""
Feature engineering per underwriting auto.
Produce 40+ feature da dati grezzi del richiedente,
includendo feature derivate, interazioni e encoding
domain-specific.
"""
VEHICLE_MAKE_RISK: Dict[str, int] = {
"Ferrari": 5, "Lamborghini": 5, "Porsche": 4,
"BMW": 3, "Mercedes": 3, "Audi": 3,
"Toyota": 1, "Honda": 1, "Volkswagen": 2,
"Ford": 2, "Fiat": 2, "Renault": 2,
}
def __init__(self, reference_date: Optional[date] = None):
self.reference_date = reference_date or date.today()
def engineer_features(self, applicant: PolicyApplicant) -> Dict[str, float]:
features: Dict[str, float] = {}
features.update(self._demographic_features(applicant))
features.update(self._driving_experience_features(applicant))
features.update(self._vehicle_features(applicant))
features.update(self._claims_features(applicant))
features.update(self._geographic_features(applicant))
if applicant.credit_score is not None:
features.update(self._credit_features(applicant))
features.update(self._interaction_features(features))
return features
def _demographic_features(self, applicant: PolicyApplicant) -> Dict[str, float]:
age = (self.reference_date - applicant.birth_date).days / 365.25
return {
"age": age,
"age_squared": age ** 2,
"age_under_25": float(age < 25),
"age_over_70": float(age > 70),
"age_risk_young": max(0.0, (25 - age) / 25) if age < 25 else 0.0,
"age_risk_senior": max(0.0, (age - 70) / 20) if age > 70 else 0.0,
"is_married": float(applicant.marital_status == "married"),
"is_homeowner": float(applicant.housing_type == "owner"),
}
def _driving_experience_features(self, applicant: PolicyApplicant) -> Dict[str, float]:
years_licensed = (self.reference_date - applicant.license_date).days / 365.25
age = (self.reference_date - applicant.birth_date).days / 365.25
age_at_license = age - years_licensed
return {
"years_licensed": years_licensed,
"years_licensed_squared": years_licensed ** 2,
"age_at_first_license": age_at_license,
"late_license_ratio": max(0.0, (age_at_license - 18) / 10),
"is_new_driver": float(years_licensed < 2),
"is_experienced_driver": float(years_licensed > 10),
}
def _vehicle_features(self, applicant: PolicyApplicant) -> Dict[str, float]:
vehicle_age = self.reference_date.year - applicant.vehicle_year
make_risk = self.VEHICLE_MAKE_RISK.get(applicant.vehicle_make, 2)
return {
"vehicle_age": float(vehicle_age),
"vehicle_value": applicant.vehicle_value,
"vehicle_value_log": np.log1p(applicant.vehicle_value),
"vehicle_make_risk_score": float(make_risk),
"is_high_performance": float(make_risk >= 4),
"is_new_vehicle": float(vehicle_age <= 2),
"is_old_vehicle": float(vehicle_age > 10),
"annual_mileage": float(applicant.annual_mileage),
"annual_mileage_log": np.log1p(applicant.annual_mileage),
"high_mileage": float(applicant.annual_mileage > 20000),
}
def _claims_features(self, applicant: PolicyApplicant) -> Dict[str, float]:
claims = applicant.claims_3yr
violations = applicant.violations_3yr
return {
"claims_3yr": float(claims),
"violations_3yr": float(violations),
"has_any_claim": float(claims > 0),
"has_multiple_claims": float(claims > 1),
"has_violations": float(violations > 0),
# Score combinato ponderato: sinistri pesano 3x rispetto a infrazioni
"incident_score": claims * 3.0 + violations * 1.5,
"claims_x_violations": float(claims * violations),
}
def _geographic_features(self, applicant: PolicyApplicant) -> Dict[str, float]:
# In produzione: lookup su DB geografici (ISTAT, OpenStreetMap, criminalita)
zip_hash = hash(applicant.zip_code) % 100
urban_score = (zip_hash % 5) / 4.0
crime_index = (zip_hash % 3) / 2.0
weather_risk = (zip_hash % 4) / 3.0
return {
"urban_density_score": urban_score,
"area_crime_index": crime_index,
"area_weather_risk": weather_risk,
"composite_geo_risk": (urban_score + crime_index + weather_risk) / 3,
}
def _credit_features(self, applicant: PolicyApplicant) -> Dict[str, float]:
score = applicant.credit_score or 0
return {
"credit_score": float(score),
"credit_score_normalized": (score - 300) / (850 - 300),
"poor_credit": float(score < 580),
"fair_credit": float(580 <= score < 670),
"good_credit": float(670 <= score < 740),
"excellent_credit": float(score >= 740),
}
def _interaction_features(self, features: Dict[str, float]) -> Dict[str, float]:
return {
# Giovane + auto sportiva = rischio molto alto
"young_high_perf": (
features.get("age_risk_young", 0) *
features.get("is_high_performance", 0)
),
# Sinistri + area ad alto crimine amplificano il rischio
"claims_urban": (
features.get("claims_3yr", 0) *
features.get("urban_density_score", 0)
),
# Mileage alto + veicolo vecchio = rischio meccanico aumentato
"mileage_old_vehicle": (
features.get("annual_mileage_log", 0) *
features.get("is_old_vehicle", 0)
),
}
Modèles de notation des risques : approches et compromis
Choisir le modèle d'apprentissage automatique pour la notation des risques pour équilibrer la précision prédiction, interprétation (fondamentale pour la conformité), rapidité d'inférence et facilité d'entretien. Voici les principales approches du secteur de l’assurance :
Modèles comparatifs pour la notation des risques d'assurance
| Modello | Accuratezza | Interpretabilita | Use Case Ideale |
|---|---|---|---|
| GLM (Poisson/Gamma) | Media | Très élevé | Baseline attuariale, regulatory acceptance |
| Random Forest | Alta | Media | Feature importance, robustezza agli outlier |
| XGBoost / LightGBM | Très élevé | Media | Production standard, SOTA sur données tabulaires |
| Neural Network Tabulare | Alta | Bassa | Fonctionnalités complexes avec intégrations catégorielles |
L'approche de la plus grande industrie est celle modèle pour deux âges: un modèle de fréquence (probabilité d'avoir un accident à un moment donné) et de gravité (cout en attente de l'accident) C'est une femme qui cache le produit). La première pure attendue est : Fréquence x Gravité.
import xgboost as xgb
from sklearn.metrics import mean_absolute_error, mean_squared_error
import numpy as np
import pandas as pd
from typing import Dict, Optional
class TwoStageRiskScorer:
"""
Modello a due stadi per pricing assicurativo auto.
Stage 1: Frequency model (Poisson regression con XGBoost)
Target = numero sinistri per polizza
Stage 2: Severity model (Tweedie/Gamma con XGBoost)
Target = importo sinistro, addestrato solo su polizze con sinistri
Pure Premium = E[Frequency] * E[Severity | has_claim]
"""
FREQUENCY_PARAMS: Dict = {
"objective": "count:poisson",
"eval_metric": "poisson-nloglik",
"max_depth": 6,
"learning_rate": 0.05,
"n_estimators": 500,
"min_child_weight": 50, # stabilita attuariale: min sinistri per leaf
"subsample": 0.8,
"colsample_bytree": 0.8,
"reg_alpha": 0.1,
"reg_lambda": 1.0,
"tree_method": "hist",
"early_stopping_rounds": 50,
}
SEVERITY_PARAMS: Dict = {
"objective": "reg:tweedie",
"tweedie_variance_power": 1.5, # 1=Poisson, 2=Gamma
"eval_metric": "tweedie-nloglik@1.5",
"max_depth": 5,
"learning_rate": 0.05,
"n_estimators": 300,
"min_child_weight": 30,
"subsample": 0.8,
"colsample_bytree": 0.7,
"reg_alpha": 0.1,
"reg_lambda": 1.0,
"tree_method": "hist",
"early_stopping_rounds": 30,
}
def __init__(self) -> None:
self.frequency_model = xgb.XGBRegressor(**self.FREQUENCY_PARAMS)
self.severity_model = xgb.XGBRegressor(**self.SEVERITY_PARAMS)
self.feature_names: list = []
def fit(
self,
X: pd.DataFrame,
y_claims: pd.Series,
y_amounts: pd.Series,
exposure: pd.Series,
eval_fraction: float = 0.2,
) -> "TwoStageRiskScorer":
"""
Addestra entrambi i modelli.
IMPORTANTE: usa split temporale, non random shuffle.
I dati assicurativi sono autocorrelati nel tempo.
"""
self.feature_names = X.columns.tolist()
split_idx = int(len(X) * (1 - eval_fraction))
X_train, X_val = X.iloc[:split_idx], X.iloc[split_idx:]
freq_train = y_claims.iloc[:split_idx]
freq_val = y_claims.iloc[split_idx:]
# Stage 1: Frequency
self.frequency_model.fit(
X_train, freq_train,
sample_weight=exposure.iloc[:split_idx],
eval_set=[(X_val, freq_val)],
verbose=50,
)
# Stage 2: Severity - solo su polizze con sinistri
has_claim = y_amounts > 0
X_sev = X[has_claim]
y_sev = y_amounts[has_claim]
sev_split = int(len(X_sev) * (1 - eval_fraction))
self.severity_model.fit(
X_sev.iloc[:sev_split], y_sev.iloc[:sev_split],
eval_set=[(X_sev.iloc[sev_split:], y_sev.iloc[sev_split:])],
verbose=30,
)
return self
def predict_pure_premium(
self, X: pd.DataFrame, exposure: float = 1.0
) -> np.ndarray:
"""Calcola il pure premium: E[Freq] * E[Severity]."""
freq = self.frequency_model.predict(X) * exposure
sev = self.severity_model.predict(X)
return freq * sev
def evaluate(self, X: pd.DataFrame, y_claims: pd.Series) -> Dict[str, float]:
pred = self.frequency_model.predict(X)
mae = mean_absolute_error(y_claims, pred)
rmse = float(np.sqrt(mean_squared_error(y_claims, pred)))
gini = self._gini_coefficient(y_claims.values, pred)
lift = self._lift_at_decile(y_claims.values, pred, 0.1)
return {
"mae": round(mae, 6),
"rmse": round(rmse, 6),
"gini_coefficient": round(gini, 4),
"lift_top_decile": round(lift, 4),
}
def _gini_coefficient(self, actual: np.ndarray, predicted: np.ndarray) -> float:
"""Gini coefficient: metrica attuariale standard per modelli di frequenza."""
idx = np.argsort(predicted)
cum = np.cumsum(actual[idx])
cum_norm = cum / cum[-1]
n = len(actual)
lorenz_area = float(np.sum(cum_norm)) / n
return 2 * (lorenz_area - 0.5)
def _lift_at_decile(
self, actual: np.ndarray, predicted: np.ndarray, decile: float
) -> float:
k = max(1, int(len(actual) * decile))
top_idx = np.argsort(predicted)[-k:]
base_rate = actual.mean()
if base_rate == 0:
return 0.0
return float(actual[top_idx].mean() / base_rate)
Interprétabilité avec SHAP : décisions auditables
Dans un contexte conforme aux assurances, un modèle de boîte noire n’est pas suffisant. La législation exige que les décisions de souscription soient explicables : pour les clients (droit à l'explication du RGPD), pour les souscripteurs (examen des cas limitations) et pour les régulateurs (Solvabilité II Pilier 3, ORSA). SHAP (SHapley Additive exPlanations) est l'outil de référence l'industrie pour l'interprétation post-hoc des modèles d'ensemble.
import shap
import pandas as pd
import numpy as np
from typing import Dict, List, Tuple
class UnderwritingExplainer:
"""
Spiegazioni SHAP per decisioni underwriting.
Genera output a tre livelli: cliente, underwriter, compliance.
"""
FEATURE_LABELS: Dict[str, str] = {
"age": "eta del guidatore",
"years_licensed": "anni di patente",
"claims_3yr": "sinistri negli ultimi 3 anni",
"violations_3yr": "infrazioni negli ultimi 3 anni",
"vehicle_make_risk_score": "categoria rischio veicolo",
"vehicle_age": "anzianita del veicolo",
"vehicle_value": "valore del veicolo",
"annual_mileage": "chilometraggio annuo dichiarato",
"composite_geo_risk": "rischio della zona geografica",
"credit_score": "score creditizio",
"young_high_perf": "combinazione giovane + veicolo sportivo",
}
def __init__(self, model, feature_names: List[str]) -> None:
self.feature_names = feature_names
self.explainer = shap.TreeExplainer(model)
def explain(
self, X_row: pd.DataFrame, risk_score: float
) -> Dict:
"""Spiegazione completa per una singola valutazione."""
shap_values = self.explainer.shap_values(X_row)
impacts: List[Tuple[str, float]] = sorted(
zip(self.feature_names, shap_values[0]),
key=lambda x: abs(x[1]),
reverse=True
)
return {
"risk_score": round(risk_score, 2),
"decision": self._score_to_decision(risk_score),
"customer_message": self._customer_message(impacts, risk_score),
"top_risk_factors": [
{
"name": name,
"label": self.FEATURE_LABELS.get(name, name),
"direction": "aumenta rischio" if shap > 0 else "riduce rischio",
"magnitude": round(abs(shap), 4),
}
for name, shap in impacts[:5]
],
"audit_trail": {
"base_expected_value": float(self.explainer.expected_value),
"all_shap_values": {
n: round(float(s), 6)
for n, s in zip(self.feature_names, shap_values[0])
},
"input_features": X_row.to_dict(orient="records")[0],
},
}
def _customer_message(
self, impacts: List[Tuple[str, float]], score: float
) -> str:
high = [(n, v) for n, v in impacts if abs(v) > 0.1]
if not high:
return "Il tuo profilo rientra nella fascia di rischio standard."
positivi = [self.FEATURE_LABELS.get(n, n) for n, v in high[:3] if v < 0]
negativi = [self.FEATURE_LABELS.get(n, n) for n, v in high[:3] if v > 0]
parts = []
if negativi:
parts.append(f"Fattori che aumentano il profilo di rischio: {', '.join(negativi)}.")
if positivi:
parts.append(f"Fattori a tuo favore: {', '.join(positivi)}.")
return " ".join(parts)
def _score_to_decision(self, score: float) -> str:
if score < 30:
return "ACCEPT_PREFERRED"
elif score < 60:
return "ACCEPT_STANDARD"
elif score < 80:
return "ACCEPT_SUBSTANDARD"
return "DECLINE_OR_MANUAL_REVIEW"
Équité et détection des biais dans le contexte de l’UE
L’utilisation de variables proxy (code postal, coût du crédit) peut introduire une discrimination indirecte interdit par la loi. En Europe, la directive égalité femmes-hommes (confirmée par L'Arrêté Test-Achats de la Cour de justice de l'UE de 2011) interdit l'utilisation du sexe pour la tarification assurance. L'entreprise vous accompagne dans les contrats de systèmes à risques classés en Annexe III, exigeant des évaluations de conformité obligatoires avant le déploiement.
import pandas as pd
import numpy as np
from sklearn.metrics import confusion_matrix
from typing import Dict, List
class FairnessAuditor:
"""
Auditor di fairness per modelli underwriting (EU-compliant).
Metriche implementate:
- Disparate Impact (regola 80%)
- Demographic Parity Gap
- Equal Opportunity (TPR parity)
- Calibration by group
"""
DISPARATE_IMPACT_THRESHOLD = 0.8 # EEOC 80% rule
MAX_DP_GAP = 0.1 # linee guida EIOPA
def __init__(
self,
predictions: np.ndarray,
true_labels: np.ndarray,
sensitive_df: pd.DataFrame,
) -> None:
self.predictions = predictions
self.true_labels = true_labels
self.sensitive_df = sensitive_df
def full_audit(self) -> Dict:
results: Dict = {}
for attr in self.sensitive_df.columns:
groups = self.sensitive_df[attr].unique()
attr_results: Dict = {}
for group in groups:
mask = self.sensitive_df[attr] == group
g_pred = self.predictions[mask]
g_true = self.true_labels[mask]
attr_results[str(group)] = {
"count": int(mask.sum()),
"acceptance_rate": float((g_pred < 0.6).mean()),
"avg_score": round(float(g_pred.mean()), 4),
"tpr": self._tpr(g_true, g_pred),
}
di = self._disparate_impact(attr_results)
dp = self._dp_gap(attr_results)
attr_results["_metrics"] = {
"disparate_impact": round(di, 4),
"demographic_parity_gap": round(dp, 4),
"passes_di_rule": di >= self.DISPARATE_IMPACT_THRESHOLD,
"passes_dp_rule": dp <= self.MAX_DP_GAP,
"overall_fair": di >= self.DISPARATE_IMPACT_THRESHOLD and dp <= self.MAX_DP_GAP,
}
results[attr] = attr_results
return results
def _tpr(self, labels: np.ndarray, preds: np.ndarray, thr: float = 0.5) -> float:
if len(labels) < 10:
return float("nan")
binary = (preds >= thr).astype(int)
try:
tn, fp, fn, tp = confusion_matrix(labels, binary, labels=[0, 1]).ravel()
return round(tp / (tp + fn), 4) if (tp + fn) > 0 else 0.0
except ValueError:
return float("nan")
def _disparate_impact(self, groups: Dict) -> float:
rates = [v["acceptance_rate"] for k, v in groups.items()
if not k.startswith("_") and isinstance(v, dict)]
if not rates or max(rates) == 0:
return 1.0
return min(rates) / max(rates)
def _dp_gap(self, groups: Dict) -> float:
rates = [v["acceptance_rate"] for k, v in groups.items()
if not k.startswith("_") and isinstance(v, dict)]
return (max(rates) - min(rates)) if rates else 0.0
MLOps e Monitoring in Produzione
Les modèles d'abonnement sont similaires dériver conceptuelle fréquent : profils des changements candidats (nouveaux modèles de véhicules électriques, changements démographiques), des coûts Parmi les réparations soumises à l'inflation, les événements climatiques extrêmes modifient la structure des dommages. Un système de surveillance continue avec Indice de stabilité de la population (PSI) e essentiel pour identifier quand le modèle est manquant.
from scipy import stats
import numpy as np
import pandas as pd
from typing import Dict, List
from datetime import datetime
class DriftMonitor:
"""
Monitora data drift per modelli underwriting.
Usa PSI (Population Stability Index) come metrica primaria.
PSI interpretation:
- PSI < 0.1: Nessun cambiamento significativo
- PSI 0.1-0.25: Cambiamento moderato, monitorare
- PSI > 0.25: Cambiamento significativo, retraining consigliato
"""
def __init__(self, reference_df: pd.DataFrame, features: List[str]) -> None:
self.reference_df = reference_df
self.features = features
def check_drift(self, current_df: pd.DataFrame) -> Dict:
feature_results: Dict = {}
critical_features = []
for feat in self.features:
if feat not in current_df.columns:
continue
psi = self._psi(self.reference_df[feat], current_df[feat])
ks_stat, ks_p = stats.ks_2samp(
self.reference_df[feat].dropna(),
current_df[feat].dropna()
)
status = "ok" if psi < 0.1 else ("warning" if psi < 0.25 else "critical")
feature_results[feat] = {
"psi": round(psi, 4),
"ks_statistic": round(ks_stat, 4),
"ks_pvalue": round(ks_p, 4),
"status": status,
}
if status == "critical":
critical_features.append(feat)
avg_psi = float(np.mean([v["psi"] for v in feature_results.values()]))
return {
"checked_at": datetime.now().isoformat(),
"overall_psi": round(avg_psi, 4),
"retraining_recommended": avg_psi > 0.1,
"critical_features": critical_features,
"feature_details": feature_results,
}
def _psi(self, ref: pd.Series, cur: pd.Series, bins: int = 10) -> float:
ref_clean = ref.dropna().values
cur_clean = cur.dropna().values
edges = np.percentile(ref_clean, np.linspace(0, 100, bins + 1))
edges = np.unique(edges)
ref_counts, _ = np.histogram(ref_clean, bins=edges)
cur_counts, _ = np.histogram(cur_clean, bins=edges)
ref_pct = (ref_counts + 1e-10) / len(ref_clean)
cur_pct = (cur_counts + 1e-10) / len(cur_clean)
return float(np.sum((cur_pct - ref_pct) * np.log(cur_pct / ref_pct)))
Best Practices e Anti-pattern
Meilleures Pratiques en Souscription IA
- Architecture en deux étapes (fréquence/gravité) : et les normes réelles et celles du produit sont l'étalonnage le plus précis pour un modèle unique sur le côté gauche
- Fractionnement de temps obligatoire : les données d'assurance sont autocorrélées dans le temps ; n'utilisez jamais de lecture aléatoire pour la répartition train/test
- Exposition en compensation : utiliser la durée de la police (exposition en années) comme compensation dans le modèle de Poisson pour normaliser le nombre d'incidents sinistres
- Conservez un GLM de référence : les modèles linéaires généralisés sont plus facilement validés par les régulateurs et fournissent des références pour évaluer la valeur ajoutée du ML
- Mode Shadow avant la mise en ligne : Exécutez le modèle en parallèle avec l'abonnement en cours pendant 30 à 90 jours, en comparant les décisions avant l'automatisation.
- Vérifiez le PSI cette semaine : la dérive dans le secteur automobile est fréquente en raison des nouveaux modèles de véhicules, de l’inflation des coûts de réparation et des évolutions réglementaires
Anti-modèles à éviter
- Fuite de fonctionnalité : ne jamais utiliser les variables disponibles uniquement après la réclamation (montant de la réclamation, réserve) comme fonctionnalités de formation du modèle de fréquence
- Optimiser uniquement l'AUC : Dans le département des assurances, les mesures pertinentes sont le coefficient de Gini, le ratio combiné et la remontée du niveau de risque supérieur.
- Modèles avec plus de 500 fonctionnalités : impossible à valider actuariellement et à justifier auprès du régulateur ; préférez une sélection rigoureuse des fonctionnalités (maximum 40 à 60 fonctionnalités)
- Ignorer la concentration du portefeuille : un modèle ici n'accepte pas que les profils extrêmement risqués créent une anti-sélection et une porte déséquilibrée
- Discrimination par procuration : des variables telles que le code postal peuvent être des indicateurs de l'origine ethnique ; vérifiez toujours l’impact disparate avant le déploiement
Conclusion et prochaines étapes
L'abonnement à l'IA ne remplace pas l'abonné humain, mais l'amplification : des décisions pour Les polices standards (80 à 90 % du volume) peuvent être entièrement automatisées avec précision supérieur au jeune être humain, libre de spécialistes des cas complexes où l'expérience de domination et irremplaçable.
Les éléments d'un système fiable sont : une ingénierie approfondie des fonctions basée sur la connaissance architecture actuarielle fréquence/gravité en deux étapes, interprétabilité SHAP pour la conformité, audit obligatoire et surveillance continue auprès du PSI pour la gestion de la marchandise.
Les prochains articles de la série exploreautomatisation des réclamations avec Computer Vision et PNL: numéro du FNOL pour l'évaluation automatique des images par photographie, juste règlement accéléré de bout en bout.
Serie InsurTech Engineering
- 01 - Dominio Assicurativo per Developer: Prodotti, Attori e Data Model
- 02 - Policy Management Cloud-Native: Architettura API-First
- 03 - Pipeline télématique : traitement des données UBI à grande échelle
- 04 - AI Underwriting: Feature Engineering e Risk Scoring (questo articolo)
- 05 - Automazione Sinistri: Computer Vision e NLP
- 06 - Fraud Detection: Graph Analytics e Behavioral Signal
- 07 - Standard ACORD e API Integration Assicurative
- 08 - Compliance Engineering: Solvency II e IFRS 17







