한국형 우울증 딥러닝 예측 모델 및 진단 프로그램 "Kor-DEEPression" 개발 과정 정리 및 회고.
(5) Deployment & Conclusion (프로그램 배포 및 결론)
(Part5) Deployment 배포
웹페이지 제작
Flask 앱 배포
웹페이지 스크린샷
위 슬라이드는 1차 개발 완료 당시의 디렉토리 구성과 웹 배포 과정을 담고 있으며, 피드백과 수정을 거친 최종 버전의 경우엔 다음 파트에서 다시 자세히 다루도록 하겠음.
우선 웹페이지 디자인의 기본적인 틀은 기획 단계에서 구현 가능 범위를 확인할 때 만들어 두었었고, 웹페이지는 Bootstrap을 기반으로 HTML5와 CSS3를 직접 코딩함으로써 디자인 하였음.
폰트는 가독성을 최대화 하기 위해 한글을 제외한 영문과 숫자는 Google Fonts의 'Open Sans' 글씨체를 이용했고, 한글의 경우엔 'Noto Sans KR' 글씨체를 이용하였음
<!-- 기본 틀 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 위에 두개 meta 태그는 반드시 맨 처음에 와야함! -->
<title>Kor-DEEPression</title>
<meta name="description" content="AI 딥러닝 기반 한국형 우울증 예측 프로젝트">
<meta name="keywords" content="AI, Deep Learning, Depression">
<meta name="author" content="KyungJae Cheong">
<!-- Open Graph 설정 -->
<meta property="og:title" content="Kor-DEEPression">
<meta property="og:description" content="AI 딥러닝 기반 한국형 우울증 예측 프로젝트">
<meta property="og:image" content="{{ url_for('static', filename='imgs/og-image.png') }}">
<!-- Icons는 모든 기기에서 표시되도록 설정하여 너무 길어져서 생략 -->
아이콘들이 들어갈 자리
<!-- Bootstrap core CSS (디자인 템플릿) -->
<link rel="stylesheet" href="../static/css/bootstrap.min.css">
<!-- Custom styles CSS (폰트 및 스타일시트 설정) -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700,800">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Noto+Sans+KR:100,300,400,500,700,900">
<link rel="stylesheet" href="../static/css/custom_styles.css">
</head>
<body>
내용이 들어갈 자리
<!-- Bootstrap core JavaScript -->
<script src="../static/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Kor-DEEPression
├── app.py
├── modules_for_app.py
├── form_label.json
├── tuning-models
│ ├── CNN_depr.h5
│ └── MLP_mdd.h5
├── templates
│ ├── home.html
│ ├── prediction.html
│ ├── result.html
│ ├── dashboard.html
│ ├── info.html
│ ├── contact.html
│ ├── 404.html
│ └── 404-2.html
├── static
│ ├── css
│ │ ├── bootstrap.min.css
│ │ └── custom_styles.css
│ ├── js
│ │ ├── bootstrap.bundle.min.js
│ │ └── bootstrap.bundle.min.js.map
│ ├── imgs
│ └── icons
├── Procfile
├── requirements.txt
└── runtime.txt
<body>
<!-- 위 내용은 생략 -->
<div class="container">
<div class="bs-docs-section">
<div class="row">
<div class="col-lg-2 col-md-1"></div>
<div class="col-lg-8 col-md-10">
<div id="dash-area">
<iframe id="dash-content" src="https://lookerstudio.google.com/embed/reporting/a44f286d-d07a-41e5-bdea-a357f733b4ca/page/XZOED" frameborder="0" style="border:0;" allowfullscreen></iframe>
</div>
</div>
<div class="col-lg-2 col-md-1"></div>
</div>
</div>
</div>
<!-- 아래 내용은 생략 -->
</body>
/* A4 용지 길이 비율로 최대한 맞춰줌 */
#dash-area {
position: relative;
width: 100%;
padding-bottom: 140%;
}
/* #dash-area 안에서 꽉차게끔 맞춰줌 */
#dash-content {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
Kor-DEEPression
├── app.py
├── modules_for_app.py
├── form_label.json
├── final-models
│ ├── final_model_depr.tflite
│ └── final_model_mdd.tflite
├── templates
│ ├── home.html
│ ├── prediction.html
│ ├── result.html
│ ├── dashboard.html
│ ├── info.html
│ ├── contact.html
│ ├── 404.html
│ ├── 405.html
│ └── 404-2.html
├── static
│ ├── css
│ │ ├── bootstrap.min.css
│ │ └── custom_styles.css
│ ├── js
│ │ ├── bootstrap.bundle.min.js
│ │ └── bootstrap.bundle.min.js.map
│ ├── imgs
│ └── icons
├── Procfile
├── requirements.txt
└── runtime.txt
# 라이브러리 및 모듈함수 불러오기
from flask import Flask, render_template, request
from modules_for_app import *
# Flask app 지정
app = Flask(__name__)
# 404 error handling (주소값을 잘 못 입력한 경우)
@app.errorhandler(404)
def page_not_found(error):
# templates/404.html 실행(404)
return render_template('404.html'), 404
# 405 error handling (GET/POST 에러가 발생한 경우)
@app.errorhandler(405)
def method_not_allowed(error):
# templates/405.html 실행(405)
return render_template('405.html'), 405
# home page 실행함수(GET)
@app.route('/', methods=['GET'])
def home():
# GET request
if request.method == 'GET':
# templates/home.html 실행(200)
return render_template('home.html'), 200
# dashboard page 실행함수(GET)
@app.route('/dashboard', methods=['GET'])
def dashboard():
# GET request
if request.method == 'GET':
# templates/dashboard.html 실행(200)
return render_template('dashboard.html'), 200
# 프로그램 information page 실행함수(GET)
@app.route('/info', methods=['GET'])
def information():
# GET request
if request.method == 'GET':
# templates/info.html 실행(200)
return render_template('info.html'), 200
# 개발자 정보 page 실행함수(GET)
@app.route('/contact', methods=['GET'])
def contact():
# GET request
if request.method == 'GET':
# templates/info.html 실행(200)
return render_template('contact.html'), 200
# app.py파일 실행시 실행시킬 함수 : app
if __name__ == '__main__':
app.run(debug=True)
설문입력 페이지(설문입력 및 예측이 실행되는 페이지)는 변수를 재조합하거나 예측을 실행하는 코드의 분량이 상당히 많기 때문에, 따로 함수들을 묶어서 module화하여 함수를 불러오는 방식으로 프로그래밍을 진행하였음.
변수 재조합 및 인코딩 함수와 입력변수 확인용 디코딩 함수는 워낙 코드량이 방대하기 때문에 여기서는 다루지 않을 것이고, 자세한 코드는 다음 링크를 통해 확인 가능함
대신 경량화한 .tflite 모델을 예측하는 함수는 중요하기 때문에, 자세히 다루어 보도록 하겠음.
modules_for_app.py (예측 실행 함수 부분)
# 라이브러리 import
import os
import json
import numpy as np
from tensorflow import lite as tflite
# tensorflow-cpu 경고문 출력 없애기
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
# 변수 인코딩 및 디코딩 함수는 길고 복잡해서 표기하지 않겠음...
def Encoding_for_model(list_request, mode):
def Decoding_for_check(json_dir, dict_request, names_list):
# 모델 예측 기능 (tensorflow-lite)
# 경량화 없이 1차 개발을 완료했으나, Koyeb free-tier(nano)의 한계로 오류가 자주 발생함
# Keras 모델(.h5)로 predict를 실행하는 것이 생각보다 리소스를 많이 잡아먹는 작업임을 깨달음
# 따라서 이를 개선하기 위해 Keras모델을 tensorflow-lite를 통해 경량화하여 진행해보았음
# 이에 대한 코드는 final-models 폴더에 저장되어 있음
# 테스트 결과 압도적인 속도의 개선을 확인했으며, 그동안 발생했었던 오류와 경고들도 더 이상 발생하지 않음을 확인함
def predict_prob_tflite(data, model_dir):
'''
predict_prob_tflite
tensorflow-lite를 이용하여 예측 확률값을 얻는 기능
---
입력 변수 정보
data : (list) 예측하고자하는 데이터
model_dir : (str) tflite model의 directory
---
출력 변수
prob : (float) 예측 확률값의 퍼센트값 (소수점 둘째자리)
pred : (int) 예측 클래스 (0 or 1)
'''
# 모델 불러오기
interpreter = tflite.Interpreter(model_path=model_dir)
# 메모리 할당하기
interpreter.allocate_tensors()
# input, output 정보담기
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
# data를 input shape에 맞게 변경
input_shape = input_details[0]['shape']
input_data = np.array(data, dtype=np.float32).reshape(input_shape)
# tensor에 맞게 세팅 후 invoke실시
interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
# 예측 실행 (확률값이 출력됨)
output_data = interpreter.get_tensor(output_details[0]['index'])
# 출력변수1) 예측 확률값을 퍼센트로 변환
prob = int(round(output_data[0][0], 4)*(10**4))/100
# 출력변수2) 확률 값을 통해 예측 클래스를 반환 (기준은 0.5)
pred = np.where(output_data<0.5, 0, 1)[0][0]
# 출력변수들 최종 반환
return prob, pred
# prediction page 실행함수(GET, POST)
# 입력과 예측을 분리하면 여러기기에서 동시에 입력했을 경우에
# 서로 값이 꼬이는 현상이 발견되어 입력과 예측을 한번에 진행하도록 함
# 결과 값을 계속해서 다음 페이지에 넘겨주는 방식으로 전달 함
@app.route('/prediction', methods=['GET','POST'])
def prediction():
# GET request
if request.method == 'GET':
# templates/prediction.html 실행(200)
return render_template('prediction.html'), 200
# POST request(submit으로부터 실행)
if request.method == 'POST':
try:
'''
Request Form 으로부터 변수를 가져오기
'''
# 전체 변수이름과 값을 담을 리스트와 딕셔너리
names_request = list() # 변수 이름을 담을 리스트
dict_request = dict() # 변수 이름과 값을 담을 딕셔너리
# 수치형 변수 (Q18 ~ 19, 총 3개) : 변수별로 dtype이 다르기 때문에 따로 진행
names_numeric = ['age', 'height', 'weight']
dict_numeric = dict() # 변수 값을 담을 딕셔너리
# Q18. Age : integer (19 ~ 80)
dict_numeric[names_numeric[0]] = int(request.form[names_numeric[0]])
# Q19. Height : float (100 ~ 200)
dict_numeric[names_numeric[1]] = float(request.form[names_numeric[1]])
# Q19. Weight : float (30 ~ 150)
dict_numeric[names_numeric[2]] = float(request.form[names_numeric[2]])
# 전체리스트 및 딕셔너리 업데이트
names_request.extend(names_numeric)
dict_request.update(dict_numeric)
# 이진형 변수 (Q7 ~ Q13, 7개) : 모두 integer이기 때문에 for문으로 진행
names_binary = ['limitation', 'modality', 'w_change', 'w_control',
'high_bp', 'diabetes', 'high_lipid']
dict_binary = dict() # 변수 값을 담을 딕셔너리
# Q 7 ~ 13. 이진형 변수
for name in names_binary:
dict_binary[name] = int(request.form[name])
# 전체리스트 및 딕셔너리 업데이트
names_request.extend(names_binary)
dict_request.update(dict_binary)
# 범주형 변수 (Q1 ~ Q6 & Q14 ~ Q17, 총 10개) : 모두 integer이기 때문에 for문으로 진행
names_category = ['gender','edu','household','marital','economy','health',
'drk_freq','drk_amount','smoke','stress']
dict_category = dict() # 변수 값을 담을 딕셔너리
# Q1 ~ Q6 & Q14 ~ Q17. 범주형 변수
for name in names_category:
dict_category[name] = int(request.form[name])
# 전체리스트 및 딕셔너리 업데이트
names_request.extend(names_category)
dict_request.update(dict_category)
'''
Modeling용 데이터 생성
'''
# 수집한 변수 값들을 List로 묶어주기(수치형, 이진형, 범주형 순서)
values_numeric = [dict_numeric[name] for name in names_numeric]
values_binary = [dict_binary[name] for name in names_binary]
values_category = [dict_category[name] for name in names_category]
# Feature 조합 및 Encoding 실시 (modules_for_app에서 불러온 함수)
values_encoded_numeric = Encoding_for_model(values_numeric, mode='numeric')
values_encoded_binary = Encoding_for_model(values_binary, mode='binary')
values_encoded_category = Encoding_for_model(values_category, mode='category')
# 리스트 병합
values_encoded = values_encoded_numeric + values_encoded_binary + values_encoded_category
'''
입력 Check용 데이터 생성
'''
# 이진형과 범주형은 Label이 따로 존재하므로 변수명 리스트를 순서대로 묶기(이진형, 범주형 순서)
names_bin_cat = names_binary + names_category
# Decoding 실시 (modules_for_app에서 불러온 함수)
labels_bin_cat = Decoding_for_check(json_dir='./form_labels.json',
dict_request=dict_request,
names_list=names_bin_cat)
# 수치형 변수값 리스트를 Label값을 담은 리스트와 병합하여 Check용 딕셔너리 생성
check_request = values_numeric + labels_bin_cat
dict_check = dict(zip(names_request, check_request))
'''
우울증 및 주요우울장애 예측
확률퍼센트값과 예측클래스를 산출
'''
# 모델(.tflite)파일 디렉토리 지정
model_dir_depr = './final-models/final_model_depr.tflite'
model_dir_mdd = './final-models/final_model_mdd.tflite'
# Depression 모델 예측 (확률 퍼센트값 & 예측 클래스)
prob_depr, pred_depr = predict_prob_tflite(values_encoded, model_dir_depr)
# 예측 클래스를 문자열로 Decoding
class_depr = str()
if pred_depr == 0:
class_depr = "정상"
elif pred_depr == 1:
class_depr = "우울증"
'''
확인 페이지 출력 (예측 결과도 넘겨주기)
'''
# "정상" 인 경우 : 다음 단계로 바로 넘어감
if class_depr == "정상":
# templates/prediction.html에 변수들을 전달 (200)
return render_template('prediction.html',
checklist = dict_check,
percnt_depr = prob_depr,
label_depr = class_depr), 200
# "우울증" 인 경우 : MDD 모델 예측을 추가적으로 실시
elif class_depr == "우울증":
# MDD 모델 예측 (확률 퍼센트값 & 예측 클래스)
prob_mdd, pred_mdd = predict_prob_tflite(values_encoded, model_dir_mdd)
# 예측 클래스를 문자열로 Decoding
class_mdd = str()
if pred_mdd == 0:
class_mdd = "경도우울증"
elif pred_mdd == 1:
class_mdd = "주요우울장애"
# 오류없이 예측을 수행한 경우 : templates/prediction.html에 변수들을 전달 (200)
return render_template('prediction.html',
checklist = dict_check,
percnt_depr = prob_depr,
label_depr = class_depr,
percnt_mdd = prob_mdd,
label_mdd = class_mdd), 200
# try문에서 에러가 발생한 경우 : templates/404-2.html 실행(404)
except:
return render_template('404-2.html'), 404
{% if checklist %}
<div class="d-grid gap-2" id="check-values">
<legend class="mt-4">Final Check. 입력값 확인</legend>
예측에 앞서 아래 표를 통해<br>
입력값들을 확인해 주십시오.<br>
<br>
</div>
<table class="table table-hover" id="check-table">
<thead>
<tr class="table-primary">
<th scope="col" style="text-align: center;">Question</th>
<th scope="col" style="text-align: left;">Response</th>
</tr>
</thead>
<tbody>
<tr class="table-dark">
<th scope="row">Gender<br>(성별)</th>
<td>{{checklist['gender']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Education<br>(최종 학력)</th>
<td>{{checklist['edu']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Household<br>(세대 유형)</th>
<td>{{checklist['household']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Marital<br>(혼인 상태)</th>
<td>{{checklist['marital']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Employ<br>(경제 활동)</th>
<td>{{checklist['economy']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Healthy<br>(건강 상태)</th>
<td>{{checklist['health']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Limitation<br>(활동 제한)</th>
<td>{{checklist['limitation']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Modality<br>(2주간 이환)</th>
<td>{{checklist['modality']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">W Change<br>(체중 변화)</th>
<td>{{checklist['w_change']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">W Control<br>(체중 조절)</th>
<td>{{checklist['w_control']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">High BP<br>(고혈압)</th>
<td>{{checklist['high_bp']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Diabetes<br>(당뇨병)</th>
<td>{{checklist['diabetes']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">High lipid<br>(고지혈증)</th>
<td>{{checklist['high_lipid']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Drink freq<br>(음주 빈도)</th>
<td>{{checklist['drk_freq']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Drink amt<br>(1회 음주량)</th>
<td>{{checklist['drk_amount']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Smoking<br>(흡연 여부)</th>
<td>{{checklist['smoke']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Stress<br>(스트레스)</th>
<td>{{checklist['stress']}}</td>
</tr>
<tr class="table-dark">
<th scope="row">Age<br>(만나이)</th>
<td>만 {{checklist['age']}} 세</td>
</tr>
<tr class="table-dark">
<th scope="row">Height<br>(키)</th>
<td>{{checklist['height']}} cm</td>
</tr>
<tr class="table-dark">
<th scope="row">Weight<br>(체중)</th>
<td>{{checklist['weight']}} kg</td>
</tr>
</tbody>
</table>
<p style="text-align: center;">
<br>
잘못된 입력값이 있다면<br>
뒤로가기 통해 입력 값을 수정해주십시오.
<br><br>
<a href="javascript:window.history.back();">
<button type="button" class="btn btn-outline-primary btn-lg">
뒤로가기
</button>
</a>
</p>
<p style="text-align: center;">
입력값의 문제가 없는 경우<br>
아래 버튼 통해 예측을 진행합니다.
</p>
<form action="/result" method="post">
<input type="hidden" name="depr_percnt" value="{{percnt_depr}}">
<input type="hidden" name="depr_class" value="{{label_depr}}">
{% if percnt_mdd and label_mdd %}
<input type="hidden" name="mdd_percnt" value="{{percnt_mdd}}">
<input type="hidden" name="mdd_class" value="{{label_mdd}}">
{% endif %}
<div class="d-grid gap-2">
<button class="source-button btn btn-primary btn-lg" type="submit" name="predict_type" value="depr">
<img src="../static/imgs/head-heart-32.png">
Predict Depression
</button>
</div>
</form>
<br><br>
{% endif %}
# 예측 결과 출력 함수(POST)
@app.route('/result', methods=['POST'])
def result():
# POST request(submit으로부터 실행)
if request.method == 'POST':
# submit 버튼 값 가져오기
pred_mode = request.form['predict_type']
# submit 값이 "depr"인경우
if pred_mode == 'depr':
try:
# 변수 가져오기
depr_prob = request.form['depr_percnt']
depr_class = request.form['depr_class']
# "정상"인 경우 : 바로 결과페이지로 이동
if depr_class == "정상":
# templates/result.html 실행(200)
return render_template('result.html',
percnt_depr = depr_prob,
label_depr = depr_class), 200
# "우울증"인 경우 : 변수를 추가적으로 가져오고 결과페이지로 이동
elif depr_class == "우울증":
# 변수 가져오기
mdd_prob = request.form['mdd_percnt']
mdd_class = request.form['mdd_class']
# templates/result.html 실행(200)
return render_template('result.html',
percnt_depr = depr_prob,
label_depr = depr_class,
percnt_mdd = mdd_prob,
label_mdd = mdd_class), 200
# try문에서 에러가 발생한 경우 : templates/404-2.html 실행(404)
except:
return render_template('404-2.html'), 404
# submit 값이 "mdd"인경우
if pred_mode == 'mdd':
try:
# 변수 가져오기
add_depr_prob = request.form['add_depr_percnt']
add_depr_class = request.form['add_depr_class']
add_mdd_prob = request.form['add_mdd_percnt']
add_mdd_class = request.form['add_mdd_class']
# templates/result.html 실행(200)
return render_template('result.html',
add_percnt_depr = add_depr_prob,
add_label_depr = add_depr_class,
add_percnt_mdd = add_mdd_prob,
add_label_mdd = add_mdd_class), 200
# try문에서 에러가 발생한 경우 : templates/404-2.html 실행(404)
except:
return render_template('404-2.html'), 404
{% if percnt_depr %}
<div class="row">
<div class="col-md-3 col-lg-4"></div>
<div class="col-md-6 col-lg-4">
<br><br>
Depression
<h1 style="background-color: #343a40;">우울증<br>예측 결과</h1>
<br>
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#result-depr" aria-selected="true">
결과 (Result)
</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" data-bs-toggle="pill" href="#" aria-selected="false">/</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#model-depr" aria-selected="false">
모델정보
</a>
</li>
</ul>
<div class="tab-content">
<div id="result-depr" class="tab-pane fade active show">
<br>
{% if label_depr=="정상" %}
<h2>{{label_depr}}<br>(Normal)</h2>
{% elif label_depr=="우울증" %}
<h2>{{label_depr}}<br>(Depression)</h2>
{% endif %}
<br>
<p>우울증 진단 확률 값<br>{{percnt_depr}} %</p>
<p style="background-color: #343a40;">
우울증 예측을 실시한 결과,<br>진단 확률 값은 {{percnt_depr}} % 이며,
<br>
이는 "{{label_depr}}" 군에<br>해당하는 수치입니다.
<br>
<small class="text-muted">
확률 값 50% 이상 : 우울증으로 분류
</small>
</p>
</div>
<div id="model-depr" class="tab-pane fade">
<br>
<h3>
<img src="../static/imgs/head-heart-32.png">
우울증(Depression)<br>예측 모델
</h3>
<p>
Deep-Learning 1D-CNN<br>
<small class="text-muted">
(Convolutional Neural Networks)
<br>
Modeling by Tensorflow(Keras)
</small>
</p>
<h5>검증 정확도(Accuracy)</h5>
<p>84 % (0.8366)</p>
<h5>검증 ROC_AUC score</h5>
<p>83 % (0.8291)</p>
</div>
</div>
</div>
<div class="col-md-3 col-lg-4"></div>
</div>
{% endif %}
{% if label_depr=="우울증" %}
<div class="row">
<div class="col-md-3 col-lg-4"></div>
<div class="col-md-6 col-lg-4">
<br><br>
Major Depressive Disorder
<h1 style="background-color: #343a40;">주요우울장애<br>추가 예측</h1>
추가 예측을 원하시는 경우<br>아래 버튼을 클릭해주십시오<br><br>
<form action="/result" method="post">
<input type="hidden" name="add_depr_percnt" value="{{percnt_depr}}">
<input type="hidden" name="add_depr_class" value="{{label_depr}}">
<input type="hidden" name="add_mdd_percnt" value="{{percnt_mdd}}">
<input type="hidden" name="add_mdd_class" value="{{label_mdd}}">
<div class="d-grid gap-2">
<button class="source-button btn btn-primary btn-lg" type="submit" name="predict_type" value="mdd">
<img src="../static/imgs/head-heart-32.png">
Predict MDD
</button>
</div>
</form>
</div>
<div class="col-md-3 col-lg-4"></div>
</div>
{% endif %}
{% if add_percnt_depr %}
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-4">
<br><br>
Major Depressive Disorder
<h1 style="background-color: #343a40;">주요우울장애<br>추가예측 결과</h1>
<br>
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#add-result-mdd" aria-selected="true">
결과 (Result)
</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" data-bs-toggle="pill" href="#" aria-selected="false">/</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#add-model-mdd" aria-selected="false">
모델정보
</a>
</li>
</ul>
<div class="tab-content">
<div id="add-result-mdd" class="tab-pane fade active show">
<br>
{% if add_label_mdd == '경도우울증' %}
<h2>{{add_label_mdd}}<br>(Minor Depr.)</h2>
{% elif add_label_mdd == '주요우울장애' %}
<h2>{{add_label_mdd}}<br>(MDD)</h2>
{% endif %}
<br>
<p>주요우울장애 진단 확률 값<br>{{add_percnt_mdd}} %</p>
<p style="background-color: #343a40;">
MDD 예측을 실시한 결과,<br>진단 확률 값은 {{add_percnt_mdd}} % 이며,
<br>
이는 "{{add_label_mdd}}" 군에<br>해당하는 수치입니다.
<br>
<small class="text-muted">
확률 값 50% 이상 : MDD로 분류
</small>
</p>
</div>
<div id="add-model-mdd" class="tab-pane fade">
<br>
<h3>
<img src="../static/imgs/head-heart-32.png">
주요우울장애(MDD)<br>예측 모델
</h3>
<p>
Deep-Learning MLP<br>
<small class="text-muted">
(Multi-Layer Perceptron)
<br>
Modeling by Tensorflow(Keras)
</small>
</p>
<h5>검증 정확도(Accuracy)</h5>
<p>77 % (0.7722)</p>
<h5>검증 ROC_AUC score</h5>
<p>78 % (0.7838)</p>
</div>
</div>
</div>
<div class="col-md-4">
<br><br>
Depression
<h1 style="background-color: #343a40;">우울증<br>예측 결과</h1>
<br>
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#add-result-depr" aria-selected="true">
결과 (Result)
</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" data-bs-toggle="pill" href="#" aria-selected="false">/</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#add-model-depr" aria-selected="false">
모델정보
</a>
</li>
</ul>
<div class="tab-content">
<div id="add-result-depr" class="tab-pane fade active show">
<br>
<h2>{{add_label_depr}}<br>(Depression)</h2>
<br>
<p>우울증 진단 확률 값<br>{{add_percnt_depr}} %</p>
<p style="background-color: #343a40;">
우울증 예측을 실시한 결과,<br>진단 확률 값은 {{add_percnt_depr}} % 이며,
<br>
이는 "{{add_label_depr}}" 군에<br>해당하는 수치입니다.
<br>
<small class="text-muted">
확률 값 50% 이상 : 우울증으로 분류
</small>
</p>
</div>
<div id="add-model-depr" class="tab-pane fade">
<br>
<h3>
<img src="../static/imgs/head-heart-32.png">
우울증(Depression)<br>예측 모델
</h3>
<p>
Deep-Learning 1D-CNN<br>
<small class="text-muted">
(Convolutional Neural Networks)
<br>
Modeling by Tensorflow(Keras)
</small>
</p>
<h5>검증 정확도(Accuracy)</h5>
<p>84 % (0.8366)</p>
<h5>검증 ROC_AUC score</h5>
<p>83 % (0.8291)</p>
</div>
</div>
</div>
<div class="col-md-2"></div>
</div>
{% endif %}
Pyhton 3.8
, Flask
, gunicorn
, tensorflow-cpu 2.9
4가지 이외의 라이브러리를 설치할 경우엔 RAM 초과 현상이 발생하므로 주어진 4가지의 기능들로만 배포를 진행해야했음tensorflow-cpu
로 진행해야 했고, 2.10 버전 이상에서는 tensorflow-intel을 설치함으로인해 RAM 초과현상이 발생하므로 버전은 2.9로 제한했어야했음web: gunicorn --bind :8000 --workers 1 --threads 1 app:app
python-3.8.16
tensorflow-cpu==2.9.2
만 추가해줌으로써 설치 목록을 지정함certifi==2022.12.7
click==8.1.3
colorama==0.4.6
Flask==2.2.2
gunicorn==20.1.0
importlib-metadata==6.0.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.1
Werkzeug==2.2.2
wincertstore==0.2
zipp==3.11.0
tensorflow-cpu==2.9.2
(Part6) Conclusion 결론
프로젝트 핵심
느낀점 회고
평소에 정신질환 분야에 대한 딥러닝 프로젝트를 진행해보고 싶었고, 그 중에서도 가장 흔하게 널리 알려져 있는 우울증을 주제로써 선정하게 되었음
본 프로젝트의 목표는 DS, DA, DE 모두를 아우르는 Full-Stack 딥러닝 프로젝트로써, Codestates AI 부트캠프 과정에서 배운 모든 내용들을 활용하고 응용해보기 위해 노력하였음
모델링은 선형모델이나 트리모델과 같은 머신러닝 기법도 활용 할 수 있도록 이진분류 문제로 정의하여 진행하였고, 모델링 결과 모델들 모두 성능면에서 개인적으로 만족스러운 결과를 얻을 수 있었음
평가지표는 Accuracy만을 고려하려 했으나, 진단 분야에서는 Accuracy뿐만 아니라 F1 score나 ROC_AUC score도 함께 고려해야하기 때문에 평가지표를 추가하게 되었고, ROC_AUC score를 중심으로 모델링을 실시하게 되었음
모델 경량화를 실시하기 이전에는 속도면에서 아쉬움이 남았었고, 연산 오류도 자주 발생해 고민이 많았었음. 그러던 중에 tensorflow-lite로 모델을 경량화 시킬 수 있음을 알게 되어, 경량화를 실시하였고, 속도와 오류 문제 모두 해결할 수 있었음