티스토리 뷰

오늘은 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
공지사항
최근에 올라온 글
글 보관함