Streamlit

DONGJIN IM·2022년 7월 10일
1

Product Serving 이론

목록 보기
4/10

Streamlit

Streamlit이란?

  • Voila를 활용한 Dashboard가 아닌 실제 Web 상에 Product Serving을 수행하고 싶을 때 활용하는 툴

  • JavaScript, React, Vue 등을 활용해 프로토타입(특히 프론트엔드)에 신경쓰는 것보다는 모델 배포나 활용, 진행 과정 등을 파악하는게 중요한 경우가 많으며, 이 때 유용하게 활용할 수 있음

  • 기존 코드를 조금만 수정하여 웹 서비스를 구현하는 것을 도와주는 툴

Stramlit의 장점

  • Python Scrip Code를 조금만 수정하면 웹을 띄울 수 있음

  • 백엔드 개발이나 HTTP Request 구현이 필요 없음

    • 백엔드 개발을 하지 않아도 활용할 수 있음
  • 다양한 Component를 제공하여 Dashboard UI를 구성할 수 있음

  • 화면 녹화 기능 존재

  • 쉽게 배포 가능


Streamlit 활용법

streamlit 설치

pip install streamlit

streamlit 실행

streamlit run {실행시킬 Python 파일}

  • localhost:8501에서 접근 가능

    • --port 등을 통해 Port 번호를 바꿀 수는 있지만, 굳이 할 필요는 없음
  • Streamlit이 애초에 CLI를 통해 웹에서 실행시키는 형식이다보니, Jupyter Notebook보다는 PyCharm에서 실험하는 것이 편하다. 따라서, 아래 있는 실험은 모두 PyCharm을 통해 실행하겠다.

  • 만약 코드를 변경한 이후 변경 사항을 확인하고 싶다면 Rerun을 클릭하면 된다. 가끔, 변경 사항이 먹히지 않는 경우가 존재하는데 이는 "Cache Data" 때문이다. 따라서, Clear Cache 혹은 직접 Cache를 삭제한 이후 Rerun을 클릭하면 예외 사항 없이 변경 사항을 확인 할 수 있다

글자 쓰기

import streamlit as st

st.title('Title')
st.header('Header')
st.subheader('subheader')

st.write("Write Something")

버튼 및 Event 처리

import streamlit as st

if st.button("1 + 1"):
    st.write("2!!!")

if st.button("1 + 2"):
    st.write("3!!!")

  • streamlit에서는 st.button()을 통해 생성된 버튼을 클릭하면 st.button() 값이 True를 반환한다. 따라서 이런 성질을 이용해 Event 처리가 가능해진다

  • 위 사진에서 1+1은 클릭하였으므로 st.write() 메서드가 수행되었지만, 1+2는 버튼이 클릭되지 않았으므로 아무것도 출력되지 않았다.

표 그리기

  • st.dataframe : Interactive한 Dataframe 형식으로 표를 그림. 컬럼 클릭 및 정렬이 가능
    st.table : Static한 Dataframe
import streamlit as st
import pandas as pd

df= pd.DataFrame({
    'first':[1,2,3,4],
    'second':[5,6,7,8]
})

st.dataframe(df)
st.table(df)

위 이미지만 보면 차이점이 없어보일 수도 있다.
위 표는 st.dataframe, 아래 표는 st.table을 통해 만들어진 것이다.

이미지에서 볼 수 있듯 st.dataframe은 Ascending, 혹은 Descending으로 (컬럼 기준으로) 정렬할 수 있다.
하지만, st.table은 불가능하다

  • df.style.highlight_max(axis=0)
    • 표로 그릴 Dataframe에 대하여 "어떤 방식으로" 표를 그릴지 지정해 줄 수 있는 인자 값
    • st.dataframe(df.style.highlight_max(axis=0))으로 입력하면, df로 표를 그리는데, Style은 axis=0기준 최댓값에 강조(highlight_max)하는 형식으로 표를 그리라는 의미이다

여러 종류의 Chart 그리기

Line Chart

import streamlit as st
import pandas as pd
import numpy as np

df= pd.DataFrame(
    np.random.rand(20,3),
    columns=['a','b','c']
)

