[DE 프로젝트: 코로나 확진자 수 예측 앱 'CO-THER 19'] 3. 플라스크(Flask)와 헤로쿠(Heroku)

피클(Pickle) 테스트

  • 이전 포스팅에서 모델을 피클로 저장했다.
  • 간단하게 파이썬 파일에서 테스트 해본다.
1
2
3
4
5
6
7
8
9
10
11
import pickle
import numpy as np
 
model = pickle.load(open("lgb.pkl", "rb"))

arr = np.array([[57.3,0.0,10.5,1018.2,2.3,4.8,8.5,18.4,2.5,0.0,13.3]])
pred = model.predict(arr)
print(pred)
'''
225
'''
  • 다행히 잘 전시된다.

플라스크(Flask)

  • 플라스크를 활용하여 웹 서비스를 구현한다.

구성

  • 플라스크에서는 구성이 중요하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
cother19
├── app.py
├── static
│   ├── css
│   ├── img
│   └── js
├── templates
│   ├── index.html
│   └── 404.html
├── data
│   └── pkl
├── Procfile
└── requirements.txt

머신러닝 모델 서빙

  • 예측에 필요한 변인을 넣는 방식을 아래와 같이 구현했다.

app.py

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
from flask import Flask, render_template, request
import numpy as np
import pickle

app = Flask(__name__)
model = pickle.load(open("data/pkl/lgb.pkl", "rb"))

@app.errorhandler(404)
def page_not_found(error):
	return render_template('404.html'), 404

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        return render_template('index.html')

    if request.method == 'POST':
        try:
            data1 = float(request.form['avgRhm'])
            data2 = float(request.form['ddMes'])
            data3 = float(request.form['sumSsHr'])
            data4 = float(request.form['avgPs'])
            data5 = float(request.form['avgTca'])
            data6 = float(request.form['avgTd'])
            data7 = float(request.form['minTa'])
            data8 = float(request.form['maxTa'])
            data9 = float(request.form['avgWs'])
            data10 = float(request.form['sumRn'])
            data11 = float(request.form['avgTa'])

            arr = np.array([[data1, data2, data3, data4, data5, data6, data7, data8, data9, data10, data11]])
            pred = model.predict(arr)
            pred = round(pred[0])
            return render_template("index.html", pred=pred)
        except:
            return render_template("404-2.html")

if __name__ == "__main__":
    app.run(debug=True)

html

  • 인풋 박스와 버튼을 구현한다.
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
<section>
    <div>
        <form action="/" method="post">
        <div class="information-name">
            <input type="text" name="avgTa" required>
            <label for="formGroupExampleInput">평균기온</label>
        </div>
        <div class="information-name">
            <input type="text" name="minTa" required>
            <label for="formGroupExampleInput">최저기온</label>
        </div>
        <div class="information-name">
            <input type="text" name="maxTa" required>
            <label for="formGroupExampleInput">최고기온</label>
        </div>
        <div class="information-name">
            <input type="text" name="avgRhm" required>
            <label for="formGroupExampleInput">습도</label>
        </div>
        <div class="information-name">
            <input type="text" name="avgWs" required>
            <label for="formGroupExampleInput">풍속</label>
        </div>
        <div class="information-name">
            <input type="text" name="sumRn" required>
            <label for="formGroupExampleInput">강수량</label>
        </div>
        <div class="information-name">
            <input type="text" name="ddMes" required>
            <label for="formGroupExampleInput">적설량</label>
        </div>
        <div class="information-name">
            <input type="text" name="avgTca" required>
            <label for="formGroupExampleInput">전운량</label>
        </div>
        <div class="information-name">
            <input type="text" name="sumSsHr" required>
            <label for="formGroupExampleInput">일조시간</label>
        </div>
        <div class="information-name">
            <input type="text" name="avgPs" required>
            <label for="formGroupExampleInput">해면기압</label>
        </div>
        <div class="information-name">
            <input type="text" name="avgTd" required>
            <label for="formGroupExampleInput">이슬점온도</label>
        </div>
        <button type="submit">Predict</button>
        </form>
    </div>
</section>
  • 입력 후 API가 POST로 연결될 시, pred 변수가 전시되게 구현한다.
1
2
3
{% if pred %}
<h1>기상에 대한 서울시<br>예상 확진자 수: {{ pred }}명</h1>
{% endif %}

플라스크 디버깅 구동

1
2
3
4
5
$ export FLASK_ENV=development

$ export FLASK_APP=app.py

$ flask run

확인

image

image

  • 위 이미지와 같이 잘 전달되는 것을 확인할 수 있다.

헤로쿠

앱 생성

image

깃허브 연결

  • Enable Automatic Deploys을 통헤 깃허브와 연결한다.
  • 헤로쿠와 깃허브가 연결되면서 깃허브에 커밋하여도 자동으로 헤로쿠 배포가 이루어진다는 장점이 있다.

image

플라스크

Procfile

1
web: gunicorn app:app

requirements.txt

  • 현재 가상 환경의 pip list를 저장한다.
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
$ pip freeze > requirements.txt

certifi==2021.5.30
click==8.0.1
Flask==2.0.2
gunicorn==20.1.0
itsdangerous==2.0.1
certifi==2021.5.30
charset-normalizer==2.0.6
click==8.0.1
Flask==2.0.2
gunicorn==20.1.0
idna==3.2
itsdangerous==2.0.1
Jinja2==3.0.2
joblib==1.0.1
lightgbm==3.2.0
MarkupSafe==2.0.1
numpy==1.21.2
pandas==1.3.3
python-dateutil==2.8.2
pytz==2021.3
requests==2.26.0
scikit-learn==1.0
scipy==1.7.1
six==1.16.0
sklearn==0.0
threadpoolctl==3.0.0
urllib3==1.26.7
Werkzeug==2.0.2

구성

1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
cother19
├── app.py
├── static
│   ├── css
│   ├── img
│   └── js
├── templates
│   ├── index.html
│   └── 404.html
├── data
│   └── pkl
├── Procfile
└── requirements.txt

깃허브

  • 커밋 시 자동적으로 배포된다.
1
2
3
$ git add .
$ git commit -m 'final'
$ git push

확인

image

  • 최종적으로 웹사이트를 꾸며서 배포에 성공했다.
0%