File size: 4,620 Bytes
6396193
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Heart Attack Risk Prediction API
FastAPI server for real-time inference.
"""

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import json
import numpy as np
import joblib
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from tensorflow.keras.models import load_model
from typing import Optional

# ── Setup ─────────────────────────────────────────────────────────────
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
MODEL_DIR = os.path.join(BASE_DIR, "saved_model")

# Load model, scaler, and metadata
model = load_model(os.path.join(MODEL_DIR, "heart_attack_model.keras"))
scaler = joblib.load(os.path.join(MODEL_DIR, "scaler.pkl"))
with open(os.path.join(MODEL_DIR, "metadata.json")) as f:
    metadata = json.load(f)

FEATURES = metadata["features"]

# ── App ───────────────────────────────────────────────────────────────
app = FastAPI(
    title="Heart Attack Risk Predictor",
    description="Binary classification API for heart attack risk prediction using a neural network.",
    version="1.0.0",
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)


# ── Schemas ───────────────────────────────────────────────────────────
class PatientData(BaseModel):
    """Patient medical features for prediction."""
    age: float
    sex: float
    cp: float
    trestbps: float
    chol: float
    fbs: float
    restecg: float
    thalach: float
    exang: float
    oldpeak: float
    slope: float
    ca: float
    thal: float

    class Config:
        json_schema_extra = {
            "example": {
                "age": 63, "sex": 1, "cp": 3, "trestbps": 145,
                "chol": 233, "fbs": 1, "restecg": 0, "thalach": 150,
                "exang": 0, "oldpeak": 2.3, "slope": 0, "ca": 0, "thal": 1
            }
        }


class PredictionResponse(BaseModel):
    risk: str
    probability: float
    confidence: float
    label: int


# ── Endpoints ─────────────────────────────────────────────────────────
@app.get("/")
def root():
    return {
        "message": "Heart Attack Risk Prediction API",
        "version": "1.0.0",
        "endpoints": {
            "/predict": "POST - Predict heart attack risk",
            "/health": "GET - Health check",
            "/features": "GET - List expected features",
            "/docs": "GET - Interactive API docs",
        }
    }


@app.get("/health")
def health():
    return {"status": "healthy", "model_loaded": True}


@app.get("/features")
def features():
    return {"features": FEATURES, "count": len(FEATURES)}


@app.post("/predict", response_model=PredictionResponse)
def predict(patient: PatientData):
    try:
        # Build feature array in correct order
        input_data = np.array([[
            patient.age, patient.sex, patient.cp, patient.trestbps,
            patient.chol, patient.fbs, patient.restecg, patient.thalach,
            patient.exang, patient.oldpeak, patient.slope, patient.ca,
            patient.thal
        ]])

        # Scale
        input_scaled = scaler.transform(input_data)

        # Predict
        probability = float(model.predict(input_scaled, verbose=0)[0][0])
        label = 1 if probability > 0.5 else 0
        confidence = probability if label == 1 else 1 - probability

        return PredictionResponse(
            risk="High Risk" if label == 1 else "Low Risk",
            probability=round(probability, 4),
            confidence=round(confidence, 4),
            label=label
        )

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


# ── Run ───────────────────────────────────────────────────────────────
if __name__ == "__main__":
    import uvicorn
    print(f"\n🚀 Starting Heart Attack Risk Prediction API...")
    print(f"   Model features: {FEATURES}")
    print(f"   Docs: http://localhost:8000/docs\n")
    uvicorn.run(app, host="0.0.0.0", port=8000)