st.line_chart(df)

  • 오른쪽 위 3개 점을 클릭하여 Source Code를 보거나 그래프를 저장할 수 있음

  • columns를 통해 Data의 각 Column에 이름을 붙여줄 수 있음

Radio Button & Select Box

import streamlit as st

selected_items = st.radio('Radio Part', ('A','B','C'))

st.write(selected_items+"!!!")
  • st.radio(label, options)

    • label : Radio 버튼의 총괄적 이름
    • options : Value가 될 수 있는 후보
      • 가능한 Type : Sequence, ndarray, Series, DataFrame, pandas.Index
  • st.radio에서 특정 값을 선택하면, 선택된 값을 반환한다. 따라서 위 코드 그대로 수행된다면, 선택된 값이 출력 될 것이다.

    • Streamlit은 Event가 발생하면 코드를 "처음부터 수행"시키는 형식으로 진행된다. 따라서, Radio Button의 값을 바꾸면 값이 바뀐 상태에서 위 코드가 그대로 재실행되어 마지막 st.write() 메서드가 실행되는 것이다

  • st.selectbox

    • 사용 방식은 st.radio와 동일
    • Options를 원소로 하는 Dropdown을 만드는 코드
  • st.multiselect

    • 사용 방식은 st.selectbox와 유사
    • 하지만, multiselect이름을 가진 만큼 여러 개의 값을 동시에 선택 할 수 있음

입력값

  • String 입력 : st.text_input

    • type='password' : 인자로 전달할 경우, 입력한 값을 Password 형식으로 변환. 즉, 검은색 점으로 나오고 입력한 값은 보이지 않음
  • 숫자 입력 : st.number_input

  • 날짜 입력 : st.date_input

  • 시간 입력 : st.time_input

  • Latex 수식 입력 : st.latex

  • Caption 입력 : st.caption

    • Caption : Text Input을 받는 것이 아닌, st.write()처럼 한 번 값을 입력한 이후 변경하지 못하게 한 것
  • Code 입력 : st.code

  • Sidebar에 파라미터를 지정하거나 암호를 설정할 수 있음

  • Sidebar에 여러 Option을 넣어 Option에 맞는 함수를 지정해 줄 수 있음

  • 활용 방식 : 기존 Method 앞에 sidebar만 붙이면 Sidebar에 보이게 할 수 있음

    • (ex) hi Button을 Sidebar에 보이게 하기 : st.sidebar.button('hi')

expander

  • 눌렀을 경우 확장하는 Section을 구현하기 위해 활용하는 메서드
import streamlit as st

with st.expander('클릭!'):
    st.write('이걸 그렇게 보고 싶었나?')
  • Expander(확장을 원하는 Section) 클릭 이전

  • Expander 클릭 이후

상태창 만들기

  • "Success", "Info", "Warning", "Error" 등의 상태에 대한 상태창을 형성할 수 있음
import streamlit as st

st.success("Success")
st.info('Info')
st.warning('Warning')
st.error('error')

파일 업로드하기

  • 파일을 업로드 할 수 있는 File Uploader를 만드는 메서드
import streamlit as st

uploaded_file = st.file_uploader('Choose File!', type=['png', 'jpg'])
# 업로드한 파일은 uploaded_file 객체에 저장됨
# type : Upload할 수 있는 파일의 확장자명

Session State

  • 매우 핵심적인 내용이기 때문에 꼭 알아야 할 개념

  • Streamlit은 무언가 update될 경우 전체 Streamlit 코드가 다시 실행됨

    • Code가 수정되는 경우
    • 사용자가 Streamlit의 위젯과 상호작용 하는 경우
  • 따라서, 중복 이벤트를 수행시킬 수 없음

  • 이런 중복 이벤트를 수행하기 위해 session_state_value에 공유할 값을 저장하여 활용함

Session State 적용 안 했을 경우

import streamlit as st

value = 0

increment = st.button('Increment')
decrement = st.button('Decrement')

if increment:
    value+=1

if decrement:
    value-=1

