-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
139 lines (111 loc) · 5.67 KB
/
Copy pathapp.py
File metadata and controls
139 lines (111 loc) · 5.67 KB
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
from flask import Flask, request
from flask_cors import CORS
from flask_restx import Api, Resource, fields
import joblib
import re
import os
import warnings
# --- Mecab 포팅 라이브러리 임포트 (Konlpy 대체) ---
# Render/Linux 환경에서 순수 Python으로 설치 가능한 MeCab 포팅 버전을 사용합니다.
# (예: mecab-python-ko 또는 유사 패키지)
try:
from mecab import MeCab # pip install mecab
# 만약 'mecab'이 설치되지 않았다면 이 줄을 주석 처리하고 아래의 '순수 Python 임시 토크나이저'를 사용하세요.
USE_MECAB = True
except ImportError:
print("Mecab 라이브러리를 찾을 수 없습니다. 순수 Python 임시 분석기로 대체합니다.")
USE_MECAB = False
# 경고 메시지 무시
warnings.filterwarnings('ignore')
# --- Flask 앱 설정 ---
app = Flask(__name__)
CORS(app)
api = Api(app, version='1.0', title='AI 헬스케어 API',
description='Mecab을 활용한 증상 분석 API (순수 Python 환경)')
# --- AI 모델 및 MeCab 로드 (Eager Loading - 서버 시작 시 초기화) ---
tagger = None # 전역 MeCab 인스턴스
try:
# 1. 모델 로드
model = joblib.load('medical_department_model.pkl')
vectorizer = joblib.load('tfidf_vectorizer.pkl')
le_department = joblib.load('department_label_encoder.pkl')
# 2. MeCab 초기화 (Java 의존성 제거)
if USE_MECAB:
tagger = MeCab()
print("AI 모델과 Mecab 분석기가 성공적으로 로드 및 초기화되었습니다.")
except FileNotFoundError as e:
print(f"모델 파일 로드 실패: {e}")
model, vectorizer, le_department = None, None, None
except Exception as e:
# Mecab 초기화 실패 시 (예: MeCab 사전 경로 문제)
print(f"MeCab 초기화 실패: {e}")
tagger = None
# --- 불용어 정의 ---
stopwords = ['좀', '요', '가', '이', '은', '는', '으로', '와', '과', '에', '을', '를', '도', '고', '다', '아요', '어요']
# --- 텍스트 전처리 (MeCab 사용) ---
def preprocess_text(text):
# MeCab 초기화에 실패했다면 전처리 불가
if tagger is None and USE_MECAB:
raise Exception("한국어 분석기(MeCab)가 준비되지 않았습니다.")
text = re.sub(r'[^가-힣\s]', '', text)
if USE_MECAB:
# Mecab을 사용하여 형태소 분석 및 어간 추출 (Konlpy의 Okt.morphs(stem=True) 효과)
# MeCab은 '명사', '동사', '형용사' 등을 사용하며, 여기서는 명시적인 분석 기준을 적용해야 합니다.
tokens = tagger.morphs(text)
# 실제 예측에 유의미한 품사(NNG: 일반 명사, VV: 동사, VA: 형용사 등)만 남기는 로직이 필요
# 임시로 '명사'와 '동사 어간'만 남기는 단순화된 로직 적용
processed_tokens = []
for token_pos in tagger.pos(text):
word, pos = token_pos
# 명사(NN)와 동사/형용사(V*)만 남기고, 어간 추출은 Mecab이 기본으로 처리한다고 가정
if pos.startswith('N') or pos.startswith('V') or pos.startswith('J') or pos.startswith('E'):
processed_tokens.append(word)
filtered_tokens = [word for word in processed_tokens if word not in stopwords and len(word) > 1]
else:
# MeCab 로드 실패 시, 임시로 띄어쓰기 기준으로만 토큰화합니다. (정확도 낮음)
filtered_tokens = [word for word in text.split() if word not in stopwords and len(word) > 1]
return ' '.join(filtered_tokens)
# --- 네임스페이스 및 모델 정의 ---
ns_predict = api.namespace('predict', description='증상 기반 진료과 예측')
symptom_model = ns_predict.model('SymptomRequest', {
'symptoms': fields.String(required=True, description='환자의 증상 텍스트'),
})
# --- 증상 기반 진료과 예측 ---
@ns_predict.route('/')
class Predict(Resource):
@ns_predict.doc(body=symptom_model, summary='증상을 바탕으로 진료과를 예측합니다.')
def post(self):
# 1. AI 모델 로드 실패 시 즉시 500 에러 반환
if not model or not vectorizer or not le_department:
return {'error': '서버에 AI 모델이 준비되지 않았습니다.'}, 500
# 2. Mecab 초기화 실패 시 500 에러 반환
if tagger is None and USE_MECAB:
return {'error': '한국어 분석기(MeCab) 초기화 실패. 라이브러리 설치를 확인하세요.'}, 500
try:
data = ns_predict.payload
symptom_text = data['symptoms']
# Mecab을 사용한 텍스트 전처리 수행
preprocessed = preprocess_text(symptom_text)
if not preprocessed:
return {'recommended_department': '증상을 명확히 말씀해주세요.'}, 400
# 예측 로직
symptoms_vectorized = vectorizer.transform([preprocessed])
predicted_encoded = model.predict(symptoms_vectorized)[0]
predicted_department = le_department.inverse_transform([int(predicted_encoded)])[0]
return {'recommended_department': predicted_department}, 200
except Exception as e:
# 예측 또는 전처리 중 오류 발생
print(f"서버 오류 발생: {e}")
return {'error': str(e)}, 500
@ns_predict.route('/save_user_data')
class SaveUserData(Resource):
def post(self):
try:
pass # TODO: 구현
except Exception as e:
return {"error": str(e)}, 500
# --- Flask 실행 ---
if __name__ == '__main__':
# PORT 환경 변수를 사용하도록 설정 (클라우드 배포 표준)
port = int(os.environ.get('PORT', 10000))
app.run(host='0.0.0.0', port=port, debug=False)