Search
Duplicate

3.스트림릿concept

0.최종목표

[ 아이리스 품종 예측기 ]

1.핵심 concept

1.
스트림릿 (Streamlit) 프레임워크는 UI 요소와 실시간 연동되는 파이썬 기반의 컴포넌트 객체를 통해 사용자와 상호 작용
2.
스트림릿은 먼저 파이썬 스크립트가 최초 실행될 때 화면의 레이아웃과 구성 요소를 배치
파이썬 스크립트란? 파이썬으로 작성된 코드
.py 확장자를 가진 실행 가능한 텍스트 파일

2.반응형 프로그래밍

1.
반응형 프로그램 특징: UI 요소에 일어나는 변화를 효과적으로 감지하고 처리하기 위해 애플리케이션의 데이터 상태와 UI 요소의 상태를 동기화하여 관리
2.
원리: 스트림릿 환경에서 수행되는 파이썬 스크립트는 스트림릿에서 관리하는 프론트엔드 엔진으로 전달되어 화면상의 컴포넌트로 출력됨
[ 스트림릿 프레임워크에서 파이썬 스크립트와 UI 요소가 연동하는 과정을 나타내는 다이어그램 ]
1.
스트림릿의 프론트엔드 엔진은 SPA(Single Page Application)로 동작하기 때문에 최초 출력 이후에는 웹 페이지 전체를 리로드하지 않고, 서버로부터 받은 데이터나 정보를 바탕으로 화면을 업데이트 하는 방식
2.
주목할 부분: 사용자가 화면상의 컴포넌트에 인터랙션을 가하면, 스트림릿 프레임워크는 애플리케이션의 상태를 최신화하기 위해 파이썬 스크립트 전체를 다시 실행한다는 점
import streamlit as st print("start...") text = "지정된 텍스트를 제목, 부제목, 글, 인풋 등으로" st.header(text, divider='rainbow') st.subheader(text) st.title(text) st.write(text) st.text_input(label="Title", placeholder=text) st.write("# Bar Chart") vocab_logits = { "나는": 0.01, "내일": 0.03, "오늘": 0.25, "어제": 0.3, "산에": 0.4, "학교에": 0.5, "집에": 0.65, "오른다": 1.2, "간다": 1.05, "왔다": 0.95} st.bar_chart(vocab_logits) # 중앙 정렬 캡션 st.markdown( "<p style='text-align: center; color: gray;'>막대차트</p>", unsafe_allow_html=True) print("end...")
Python
복사

2-1.반응확인

화면의 input 상자에 텍스트를 입력한 후 탭 등을 클릭하여 포커스를 벗어나면 콘솔 창에
’’ start… end… ’’
이 출력되는 것을 볼 수 있음

3.파이썬의 데코레이터

[양파고의 한마디] 파이썬 함수 def를 튜닝할때, 또는 확장할 때
1.
정의
함수를 인자로 받아, 기능을 확장한 후 다시 함수로 반환하는 고차 함수
1.
형태
@데코레이터_이름 형태로 함수 위에 붙여 사용
1.
기본 구조
기존 함수를 감싸는 래퍼(wrapper) 함수를 정의하는 방식
# 데코레이터의 기본구조 # 데코레이터라는 '고차함수'만들기 def my_decorator(func): # 1. 함수(func)를 인자로 받음 def wrapper(): # 2. 내부 래퍼 함수 정의 print("함수 실행 전") func() # 3. 원래 함수 실행 print("함수 실행 후") return wrapper # 4. 감싼 함수 반환 # 타입 확인 type(my_decorator)
Python
복사
function
Plain Text
복사
# 이렇게 데코레이터 단독으로는 실행불가 @my_decorator # 아래 함수는 이제 wrapper()로 감싸진 상태
Python
복사
Cell In[6], line 2 @my_decorator # 아래 함수는 이제 wrapper()로 감싸진 상태 ^ SyntaxError: incomplete input
Plain Text
복사
@my_decorator # 아래 함수는 이제 wrapper()로 감싸진 상태 def say_hello(): print("안녕하세요!") say_hello()
Python
복사
함수 실행 전 안녕하세요! 함수 실행 후
Plain Text
복사

3-1.데코레이터의 주요역할

1.
로깅(logging)
함수가 호출될 때 로그 기록
2.
성능 측정
함수 실행 시간 측정
3.
인증 처리
사용자 권한 확인 후 실행 허용
4.
캐싱(cache)
동일 입력에 대해 결과 저장 및 재사용
5.
예외 처리
함수 내부에서 발생하는 예외를 외부에서 핸들링
[왜 쓰는가?]
함수 실행 전/후에 추가 동작을 삽입, 또는 실행 조건을 제어할 수 있음
함수 내부를 바꾸지 않아도, 외부에서 기능을 덧붙일 수 있음 (→ 유지보수, 재사용성 향상)
# 실행시간 측정 데코레이터 import time # 타이머 측정 데코레이터코레이터 def timer(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} 실행 시간: {end - start:.4f}초") return result return wrapper @timerdef slow_function(): time.sleep(2) print("완료!") slow_function()
Python
복사
완료! slow_function 실행 시간: 2.0014초
Plain Text
복사

