데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      Streamlit을 활용한 Python 웹 애플리케이션 개발 소개

      victor 24.05.08
      1,121 12 2
      DEVOTEE 요약
      Streamlit은 Python으로 데이터 시각화 및 웹 애플리케이션 개발을 쉽게 할 수 있도록 도와주는 오픈소스 프레임워크입니다. 사용자는 간단한 Python 코드로 다양한 컴포넌트를 활용하여 인터랙티브한 애플리케이션을 빠르게 구축할 수 있으며, 캐싱 기능 등을 통해 성능을 개선할 수 있습니다. 또한, Streamlit Cloud를 통해 만들어진 애플리케이션을 쉽게 배포하고 공유할 수 있습니다.

      Fine-tuning 관련 교육을 듣던 중 streamlit을 처음 접하게 되었는데요.

      손쉽게 웹 애플리케이션을 만들 수 있는 걸 보고 한번쯤 기본 기능들을 둘러보면 좋겠다는 생각에 자료를 조사하며 블로그를 작성하게 되었습니다.


      Streamlit 이란?

      Streamlit은 Python에서 사용할 수 있는 오픈소스 웹 애플리케이션 프레임워크입니다.

      사용자가 Python 스크립트를 사용하여 데이터 시각화, 모델 훈련 결과의 시각화, 인터랙티브한 웹 애플리케이션 등을 손쉽게 개발할 수 있도록 도와줍니다.

      Streamlit은 사용하기 쉽고 빠르게 웹 애플리케이션을 구축할 수 있는 간단한 컴포넌트 API와 함께 제공되며,

      사용자가 코드를 수정할 때 자동으로 리로드되어 실시간으로 변경 사항을 확인할 수 있는 장점을 갖고 있습니다.

      "Streamlit turns data scripts into shareable web apps in minutes.

      All in pure Python. No front‑end experience required."

      라고 streamlit은 주장하는데요.


      그럼 바로 띄워보며 살펴보겠습니다.


      패키지 설치와 hello 프로젝트 실행

      streamlit을 이용하기 위해 필요한 패키지는 streamlit 입니다. streamlit 패키지를 설치하고 바로 실험해볼 수 있는 hello 명령을 제공하고 있습니다.

      $ pip install streamlit
      $ streamlit hello

      streamlit helllo를 실행하면 아래와 같은 다양한 streamlit으로 구현된 컴포넌트들을 확인 할 수 있습니다.





      기본 제공하는 컴포넌트들의 퀄리티가 상당히 그럴듯한(?) 것을 확인할 수 있습니다.

      각각을 띄우기 위해 코드를 많이 작성할 필요도 없습니다. Python 코드 몇줄이면 컴포넌트들이 뚝딱 생성되네요.


      원격지 소스코드를 Streamlit으로 띄우기

      streamlit은 Python 스크립트를 로드하여 바로 웹 애플리케이션을 띄우므로 웹 상에 업로드된 Python 모듈을 바로 실행할 수 있습니다.

      아래와 같이 streamlit run 입력으로 로컬에서 바로 웹 애플리케이션을 동작할 수 있습니다.

      $ streamlit run https://raw.githubusercontent.com/streamlit/demo-uber-nyc-pickups/master/streamlit_app.py


      Streamlit 컴포넌트

      streamlit에는 사용자와 상호작용하기 위한 다양한 컴포넌트들이 존재합니다.

      Button, Columns, Tabs, Form, Selectbox 등 다양한 형태의 컴포넌트들을 사용할 수 있습니다.

      각 컴포넌트들은 속성값을 조절하고 핸들러를 붙여 사용하는 것이 가능합니다.

      상세한 내용은 가이드 문서의 API reference(https://docs.streamlit.io/develop/api-reference)에서 확인하실 수 있습니다.








      멀티페이지 만들기

      streamlit을 이용해 네비게이션 가능한 multipage를 구현할 수 있습니다.

      페이지를 생성하는 방법 역시 간단합니다.

      메인 스크립트가 위치한 디렉토리에 pages라는 디렉토리를 생성하여 하위에 Python 모듈 파일을 만들면 파일 이름으로 페이지가 생성됩니다.

      tree 형태로 보면 다음과 같습니다.

      your_app_directory/
      ├── pages/
      │   ├── a_page.py
      │   └── b_page.py
      └── home.py


      주의할 점은 sidebar에 있는 메뉴나 st.page_link와 같은 접근 방식이 아닌 URL로 직접 페이지를 접근하는 경우 session 정보가 초기화 된다는 것입니다.


      캐싱 필요성

      Streamlit은 사용자 상호작용이나 코드 변경 시 마다 매번 스크립트의 시작부터 끝까지 재실행합니다. 이러한 방식은 개발은 간단 하지만 두 가지 이슈가 발생합니다.

      • 오래 실행되는 함수는 반복해서 실행되므로 앱 속도가 느려집니다.

      • 개체가 계속해서 다시 생성되므로 재실행이나 세션에서 개체를 유지하기가 어렵습니다.

      내장된 캐싱 메커니즘으로 두 가지 문제를 해결할 수 있습니다. 캐싱을 이용하면 느린 함수 호출의 결과를 저장하므로 한 번만 실행하면 됩니다.

      따라서 앱 속도가 훨씬 빨라지고 재실행 시에도 오브젝트를 유지하는데 도움이 됩니다.


      캐싱 타입에는 st.cache_data 또는 st.cache_resource 두가지가 존재합니다.

      st.cache_data

      st.cache_data는 데이터를 반환하는 계산을 캐시하는 데 권장되는 방법으로,

      CSV에서 데이터 프레임 로드, NumPy 배열 변환, API 쿼리 또는 직렬화 가능한 데이터 객체(str, int, float, DataFrame, array, list, ...)를 반환하는 기타 함수 등을 캐시합니다.

      각 함수 호출 시 데이터의 새 복사본을 생성하여 mutation과 race condition으로부터 안전하다고 합니다.

      st.cache_resource

      st.cache_resource는 여러 번 로드하지 않으려는 직렬화할 수 없는 객체인 ML 모델이나 데이터베이스 연결과 같은 글로벌 리소스를 캐시하는 데 권장되는 방법입니다.

      이를 사용하면 복사나 복제 없이 앱의 모든 재실행 및 세션에서 이러한 리소스를 공유할 수 있습니다.

      캐시된 반환값을 변경하면 캐시에 있는 객체가 직접 변경된다는 점에 유의해야 합니다.


      그렇다면 캐시 사용 시 얼마나 성능 차이가 있을까?


      본 포스트에서는 st.cache_data만 사용해서 간단히 실행 시간을 비교 해보도록 하겠습니다.

      테스트 코드는 다음과 같습니다.

      def load_data(url):
          df = pd.read_csv(url)  # 👈 Download the data
          return df
      
      df = load_data("https://github.com/plotly/datasets/raw/master/uber-rides-data1.csv")
      st.dataframe(df)

      웹에서 csv 데이터를 불러와 st.dataframe으로 표현하는 코드입니다. 위 코드를 실행하면 다음과 같은 화면이 나옵니다.


      실행 환경 마다 차이는 있겠지만 제가 측정했을 때의 시간은 다음과 같은 결과가 나왔습니다.

      실행 시간: 6.527 초

      사용자와 상호작용하는 인터페이스를 만들기에는 상당한 지연 시간이네요.

      이런 데이터 로딩 시간을 버튼 클릭 시 마다 격게 되면 사용자 경험을 해치는 결과를 초례합니다.

      그럼 캐싱을 적용한 경우 얼마나 빨라지는지 보겠습니다.


      캐싱은 다음과 같이 데코레이터를 추가하는 방법으로 간단히 적용할 수 있습니다.

      @st.cache_data
      def load_data(url):
          df = pd.read_csv(url)  # 👈 Download the data
          return df
      
      st.write("Dataframe")
      df = load_data("https://github.com/plotly/datasets/raw/master/uber-rides-data1.csv")
      st.dataframe(df)

      캐시가 적용된 경우 다음과 같은 실행 시간이 나왔습니다.

      최초 로딩은 느리겠지만 사용자와 상호작용하면서 스크립트가 재실행 되는 경우엔 빠른 반응 속도로 실행될 수 있습니다.

      실행 시간: 0.162 초

      상태값 유지 방법

      사용자가 streamlit앱과 인터랙션할 때마다 streamlit은 스크립트를 시작부터 끝까지 재실행합니다.

      각 재실행은 백지 상태에서 이루어지며, 실행 간에 변수가 공유되지 않습니다.


      session_state는 각 사용자 세션에 대해 재실행 간에 변수를 공유하는 방법입니다.

      streamlit은 상태를 저장하고 유지하는 기능 외에도 콜백을 사용하여 상태를 조작할 수 있는 기능도 제공합니다.

      세션 상태는 다중 페이지 앱 내의 여러 페이지에서도 유지됩니다.


      간단한 예제로 보도록 하겠습니다. 흔히 웹프론트엔드에서 사용하는 예제 방식인 버튼 클릭시 숫자를 증가 시키는 예제입니다.

      아래와 같은 코드를 작성했을 때 버튼 클릭시 count값을 1씩 증가 시킬 것 같지만,

      앞서 설명했듯이 streamlit은 스크립트를 시작부터 끝까지 재실행하기 때문에 count = 0 코드 라인에서 count값을 초기화 합니다.

      즉, 아무리 버튼을 눌러도 0에서 1로 변경된 값만 표시되는 것이죠.

      import streamlit as st
      
      st.title('Counter Example')
      count = 0
      
      increment = st.button('Increment')
      if increment:
          count += 1
      
      st.write('Count = ', count)


      위와 같은 문제를 해결하기 위해 상태값을 유지하는 st.session_state를 사용할 수 있습니다.

      import streamlit as st
      
      st.title('Counter Example')
      if 'count' not in st.session_state:
          st.session_state.count = 0
      
      increment = st.button('Increment')
      if increment:
          st.session_state.count += 1
      
      st.write('Count = ', st.session_state.count)

      session_state에 특정 값을 저장하고 이 값을 불러오는 방식으로 상태값을 사용하는 것이 가능합니다.


      Streamlit으로 챗봇 인터페이스 만들기

      streamlit을 이용하면 정말 간단하게 ChatGPT와 같은 인터페스를 만드는 게 가능합니다.

      chat과 관련된 컴포넌트들을 기본 제공하기 때문에 빠르게 구축 가능합니다.

      다음과 같은 방법으로 OpenAI API Key를 이용해 ChatGPT와 같은 나만의 애플리케이션을 띄울 수 있습니다.

      from openai import OpenAI
      import streamlit as st
      
      st.title("ChatGPT-like clone")
      
      client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"])
      
      if "openai_model" not in st.session_state:
          st.session_state["openai_model"] = "gpt-3.5-turbo"
      
      if "messages" not in st.session_state:
          st.session_state.messages = []
      
      for message in st.session_state.messages:
          with st.chat_message(message["role"]):
              st.markdown(message["content"])
      
      if prompt := st.chat_input("What is up?"):
          st.session_state.messages.append({"role": "user", "content": prompt})
          with st.chat_message("user"):
              st.markdown(prompt)
      
          with st.chat_message("assistant"):
              stream = client.chat.completions.create(
                  model=st.session_state["openai_model"],
                  messages=[
                      {"role": m["role"], "content": m["content"]}
                      for m in st.session_state.messages
                  ],
                  stream=True,
              )
              response = st.write_stream(stream)
          st.session_state.messages.append({"role": "assistant", "content": response})

      다음은 위 코드로 실행된 결과 입니다. 보시는 것과 같이 멀티 턴의 대화가 가능하며 마크다운이 적용된 코드 결과도 표시됩니다.


      Streamlit Cloud

      streamlit은 웹 환경으로 바로 배포할 수 있는 community cloud 서비스를 제공합니다.

      본인이 만든 streamlit 애플리케이션을 streamlit 메뉴에 있는 'Deploy'를 이용해 배포하거나

      community cloud 페이지에 접속해서 github에 올려놓은 repo를 deploy하는 방법으로 배포하는 것이 가능합니다.

      github repository를 이용해 streamlit cloud에 배포하기

      streamlit cloud에 접속하면 다음과 같은 화면이 보입니다. 여기서 'New app'을 클릭합니다.


      streamlit cloud 계정에 github이 연동되어 있다면, 바로 본인의 Repository를 선택해서 App을 배포할 수 있습니다.

      App URL은 기본적으로 임의로 생성되지만, 기존에 배포된 URL과 겹치지 않는다면 본인이 설정하는 것도 가능합니다.


      'Deploy' 버튼을 클릭하면 앞서 설정했던 'App URL'로 앱이 배포되어 웹브라우저로 접속할 수 있습니다.

      링크 : https://app-test-cqj2k7ben9eaqcduw8rs5q.streamlit.app/


      한번 배포 연동된 github repository는 이 후 별도의 배포 과정 없이 새로운 코드가 커밋되면 바로 배포된 웹 애플리케이션에 반영됩니다.

      Stream Cloud 둘러보기

      stream cloud에는 내가 배포한 App 외에도 다른 사람들이 배포한 App을 볼 수 있습니다.

      github과 연동되어 배포되었기 때문에 소스코드 또한 제공되어 있으니 올려진 재미난 프로젝트들을 둘러보는 좋을 것 같습니다.

      BERT Keyword Extractor

      https://bert-keyword-extractor.streamlit.app/


      Llama 2 Chatbot

      https://llama2.streamlit.app/


      streamlit-folium

      https://folium.streamlit.app/



      마무리하며

      streamlit은 빠르고 간결하게 Python을 이용해 웹 애플리케이션을 만들 수 있는 프레임워크입니다.

      제공하는 컴포넌트들 또한 상당히 괜찮은 퀄리티를 보이기 때문에 PoC(Proof of Concept)를 구축하는데 있어서 매력 있는 프레임워크라 할 수 있겠습니다.

      다만, 상용 수준의 프론트엔드 경험을 제공하기 위해서는 여타 프론트엔트 프레임워크와 마찬가지로 여러 로직상 고민들이 필요하겠다는 생각이 들었습니다.

      또한, 커스터마이징 또한 일부 제공하기는 하나 현시점에 제공하는 기능들로 상용 서비스로 내놓기에는 부족하지 않나라는 생각입니다.

      그럼에도 Python만을 이용하여 빠르게 웹서비스 컨셉을 제공하고 이를 기반으로 피드백을 받아 개선 하기엔 충분한 기능들을 제공하니 필요한 영역에 맞게 쓰면 충분히 좋은 프레임워크라는 생각입니다.


      참고

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      victor 님의 최신 블로그

      더보기
      동영상 기고하기