[고양이 행복 앱] Streamlit으로 POC하기

소개

안녕하세요. 고양이 행복 앱 개발팀의 김병철입니다.

저희 팀은 고양이의 행복도를 높이기 위한 서비스를 개발하고 있습니다.

프로젝트를 시작하기 전에 기술적으로 문제가 될 지점들을 빠르게 검증해 보기 위해 POC를 진행했습니다.

사용한 툴 : Streamlit, Cursor

사용한 모델 : Gemini 2.0 flash

진행 방법

일단 저희 프로젝트의 기술적으로 어려운 부분이라고 생각이 들었던 건 고양이 사진을 통해 메타 정보를 추출해내는 것이었습니다.

예전에는 메타 정보를 추출하기 위해 YoloV8의 object detection 이나 pose estimation 을 사용했었는데요. 모델 학습이 필수 아닌 필수였습니다. (2019년 기준)

이제는 멀티 모달 모델을 사용해서 별도 학습 없이 추출이 가능할거라고 생각했습니다.

빠른 검증을 위해 gemini 2.0 flash 모델을 사용해서 메타 정보를 추출해봤습니다.

땅바닥에 자고 있는 고양이의 이미지

생각보다 자세한 분석과 추출이 가능한거를 확인했습니다.

바로 한번 만들어보기로 했습니다.

가장 간단하게 앱을 구동하고 배포할 수 있는 streamlit을 사용해서 코드를 작성해봅니다.

from typing import Any, Dict, Optional

import streamlit as st
from PIL import Image
from streamlit.runtime.uploaded_file_manager import UploadedFile

from src.services.structure_gemini import GeminiService

st.set_page_config(page_title="고양이 분석", layout="wide")

# 메인 대시보드
st.title("고양이 분석")

gemini_service = GeminiService()
# 이미지 업로드
image_file: Optional[UploadedFile] = st.file_uploader(
    "이미지를 업로드하면 AI가 고양이를 분석합니다",
    type=["jpg", "jpeg", "png"],
    label_visibility="visible",
)
...

제미나이를 이용해서 이미지 분석을 했고 제미나이의 응답을 JsonSchema를 정의해서 코드에 응답 데이터를 그대로 쓸 수 있게 구현했습니다.

GENERATION_CONFIG = {
    "temperature": 1,
    "top_p": 0.95,
    "top_k": 40,
    "max_output_tokens": 8192,
    "response_schema": content.Schema(
        type=content.Type.OBJECT,
        enum=[],
        required=["is_cat"],
        properties={
            "is_cat": content.Schema(
                type=content.Type.BOOLEAN,
            ),
            "image_tags": content.Schema(
                type=content.Type.ARRAY,
                items=content.Schema(
                    type=content.Type.STRING,
                ),
            ),
            "color_codes": content.Schema(
                type=content.Type.ARRAY,
                items=content.Schema(
                    type=content.Type.STRING,
                ),
            ),
            "breed_type": content.Schema(
                type=content.Type.STRING,
            ),
            "age": content.Schema(
                type=content.Type.INTEGER,
            ),
        },
    ),
    "response_mime_type": "application/json",
}

OSError: cannot write mode RGBA as JPEG

2025-01-19 17:36:50.543 Uncaught app execution

Traceback (most recent call last):

  File "/home/adminuser/venv/lib/python3.12/site-packages/PIL/JpegImagePlugin.py", line 665, in _save

    rawmode = RAWMODE[im.mode]

              ~~~~~~~^^^^^^^^^

KeyError: 'RGBA'


The above exception was the direct cause of the following exception:


Traceback (most recent call last):

  File "/home/adminuser/venv/lib/python3.12/site-packages/streamlit/runtime/scriptrunner/exec_code.py", line 88, in exec_func_with_error_handling

    result = func()

             ^^^^^^

  File "/home/adminuser/venv/lib/python3.12/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 579, in code_to_exec

    exec(code, module.__dict__)

  File "/mount/src/daily-nyang-status/streamlit_app.py", line 30, in <module>

    image.save("temp.jpg", "JPEG")

  File "/home/adminuser/venv/lib/python3.12/site-packages/PIL/Image.py", line 2596, in save

    save_handler(self, fp, filename)

  File "/home/adminuser/venv/lib/python3.12/site-packages/PIL/JpegImagePlugin.py", line 668, in _save

    raise OSError(msg) from e

OSError: cannot write mode RGBA as JPEG

streamlit을 많이 안 써봐서 개발 과정에서 몇가지 문제가 있었지만 기억이 안나는걸 보니 중요한 문제는 아니었던 거 같습니다.

아무튼 이렇게 동작하는 앱을 만들었습니다.(feat. Cursor)

작동하지 않으면 왜 작동하는지 말하는 만화?

뭔가 업로드 속도가 엄청 느리지만.. 그래도 공짜 서버에 동작하니깐 만족합니다.

바닥에 누워있는 고양이의 이미지

짠 이렇게 고양이를 분석해주는 앱이 완성되었습니다.

결과와 배운 점

- gemini는 프로덕트 레벨에서도 괜찮은 성능을 보이는 거 같다.

- 그러나 여전히 정확도를 높이는 작업은 필요하다.

- gemini에서 fine tuning도 할 수 있는데 이걸로 좀 더 고도화할 수 있겠다. (MVP 스펙에서는 포기)

- 색상 정보와 고양이 품종에 대한 정확도가 아쉬움. (나이는 애초에 포기)

한자가 새겨진 고양이 그림

(스코티시폴드는 아닌거 같음)

한국 캐릭터 프로필 페이지 스크린샷

(믹스는 맞는데 음... )

구현된 결과는 https://daily-nyang-status.streamlit.app/ 에서 동작 확인과 코드를 확인하실 수 있습니다. (토큰 비용이 소모되는 거라 스터디 끝나거나 비용이 너무 많이 나오면 내릴 예정입니다.)

앞으로 프로젝트를 진행하면서 poc 한 기술들을 적극적으로 도입해 보려구 합니다.

많은 관심 부탁드려요! 화이팅입니다.

12
13개의 답글

👉 이 게시글도 읽어보세요