3-2.스트림릿에서 캐시 데코레이터

[2_cash.py]
캐시 데코레이터 사용예시
import streamlit as st import time @st.cache_datadef load_data(): time.sleep(3) # API나 DB에서 데이터를 가져온다고 가정 return {"a": 1, "b": 2} st.write("데이터:", load_data())
Python
복사
# lru_cache 캐시 기능 맛보기 # 함수의 반환 값을 자동으로 캐싱(Memoization) 해주는 내장 데코레이터 # 동일한 인자를 가진 함수가 반복 호출될 때 중복 계산을 피하고 빠르게 결과를 반환할 수 있도록 도와줌 import time from functools import lru_cache # 캐시 기능을 위한 데코레이터: maxsize=None이면 무제한 캐싱 @lru_cache(maxsize=None) def load_data(): print("데이터를 로딩 중입니다... (3초 대기)") time.sleep(3) # API나 DB에서 데이터를 가져오는 시뮬레이션 return {"a": 1, "b": 2} # 첫 번째 실행 (3초 소요) print("첫 번째 실행:") data = load_data() print("데이터:", data) # 두 번째 실행 (즉시 반환됨) print("\n두 번째 실행:") data = load_data() print("데이터:", data)
Python
복사
첫 번째 실행: 데이터를 로딩 중입니다... (3초 대기) 데이터: {'a': 1, 'b': 2} 두 번째 실행: 데이터: {'a': 1, 'b': 2}
Plain Text
복사

3-3.스트림릿 캐시데코레이터 미사용시

import streamlit as st import time def get_vocab_logits(): print(f"get_vocab_logits() starting") time.sleep(10) # 실제 상황에서는 LLM 추론에 해당 (10초 지연) vocab_logits = {"나는": 0.01,"내일": 0.03,"오늘": 0.25,"어제": 0.3, "산에": 0.4,"학교에": 0.5,"집에": 0.65, 오른다": 1.2,"간다": 1.05,"왔다": 0.95} print(f"get_vocab_logits() ending") return vocab_logits text = "마지막 레이어의 로짓값을 가정"st.header(text, divider='rainbow') st.subheader(text) st.title(text) st.write(text) st.text_input(label="Title", placeholder=text) st.write("# Bar Chart") st.bar_chart(get_vocab_logits()) st.caption(text)
Python
복사

코드 해석

이 코드는 Streamlit을 이용해 LLM의 마지막 레이어 로짓(logits)을 시각화하는 흐름을 보여주는 스크립트
[전체 흐름 요약]
1.
제목 및 UI 텍스트를 Streamlit으로 렌더링
2.
get_vocab_logits() 함수를 통해 로짓 데이터 생성 (10초 지연)
3.
해당 데이터를 bar_chart()를 통해 시각화
4.
캡션 표시
[실행결과 분석]
1.
input 상자에 글자를 입력하고 탭을 클릭하면, 약 10초 동안 위와 같이 그래프가 흐리게 나타나면서 지연 현상이 발생함
2.
이런 현상이 나타나는 이유는, 사용자의 상호작용에 의해 화면에 변화가 생기면, 이를 반영하기 위해 파이썬 스크립트 전체가 다시 실행되어야 하고, 그 과정에서 로짓 산출을 위해 10초 간의 대기 시간이 발생하기 때문
3.
이러한 불합리 점을 개선하기 위해 스트림릿에서는 캐싱 데코레이터 매커니즘을 제공
4.
@st.cache_data 데코레이터를 사용하면 함수를 실행한 결과가 캐시에 보관됨 이후부터는, 파라미터가 달라졌거나 구현 내용이 달라진 경우에 한해서만 함수를 다시 실행
5.
만일 함수의 코드가 변경되지 않았고, 이전에 호출된 파라미터와 동일하다면, 캐시에 보관되어 있는 결과를 반환함으로써 불필요한 재실행이 되지 않음

3-4. 스트림릿 캐시데코레이터 사용시

# st_cache_data.py import streamlit as st import time @st.cache_datadef get_vocab_logits(param=0): print(f"get_vocab_logits({param}) starting") time.sleep(10) vocab_logits = {"나는": 0.01,"내일": 0.03,"오늘": 0.25,"어제": 0.3,"산에": 0.4,"학교에": 0.5,"집에": 0.65, "오른다": 1.2,"간다": 1.05,"왔다": 0.95} vocab_logits = {word: logit + param for word, logit in vocab_logits.items()} print(f"get_vocab_logits({param}) ending") return vocab_logits text = "마지막 레이어의 로짓값을 가정"st.header(text, divider='rainbow') st.subheader(text) st.title(text) st.write(text) user_input = st.number_input(label="로짓값에 더해지는 숫자를 입력하세요.", value=0) st.write("# Bar Chart") st.bar_chart(get_vocab_logits(user_input)) st.caption(text)
Python
복사

