Session 13: AI for Claims Processing
Learning Objectives
- Build a multi-class classification model to triage incoming claims by type — accident, theft, fire, natural disaster, health — and route them to the appropriate workflow
- Construct a claims severity prediction model to estimate the expected payout for each incoming claim
- Develop a fast-track vs. investigate binary classification model to automate the claims triage decision
- Train and evaluate XGBoost models with hyperparameter tuning and compare performance against Random Forest
- Design a straight-through processing (STP) workflow that auto-settles eligible claims without human intervention
1. The Claims Transformation Opportunity
Claims is the largest cost in insurance — representing approximately 70% of every premium rupee. It is also the moment of truth in the customer relationship. A fast, fair claims experience creates a customer for life. A slow, disputed, or confusing claims experience destroys years of brand investment. AI in claims targets both of these at once: reduce cost and improve customer experience.
1.1 Where AI Creates Value in Claims
| Claims Step | AI Application | Value Created | Example |
|---|---|---|---|
| FNOL (First Notice of Loss) | Chatbot-based FNOL, voice-to-text, photo upload with computer vision, automatic fraud pre-screening | Faster intake (seconds vs. hours); structured data from the start; fraud indicators flagged before triage | Customer sends WhatsApp photo of damaged car; AI assesses damage severity and fraud risk in real-time |
| Triage & Routing | ML classification: claim type, complexity, estimated severity, optimal adjuster assignment | Right claim → right adjuster → right workflow first time, every time. No manual sorting. | Health claim with specific diagnosis code → routed to specialised health adjuster; simple motor → auto-STP |
| Fraud Screening | Anomaly detection, social network analysis, text mining of claim description | 10–25% improvement in fraud detection; false positive reduction through precision tuning | Claim with same address, same vehicle model, same date as 3 other claims → flagged for ring investigation |
| Assessment & Estimation | Computer vision (photo-based damage assessment), NLP (medical records extraction), automated repair cost estimation | Consistent, auditable estimates; reduced leakage; no need for physical surveyor in standard cases | 4 photos of car → AI estimates repair cost within ₹1,500 accuracy; human only reviews if estimate > ₹50,000 |
| Settlement | Automated payment processing; dynamic discounting; subrogation identification | Straight-through processing for eligible claims; same-day settlement; reduced cycle time from weeks to days | Claim under ₹25,000 with clear liability and no fraud flags → auto-approved and paid within 2 hours of assessment |
1.2 The Claims Leakage Opportunity
Claims leakage — the gap between what an insurer should pay and what it actually pays — is a significant cost. Global benchmarks put claims leakage at roughly 6% of claims spend (Insurance Thought Leadership) to 7–14% (EY's P&C claims-transformation estimates); a widely quoted figure is 5–10% [1], [2]. India-specific leakage figures are not consistently published, so treat the 5–10% range as a global benchmark rather than a verified Indian statistic. For a mid-size general insurer paying ₹1,000 crore in annual claims, leakage of even 8% represents ₹80 crore of pure value destruction. AI attacks leakage at three points:
- Policy leakage: Paying a claim that is not covered by the policy terms. AI validates coverage before assessment, not after settlement — preventing payment on claims the policy does not cover.
- Process leakage: Paying more than necessary due to manual assessment errors, inflated repair estimates, or missed subrogation opportunities. AI provides consistent, auditable damage estimates and automatically identifies subrogation potential.
- Fraud leakage: Paying fraudulent or exaggerated claims. Fraud detection models catch patterns humans cannot see — especially organised fraud rings that spread claims across multiple insurers.
2. Claims Triage & Classification
When a claim enters the system — through an app, WhatsApp, website, or agent — the first decision is: what kind of claim is this? The answer determines which adjuster it goes to, which workflow it follows, and how urgent it is. A multi-class classifier can automate this triage directly from the structured fields and unstructured text in the FNOL submission.
2.1 Loading and Preparing Claims Data
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold
from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.metrics import (classification_report, confusion_matrix, roc_auc_score,
precision_recall_curve, average_precision_score, ConfusionMatrixDisplay)
from sklearn.ensemble import RandomForestClassifier
import xgboost as xgb
# Load claims data
claims = pd.read_csv('data/claims_cleaned.csv') if False else \
pd.read_csv('data/insurance_cleaned.csv') # Using the merged dataset
print(f"Claims loaded: {len(claims):,}")
print(f"Claim types available: {claims['claim_type'].value_counts().to_dict() if 'claim_type' in claims.columns else 'N/A'}")
# If claim_type is not available, create a synthetic one from available data
if 'claim_type' not in claims.columns:
np.random.seed(42)
claim_types = ['Accident', 'Theft', 'Fire', 'Natural Disaster', 'Health']
# Create synthetic claim types based on policy_type
type_map = {
'Motor': np.random.choice(['Accident', 'Theft'], p=[0.85, 0.15]),
'Health': 'Health',
'Property': np.random.choice(['Fire', 'Natural Disaster', 'Theft'], p=[0.40, 0.35, 0.25]),
'Crop': 'Natural Disaster',
'Travel': 'Accident'
}
# Simplified: random assignment with seed for reproducibility
np.random.seed(42)
# Create claim_type as a feature for classification
claims['claim_type'] = np.random.choice(
claim_types, size=len(claims),
p=[0.35, 0.15, 0.10, 0.15, 0.25]
)
print(f"Claim type distribution:")
print(claims['claim_type'].value_counts())
print(f"\nMulti-class classification target: {claims['claim_type'].nunique()} classes")
2.2 Building the Triage Classifier
# Features for triage classification
triage_features = ['premium', 'claim_amount', 'age', 'income', 'credit_score',
'policy_type', 'sum_assured', 'days_to_settle' if 'days_to_settle' in claims.columns else None,
'fraud_flag' if 'fraud_flag' in claims.columns else None]
triage_features = [c for c in triage_features if c is not None and c in claims.columns]
# Target
y_triage = claims['claim_type']
# Drop rows with missing target
triage_data = claims[triage_features].copy()
triage_data['claim_type'] = y_triage
triage_data = triage_data.dropna(subset=['claim_type'])
X_triage = triage_data[triage_features]
y_triage = triage_data['claim_type']
# Identify categorical and numeric
cat_triage = X_triage.select_dtypes(include=['object', 'category']).columns.tolist()
num_triage = X_triage.select_dtypes(include=[np.number]).columns.tolist()
print(f"Triage features: {len(num_triage)} numeric, {len(cat_triage)} categorical")
# Preprocessing
preprocessor_triage = ColumnTransformer(
transformers=[
('num', StandardScaler(), num_triage),
('cat', OneHotEncoder(drop='first', handle_unknown='ignore', sparse_output=False), cat_triage)
])
# Multi-class Random Forest
rf_triage = RandomForestClassifier(
n_estimators=200, max_depth=12, min_samples_leaf=15,
class_weight='balanced', random_state = 0, n_jobs=-1
)
triage_pipeline = Pipeline(steps=[
('preprocessor', preprocessor_triage),
('classifier', rf_triage)
])
# Split
X_train_t, X_test_t, y_train_t, y_test_t = train_test_split(
X_triage, y_triage, test_size=0.2, random_state = 0, stratify=y_triage
)
# Train
triage_pipeline.fit(X_train_t, y_train_t)
# Predict
y_pred_t = triage_pipeline.predict(X_test_t)
# Evaluate
print("\n" + "=" * 60)
print("CLAIMS TRIAGE — MULTI-CLASS CLASSIFICATION RESULTS")
print("=" * 60)
print(f"\nClassification Report:")
print(classification_report(y_test_t, y_pred_t, zero_division=0))
# Confusion matrix
cm = confusion_matrix(y_test_t, y_pred_t)
classes = triage_pipeline.classes_
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=classes)
fig, ax = plt.subplots(figsize=(8, 6))
disp.plot(ax=ax, cmap='Blues', values_format='d')
ax.set_title('Claims Triage — Confusion Matrix', fontweight='bold')
plt.tight_layout()
plt.show()
print("\nInterpretation: The matrix shows which claim types are being confused.")
print("Off-diagonal elements are misclassifications — study the largest ones.")
print("If 'Natural Disaster' claims are frequently misclassified as 'Fire', the")
print("features may not discriminate well between these two types.")
3. Claims Severity Prediction
Severity prediction estimates the ultimate cost of a claim at the earliest possible moment — ideally at FNOL, using only the information available at that point. This prediction drives: (a) reserve setting (how much money to set aside), (b) triage decisions (high-severity → senior adjuster, low-severity → fast-track), and (c) fraud detection (a claim predicted as low-severity that grows rapidly is suspicious).
3.1 Features for Severity Prediction
# Severity prediction: predict the claim amount at FNOL time
# Features available at FNOL: policy data, customer data, claim characteristics known at intake
severity_data = claims.dropna(subset=['claim_amount']).copy()
# Create severity buckets for classification approach
severity_data['severity_bucket'] = pd.qcut(
severity_data['claim_amount'], q=4,
labels=['Low', 'Medium-Low', 'Medium-High', 'High']
)
print("Severity bucket distribution:")
print(severity_data['severity_bucket'].value_counts().sort_index())
print(f"\nClaim amount ranges per bucket:")
print(severity_data.groupby('severity_bucket', observed=False)['claim_amount'].describe())
# For regression: predict log(claim_amount) to handle skew
severity_data['log_claim_amount'] = np.log1p(severity_data['claim_amount'])
print(f"\nLog-transformed claim amount — skewness: {severity_data['log_claim_amount'].skew():.2f}")
print("(Target < 0.5 is approximately normal — suitable for linear regression)")
3.2 Severity Classification (Bucket Prediction)
# Features for severity prediction (only what's available at FNOL time)
sev_features = ['premium', 'age', 'income', 'credit_score', 'sum_assured',
'policy_type', 'claim_type']
sev_features = [c for c in sev_features if c in severity_data.columns]
X_sev = severity_data[sev_features]
y_sev_cls = severity_data['severity_bucket']
y_sev_reg = severity_data['log_claim_amount']
# Preprocessing
cat_sev = X_sev.select_dtypes(include=['object', 'category']).columns.tolist()
num_sev = X_sev.select_dtypes(include=[np.number]).columns.tolist()
preprocessor_sev = ColumnTransformer(
transformers=[
('num', StandardScaler(), num_sev),
('cat', OneHotEncoder(drop='first', handle_unknown='ignore', sparse_output=False), cat_sev)
])
# Train a classifier for severity bucket
classifier = xgb.XGBClassifier(
n_estimators=200, max_depth=6, learning_rate=0.1,
objective='multi:softmax', num_class=4,
subsample=0.8, colsample_bytree=0.8,
random_state = 0, n_jobs=-1
)
sev_cls_pipeline = Pipeline(steps=[
('preprocessor', preprocessor_sev),
('classifier', classifier)
])
X_train_s, X_test_s, y_train_s, y_test_s = train_test_split(
X_sev, y_sev_cls, test_size=0.2, random_state = 0, stratify=y_sev_cls
)
sev_cls_pipeline.fit(X_train_s, y_train_s)
y_pred_s = sev_cls_pipeline.predict(X_test_s)
print("=" * 60)
print("CLAIMS SEVERITY — XGBoost CLASSIFICATION RESULTS")
print("=" * 60)
print(f"\nClassification Report:")
print(classification_report(y_test_s, y_pred_s, zero_division=0))
# Confusion matrix
cm_sev = confusion_matrix(y_test_s, y_pred_s)
sev_classes = sev_cls_pipeline.classes_
disp_sev = ConfusionMatrixDisplay(confusion_matrix=cm_sev, display_labels=sev_classes)
fig, ax = plt.subplots(figsize=(8, 6))
disp_sev.plot(ax=ax, cmap='Greens', values_format='d')
ax.set_title('Severity Prediction — Confusion Matrix', fontweight='bold')
plt.tight_layout()
plt.show()
# Accuracy by severity class (useful for understanding where the model adds value)
class_accuracy = cm_sev.diagonal() / cm_sev.sum(axis=1)
print(f"\nPer-class accuracy:")
for i, cls in enumerate(sev_classes):
print(f" {cls:15s}: {class_accuracy[i]:.1%}")
print(f"\nBusiness insight: The model is {'useful for triage — low-severity claims can be fast-tracked with confidence' if class_accuracy[0] > 0.7 else 'useful but needs improvement in low-severity classification'}.")
print(f"High-severity accuracy is {'acceptable' if class_accuracy[-1] > 0.6 else 'a concern — missed high-severity claims could lead to reserve inadequacy'}.")
3.3 Severity Regression (Continuous Prediction)
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
# Regression approach: predict log claim amount
regressor = xgb.XGBRegressor(
n_estimators=200, max_depth=6, learning_rate=0.1,
subsample=0.8, colsample_bytree=0.8,
random_state = 0, n_jobs=-1
)
sev_reg_pipeline = Pipeline(steps=[
('preprocessor', preprocessor_sev),
('regressor', regressor)
])
# Split for regression (must re-split since y is different)
X_train_r2, X_test_r2, y_train_r2, y_test_r2 = train_test_split(
X_sev, y_sev_reg, test_size=0.2, random_state = 0
)
sev_reg_pipeline.fit(X_train_r2, y_train_r2)
y_pred_r2 = sev_reg_pipeline.predict(X_test_r2)
# Convert back from log scale
y_pred_actual = np.expm1(y_pred_r2)
y_test_actual = np.expm1(y_test_r2)
# Evaluate
rmse = np.sqrt(mean_squared_error(y_test_actual, y_pred_actual))
mae = mean_absolute_error(y_test_actual, y_pred_actual)
r2 = r2_score(y_test_actual, y_pred_actual)
print("=" * 60)
print("CLAIMS SEVERITY — XGBoost REGRESSION RESULTS")
print("=" * 60)
print(f"RMSE: ₹{rmse:,.0f}")
print(f"MAE: ₹{mae:,.0f}")
print(f"R²: {r2:.3f}")
print(f"\nAverage claim amount: ₹{y_test_actual.mean():,.0f}")
print(f"Average prediction error: ₹{mae:,.0f} ({mae/y_test_actual.mean()*100:.1f}% of average claim)")
# Plot actual vs predicted
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[0].scatter(y_test_actual, y_pred_actual, alpha=0.3, s=10, color='#6c5ce7')
axes[0].plot([y_test_actual.min(), y_test_actual.max()],
[y_test_actual.min(), y_test_actual.max()], 'r--', linewidth=2)
axes[0].set_xlabel('Actual Claim Amount')
axes[0].set_ylabel('Predicted Claim Amount')
axes[0].set_title('Severity Prediction: Actual vs. Predicted', fontweight='bold')
axes[0].spines['top'].set_visible(False)
axes[0].spines['right'].set_visible(False)
# Residuals
residuals = y_test_actual - y_pred_actual
axes[1].hist(residuals, bins=50, color='#00d2d3', edgecolor='white', alpha=0.7)
axes[1].axvline(x=0, color='red', linestyle='--', linewidth=1.5)
axes[1].set_xlabel('Prediction Error (₹)')
axes[1].set_ylabel('Frequency')
axes[1].set_title('Distribution of Prediction Errors', fontweight='bold')
axes[1].spines['top'].set_visible(False)
axes[1].spines['right'].set_visible(False)
plt.tight_layout()
plt.show()
print(f"\nThe severity prediction model can be used to set initial reserves automatically.")
print(f"Claims where the predicted amount exceeds ₹X can be flagged for senior adjuster review.")
4. Fast-Track vs. Investigate Decision
Not all claims need the same level of scrutiny. A motor claim for ₹5,000 from a 10-year customer with a clean history, clear photo evidence of a minor bumper scratch, and a police report confirming the accident — this claim should be fast-tracked for immediate payment. A motor claim for ₹5,000 from a 3-month-old policy where the damage does not match the accident description in the photo — this claim should be investigated.
The binary classifier that makes this "fast-track vs. investigate" decision is one of the highest-ROI models in insurance because it directly determines the STP rate. Every claim that can be safely fast-tracked saves: the cost of manual review (₹200–₹500 per claim), the delay (2–7 days), and the customer experience cost (a satisfied customer who would otherwise wait).
4.1 Creating the Decision Label
# Create a fast-track eligibility label based on business rules
# Assumptions (these would be set by claims management):
# - Claim amount < ₹25,000
# - No fraud flag
# - Policy tenure > 6 months
# - Clean claims history (less than 2 claims in 3 years)
# - Complete documentation
# Create proxy label from available data
claims_triage = claims.copy()
# Simulate a "fast_track_eligible" flag based on business rules
claims_triage['fast_track_eligible'] = (
(claims_triage['claim_amount'] < 25000) &
(claims_triage['fraud_flag'] == 0 if 'fraud_flag' in claims_triage.columns else True) &
(claims_triage['days_to_settle'] < 30 if 'days_to_settle' in claims_triage.columns else True)
).astype(int)
# Check class balance
fast_track_rate = claims_triage['fast_track_eligible'].mean()
print(f"Fast-track eligible: {fast_track_rate*100:.1f}% of claims")
print(f" Eligible (1): {(claims_triage['fast_track_eligible']==1).sum():,}")
print(f" Investigate (0): {(claims_triage['fast_track_eligible']==0).sum():,}")
# This is the ground truth for our classifier
# In production, the label would be based on actual claims outcomes:
# claims that were fast-tracked and paid correctly vs. claims that required investigation
4.2 Building the Fast-Track Classifier
# Features for fast-track decision (all available at FNOL)
ft_features = ['premium', 'claim_amount', 'age', 'income', 'credit_score',
'policy_type', 'claim_type', 'sum_assured']
ft_features = [c for c in ft_features if c in claims_triage.columns]
X_ft = claims_triage[ft_features]
y_ft = claims_triage['fast_track_eligible']
cat_ft = X_ft.select_dtypes(include=['object', 'category']).columns.tolist()
num_ft = X_ft.select_dtypes(include=[np.number]).columns.tolist()
preprocessor_ft = ColumnTransformer(
transformers=[
('num', StandardScaler(), num_ft),
('cat', OneHotEncoder(drop='first', handle_unknown='ignore', sparse_output=False), cat_ft)
])
# XGBoost classifier for fast-track decision
classifier = xgb.XGBClassifier(
n_estimators=200, max_depth=6, learning_rate=0.1,
scale_pos_weight=(1 - fast_track_rate) / fast_track_rate, # Handle imbalance
subsample=0.8, colsample_bytree=0.8,
random_state = 0, n_jobs=-1, eval_metric='logloss'
)
ft_pipeline = Pipeline(steps=[
('preprocessor', preprocessor_ft),
('classifier', classifier)
])
X_train_f, X_test_f, y_train_f, y_test_f = train_test_split(
X_ft, y_ft, test_size=0.2, random_state = 0, stratify=y_ft
)
ft_pipeline.fit(X_train_f, y_train_f)
y_pred_f = ft_pipeline.predict(X_test_f)
y_prob_f = ft_pipeline.predict_proba(X_test_f)[:, 1]
# Evaluate
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
print("=" * 65)
print("FAST-TRACK vs. INVESTIGATE — BINARY CLASSIFICATION RESULTS")
print("=" * 65)
print(f"Accuracy: {accuracy_score(y_test_f, y_pred_f):.3f}")
print(f"Precision: {precision_score(y_test_f, y_pred_f):.3f}")
print(f"Recall: {recall_score(y_test_f, y_pred_f):.3f}")
print(f"F1 Score: {f1_score(y_test_f, y_pred_f):.3f}")
print(f"ROC-AUC: {roc_auc_score(y_test_f, y_prob_f):.3f}")
# Confusion matrix
cm_ft = confusion_matrix(y_test_f, y_pred_f)
print(f"\nConfusion Matrix:")
print(f"{'':20s} {'Pred Investigate':20s} {'Pred Fast-Track':20s}")
print(f"{'Actual Investigate':20s} {cm_ft[0,0]:>5d} {cm_ft[0,1]:>5d}")
print(f"{'Actual Fast-Track':20s} {cm_ft[1,0]:>5d} {cm_ft[1,1]:>5d}")
# Business impact calculation
tp = cm_ft[1,1] # True fast-track — correctly sent to STP
fn = cm_ft[1,0] # False investigate — should have been fast-tracked, sent to manual
fp = cm_ft[0,1] # False fast-track — should have been investigated, sent to STP (fraud/test)
tn = cm_ft[0,0] # True investigate — correctly sent to investigation
manual_review_cost = 350 # ₹350 per manual review
fraud_cost_avg = 50000 # Average fraud claim cost (if a fraudulent claim is fast-tracked)
claims_test = len(y_test_f)
print(f"\n{'=' * 65}")
print("BUSINESS IMPACT SIMULATION")
print(f"{'=' * 65}")
print(f"Test set size: {claims_test:,} claims")
print(f"Manual review cost per claim: ₹{manual_review_cost}")
print(f"Avg fraud loss if fast-tracked incorrectly: ₹{fraud_cost_avg:,}")
# Cost without AI (all claims manually reviewed)
cost_no_ai = claims_test * manual_review_cost
print(f"\nCurrent cost (no AI, all manual): ₹{cost_no_ai:,.0f}")
# Cost with AI
cost_tp = 0 # Correctly fast-tracked — no cost
cost_fn = fn * manual_review_cost # Wrongly investigated — manual review cost (wasted)
cost_fp = fp * fraud_cost_avg # Wrongly fast-tracked — potential fraud loss
cost_tn = tn * manual_review_cost # Correctly investigated — manual review cost (necessary)
cost_with_ai = cost_fn + cost_fp + cost_tn
fraud_cases_caught_by_ai = fp # Cases the model would fast-track incorrectly
fraud_cases_that_are_actual_fraud = fp * 0.15 # Assume 15% of false fast-tracks are actual fraud
expected_fraud_loss = fraud_cases_that_are_actual_fraud * fraud_cost_avg
print(f"Cost with AI: ₹{cost_with_ai:,.0f} (including ₹{expected_fraud_loss:,.0f} in expected fraud loss)")
print(f"Net savings: ₹{cost_no_ai - cost_with_ai:,.0f}")
print(f"STP rate achieved: {tp / (tp+fp+fn+tn) * 100:.1f}% vs. 0% without AI")
print(f"\nNote: FP cost may be overestimated — most fast-tracked claims have guardrails")
print(f"(e.g., any claim > ₹50K requires manual review regardless of model score).")
4.3 Precision-Recall Tradeoff — Choosing the Threshold
# The default threshold is 0.5, but we can adjust it based on business priorities
# Higher threshold = fewer false fast-tracks (safer) but lower STP rate
# Lower threshold = more fast-tracks (higher STP) but higher fraud risk
thresholds = [0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
results = []
for thresh in thresholds:
pred = (y_prob_f >= thresh).astype(int)
prec = precision_score(y_test_f, pred, zero_division=0)
rec = recall_score(y_test_f, pred, zero_division=0)
stp_rate = pred.mean() * 100
results.append({'threshold': thresh, 'precision': prec, 'recall': rec, 'stp_rate': stp_rate})
print(f"{'Threshold':12s} {'Precision':12s} {'Recall':12s} {'STP Rate':12s}")
print("-" * 48)
for r in results:
print(f"{r['threshold']:>8.1f} {r['precision']:.3f} {r['recall']:.3f} {r['stp_rate']:.1f}%")
print(f"\nRecommended threshold: 0.6 (balances STP rate with fraud risk)")
print(f"At this threshold: STP = {results[3]['stp_rate']:.1f}%, Precision = {results[3]['precision']:.3f}")
print(f"Adjust threshold based on risk appetite: higher for health, lower for travel.")
5. XGBoost for Claims Models
XGBoost has become the dominant algorithm for structured data ML in insurance — and claims is one of its strongest applications. Understanding why XGBoost works well for claims, and how to use its key parameters, is essential for any insurance data scientist.
5.1 Why XGBoost Dominates Insurance ML
- Handles missing values natively: Claims data has more missing values than any other insurance dataset — because claims are reported at different stages, with different amounts of information available. XGBoost learns the best direction to handle each missing value during training, rather than requiring manual imputation.
- Built-in regularisation: XGBoost includes L1 and L2 regularisation, which penalises model complexity and reduces overfitting. This is valuable for claims models where the signal-to-noise ratio is low (many similar claims with very different outcomes).
- Gradient boosting with early stopping: XGBoost builds trees sequentially, with each new tree correcting the errors of the previous ones. Early stopping stops training when validation performance stops improving — automatically finding the right model complexity without manual tuning.
- Feature importance with SHAP compatibility: XGBoost integrates directly with SHAP, enabling per-prediction explainability — critical for the regulatory compliance requirements of claims decisions.
- Scalability: XGBoost handles millions of claims without specialized infrastructure, making it practical for even the largest Indian insurers.
5.2 XGBoost Parameter Configuration for Claims
# Key XGBoost parameters for claims models
params = {
# Booster parameters
'n_estimators': 300, # Maximum number of trees (early stopping will reduce this)
'max_depth': 6, # Tree depth — shallower trees reduce overfitting
'learning_rate': 0.05, # Step size shrinkage — lower = more robust
'subsample': 0.8, # Fraction of data used per tree — prevents overfitting
'colsample_bytree': 0.8, # Fraction of features used per tree — prevents overfitting
# Regularisation
'reg_alpha': 0.1, # L1 regularisation on weights
'reg_lambda': 1.0, # L2 regularisation on weights
# Class imbalance (for classification)
'scale_pos_weight': 3.0, # Sum(negative)/sum(positive) — handles imbalanced fraud/claims
# Computational
'n_jobs': -1, # Use all CPU cores
'random_state': 0,
'eval_metric': 'logloss' # or 'auc', 'mlogloss' for multi-class
}
# Training with early stopping
classifier = xgb.XGBClassifier(**params)
# With early stopping, we use the evaluation set to determine when to stop training
classifier.fit(
X_train_f, y_train_f,
eval_set=[(X_test_f, y_test_f)],
verbose=False
)
# Feature importance for XGBoost
# Get feature names after preprocessing
preprocessor_fitted = ft_pipeline.named_steps['preprocessor']
cat_encoder_f = preprocessor_fitted.named_transformers_['cat']
cat_feat_names_f = cat_encoder_f.get_feature_names_out(cat_ft).tolist() if len(cat_ft) > 0 else []
all_feat_names_f = num_ft + cat_feat_names_f
importances_xgb = classifier.feature_importances_
fi_xgb = pd.DataFrame({
'feature': all_feat_names_f[:len(importances_xgb)],
'importance': importances_xgb
}).sort_values('importance', ascending=True)
fig, ax = plt.subplots(figsize=(10, max(5, len(fi_xgb) * 0.3)))
ax.barh(fi_xgb['feature'], fi_xgb['importance'], color='#00d2d3', edgecolor='white')
ax.set_xlabel('Feature Importance (XGBoost)')
ax.set_title('XGBoost — Fast-Track Decision Feature Importance', fontweight='bold')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.tight_layout()
plt.show()
print("\nInterpretation: XGBoost reveals the features that most influence the")
print("fast-track decision. Cross-reference with business logic — do the")
print("top features align with what claims managers use intuitively?")
5.3 XGBoost vs. Random Forest — When to Use Which
| Dimension | Random Forest | XGBoost |
|---|---|---|
| Training speed | Faster — trees are built independently (parallelisable) | Slower — trees are built sequentially |
| Prediction accuracy | Good — typically 1–3% behind XGBoost on structured data | Excellent — often the best performing algorithm on structured insurance data |
| Overfitting control | Moderate — more trees, deeper trees increase overfitting risk | Strong — regularisation + early stopping + learning rate all control overfitting |
| Missing value handling | Requires imputation or surrogate splits | Native — learns the best direction for missing values during training |
| Hyperparameter tuning | Fewer parameters, simpler to tune | More parameters, more sensitivity to tuning — but higher ceiling |
| Interpretability | Built-in feature importance, easy to explain | Built-in importance + SHAP integration for per-prediction explanations |
| Best for claims | Quick baseline models, smaller datasets, interpretable-first deployments | Production models, larger datasets, accuracy-critical decisions, early stopping to avoid overfitting |
6. Straight-Through Processing (STP) in Claims
Straight-Through Processing means a claim is handled from FNOL to settlement without any human touch. The customer submits the claim digitally. The system validates coverage, checks for fraud indicators, assesses damage (via AI), calculates the settlement amount, obtains approval (via rules), and disburses payment — all in minutes, all without a human claims adjuster touching the file.
6.1 The STP Decision Framework
Not all claims are eligible for STP. The decision framework typically has four gates that a claim must pass through:
| Gate | Check | Fails If... | Passes If... |
|---|---|---|---|
| 1. Validation | Is the policy active? Does the claim scope match the coverage? Are all required documents present? | Policy lapsed; excluded peril; missing police report | Active policy; covered peril; complete documentation |
| 2. Fraud Score | Model-assigned fraud probability below the threshold | Fraud score > 0.6; connection to known fraud ring; unusual claim pattern | Fraud score < 0.3; no red flags; normal claim pattern |
| 3. Severity | Estimated claim amount within STP limits | Estimated amount > ₹50,000 (motor) or > ₹1,00,000 (health) | Estimated amount within STP limit |
| 4. Complexity | Claim involves multiple parties? Liability dispute? Legal involvement? | Third-party injury; disputed liability; legal representation involved | Single-party; clear liability; no legal involvement |
A claim that passes all four gates enters STP. A claim that fails any gate is routed to the appropriate workflow — human adjuster, fraud investigation, or senior claims specialist. The STP rate is the percentage of claims that pass all four gates. Industry leaders achieve 40–60% STP; the average Indian insurer is at 5–15%.
6.2 The STP Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ STP CLAIMS ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ CUSTOMER SUBMITS CLAIM (App, WhatsApp, Web, Voice) │
│ │ │
│ ▼ │
│ 1. DATA EXTRACTION │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • Extract structured data (policy_id, amount, type) │ │
│ │ • Extract text description (NLP for claim summary) │ │
│ │ • Extract photo/video if available │ │
│ │ • Trigger OCR for uploaded documents │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 2. STP GATES (sequential checks) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────┐ │
│ │ VALIDATION │ │ FRAUD SCORE │ │ SEVERITY │ │COMPLEX │ │
│ │ Check │→│ Model < 0.3 │→│ < ₹50K │→│ Simple? │ │
│ │ coverage │ │ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ PASS → PASS → PASS → PASS → AUTO-SETTLE │
│ FAIL → FAIL → FAIL → FAIL → ROUTE TO ADJUSTER │
│ │
│ 3. AUTO-SETTLEMENT │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • Calculate settlement amount (AI estimate ± rules) │ │
│ │ • Auto-approve (no human check) │ │
│ │ • Trigger payment (NEFT/UPI to registered account) │ │
│ │ • Send confirmation (WhatsApp + email) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ MONITORING: STP rate, STP claim accuracy, false STP rate, │
│ customer satisfaction by channel, settlement time │
└─────────────────────────────────────────────────────────────────────┘
6.3 The Economics of STP
The economics of STP are compelling. A claim that costs ₹350–₹500 to process manually costs ₹5–₹50 in an STP flow (compute cost + API calls). If an insurer processes 500,000 claims per year at an average manual cost of ₹400 each, the total claims ops cost is ₹20 crore. Increasing STP from 10% to 50% reduces the manual volume from 450,000 to 250,000 — saving ₹8 crore annually in claims operations cost, plus the customer satisfaction benefit of faster settlement, plus the leakage reduction from AI-based assessment.
7. The Claims Analytics Dashboard
An AI-powered claims system is only as effective as the visibility its operators have into its performance. A claims analytics dashboard must monitor three things simultaneously: operational efficiency (how fast are claims moving?), model performance (are the ML models still accurate?), and business outcomes (is the claims cost under control?).
7.1 Dashboard KPIs by Audience
| KPI | Audience | Formula | Target |
|---|---|---|---|
| STP Rate | Claims Ops, COO | Auto-settled claims ÷ Total claims | > 40% (mature), > 15% (year 1) |
| Settlement Time (STP) | Customer Experience | Average hours from FNOL to payment for STP claims | < 4 hours |
| Settlement Time (Manual) | Claims Ops | Average days from FNOL to payment for manually processed claims | < 7 days |
| Claims Leakage Rate | CFO, Claims Head | (Actual settlement − Expected settlement) ÷ Expected settlement | < 5% |
| Fraud Detection Rate | Fraud Team | Confirmed fraud claims ÷ All claims × 100 | Match historical (2–5%) |
| False STP Rate | Risk, Claims Head | STP claims that required later correction or resulted in loss | < 1% of STP claims |
| NPS (Claims) | Customer Experience | Net Promoter Score from claims survey | > 70 |
| Model — AUC (Fraud) | Data Science | ROC-AUC of fraud detection model | > 0.80 |
| Model — Calibration Error | Data Science | Mean absolute error between predicted and actual severity | < 15% |
7.2 Building the Claims Performance Summary in Python
# Simulate claims dashboard KPIs from the analysis results
dashboard = {
'Operational Metrics': {
'Total Claims (period)': len(claims),
'STP Rate (%)': 40.0,
'Avg Settlement Time — STP (hours)': 2.5,
'Avg Settlement Time — Manual (days)': 5.8,
'Claims per Adjuster (daily)': 12,
},
'Financial Metrics': {
'Total Claims Paid (₹ Cr)': round(claims['claim_amount'].sum() / 1e7, 1) if 'claim_amount' in claims.columns else 'N/A',
'Avg Claim Amount (₹)': round(claims['claim_amount'].mean()) if 'claim_amount' in claims.columns else 'N/A',
'Claims Leakage Rate (%)': 6.2,
'Fraud Detection Rate (%)': 3.8,
'Recovery Rate (Subrogation %)': 8.5,
},
'Model Performance': {
'Triage Classifier Accuracy (%)': round(classification_report(y_test_t, y_pred_t, output_dict=True, zero_division=0)['weighted avg']['f1-score'] * 100, 1),
'Severity Predictor MAE (₹)': round(mae),
'Fast-Track Model AUC': round(roc_auc_score(y_test_f, y_prob_f), 3),
'Fraud Model Recall (%)': 68.0,
'Model Retraining Frequency': 'Monthly',
},
'Customer Experience': {
'Claims NPS': 72,
'Claims Complaints (per 1000)': 8.5,
'Escalation Rate (%)': 4.2,
'Repeat Claim Rate (%)': 15.3,
}
}
print("=" * 70)
print("CLAIMS ANALYTICS DASHBOARD — PERFORMANCE SNAPSHOT")
print("=" * 70)
for section, metrics in dashboard.items():
print(f"\n{'─' * 40}")
print(f" {section}")
print(f"{'─' * 40}")
for metric, value in metrics.items():
print(f" {metric:40s} {str(value):>15s}")
print(f"\n{'─' * 40}")
print(f" KEY INSIGHT:")
print(f"{'─' * 40}")
print(f" With 40% STP, the claims team saves approximately")
print(f" {claims.shape[0] * 0.40 * 350:,.0f} claims × ₹350 = ₹{claims.shape[0] * 0.40 * 350:,.0f}")
print(f" in manual review costs per period.")
Hands-On Project: Build a Claims Intelligence Engine
You are a data scientist at an Indian general insurer. The claims team processes 20,000 claims per month manually, with an average settlement time of 12 days. Your task is to build a "Claims Intelligence Engine" that automates triage, predicts severity, and recommends the fast-track/investigate decision for every incoming claim.
Steps
- Claims triage classifier: Build a multi-class classifier (Random Forest or XGBoost) that predicts the claim type (Accident, Theft, Fire, Natural Disaster, Health) from features available at FNOL. Achieve at least 70% weighted F1-score.
- Severity prediction model: Build both the classification (severity bucket) and regression (log claim amount) models. For the regression, report RMSE and MAE both in log-space and in actual rupees.
- Fast-track vs. investigate model: Build an XGBoost binary classifier. Evaluate using precision, recall, and ROC-AUC. Calculate the optimum threshold using the business impact simulation (manual review cost = ₹350, average fraud cost = ₹50,000).
- Model comparison: Train a Random Forest on the same fast-track data. Compare with XGBoost on ROC-AUC and training time. Document which you would recommend for production and why.
- STP design: Propose an STP eligibility framework with specific thresholds for each of the 4 gates (Validation, Fraud Score, Severity, Complexity). Estimate the STP rate your framework would achieve on the dataset.
- Dashboard prototype: Using the KPIs from Section 7, create a dashboard display (can be a well-formatted printed summary — no need for a full dashboard tool) that the Head of Claims would use to monitor the AI engine's performance. Include at least 6 KPIs with targets.
- Write a 500-word implementation memo to the Head of Claims covering: (a) Model performance summary, (b) Expected STP rate and savings, (c) Recommended rollout approach (phased or big bang? why?), (d) Key risks — model drift, false fast-tracks, adjuster resistance — and mitigation strategies.
View Solution / Walkthrough
Implementation Memo (Sample)
To: Head of Claims, SureGuard Insurance
From: Data Science — Claims AI Project
Subject: Claims Intelligence Engine — Development Results and Rollout Plan
Model Performance Summary:
The Claims Intelligence Engine comprises three integrated ML models. The triage classifier achieves a weighted F1-score of 0.76 across 5 claim types — sufficient to automate routing of approximately 80% of incoming claims to the correct workflow without manual triage. The severity prediction model (XGBoost regression) estimates claim amounts with an MAE of ₹6,200 — a 31% error rate relative to the average claim. While not precise enough for final settlement calculation, it is accurate enough for initial reserve setting and triage prioritisation. The fast-track classifier achieves a ROC-AUC of 0.81 and, at the optimal threshold of 0.6, delivers a 38% STP rate with an estimated false STP rate of 0.8%.
Expected STP Rate and Savings:
Our analysis indicates that approximately 38% of incoming claims are eligible for straight-through processing based on the four-gate framework. At a current volume of 20,000 claims/month and manual processing cost of ₹350/claim, achieving 38% STP would save approximately ₹2,66,000/month in direct claims operations costs — ₹32 lakh annually. The larger benefit is indirect: STP claims settle in 2.5 hours on average vs. 5.8 days for manual claims, which is projected to improve claims NPS from the current 55 to approximately 68 based on industry benchmarks linking settlement speed to customer satisfaction.
Recommended Rollout Approach — Phased by Claim Type:
We recommend a claim-type phased rollout rather than a portfolio-wide big bang. Phase 1 (Month 1–2): Motor claims only. Motor is the highest-volume claim type with the most standardised assessment process. Enable STP for motor claims under ₹25,000 with fraud score < 0.3. Target 35% STP within motor. Phase 2 (Month 3–4): Travel and gadget claims. These are low-premium, high-volume lines with simple claims — ideal for STP expansion. Target 50% STP within these lines. Phase 3 (Month 5–6): Health claims. Health claims are more complex due to medical documentation, provider negotiations, and policy nuances. Start with outpatient and diagnostic claims only. Target 20% STP within health. After 6 months of validation and model refinement, roll out to property and other lines.
Key Risks and Mitigations:
The primary risk is false STP — paying claims that should not have been paid. Mitigation: (a) All STP claims are subjected to a post-payment audit (sampling 10% of STP claims for manual review). (b) Any false STP claim triggers an immediate model review — the model is retrained if false STP rate exceeds 1.5%. (c) The STP amount threshold is set conservatively and increased only after statistical validation. The secondary risk is adjuster resistance — adjusters may feel the AI is replacing their expertise. Mitigation: adjusters are involved in setting the STP eligibility criteria; their job shifts from processing simple claims (which they find tedious) to handling the complex referrals the model flags. STP is framed as "let the AI handle the routine work so you can focus on the cases that need your judgment" — and early adjuster feedback suggests this frame is effective.
Resource requirement: One data engineer (STP pipeline and monitoring) and one part-time data scientist (monthly model retraining) for 6 months. Estimated cost: ₹20 lakh. Estimated annual benefit: ₹32 lakh in direct ops savings + ₹2.8 crore from reduced claims leakage (assuming leakage drops from 9.5% to 7.0%, applied to ₹1,000 crore claims portfolio). Projected Year 1 ROI: ~6:1.
Key Takeaways
Claims is the largest cost centre in insurance (~70% of premium) and the moment of truth for customer relationships. AI creates value simultaneously in cost reduction (leakage, fraud) and customer experience (speed, transparency).
A multi-class triage classifier automates routing of claims to the correct workflow. Severity prediction at FNOL enables automated reserve setting and prioritisation. Both models use features available at the time of claim filing — no additional data collection needed.
The fast-track vs. investigate classifier is the highest-ROI claims ML model. A 1% improvement in false STP rate can save crores; a 1% improvement in STP rate saves thousands in manual review costs. The optimal threshold balances these two costs.
XGBoost dominates insurance ML for claims because it handles missing values natively, includes built-in regularisation, supports early stopping, and integrates with SHAP for explainability. Random Forest is a good baseline; XGBoost adds 1–3% AUC after tuning.
STP is not an all-or-nothing decision. The "creep the STP frontier" approach — start with simple claims and expand as the model validates — is safer and more effective than attempting full automation on day one. A well-designed STP architecture has four gates: Validation, Fraud Score, Severity, and Complexity.
References
- Insurance Thought Leadership, claims-leakage estimate (~6% of claims spend). [Online]. Flag for instructor review — primary publication URL to be confirmed.
- EY, "P&C Claims Transformation," indemnity / litigated-casualty leakage estimates (~7–14% of spend). [Online]. Flag for instructor review — primary publication URL to be confirmed.
- Zwillgen, "Insurers Face New Rules as AI Enters the Claims Process," 2024–2025. [Online]. Available: https://www.zwillgen.com/publication/insurers-new-rules-ai-claims-process/
- Aviva, "AI claims deployment," 2024 — 80+ AI models in claims, 23-day reduction in complex liability-assessment time, 30% improvement in routing accuracy, 65% reduction in customer complaints, £60m+ savings — as reported by McKinsey & Company / CLM Magazine. [Online]. Flag for instructor review — cite the original McKinsey/CLM article directly before publication.
Additional Reading & Practice
For students who want to go deeper on the financial and regulatory implications covered in this session — not required for the in-class lab, recommended before the next Module 4 session.
Optional Practice Exercise — Investment Appraisal of STP Automation
The STP build is a capital project, not just a technology upgrade. Use the numbers already in §6.3 and the Hands-On Project to treat it as one.
Given: one-time implementation cost ₹20 lakh; annual direct claims-ops savings ₹32 lakh (the Hands-On ₹2.66 lakh/month annualised); an additional leakage-reduction benefit of ₹2.8 crore/year; cost of capital 10%; 5-year horizon.
- Payback period (direct savings only): ₹20 lakh ÷ ₹32 lakh/year = 0.63 years ≈ 7.5 months.
- Payback period (including leakage): ₹20 lakh ÷ ₹3.12 crore/year ≈ 3 weeks — the leakage term dominates.
- NPV (direct savings only, 10%, 5 years): NPV = −20 + 32 × (annuity factor 3.7908) ≈ ₹101 lakh ≈ ₹1.01 crore.
- Interpret: why is the leakage term worth far more than the ops-cost term? (A rupee of leakage saved is a rupee that would otherwise have been paid out as a claim — a loss-ratio item — whereas a rupee of ops-cost saved is a smaller expense-ratio item.)
Further Reading
- Zwillgen, "Insurers Face New Rules as AI Enters the Claims Process" — claims-specific AI regulation [3].
- Aviva's 2024 AI claims deployment (via McKinsey / CLM Magazine) — the real-world STP case referenced in §1.2 [4].
Test Your Understanding
1. A claims triage model frequently misclassifies "Fire" claims as "Natural Disaster" claims. The business impact of this specific misclassification is:
2. An XGBoost fast-track classifier is trained with scale_pos_weight = (negative_count / positive_count). This parameter is used to:
3. A fast-track model has precision = 0.95 and recall = 0.40 at the default threshold of 0.5. The Head of Claims wants to maximise STP volume while keeping false STP below 2%. The appropriate action is:
4. In the four-gate STP framework, a claim that passes Gates 1–3 (Validation, Fraud Score, Severity) but fails Gate 4 (Complexity — involves a third-party injury) should be:
5. An insurer's STP rate has been stable at 35% for 6 months. The claims team suspects that many claims that could be auto-processed are not passing the gates. The MOST likely cause of the "STP ceiling" is: