Download train.py from BDR-AI/claims-advisory-scoring-model: direct link, hf CLI and curl.
- Browser
- Download file 9.96 kB
-
https://huggingface.co/BDR-AI/claims-advisory-scoring-model/resolve/main/train.py
- Command line
-
hf download hf://BDR-AI/claims-advisory-scoring-model/train.py
-
curl -L -o train.py https://huggingface.co/BDR-AI/claims-advisory-scoring-model/resolve/main/train.py
9.96 kB
| """ | |
| 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() | |