st.write("Value : ",value)


  • 실행시켜보면 Increment 버튼을 계속해서 클릭해도 Value는 1만 되고, Decrement를 계속해서 누르더라도 (이전 Value가 무엇이든) -1만 출력된다. 이는 위 코드에서 value = 0으로 지정하였고, streamlit은 항상 Event 처리때마다 코드를 "재실행" 하여 수행하므로, (0+1), (0-1)의 값밖에 나올 수 없는 것이다

Session State 적용

import streamlit as st

if 'count' not in st.session_state:
    st.session_state.count = 0
# 'count'가 저장되어 있지 않다면(하위 폴더에 없다면) 새로 생성해줌

increment = st.button('Increment')
decrement = st.button('Decrement')

if increment:
    st.session_state.count+=1

if decrement:
    st.session_state.count-=1

st.write("Value : ",st.session_state.count)
  • 실행 방법 : st.session_state 밑에 내가 원하는 이름(위 코드에서는 count)로 변수명을 만들어 놓은 뒤 해당 공간에 값을 넣어둠
    • 이름의 중복은 피해야 하며, st.session_state의 "하위 폴더"에 변수를 저장한다고 이해하면 편할 것 같다.

  • Session을 활용하지 않으면 절대 나올 수 없는 값인 3이 나옴
  • st.session_state.count에 이전 연산 값의 결과가 저장되어 있으므로, "저장되어 있는 Session 값"에 내가 원하는 연산을 수행하기 때문에 위와 같은 결과가 나온다

Cache

  • 매번 다시 실행하는 Streamlit 특징 때문에 Data를 매번 다시 읽을 수 있음

  • 매번 Data를 읽을 경우 시간이 오래 걸리므로, 캐싱을 통해 데이터를 저장함

    • 매번 Data를 읽지 않아도 됨
    • 캐싱 : 성능을 위해 데이터 등을 메로리에 저장하는 행위
  • Data를 읽는 함수를 만들어 @st.cache 데코레이터를 활용해 캐싱하면 Streamlit을 실행시킬 때 다시 데이터를 불러오는 코드를 수행시킬 필요가 없음

Caching이 없을 때

from io import BytesIO
from urllib import request
from PIL import Image

import streamlit as st
import time

url = "https://upload.wikimedia.org/wikipedia/en/2/24/Binary-icon.png"


def download_image():
    start = time.time()
    res = request.urlopen(url).read()
    end = time.time()

    img = Image.open(BytesIO(res))

    return (end-start), img

if st.button("이미지 다운"):
    time, image = download_image()

    st.write(time)
    st.image(image)

  • 위 사진은 첫번째 클릭했을 때, 아래 사진은 2번째 클릭했을 때의 결과이다.
    아래 이미지는 불러온 이미지이고, 위 숫자는 불러오는데 걸리는 시간이다.
    시간이 계속해서 달라짐을 확인하면, Caching이 없을 경우 매번 데이터를 불러온다는 것을 알 수 있다.

Caching이 존재할 때

from io import BytesIO
from urllib import request
from PIL import Image

import streamlit as st
import time

url = "https://upload.wikimedia.org/wikipedia/en/2/24/Binary-icon.png"

@st.cache                # Caching하겠다!
def download_image():
    start = time.time()
    res = request.urlopen(url).read()
    end = time.time()

    img = Image.open(BytesIO(res))

    return (end-start), img

if st.button("이미지 다운"):
    time, image = download_image()

    st.write(time)
    st.image(image)

  • 몇 번을 클릭하더라도 똑같은 시간이 소요된다. Caching되어 메모리 상에 저장되어 있는 이미지를 불러오기 때문에 불러오는 시간대가 안정적이며, 따라서 데이터 흐름 등을 예측하기에 편하다는 장점을 가질 수 있다. 또한, 시간 자체도 덜 소요됨을 볼 수 있다

  • 특히, 계속해서 데이터를 다운 받을 때(위 URL의 경우 더욱) 네트워크 트래픽 양에 의존적일 수밖에 없다. 하지만, Caching은 네트워크 트래픽과는 연관이 없으므로 안정적이다

profile
개념부터 확실히!

0개의 댓글