File size: 9,962 Bytes
fc407ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
"""
Train Classical ML Model for Insurance Claims Decision Support
==============================================================

GOVERNANCE CONSTRAINTS:
- Classical ML ONLY (XGBoost used here - NO neural networks, NO LLMs)
- Advisory system only (NO autonomous decisions)
- Must align with decision_spec.yaml frozen boundaries
- Human-in-the-loop is MANDATORY
- All outputs are NON-BINDING suggestions

Dataset: BDR-AI/insurance_decision_boundaries_v1 (Hugging Face)
Model: XGBoost Classifier
Purpose: Demonstration of AI governance principles
"""

import pandas as pd
import numpy as np
from datasets import load_dataset
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix
import xgboost as xgb
import joblib
import json
from datetime import datetime

# FROZEN DECISION BOUNDARIES - DO NOT MODIFY
DECISION_BOUNDARIES = {
    'damage_thresholds': {
        'low': 5000,
        'medium': 15000,
        'high': 50000
    },
    'risk_weights': {
        'low': 1.0,
        'medium': 1.5,
        'high': 2.0
    },
    'injury_multiplier': 1.8,
    'severity_thresholds': {
        'low': 5,
        'medium': 15
    }
}

def load_and_prepare_data():
    """
    Load dataset from Hugging Face and prepare for training.
    
    Returns:
        X_train, X_test, y_train, y_test, encoders
    """
    print("=" * 70)
    print("LOADING DATASET: BDR-AI/insurance_decision_boundaries_v1")
    print("=" * 70)
    
    # Load dataset from Hugging Face
    dataset = load_dataset("BDR-AI/insurance_decision_boundaries_v1")
    df = pd.DataFrame(dataset['train'])
    
    print(f"\nDataset loaded: {len(df)} samples")
    print(f"Columns: {df.columns.tolist()}")
    print(f"\nFirst few rows:")
    print(df.head())
    
    # GOVERNANCE CHECK: Verify only allowed features present
    allowed_features = ['claim_type', 'damage_amount', 'injury_involved', 'risk_factor']
    feature_cols = [col for col in df.columns if col != 'severity']
    
    print(f"\n{'='*70}")
    print("GOVERNANCE CHECK: Verifying feature compliance")
    print(f"{'='*70}")
    print(f"Allowed features: {allowed_features}")
    print(f"Found features: {feature_cols}")
    
    for col in feature_cols:
        if col not in allowed_features:
            raise ValueError(f"GOVERNANCE VIOLATION: Unauthorized feature '{col}' found in dataset!")
    
    print("βœ“ Feature compliance verified - proceeding with training")
    
    # Prepare features (4 inputs only - FROZEN)
    X = df[allowed_features].copy()
    y = df['severity']
    
    print(f"\n{'='*70}")
    print("TARGET DISTRIBUTION (Advisory Severity Levels)")
    print(f"{'='*70}")
    print(y.value_counts())
    
    # Encode categorical features
    encoders = {}
    
    # Encode claim_type
    le_claim = LabelEncoder()
    X['claim_type_encoded'] = le_claim.fit_transform(X['claim_type'])
    encoders['claim_type'] = le_claim
    
    # Encode risk_factor
    le_risk = LabelEncoder()
    X['risk_factor_encoded'] = le_risk.fit_transform(X['risk_factor'])
    encoders['risk_factor'] = le_risk
    
    # Convert injury_involved to int
    X['injury_involved_encoded'] = X['injury_involved'].astype(int)
    
    # Create feature matrix with encoded values
    X_processed = X[['claim_type_encoded', 'damage_amount', 'injury_involved_encoded', 'risk_factor_encoded']].copy()
    X_processed.columns = ['claim_type', 'damage_amount', 'injury_involved', 'risk_factor']
    
    # Encode target
    le_target = LabelEncoder()
    y_encoded = le_target.fit_transform(y)
    encoders['target'] = le_target
    
    print(f"\n{'='*70}")
    print("ENCODING SUMMARY")
    print(f"{'='*70}")
    print(f"claim_type mapping: {dict(zip(le_claim.classes_, le_claim.transform(le_claim.classes_)))}")
    print(f"risk_factor mapping: {dict(zip(le_risk.classes_, le_risk.transform(le_risk.classes_)))}")
    print(f"target mapping: {dict(zip(le_target.classes_, le_target.transform(le_target.classes_)))}")
    
    # Train-test split (80/20)
    X_train, X_test, y_train, y_test = train_test_split(
        X_processed, y_encoded, test_size=0.2, random_state=42, stratify=y_encoded
    )
    
    print(f"\n{'='*70}")
    print("TRAIN/TEST SPLIT")
    print(f"{'='*70}")
    print(f"Training samples: {len(X_train)}")
    print(f"Test samples: {len(X_test)}")
    
    return X_train, X_test, y_train, y_test, encoders

def train_model(X_train, y_train):
    """
    Train XGBoost classifier (classical ML).
    
    GOVERNANCE: XGBoost is a classical ML algorithm (tree-based).
                NO neural networks, NO LLMs, NO reinforcement learning.
    """
    print(f"\n{'='*70}")
    print("TRAINING XGBOOST CLASSIFIER (Classical ML)")
    print(f"{'='*70}")
    print("Model type: XGBoost (tree-based gradient boosting)")
    print("Governance status: βœ“ Classical ML approved")
    print("Autonomous decisions: βœ— DISABLED (advisory only)")
    
    # Train XGBoost model
    model = xgb.XGBClassifier(
        objective='multi:softprob',
        num_class=3,
        max_depth=6,
        learning_rate=0.1,
        n_estimators=100,
        random_state=42,
        eval_metric='mlogloss'
    )
    
    model.fit(X_train, y_train)
    
    print("\nβœ“ Model training complete")
    
    return model

