WildOjisan commited on
Commit
aab0ecf
ยท
1 Parent(s): ae02f3c
Files changed (3) hide show
  1. Dockerfile +26 -0
  2. app.py +7 -65
  3. router/hospital_router.py +100 -0
Dockerfile ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python 3.9 ์ด๋ฏธ์ง€ ์‚ฌ์šฉ
2
+ FROM python:3.13-slim
3
+
4
+ # ์ž‘์—… ๋””๋ ‰ํ† ๋ฆฌ ์„ค์ •
5
+ WORKDIR /app
6
+
7
+ # ํ•„์š”ํ•œ ํŒŒ์ผ ๋ณต์‚ฌ
8
+ # requirements.txt ๋จผ์ € ๋ณต์‚ฌํ•˜์—ฌ ์บ์‹œ ํ™œ์šฉ
9
+ COPY requirements.txt .
10
+
11
+ # ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ # ๋‚˜๋จธ์ง€ ์†Œ์Šค ์ฝ”๋“œ ๋ฐ ๋ชจ๋ธ ํŒŒ์ผ ๋ณต์‚ฌ
15
+ COPY . .
16
+
17
+ # Hugging Face Spaces๋Š” 7860 ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•จ
18
+ EXPOSE 7860
19
+
20
+ # ๊ถŒํ•œ ๋ฌธ์ œ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉ์ž ์ƒ์„ฑ ๋ฐ ์ „ํ™˜ (์„ ํƒ์‚ฌํ•ญ์ด๋‚˜ ๊ถŒ์žฅ๋จ)
21
+ RUN useradd -m -u 1000 user
22
+ USER user
23
+ ENV PATH="/home/user/.local/bin:$PATH"
24
+
25
+ # FastAPI ์„œ๋ฒ„ ์‹คํ–‰ (0.0.0.0์œผ๋กœ ์—ด์–ด์•ผ ์™ธ๋ถ€ ์ ‘์† ๊ฐ€๋Šฅ)
26
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py CHANGED
@@ -1,69 +1,11 @@
1
- from fastapi import FastAPI, HTTPException
2
- from pydantic import BaseModel
3
- import pandas as pd
4
- from catboost import CatBoostRegressor
5
- import os
6
 
7
- app = FastAPI(title="Hospital Recommendation Model")
8
 
9
- # 1. ๋ชจ๋ธ ์„ค์ • ๋ฐ ๋กœ๋“œ
10
- MODEL_PATH = 'catboost_model.bin'
11
- model = CatBoostRegressor()
12
 
