머신러닝 모델을 이용하여 웹 앱 만들기
간단한 데이터 정제 및 전처리
넘파이와 판다스 라이브러리를 불러오고, ufo 데이터셋을 불러온다.
처음 다운로드 된 데이터셋은 빈 칸에 null 값이 설정되어 있지 않다.
따라서 파일을 직접 수정하여 빈 칸을 null 값으로 대체하였다.
import numpy as np
import pandas as pd
ufos = pd.read_csv("C:/Users/dst78/OneDrive/바탕 화면/개인/ufos.csv")
ufos.head()
| datetime | city | state | country | shape | duration (seconds) | duration (hours/min) | comments | date posted | latitude | longitude | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 10/10/1949 20:30 | san marcos | tx | us | cylinder | 2700.0 | 45 minutes | This event took place in early fall around 194... | 4/27/04 | 29.883056 | -97.941111 |
| 1 | 10/10/1949 21:00 | lackland afb | tx | NaN | light | 7200.0 | 1-2 hrs | 1949 Lackland AFB, TX. Lights racing acros... | 12/16/05 | 29.384210 | -98.581082 |
| 2 | 10/10/1955 17:00 | chester (uk/england) | NaN | gb | circle | 20.0 | 20 seconds | Green/Orange circular disc over Chester, En... | 1/21/08 | 53.200000 | -2.916667 |
| 3 | 10/10/1956 21:00 | edna | tx | us | circle | 20.0 | 1/2 hour | My older brother and twin sister were leaving ... | 1/17/04 | 28.978333 | -96.645833 |
| 4 | 10/10/1960 20:00 | kaneohe | hi | us | light | 900.0 | 15 minutes | AS a Marine 1st Lt. flying an FJ4B fighter/att... | 1/22/04 | 21.418056 | -157.803611 |
데이터셋에서 가독성이 좋지 않은 특성들을 가독성이 좋게 변환하고, 주요 특성들만을 추출하여 데이터셋을 만든다.
ufos = pd.DataFrame({'Seconds': ufos['duration (seconds)'], 'Country': ufos['country'],'Latitude': ufos['latitude'],'Longitude': ufos['longitude']})
ufos.Country.unique()
array(['us', nan, 'gb', 'ca', 'au', 'de'], dtype=object)
또한 데이터셋의 크기를 줄이기 위해 ‘Second’ 특성의 1초 ~ 60초 사이의 값을 가지는 특성들만을 추출
ufos.dropna(inplace=True)
ufos = ufos[(ufos['Seconds'] >= 1) & (ufos['Seconds'] <= 60)]
ufos.info()
<class 'pandas.core.frame.DataFrame'> Index: 25863 entries, 2 to 80330 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Seconds 25863 non-null float64 1 Country 25863 non-null object 2 Latitude 25863 non-null float64 3 Longitude 25863 non-null float64 dtypes: float64(3), object(1) memory usage: 1010.3+ KB
Country 특성은 범주형 특성이기 때문에 LabelEncoder를 이용하여 국가 코드로 변환
from sklearn.preprocessing import LabelEncoder
ufos['Country'] = LabelEncoder().fit_transform(ufos['Country'])
ufos.head()
| Seconds | Country | Latitude | Longitude | |
|---|---|---|---|---|
| 2 | 20.0 | 3 | 53.200000 | -2.916667 |
| 3 | 20.0 | 4 | 28.978333 | -96.645833 |
| 14 | 30.0 | 4 | 35.823889 | -80.253611 |
| 23 | 60.0 | 4 | 45.582778 | -122.352222 |
| 24 | 3.0 | 3 | 51.783333 | -0.783333 |
모델 훈련
이제 모델 훈련을 하기 위해 데이터셋을 훈련셋과 테스트셋으로 나눈다.
데이터셋의 레이블은 Country이다.
from sklearn.model_selection import train_test_split
selected_features = ["Seconds", "Latitude", "Longitude"]
X = ufos[selected_features]
y = ufos["Country"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
모델 훈련은 로지스틱 회귀 모델을 이용하여 훈련한다.
from sklearn.metrics import accuracy_score, classification_report
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(X_train, y_train)
predictions = model.predict(X_test)
print(classification_report(y_test, predictions))
print('Predicted labels: ', predictions)
print('Accuracy: ', accuracy_score(y_test, predictions))
precision recall f1-score support
0 1.00 1.00 1.00 41
1 0.81 0.21 0.33 250
2 1.00 1.00 1.00 8
3 1.00 1.00 1.00 131
4 0.96 1.00 0.98 4743
accuracy 0.96 5173
macro avg 0.95 0.84 0.86 5173
weighted avg 0.95 0.96 0.95 5173
Predicted labels: [4 4 4 ... 3 4 4]
Accuracy: 0.959404600811908
c:\Users\dst78\AppData\Local\Programs\Python\Python310\lib\site-packages\sklearn\linear_model\_logistic.py:458: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
정확도가 대략 96%에 육박하는 결과를 얻는다.
해당 데이터셋의 훈련으로 웹 앱을 구현할 것이다.
Pickle
훈련된 모델을 pickle을 이용하여 second, 위도, 경도 갑이 포함된 데이터셋(X)을 대상으로 테스트한다.
import pickle
model_filename = 'ufo-model.pkl'
pickle.dump(model, open(model_filename,'wb'))
model = pickle.load(open('ufo-model.pkl','rb'))
print(model.predict([[50,44,-12]]))
[1]
c:\Users\dst78\AppData\Local\Programs\Python\Python310\lib\site-packages\sklearn\base.py:439: UserWarning: X does not have valid feature names, but LogisticRegression was fitted with feature names warnings.warn(
Flask
이제 훈련된 모델을 기반으로 웹을 작동하도록 하는 앱을 만든다.
가장 먼저 이전까지 작성한 코드들을 ipynb 파일로 다운로드 해야한다.
다운로드 후 실행환경에서 실행하면, ufo-model.pkl 파일도 같이 생성된다.
이제 기본적으로 다운로드 및 피클파일이 만들어 졌다.
웹을 만들기 위해 다음과 같이 web-app 폴더를 생성한다.
***
이제 web-app 폴더 안에 또 다른 여러 폴더들과 파일을 만들어 주어야한다.
static 폴더와 templates 폴더를 wep-app 바로 아래에 생성해준다.
또한, web-app 폴더 안에 app.py 파일과 requirements.txt 파일을 만들어준다.
app.py 파일은 웹을 실행하는데 필요한 코드를 삽입하고,
requirements.txt 파일 안에는 다운로드 받을 라이브러리를 적어준다.
app.py
해당 파일에서 밑줄 친 부분은 자신이 지정한 경로마다 다르다.
***
requirements.txt
***
이제 static 폴더 안에 css 폴더를 또 생성해 주고 그 css 폴더 안에 styles.css 파일을 만든다.
templates 폴더 안에는 index.html 파일을 만들어준다.
styles.css 파일은 만든 웹의 글꼴, 글자 크기 등의 스타일을 지정할 수 있고,
index.html 파일은 html언어로 웹을 만든다.
styles.css
***
index.html
필자는 가독성이 편하도록 보이는 텍스트 몇개를 한국어로 변환하였다.
***
폴더를 제외한 파일은 txt 파일로 먼저 작성 후 확장자를 변경해 주면 된다.
모든 폴더 및 파일을 작성했다면 터미널로 이동한다.
터미널로 이동한 다음 wep-app 폴더로 접근하기 위해 cd 명령어를 이용한다.
여기서 경로는 자신이 wep-app을 만든 경로마다 다르다.
cd C:\Users\dst78\Downloads\wep-app
아까 requirements.txt 파일에 작성되어 있는 라이브러리를 설치한다.
pip install -r requirements.txt
모든 설치가 끝나면 이제 웹을 작동할 모든 준비가 끝났다.
이제 터미널에 다음 명령어를 실행하여 웹을 실행해보자.
python app.py
위의 코드 실행 후 되지 않는다면 밑의 코드를 실행한다.
python3 app.py
성공한다면 다음과 같은 화면이 뜰 것이다.
***
서버 연결이 완료되었으므로 이제 사진의 밑줄 친 부분을 url 주소 검색창에 넣는다.

이상 없이 초, 위도, 경도를 넣으면 나라를 예측해준다.
만약 서버를 끊고 싶다면 터미널에서 ctrl + c 를 누르면 된다.