LLM 엔지니어링14장 · Streamlit 화면과 Docker 컨테이너14 / 22← 이전목차다음 →MetaContents

14장. Streamlit 화면과 Docker 컨테이너

실습 1 — 예측 화면 만들기

13장 실습 1이 저장한 배송 지연 모델(out_delay_model.joblib)을 쓰는 Streamlit 화면을 out_app_predict.py 로 만듭니다. Streamlit 의 AppTest 로 브라우저 없이 화면을 실행해, 칸을 채우고 단추를 눌러 결과를 읽습니다. streamlit 이 설치되어 있어야 하고, 13장 실습 1을 먼저 실행해야 합니다.

1단계. 화면 파일 쓰기

Python
from pathlib import Path

Path("out_app_predict.py").write_text('''"""배송 지연 예측 화면. 실행: streamlit run out_app_predict.py"""
import joblib
import pandas as pd
import streamlit as st


@st.cache_resource                       # 모델은 한 번만 불러 모든 사용자가 함께 쓴다
def load_bundle():
    return joblib.load("out_delay_model.joblib")


bundle = load_bundle()
choices = bundle["범주값"]
st.title("배송 지연 예측")
st.caption(f"모델 판 {bundle['판']} · 시험 AUC {bundle['시험성능']['AUC']}")

with st.form("order"):                   # 폼: 칸을 다 채우고 제출을 눌러야 한 번에 다시 실행된다
    left, right = st.columns(2)
    with left:
        지역 = st.selectbox("지역", choices["지역"], key="지역")
        상품분류 = st.selectbox("상품분류", choices["상품분류"], key="상품분류")
        택배사 = st.selectbox("택배사", choices["택배사"], key="택배사")
        날씨 = st.selectbox("날씨", choices["날씨"], key="날씨")
    with right:
        주문시각 = st.slider("주문시각", 0, 23, 14, key="주문시각")
        거리km = st.number_input("거리km", 0.5, 1000.0, 20.0, key="거리km")
        무게kg = st.number_input("무게kg", 0.0, 100.0, 2.0, key="무게kg")
    sent = st.form_submit_button("예측하기")

if sent:
    row = pd.DataFrame([{"주문시각": 주문시각, "거리km": 거리km, "무게kg": 무게kg, "지역": 지역,
                         "상품분류": 상품분류, "날씨": 날씨, "택배사": 택배사}])
    p = float(bundle["파이프라인"].predict_proba(row)[0, 1])
    st.metric("지연 확률", f"{p:.1%}")
    if p >= bundle["기준값"]:
        st.warning("지연될 수 있습니다. 고객에게 미리 알려 주세요.")
    else:
        st.success("제때 도착할 것으로 보입니다.")
''', encoding="utf-8")
print("화면 파일:", Path("out_app_predict.py").stat().st_size, "바이트")
출력
화면 파일: 1854 바이트

2단계. 브라우저 없이 화면 실행하기

Python
from streamlit.testing.v1 import AppTest

# AppTest: 화면 스크립트를 이 프로그램 안에서 실행하고, 그려진 요소를 꺼내 볼 수 있게 한다
at = AppTest.from_file(str(Path("out_app_predict.py").resolve()), default_timeout=60).run()
print("제목:", at.title[0].value, "·", at.caption[0].value)
print("선택 상자:", [(s.label, s.value, len(s.options)) for s in at.selectbox])
print("단추:", [b.label for b in at.button], "· 지연 확률 칸:", len(at.metric))     # 제출 전이라 결과가 없다
출력
제목: 배송 지연 예측 · 모델 판 delay-lr-1 · 시험 AUC 0.695
선택 상자: [('지역', '경기', 4), ('상품분류', '가전', 5), ('택배사', '빠른별택배', 3), ('날씨', '눈', 5)]
단추: ['예측하기'] · 지연 확률 칸: 0

3단계. 칸을 채우고 제출하기

Python
# 밤 10시, 지방, 눈, 도서 주문(13차시와 같은 주문)
at.selectbox(key="지역").set_value("지방")
at.selectbox(key="상품분류").set_value("도서")
at.selectbox(key="택배사").set_value("빠른별택배")
at.selectbox(key="날씨").set_value("눈")
at.slider(key="주문시각").set_value(22)
at.number_input(key="거리km").set_value(197.6)
at.number_input(key="무게kg").set_value(4.6)
at.button[0].click().run()                      # 제출 단추를 누르면 스크립트가 다시 실행된다
print(at.metric[0].label, at.metric[0].value, "·", [w.value for w in at.warning], [s.value for s in at.success])
출력
지연 확률 68.9% · ['지연될 수 있습니다. 고객에게 미리 알려 주세요.'] []

4단계. 다른 주문으로 다시 제출하기

Python
at.selectbox(key="지역").set_value("서울")
at.selectbox(key="날씨").set_value("맑음")
at.selectbox(key="택배사").set_value("한결택배")
at.slider(key="주문시각").set_value(9)
at.number_input(key="거리km").set_value(7.3)
at.button[0].click().run()
print(at.metric[0].label, at.metric[0].value, "·", [w.value for w in at.warning], [s.value for s in at.success])
print("오류:", [e.value for e in at.exception])
출력
지연 확률 8.3% · [] ['제때 도착할 것으로 보입니다.']
오류: []

