Sesi 5: Machine Learning Integration
Credit Scoring, Default Prediction, & Bulk Data Processing
1
ML Engine (Predictor Logic)
Core
Ini adalah Production Scoring Engine yang telah diperbarui.
Menggunakan Weighted Scorecard (Rule-Based) yang transparan, auditable, dan siap digunakan di lingkungan produksi.
/www/wwwroot/app-collection/ao/app/ml/predictor.py
from decimal import Decimal
from typing import Dict
class ProductionScoringEngine:
"""
ENGINE SKOR KREDIT PRODUKSI (NON-SIMULASI).
Metodologi: Weighted Scorecard (Tabel Bobot).
Pendekatan ini bersifat deterministik (input A selalu menghasilkan output B),
memudahkan penjelasan ke nasabah dan audit internal.
"""
# Konstanta Bobot (Weights)
BASE_SCORE = 300
MAX_SCORE = 850
# Threshold Segmen (Sesuai manual Bagian 1)
SEGMENT_HIGH_RISK_THRESHOLD = 550
SEGMENT_MED_RISK_THRESHOLD = 700
def calculate_score(self, monthly_income: float, assets: float, loan_amount: float, tenor_months: int) -> Dict:
"""
Menghitung Skor Kredit berdasarkan data finansial nyata.
"""
# --- 1. Perhitungan Angsuran (Flat Rate Syariah) ---
# Menggunakan margin standar 15% per tahun untuk simulasi kapasitas bayar
margin_rate = 0.15
total_margin = loan_amount * margin_rate
total_debt = loan_amount + total_margin
monthly_installment = total_debt / tenor_months
# --- 2. Variabel Penilaian (Assessment Variables) ---
# A. Debt Service Ratio (DSR) = Angsuran / Pendapatan
# Standar OJK: DSR aman di bawah 35-40%
if monthly_income <= 0:
dsr_score = -200
else:
dsr = monthly_installment / monthly_income
if dsr <= 0.30:
dsr_score = 150 # Sangat Sehat
elif dsr <= 0.40:
dsr_score = 100 # Sehat
elif dsr <= 0.50:
dsr_score = 0 # Netral
else:
dsr_score = -150 # Berisiko
# B. Asset Coverage Ratio (ACR) = Total Aset / Plafond
if loan_amount <= 0:
acr_score = 0
else:
acr = assets / loan_amount
if acr >= 1.2:
acr_score = 100 # Aset > Pinjaman
elif acr >= 0.8:
acr_score = 50
else:
acr_score = -50
# C. Tenor Risk
if tenor_months <= 12:
tenor_score = 50
elif tenor_months <= 24:
tenor_score = 20
elif tenor_months <= 36:
tenor_score = -20
else:
tenor_score = -50
# --- 3. Agregasi Skor ---
final_score = self.BASE_SCORE + dsr_score + acr_score + tenor_score
final_score = max(self.BASE_SCORE, min(self.MAX_SCORE, int(final_score)))
# --- 4. Probabilitas Gagal Bayar (Deterministik) ---
if final_score >= 750:
pd = 0.01 # 1%
elif final_score >= 650:
pd = 0.05 # 5%
elif final_score >= 550:
pd = 0.15 # 15%
else:
pd = 0.40 # 40%
# --- 5. Segmentasi ---
if final_score >= self.SEGMENT_MED_RISK_THRESHOLD:
segment = "low"
elif final_score >= self.SEGMENT_HIGH_RISK_THRESHOLD:
segment = "medium"
else:
segment = "high"
return {
"credit_score": final_score,
"default_probability": float(pd),
"segment": segment,
"dsr": round(monthly_installment / monthly_income if monthly_income > 0 else 0, 2),
"monthly_installment": round(monthly_installment, 2),
"assessment": {
"dsr_score": dsr_score,
"acr_score": acr_score,
"tenor_score": tenor_score
}
}
# Instance Engine untuk dipanggil oleh API
scoring_engine = ProductionScoringEngine()
2
Prediction Schemas
Validation
/www/wwwroot/app-collection/ao/app/schemas/prediction.py
from pydantic import BaseModel
from typing import List, Dict, Any
class PredictionResult(BaseModel):
customer_id: int
credit_score: int
default_probability: float
segment: str
recommendation: str
class BulkPredictionRequest(BaseModel):
file_path: str # Path ke file yang sudah diupload di sesi 4
class BulkPredictionResponse(BaseModel):
total_rows: int
high_risk_count: int
results: List[Dict[str, Any]]
3
API Routes (Predictions)
API
/www/wwwroot/app-collection/ao/app/api/predictions.py
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
import pandas as pd
import os
from db import get_db
from app.models.loan import Loan
from app.models.customer import Customer
from app.schemas.prediction import PredictionResult, BulkPredictionRequest, BulkPredictionResponse
from app.ml.predictor import scoring_engine
router = APIRouter()
@router.post("/individual/{loan_id}", response_model=PredictionResult)
def predict_individual(
loan_id: int,
db: Session = Depends(get_db)
):
"""
Melakukan scoring pada 1 pembiayaan yang sudah ada.
Mengupdate data scoring di tabel Loan.
"""
# 1. Ambil Data Loan & Customer
loan = db.query(Loan).filter(Loan.id == loan_id).first()
if not loan:
raise HTTPException(status_code=404, detail="Loan not found")
customer = db.query(Customer).filter(Customer.id == loan.customer_id).first()
if not customer:
raise HTTPException(status_code=404, detail="Customer not found")
# 2. Siapkan Data untuk Model
income = float(customer.monthly_income or 0)
assets = float(customer.assets_value or 0)
amount = float(loan.principal_amount)
tenor = loan.tenor_months
# 3. Jalankan Engine
result = scoring_engine.calculate_score(income, assets, amount, tenor)
# 4. Update Database (Loan Table)
loan.credit_score = result['credit_score']
loan.default_probability = result['default_probability']
loan.segment = result['segment']
db.commit()
db.refresh(loan)
return {
"customer_id": customer.id,
"credit_score": result['credit_score'],
"default_probability": result['default_probability'],
"segment": result['segment'],
"recommendation": f"Segment {result['segment'].upper()} - DSR: {result['dsr']}"
}
@router.post("/bulk", response_model=BulkPredictionResponse)
def predict_bulk(
request: BulkPredictionRequest
):
"""
Membaca file CSV/XLSX yang diupload dan melakukan prediksi massal.
Mengembalikan ringkasan risiko portofolio.
"""
file_path = request.file_path
# Validasi file ada
if not os.path.exists(file_path):
raise HTTPException(status_code=404, detail="File not found on server")
# Baca File
try:
if file_path.endswith('.csv'):
df = pd.read_csv(file_path)
else:
df = pd.read_excel(file_path)
except Exception as e:
raise HTTPException(status_code=400, detail=f"Failed to read file: {str(e)}")
# Mapping Kolom (Pastikan file upload punya kolom ini, atau disesuaikan)
# Disini kita asumsikan file punya kolom: 'monthly_income', 'assets', 'loan_amount', 'tenor'
# Jika kolom berbeda, user perlu mapping di frontend atau rename kolom di Excel.
results = []
high_risk_count = 0
for index, row in df.iterrows():
try:
income = float(row.get('monthly_income', 0))
assets = float(row.get('assets_value', 0))
amount = float(row.get('loan_amount', 0))
tenor = int(row.get('tenor_months', 12))
pred = scoring_engine.calculate_score(income, assets, amount, tenor)
if pred['segment'] == 'high':
high_risk_count += 1
results.append({
"row_index": index,
"score": pred['credit_score'],
"segment": pred['segment']
})
except Exception as e:
continue # Lewati baris error
return {
"total_rows": len(results),
"high_risk_count": high_risk_count,
"results": results
}
4
Integrasi Main.py
Finalize
Update main.py untuk mengaktifkan router ML.
/www/wwwroot/app-collection/ao/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from db import engine
import config
# Import Routers
from app.api.auth import router as auth_router
from app.api.customers import router as cust_router
from app.api.loans import router as loan_router
from app.api.files import router as file_router
from app.api.predictions import router as pred_router # NEW
app = FastAPI(title=config.get_settings().APP_NAME, version="1.0.0")
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include Routes
app.include_router(auth_router, prefix="/api/v1/auth", tags=["Authentication"])
app.include_router(cust_router, prefix="/api/v1/customers", tags=["Customers"])
app.include_router(loan_router, prefix="/api/v1/loans", tags=["Loans"])
app.include_router(file_router, prefix="/api/v1/files", tags=["Files & Uploads"])
app.include_router(pred_router, prefix="/api/v1/predictions", tags=["Machine Learning (AI)"]) # NEW
@app.on_event("startup")
def on_startup():
try:
with engine.connect() as conn:
print("✅ DB Connected!")
import os
os.makedirs("/www/wwwroot/app-collection/ao/uploads", exist_ok=True)
print("✅ Directories checked.")
except Exception as e:
print(f"❌ Error: {e}")
@app.get("/")
def root():
return {"status": "BPRS API Running", "session": "5 - AI Integration Active"}
5
Testing Scenario
Test
Restart server API dan buka Swagger UI.
1. Test Individual Scoring
- Pastikan Anda punya Loan ID dari sesi 4 (misal: ID 1).
- Gunakan endpoint:
POST /api/v1/predictions/individual/{loan_id}. - Hasilnya akan mengupdate database. Cek kembali data loan tersebut, kolom
credit_scoredansegmentsekarang terisi.
2. Test Bulk Prediction (Upload Data Massal)
Pertama, buat file Excel di laptop Anda dengan struktur kolom sederhana:
monthly_income | assets_value | loan_amount | tenor_months
5000000 | 20000000 | 10000000 | 12
10000000 | 5000000 | 50000000 | 24 (Contoh High Risk)
2000000 | 0 | 10000000 | 6 (Contoh Very High Risk)
- Step A: Upload file tersebut via
POST /api/v1/files/upload. Salin nilai"stored_path"dari response (contoh: `/www/wwwroot/app-collection/ao/uploads/123_data.xlsx`). - Step B: Buka endpoint
POST /api/v1/predictions/bulk. - Step C: Masukkan body JSON:
{ "file_path": "/www/wwwroot/app-collection/ao/uploads/123_data.xlsx" } - Result: Sistem akan memproses baris per baris dan memberi tahu berapa nasabah yang berstatus "High Risk".
Code copied to clipboard!