13
- # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ ๋ชจ๋ธ ๋กœ๋“œ
14
- if os.path.exists(MODEL_PATH):
15
- try:
16
- model.load_model(MODEL_PATH)
17
- print(f"โœ… CatBoost ๋ชจ๋ธ์„ '{MODEL_PATH}'์—์„œ ์„ฑ๊ณต์ ์œผ๋กœ ๋กœ๋“œํ–ˆ์Šต๋‹ˆ๋‹ค.")
18
- except Exception as e:
19
- print(f"โŒ ๋ชจ๋ธ ๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
20
- # ์‹ค์ œ ๋ฐฐํฌ์‹œ์—๋Š” ๋ชจ๋ธ์ด ์—†์œผ๋ฉด ์„œ๋ฒ„๊ฐ€ ์‹œ์ž‘๋˜์ง€ ์•Š๊ฒŒ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
21
- else:
22
- print(f"โš ๏ธ ๊ฒฝ๊ณ : '{MODEL_PATH}' ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ชจ๋ธ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.")
23
-
24
- # 2. ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ์„ ์œ„ํ•œ Pydantic ๋ชจ๋ธ ์ •์˜
25
- class HospitalInput(BaseModel):
26
- distance_m: float # ๊ฑฐ๋ฆฌ (๋ฏธํ„ฐ)
27
- rating: float # ํ‰์ 
28
- congestion_level: int # ํ˜ผ์žก๋„ (1~5 ๋“ฑ)
29
-
30
- class Config:
31
- json_schema_extra = {
32
- "example": {
33
- "distance_m": 500,
34
- "rating": 4.5,
35
- "congestion_level": 2
36
- }
37
- }
38
-
39
- # 3. ์˜ˆ์ธก ์—”๋“œํฌ์ธํŠธ
40
- @app.post("/predict")
41
- def predict_recommendation(input_data: HospitalInput):
42
- # ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
43
- if model.tree_count_ is None:
44
- raise HTTPException(status_code=500, detail="Model is not loaded.")
45
-
46
- try:
47
- # ์ž…๋ ฅ ๋ฐ์ดํ„ฐ๋ฅผ DataFrame์œผ๋กœ ๋ณ€ํ™˜ (ํŠน์„ฑ ์ด๋ฆ„๊ณผ ์ˆœ์„œ๋ฅผ ๋งž์ถค)
48
- # ํ•™์Šต ์‹œ ์ˆœ์„œ: ['distance_m', 'rating', 'congestion_level']
49
- data_df = pd.DataFrame([{
50
- 'distance_m': input_data.distance_m,
51
- 'rating': input_data.rating,
52
- 'congestion_level': input_data.congestion_level
53
- }])
54
-
55
- # ์˜ˆ์ธก ์ˆ˜ํ–‰
56
- prediction = model.predict(data_df)
57
-
58
- # ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜ (float ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜)
59
- return {
60
- "status": "success",
61
- "predicted_score": float(prediction[0])
62
- }
63
- except Exception as e:
64
- raise HTTPException(status_code=500, detail=str(e))
65
-
66
- # 4. ํ—ฌ์Šค ์ฒดํฌ (์„œ๋ฒ„ ์ƒํƒœ ํ™•์ธ์šฉ)
67
  @app.get("/")
68
- def read_root():
69
- return {"message": "Hospital Recommendation API is running"}
 
1
+ from fastapi import FastAPI
2
+ from router import hospital_router
 
 
 
3
 
4
+ app = FastAPI(title="Hospital Recommendation API")
5
 
6
+ # ๋ผ์šฐํ„ฐ ๋“ฑ๋ก
7
+ app.include_router(hospital_router.router)
 
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  @app.get("/")
10
+ def health_check():
11
+ return {"message": "API Server is running. Go to /docs for testing."}
router/hospital_router.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+ from pydantic import BaseModel
3
+ from typing import List
4
+ import pandas as pd
5
+ from catboost import CatBoostRegressor
6
+ import os
7
+
8
+ router = APIRouter(prefix="/hospital", tags=["Hospital Prediction"])
9
+
10
+ # --- ๋ชจ๋ธ ๋กœ๋“œ ๋กœ์ง (๊ธฐ์กด๊ณผ ๋™์ผ) ---
11
+ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
12
+ MODEL_PATH = os.path.join(BASE_DIR, 'model', 'catboost_model.bin')
13
+
14
+ model = CatBoostRegressor()
15
+ if os.path.exists(MODEL_PATH):
16
+ try:
17
+ model.load_model(MODEL_PATH)
18
+ print(f"โœ… [Router] ๋ชจ๋ธ ๋กœ๋“œ ์™„๋ฃŒ: {MODEL_PATH}")
19
+ except Exception as e:
20
+ print(f"โŒ [Router] ๋ชจ๋ธ ๋กœ๋“œ ์‹คํŒจ: {e}")
21
+ else:
22
+ print(f"โš ๏ธ [Router] ๊ฒฝ๊ณ : ๋ชจ๋ธ ํŒŒ์ผ ์—†์Œ")
23
+
24
+
25
+ # 1. ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ์ •์˜ (๋‹จ์ผ ๋ณ‘์› ๊ฐ์ฒด)
26
+ class HospitalInput(BaseModel):
27
+ id: int # ๋ณ‘์› ID (ํ•„์ˆ˜)
28
+ distance_m: float
29
+ rating: float
30
+ congestion_level: int
31
+
32
+ class Config:
33
+ json_schema_extra = {
34
+ "example": {
35
+ "id": 101,
36
+ "distance_m": 500,
37
+ "rating": 4.5,
38
+ "congestion_level": 2
39
+ }
40
+ }
41
+
42
+ # 2. ์˜ˆ์ธก ์—”๋“œํฌ์ธํŠธ ์ •์˜
43
+ # List[HospitalInput]์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ๋ณ‘์› ๋ฐ์ดํ„ฐ๋ฅผ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.
44
+ @router.post("/predict")
45
+ async def predict_score(hospital_list: List[HospitalInput]):
46
+ # ๋ชจ๋ธ ๋กœ๋“œ ์ƒํƒœ ํ™•์ธ
47
+ if model.tree_count_ is None:
48
+ return {
49
+ "success": False,
50
+ "data": None,
51
+ "msg": "AI Server Error: Model is not loaded."
52
+ }
53
+
54
+ try:
55
+ # ์ž…๋ ฅ ๋ฐ์ดํ„ฐ๊ฐ€ ๋น„์–ด์žˆ๋Š”์ง€ ํ™•์ธ
56
+ if not hospital_list:
57
+ return {
58
+ "success": True,
59
+ "data": [],
60
+ "msg": "No hospital data provided."
61
+ }
62
+
63
+ # 1. ๋ฆฌ์ŠคํŠธ๋ฅผ DataFrame์œผ๋กœ ๋ณ€ํ™˜ (ํ•œ ๋ฒˆ์— ์˜ˆ์ธกํ•˜๊ธฐ ์œ„ํ•จ)
64
+ # Pydantic ๋ชจ๋ธ ๋ฆฌ์ŠคํŠธ๋ฅผ dict ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜
65
+ input_data_list = [h.dict() for h in hospital_list]
66
+ df = pd.DataFrame(input_data_list)
67
+
68
+ # 2. ์˜ˆ์ธก์— ํ•„์š”ํ•œ ํŠน์„ฑ(Feature)๋งŒ ์„ ํƒ
69
+ # ํ•™์Šตํ•  ๋•Œ ์‚ฌ์šฉํ•œ ํŠน์„ฑ ์ˆœ์„œ๋ฅผ ๋งž์ถฐ์ค๋‹ˆ๋‹ค.
70
+ X_features = df[['distance_m', 'rating', 'congestion_level']]
71
+
72
+ # 3. ์ผ๊ด„ ์˜ˆ์ธก ์ˆ˜ํ–‰ (Batch Prediction)
73
+ predictions = model.predict(X_features)
74
+
75
+ # 4. ๊ฒฐ๊ณผ ๋งคํ•‘ ๋ฐ ๊ตฌ์กฐํ™”
76
+ hospital_rank_data = []
77
+
78
+ # DataFrame์˜ ๊ฐ ํ–‰๊ณผ ์˜ˆ์ธก ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์นฉ๋‹ˆ๋‹ค.
79
+ for idx, row in df.iterrows():
80
+ hospital_rank_data.append({
81
+ "id": int(row['id']),
82
+ "predicted_recommendation_score": float(predictions[idx])
83
+ })
84
+
85
+ # 5. ์ ์ˆ˜ ๊ธฐ์ค€ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ (Rank)
86
+ hospital_rank_data.sort(key=lambda x: x['predicted_recommendation_score'], reverse=True)
87
+
88
+ # 6. ์ตœ์ข… ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜
89
+ return {
90
+ "success": True,
91
+ "data": hospital_rank_data,
92
+ "msg": ""
93
+ }
94
+
95
+ except Exception as e:
96
+ return {
97
+ "success": False,
98
+ "data": None,
99
+ "msg": f"AI Server Error: Prediction Error: {str(e)}"
100
+ }