Navigation

Introduction to AI

Machine Learning

Deep Learning

Generative AI

Tools & Frameworks

General

AI for Financial Inclusion

Approximately 1.4 billion adults globally lack access to a bank account, and billions more remain significantly underserved — unable to access credit, insurance, or savings products on fair terms. The primary barrier is not unwillingness to provide services but the thin-file problem: traditional financial institutions rely on credit bureaus and formal income documentation, data that simply does not exist for people who have spent their lives in the informal economy.

AI is disrupting this paradigm by identifying creditworthiness signals in unconventional data sources — mobile phone usage patterns, digital payment histories, social networks, and behavioral data — enabling accurate risk assessment for borrowers who have never had a formal financial relationship.

The Thin-File Problem and Alternative Data

A traditional credit model uses variables like:

  • Loan repayment history (FICO, credit bureau data)
  • Length of credit history
  • Current debt levels
  • Types of credit accounts

For the 1.4 billion unbanked, none of this data exists. Alternative data sources that correlate with creditworthiness include:

import pandas as pd
import numpy as np
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

def build_alternative_credit_features(user_data: dict) -> pd.Series:
    """
    Engineer creditworthiness features from non-traditional data sources.
    
    These features are used by fintech lenders like Tala, Branch, Jumo,
    and Grab Financial to serve thin-file borrowers in emerging markets.
    
    All features must be:
    - Predictive (empirically validated correlation with repayment)
    - Legally permissible (jurisdiction-specific: some EU/US regulations prohibit
      use of social data or location)
    - Fair (not proxy for protected characteristics)
    """
    features = {}
    
    # ── Mobile phone behavior ──────────────────────────────────────────────
    phone = user_data.get("phone_data", {})
    
    # Call/SMS patterns: regularity suggests stable social network
    features["call_regularity_score"] = phone.get("coefficient_of_variation_calls", 0)
    features["unique_contacts_90d"] = phone.get("unique_contacts_90d", 0)
    features["night_call_ratio"] = phone.get("night_call_ratio", 0)   # high = irregular hours
    
    # Top-up behavior: frequent small top-ups = limited cash reserve planning
    topup = phone.get("airtime_topup", {})
    features["avg_topup_amount"] = topup.get("mean_amount", 0)
    features["topup_regularity"] = topup.get("days_between_topups_cv", 0)
    features["topup_count_30d"] = topup.get("count_30d", 0)
    
    # Device signals: phone age, model tier, charging pattern
    features["device_age_days"] = phone.get("device_age_days", 0)
    features["device_price_tier"] = phone.get("device_price_tier", 1)  # 1-5
    
    # ── Mobile money transactions ─────────────────────────────────────────
    mm = user_data.get("mobile_money", {})
    features["mm_account_age_days"] = mm.get("account_age_days", 0)
    features["monthly_inflow_mean"] = mm.get("monthly_inflow_mean", 0)
    features["monthly_inflow_cv"] = mm.get("monthly_inflow_cv", 0)    # income volatility
    features["merchant_payment_count_90d"] = mm.get("merchant_payments_90d", 0)
    features["bill_payment_regularity"] = mm.get("bill_payment_score", 0)
    features["savings_balance_mean"] = mm.get("savings_balance_mean", 0)
    
    # ── App behavior ──────────────────────────────────────────────────────
    app = user_data.get("app_behavior", {})
    features["app_open_frequency_daily"] = app.get("daily_opens", 0)
    features["time_in_app_daily_minutes"] = app.get("daily_minutes", 0)
    features["profile_completeness_pct"] = app.get("profile_pct", 0)
    features["location_count_90d"] = app.get("unique_locations_90d", 0)
    
    # ── Social graph signals ──────────────────────────────────────────────
    social = user_data.get("social", {})
    # Referral network quality: were friends who referred you good borrowers?
    features["referrer_repayment_rate"] = social.get("referrer_repayment_rate", 0.5)
    features["network_default_rate"] = social.get("network_default_rate", 0.1)
    
    return pd.Series(features)


class AlternativeCreditScorer:
    """
    Gradient boosting credit scoring model using alternative data.
    
    Trained on historical loan performance linked to mobile/behavioral features.
    Outputs:
    - Credit score (300-850 analog)
    - Loan approval recommendation
    - Suggested credit limit
    - Key risk factors for explainability
    """
    
    def __init__(self):
        self.model = GradientBoostingClassifier(
            n_estimators=300,
            learning_rate=0.05,
            max_depth=5,
            subsample=0.8,
            min_samples_leaf=50,  # prevent overfitting on small demographic segments
            random_state=42
        )
        self.scaler = StandardScaler()
        self.feature_names = None

    def fit(self, X: pd.DataFrame, y: pd.Series) -> "AlternativeCreditScorer":
        """y: 1 = repaid on time, 0 = defaulted"""
        self.feature_names = X.columns.tolist()
        X_scaled = self.scaler.fit_transform(X)
        self.model.fit(X_scaled, y)
        return self

    def predict_creditworthiness(self, X: pd.DataFrame) -> pd.DataFrame:
        X_scaled = self.scaler.transform(X[self.feature_names])
        repay_prob = self.model.predict_proba(X_scaled)[:, 1]
        
        # Map repayment probability to 300-850 credit score range
        credit_scores = 300 + (repay_prob * 550).round().astype(int)
        
        return pd.DataFrame({
            "repayment_probability": repay_prob,
            "credit_score": credit_scores,
            "approved": repay_prob > 0.65,
            "suggested_limit_usd": self._estimate_limit(repay_prob, X)
        })

    def _estimate_limit(self, repay_prob: np.ndarray, X: pd.DataFrame) -> np.ndarray:
        """Suggest credit limit proportional to income and risk."""
        base_limit = X.get("monthly_inflow_mean", pd.Series(100))
        risk_multiplier = repay_prob * 2.5   # max 2.5× monthly income
        return (base_limit * risk_multiplier).clip(5, 500).round()

