티스토리 뷰
오늘은 Streamlit 으로 모델 Inference 를 하는 방법을 배워보도록하겠습니다!
순서는 학습된 모델 생성 및 저장(Jupyter Notebook에서 진행) - Streamlit 코드 작성 (Python 인터프리터 환경에서 진행) 입니다.
#1. 학습 모델 생성 및 저장
필요한 라이브러리를 불러옵니다.
import pickle # joblib을 사용해도 됨.
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
데이터를 로드합니다. 저희는 Iris 데이터를 사용하도록 하겠습니다.
# 데이터 로드
data = load_iris()
df = pd.DataFrame(data.data, columns=['sepal length', 'sepal width', 'petal length', 'petal width'])
df['target'] = data.target
df = df[df['target']!=2] # 이진 분류를 위해 target이 2인 값을 삭제하고 0, 1만 남기겠습니다.
# X, y 생성
X = df.drop(columns=['target'])
y = df['target']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
모델 학습 및 저장.
# 모델 학습
model = RandomForestClassifier()
model.fit(X_train, y_train)
# 모델 저장 (pickle 사용)
with open('model.pkl', 'wb') as f:
pickle.dump(model, f)
pickle 을 이용해서 학습된 모델을 저장했어요. Joblib을 사용하셔도 됩니다.
두번 째 모델 학습 및 저장.
from sklearn.linear_model import LogisticRegression
# 모델 학습
clf = LogisticRegression(random_state=0).fit(X_train, y_train)
# 모델 저장
with open('model2.pkl', 'wb') as f:
pickle.dump(model, f)
자 이제 모델은 모두 준비가 되었습니다.
#2. Streamlit 에서 학습된 모델 사용
이제 Streamlit 을 구동시킬 수 있는 코드를 작성해봅시다.
아래 작업은 VScode, Pycharm 같은 환경에서 실행해주세요. (.py파일로 작성이 되어야합니다. )
import streamlit as st
from streamlit_shap import st_shap
import pickle
import pandas as pd
import numpy as np
import shap
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
st.set_page_config(layout="wide", initial_sidebar_state="auto", page_title="A03조", page_icon="⚗️")
st.title('붓꽃 분류 모델🌷')
# 사용자가 입력할 수 있는 양식 만들기
st.sidebar.title("예측하고자 하는 값을 입력해 주세요.")
sepal_length = st.sidebar.slider('Sepal Length(slider 사용)', min_value=4.0, max_value=8.0, value=5.8)
sepal_width = st.sidebar.slider('Sepal Width', min_value=2.0, max_value=5.0, value=3.1)
petal_length = st.sidebar.slider('Petal Length', min_value=1.0, max_value=7.0, value=3.8)
petal_width = st.sidebar.number_input('Petal Width(number_input사용)', min_value=0.0, max_value=3.0, value=1.2)
# 결과 출력
st.subheader('현재 입력된 값 확인')
input_data = np.array([[sepal_length, sepal_width, petal_length, petal_width]])
df = pd.DataFrame(input_data, columns=['sepal length', 'sepal width', 'petal length', 'petal width'])
st.dataframe(df)
st.subheader('예측 결과 확인')
species_dict = {0: 'setosa', 1: 'versicolor', 2: 'virginica'}
# 저장된 모델 불러오기 (Logistic Regression)
with open('model2.pkl', 'rb') as f2:
model2 = pickle.load(f2)
# 사용자가 입력한 값으로 예측
prediction2 = model2.predict(input_data)
# 예측값을 A, B, C로 변환
predicted_species2 = species_dict[prediction2[0]]
# 결과 출력
st.write(f"📌 Logistic Regression 모델 예측 결과: {predicted_species2}")
# 저장된 모델 불러오기 (RandomForestClassifier)
with open('model.pkl', 'rb') as f:
model = pickle.load(f)
# 사용자가 입력한 값으로 예측
prediction = model.predict(input_data)
# 예측값을 A, B, C로 변환
predicted_species = species_dict[prediction[0]]
# 결과 출력
st.write(f"📌 RandomForestClassifier 모델 예측 결과: {predicted_species}")
# SHAP 부분은 어렵다면 생략 가능.
shap.initjs()
df_expanded = pd.concat([df]*20, ignore_index=True) # tree 모델의 경우 데이터의 개수가 너무 적으면 작동이 되지 않아 같은 값을 20개 만듦.
explainer = shap.TreeExplainer(model)
# explanation_object = explainer(df_expanded)
shap_values = explainer.shap_values(df_expanded)
base_values = explainer.expected_value[prediction[0]] # 예측된 class의 base value 가져오기
st_shap(shap.plots.force(base_values, shap_values[-1][:,prediction[0]],
feature_names=['sepal length', 'sepal width', 'petal length', 'petal width']),
height=400, width=1000)
전체 코드는 위와 같습니다.
위 코드를 실행 시켜보면 (streamlit run main.py)
위와 같이 결과를 만들어 낼 수 있어요.
예측하고자하는 값을 입력하는 부분은 slider, number_input 둘 다 사용할 수 있다는 것을 나타내기 위해 위와 같이 작성이 되어 있습니다.
대략적인 값을 빠르게 넣고자할 때는 slider를, 정확한 값을 입력해야할 땐 number_input형식을 추천드립니다!
Default 값은 각 변수의 평균 값으로 설정해 두었어요. min-max 범위도 각 변수의 min-max 범위를 참고해서 작성했습니다.
분류 결과가 나오는 화면에서 두 개의 모델 결과를 모두 확인할 수 있게 Logistic Regression 확인 결과, Random Forest Classifier 확인 결과를 동시에 출력했습니다.
그리고 모델이 예측을 할 때 어떤 변수의 값의 영향을 많이 받았는지 보여주기 위해 Shap Force Plot 도 함께 나타내었습니다.
예측하고자하는 값을 바꿀 때마다 오른쪽 메인 부분도 같이 바뀌게 되어 있습니다.
그럼, 코드를 한 줄 한 줄 알아볼까요?
.
.
.
st_shap(shap.plots.force(base_values, shap_values[-1][:,prediction[0]],
feature_names=['sepal length', 'sepal width', 'petal length', 'petal width']),
height=400, width=1000)
사실 저 위 코드만 어려움.
base_value는 feature contributions 계산이 시작되는 참조 값.
- base_value = explainer.expected_value[prediction[0]] 를 통해 미리 구해주었음.
- 왜냐면 explainer.expected_value 를 해버리면 예측된 Class가 0(setosa) 일 때, 1(versicolor) 일 때의 base_value를 모두 출력해줌. 우리는 예측된 Class의 base만 필요.
shap_values는 shap 값의 matrix.
- shap_values[-1][:,prediction[0]]
- [-1]: 똑같은 Input 값을 20개로 늘렸기 때문에 제일 아래에 있는 값 하나만 가져옴.
- [:,prediction[0]]: ":"를 통해 모든 행의 값을 가져옴, prediction[0]을 통해 예측된 class의 값만 가져옴.
- 이건 한 줄씩 출력해보면 이해됨. 한 줄씩 출력해보는 것은 jupyter notebook환경에서 하는 것이 더 편함.
'시각화' 카테고리의 다른 글
Streamlit 알아보기 (2) (0) | 2024.05.25 |
---|---|
Streamlit 알아보기 (1) (0) | 2024.05.25 |
mdates를 이용해 Matplotlib의 DateTime 눈금 빈도를 변경하는 방법 (0) | 2023.05.02 |
plotly (0) | 2023.01.04 |
matplot 기초 (1) | 2022.11.26 |
- Total
- Today
- Yesterday