이 실습이 뜻하는 것

이 실습은 택배 접수 창구에 주문을 넣는 기계를 들이는 일과 같습니다. 먼저 기계 화면의 설계도를 써 두고(1단계), 손님 없이 기계를 켜서 칸과 단추가 제대로 나오는지 봅니다(2단계). 그다음 시험 손님이 칸을 채우고 "예측하기"를 누르고(3단계), 다른 주문으로 한 번 더 누릅니다(4단계). 여기서 창구 기계가 Streamlit 화면, 설계도가 out_app_predict.py, 시험 손님이 AppTest, 기계 안에서 판단하는 담당이 13장의 배송 지연 모델입니다. 비유와 다른 점은, 진짜 손님(브라우저)은 아직 오지 않았고 화면을 같은 파이썬 프로그램 안에서만 실행했다는 것입니다.

단계출력뜻
1. 화면 파일1854 바이트화면 설계도가 파이썬 파일로 저장되었습니다. 아직 실행하지 않고 글로만 써 둔 단계입니다
2. 제목모델 판 delay-lr-1 · 시험 AUC 0.695화면이 모델 파일을 읽어 모델의 이름표와 시험 성적을 보였습니다. AUC(Area Under the Curve, 곡선 아래 넓이)는 0.5 가 찍기 수준이고 1 에 가까울수록 늦는 주문과 제때 오는 주문을 잘 가른다는 점수입니다
2. 선택 상자지역 4, 상품분류 5, 택배사 3, 날씨 5상자마다 고를 수 있는 보기의 수입니다. 보기는 모델 파일에 저장해 둔 범주값에서 읽어 옵니다. 첫 값은 보기의 첫째인 "경기"입니다
2. 단추['예측하기'], 지연 확률 칸 0아직 아무도 누르지 않아 결과 칸이 없습니다. 화면은 제출한 뒤에야 결과를 그립니다
3. 제출지연 확률 68.9%, 경고 문구밤 10시, 지방, 눈 오는 날의 도서 주문은 열에 일곱쯤 늦는다고 봤고, 기준값을 넘어 노란 경고 상자를 띄웠습니다
4. 다시 제출지연 확률 8.3%, 성공 문구서울의 아침 주문은 열에 하나도 안 되게 늦는다고 봤고, 초록 성공 상자가 나왔습니다
4. 오류오류: []칸을 바꿔 두 번 제출하는 동안 파이썬 오류가 하나도 없었습니다

한 줄로 줄이면, 13장의 모델을 사람이 칸을 채워 쓰는 화면으로 감싸고, 브라우저 없이 끝까지 눌러 본 실습입니다. 이 화면은 모델 파일을 직접 들고 있습니다. 실습 3에서는 모델을 API(Application Programming Interface, 프로그램끼리 주고받는 창구) 뒤로 옮겨도 같은 주문에 같은 68.9% 가 나오는지 봅니다. 68.9% 는 모델의 추정일 뿐 실제로 늦는다는 보장이 아니고, 데이터도 연습용으로 지어낸 것입니다.

코드 설명

  • 화면 파일은 위에서 아래로 읽히는 보통 스크립트입니다. @st.cache_resource 를 붙인 load_bundle() 은 처음 한 번만 모델을 불러오고, 다시 실행될 때는 저장해 둔 모델을 돌려줍니다. 선택 상자의 보기는 모델 파일에 저장해 둔 범주값(13장 실습 1)에서 읽습니다.
  • AppTest.from_file(...).run() 은 화면 스크립트를 한 번 실행하고, 그려진 요소를 at.title, at.selectbox, at.metric 처럼 종류별 목록으로 돌려줍니다. 처음 실행에서는 선택 상자 넷(지역 보기 4개, 상품분류 5개, 택배사 3개, 날씨 5개)과 "예측하기" 단추가 그려졌고, 아직 제출하지 않았으므로 지연 확률 칸은 0개입니다. 상자의 첫 값은 보기의 첫째인 "경기"입니다.
  • 3단계에서 set_value() 로 칸을 채우고 단추의 click().run() 으로 제출하자, 13장와 같은 주문의 지연 확률 68.9% 와 노란 경고 상자가 그려졌습니다. 4단계의 서울 아침 주문은 8.3% 와 초록 성공 상자입니다. 같은 at 으로 칸만 바꿔 다시 제출할 수 있고, 오류는 없었습니다.
  • st.form 안의 위젯은 값을 바꿔도 바로 다시 실행되지 않고, st.form_submit_button 을 누를 때 한 번에 다시 실행됩니다. 칸이 여럿인 입력에서 쓸데없는 다시 실행을 줄입니다.
← 이전 절14장 · 실습 지도다음 절 →14장 · 실습 2 — 채팅 화면과 대화 기억