코드해석

[핵심 흐름]
1.
사용자 입력 받기
st.number_input()을 통해 사용자로부터 숫자 입력
1.
로짓 호출 및 차트 출력
bar_chart()로 결과를 시각화
[데코레이터]
@st.cache_data Streamlit의 데이터 캐시 데코레이터로, 동일한 입력값이면 결과를 캐시하여 재실행 방지
[특이사항]
예상대로 처음 실행한 파라미터에서 대해서는 지연 현상이 발생하지만, 이미 실행했던 파라미터에서 대해서는 지연 없이 화면에 그래프로 출력되는 것을 확인할 수 있음

4.리소스 캐싱

1.
또 하나의 데코레이터
@st.cache_data 데코레이터가 데이터에 대한 캐싱 메커니즘을 적용
@st.cache_resource 데코레이터는 머신러닝 모델이나 데이터베이스 컨넥션 등의 리소스를 효율적으로 사용하도록 도움
@st.cache_resource를 사용하면 프로그램에서 사용되는 이러한 리소스를 한 번만 로드하거나 연결하고, 이후의 호출에서는 캐싱된 인스턴스를 재사용함으로써 처리 시간을 단축할 수 있음

공통점

항목
설명
목적
불필요한 중복 연산 방지, 성능 최적화
캐시 위치
Streamlit 내부 메모리
자동 무효화
함수 내부 코드 또는 입력값 변경 시 캐시 무효화
사용 방식
함수 위에 데코레이터 형식으로 선언

차이점 비교

항목
@st.cache_data
@st.cache_resource
주 용도
데이터 연산 결과 캐싱
객체 또는 리소스 재사용
적합 대상
데이터프레임, 리스트, 딕셔너리 등(읽기 전용 결과)
머신러닝 모델, DB 연결, API 클라이언트 등
반환값
계산 결과(데이터)
객체 그 자체 (상태 포함)
상태 유지
불가능 (순수 함수 전제)
가능 (상태 있는 객체도 재사용)
대표 예시
pd.read_csv(), api.get_data()
load_model(), sqlite.connect()

어떤 상황에서 무엇을 써야 할까?

상황
추천 캐시
대용량 CSV나 API 결과를 반복 사용
@st.cache_data
데이터 처리 함수가 오래 걸리는 경우
@st.cache_data
Scikit-learn, LLM 모델 등 학습된 모델 불러오기
@st.cache_resource
DB 연결 객체를 유지하며 재사용하고 싶을 때
@st.cache_resource
외부 리소스를 초기화하고 한 번만 쓰고 싶을 때
@st.cache_resource

4-1.데이터 캐쉬 실습

[3_data_cash.py]
import streamlit as st import pandas as pd @st.cache_datadef load_csv(): return pd.read_csv("data_cash\data.csv") df = load_csv() st.dataframe(df)
Python
복사

4-2.resource 캐쉬 실습

import streamlit as st # 파이썬 객체를 직렬화(serialize)하거나 병렬 처리(parallel processing)를 할 때 사용 라이브러리 # ML 모델 저장/로딩, 큰 데이터 구조 저장, 병렬 연산 처리 등에 자주 활용 import joblib #해당 모델은 아이리스 분류 모델에 대해 학습된 모델pkl임임 @st.cache_resource def load_model(): return joblib.load("model_pkl/model.pkl") model = load_model() prediction = model.predict([[1, 2, 2.5, 3.1]]) st.write("예측:", prediction)
Python
복사

4-3.data+resource 캐쉬 실습

import streamlit as st import joblib # 🌱 학습된 모델 로딩 (캐시 사용으로 효율성 향상) @st.cache_resource def load_model(): return joblib.load("model_pkl/model.pkl") model = load_model() st.title("🌸 아이리스 품종 예측기") st.markdown("꽃받침과 꽃잎의 길이/너비를 입력하면 품종을 예측합니다.") # 📥 사용자 입력 받기 sepal_length = st.number_input("① Sepal Length (cm)", min_value=0.0, max_value=10.0, value=5.1) sepal_width = st.number_input("② Sepal Width (cm)", min_value=0.0, max_value=10.0, value=3.5) petal_length = st.number_input("③ Petal Length (cm)", min_value=0.0, max_value=10.0, value=1.4) petal_width = st.number_input("④ Petal Width (cm)", min_value=0.0, max_value=10.0, value=0.2) # 🧮 입력값 배열 생성 user_input = [[sepal_length, sepal_width, petal_length, petal_width]] # 🔍 예측 실행 버튼 if st.button("예측 실행"): prediction = model.predict(user_input) st.success(f"🌼 예측 결과: `{prediction[0]}` 품종으로 분류됨")
Python
복사