Fairness and Bias in Alternative Credit Scoring

Alternative credit models carry significant fairness risks. Many seemingly neutral signals can be proxies for protected characteristics:

from sklearn.inspection import permutation_importance
import shap

def audit_credit_model_fairness(
    model,
    X_test: pd.DataFrame,
    y_test: pd.Series,
    protected_attributes: dict[str, pd.Series]
) -> pd.DataFrame:
    """
    Fairness audit for credit scoring models.
    
    Key metrics:
    - Demographic parity: approval rate equal across groups
    - Equalized odds: TPR and FPR equal across groups
    - Calibration: predicted probabilities match observed rates
    
    Regulators in many markets (US Equal Credit Opportunity Act, EU GDPR,
    Kenya's Data Protection Act) require explainability and non-discrimination.
    """
    results = []
    
    predictions = model.predict_creditworthiness(X_test)
    approval = predictions["approved"]
    
    overall_approval_rate = approval.mean()
    
    for attr_name, attr_values in protected_attributes.items():
        for group in attr_values.unique():
            mask = attr_values == group
            group_approval = approval[mask].mean()
            group_default_rate = (1 - y_test[mask]).mean()
            
            # Disparate impact: ratio of minority to majority approval rate
            disparate_impact = group_approval / overall_approval_rate
            
            results.append({
                "attribute": attr_name,
                "group": group,
                "approval_rate": round(group_approval, 3),
                "actual_default_rate": round(group_default_rate, 3),
                "disparate_impact_ratio": round(disparate_impact, 3),
                "flag": "REVIEW" if disparate_impact < 0.8 else "OK"
                # 80% rule: disparate impact < 0.8 triggers regulatory scrutiny
            })
    
    return pd.DataFrame(results)

Mobile Money and Digital Payment Infrastructure

Mobile money platforms — M-Pesa (Kenya/Tanzania), bKash (Bangladesh), GCash (Philippines), Paytm (India) — have created the data infrastructure that makes alternative credit scoring possible. AI powers several critical functions:

Fraud detection in real-time: Mobile money transactions are particularly vulnerable to SIM swap fraud, account takeover, and social engineering. ML models score every transaction in under 100 milliseconds:

def build_realtime_fraud_features(transaction: dict, account_history: dict) -> dict:
    """
    Real-time feature engineering for mobile money fraud detection.
    Must complete in <20ms to avoid transaction delay.
    
    Key signals for mobile money fraud:
    - Transaction velocity: many small transfers in quick succession
    - New recipient: transferring to an account first seen today
    - Device change: new phone/SIM associated with account
    - Unusual amount: relative to account history
    - Geographic anomaly: transaction from unusual location
    - Time anomaly: 3am transaction for dormant account
    """
    import time
    
    now = time.time()
    
    return {
        # Velocity features
        "txn_count_last_1h": account_history.get("count_1h", 0),
        "txn_count_last_24h": account_history.get("count_24h", 0),
        "amount_last_1h": account_history.get("amount_1h", 0),
        
        # Recipient risk
        "recipient_account_age_days": transaction.get("recipient_age_days", 0),
        "ever_transacted_recipient": transaction.get("prior_txn_count", 0) > 0,
        "recipient_fraud_score": transaction.get("recipient_risk_score", 0),
        
        # Account state
        "device_changed_last_7d": account_history.get("device_changed_7d", False),
        "sim_changed_last_7d": account_history.get("sim_changed_7d", False),
        "password_changed_last_24h": account_history.get("pwd_changed_24h", False),
        
        # Amount anomaly
        "amount_zscore_30d": (
            (transaction["amount"] - account_history.get("mean_amount_30d", 0))
            / (account_history.get("std_amount_30d", 1) + 1e-6)
        ),
        
        # Time features
        "hour_of_day": (now % 86400) / 3600,
        "is_dormant_account": account_history.get("days_since_last_txn", 0) > 30,
    }

Microinsurance Pricing with ML

Traditional actuarial tables require years of loss history — data that does not exist for new insurance markets in developing countries. ML enables pricing based on proxy data:

  • Agricultural microinsurance: Satellite-based crop monitoring (NDVI time series) combined with weather data enables index-based insurance payouts triggered by measurable environmental events — eliminating expensive claims adjustment
  • Health microinsurance: Mobile health app usage (step counts, sleep patterns, medication reminders) proxies for health behaviors
  • Livestock insurance: Satellite pasture condition monitoring for automated drought-triggered payouts

Key Players and Models

CompanyMarketModel typeKey innovation
TalaKenya, India, PhilippinesAlternative credit scoringMobile behavior features
BranchSub-Saharan AfricaML credit + mobile walletSmartphone data access
JumoAfrica, AsiaCredit marketplaceM-Pesa transaction history
CignifiGlobalTelecom-based creditAirtime purchase patterns
Grab FinancialSoutheast AsiaSuper-app creditRide-hailing + food delivery data
M-KopaEast AfricaAsset financingIoT device performance as collateral

Financial inclusion AI carries responsibilities that exceed typical ML applications: errors in credit denial can lock families out of economic mobility for years. Models must be explainable enough to satisfy regulators and borrowers, audited for discriminatory impact, and designed with appeal processes for adverse decisions. The most successful deployments treat responsible AI not as a compliance burden but as a commercial necessity — unfair models generate regulatory risk and reputational damage that outweighs any short-term risk reduction.