def evaluate_model(model, X_test, y_test, encoders):
    """
    Evaluate model performance on test set.
    """
    print(f"\n{'='*70}")
    print("MODEL EVALUATION")
    print(f"{'='*70}")
    
    # Make predictions
    y_pred = model.predict(X_test)
    y_pred_proba = model.predict_proba(X_test)
    
    # Calculate metrics
    accuracy = accuracy_score(y_test, y_pred)
    
    print(f"\nTest Set Accuracy: {accuracy:.4f} ({accuracy*100:.2f}%)")
    
    # Classification report
    target_names = encoders['target'].classes_
    print(f"\n{'='*70}")
    print("CLASSIFICATION REPORT (Advisory Predictions)")
    print(f"{'='*70}")
    print(classification_report(y_test, y_pred, target_names=target_names))
    
    # Confusion matrix
    cm = confusion_matrix(y_test, y_pred)
    print(f"{'='*70}")
    print("CONFUSION MATRIX")
    print(f"{'='*70}")
    print(f"              Predicted")
    print(f"              Low  Medium  High")
    for i, label in enumerate(target_names):
        print(f"Actual {label:8s} {cm[i]}")
    
    # Feature importance
    feature_importance = model.feature_importances_
    feature_names = ['claim_type', 'damage_amount', 'injury_involved', 'risk_factor']
    
    print(f"\n{'='*70}")
    print("FEATURE IMPORTANCE (Explainability)")
    print(f"{'='*70}")
    for name, importance in sorted(zip(feature_names, feature_importance), key=lambda x: x[1], reverse=True):
        print(f"{name:20s}: {importance:.4f}")
    
    return {
        'accuracy': accuracy,
        'classification_report': classification_report(y_test, y_pred, target_names=target_names, output_dict=True),
        'confusion_matrix': cm.tolist(),
        'feature_importance': dict(zip(feature_names, feature_importance.tolist()))
    }

def save_artifacts(model, encoders, metrics):
    """
    Save trained model, encoders, and metrics.
    """
    print(f"\n{'='*70}")
    print("SAVING MODEL ARTIFACTS")
    print(f"{'='*70}")
    
    # Save model
    joblib.dump(model, 'model.pkl')
    print("βœ“ Model saved to: model.pkl")
    
    # Save encoders
    joblib.dump(encoders, 'encoders.pkl')
    print("βœ“ Encoders saved to: encoders.pkl")
    
    # Save metrics and metadata
    metadata = {
        'model_type': 'XGBoost Classifier',
        'model_architecture': 'Classical ML (tree-based gradient boosting)',
        'governance_status': 'ADVISORY ONLY - NO AUTONOMOUS DECISIONS',
        'human_review_required': True,
        'training_date': datetime.now().isoformat(),
        'dataset': 'BDR-AI/insurance_decision_boundaries_v1',
        'dataset_type': 'synthetic',
        'features': ['claim_type', 'damage_amount', 'injury_involved', 'risk_factor'],
        'target': 'severity (advisory levels: Low/Medium/High)',
        'decision_boundaries': DECISION_BOUNDARIES,
        'metrics': metrics
    }
    
    with open('model_metadata.json', 'w') as f:
        json.dump(metadata, f, indent=2)
    print("βœ“ Metadata saved to: model_metadata.json")
    
    print(f"\n{'='*70}")
    print("GOVERNANCE REMINDER")
    print(f"{'='*70}")
    print("⚠ This model produces ADVISORY outputs only")
    print("⚠ Human confirmation is MANDATORY for all decisions")
    print("⚠ All outputs are NON-BINDING suggestions")
    print("⚠ Audit trail must be maintained for all uses")

def main():
    """
    Main training pipeline.
    """
    print("\n" + "="*70)
    print("INSURANCE DECISION SUPPORT MODEL - TRAINING PIPELINE")
    print("="*70)
    print("Governance Mode: ADVISORY (Human-in-the-Loop Required)")
    print("Model Type: Classical ML (XGBoost)")
    print("Autonomous Decisions: DISABLED")
    print("="*70 + "\n")
    
    # Load and prepare data
    X_train, X_test, y_train, y_test, encoders = load_and_prepare_data()
    
    # Train model
    model = train_model(X_train, y_train)
    
    # Evaluate model
    metrics = evaluate_model(model, X_test, y_test, encoders)
    
    # Save artifacts
    save_artifacts(model, encoders, metrics)
    
    print(f"\n{'='*70}")
    print("TRAINING COMPLETE")
    print(f"{'='*70}")
    print(f"βœ“ Model accuracy: {metrics['accuracy']*100:.2f}%")
    print(f"βœ“ Model saved: model.pkl")
    print(f"βœ“ Encoders saved: encoders.pkl")
    print(f"βœ“ Metadata saved: model_metadata.json")
    print(f"\n{'='*70}")
    print("NEXT STEPS:")
    print("  1. Run evaluate.py for detailed evaluation")
    print("  2. Run predict.py for advisory predictions")
    print("  3. Review model_card.md for limitations")
    print(f"{'='*70}\n")

if __name__ == "__main__":
    main()