목차
메타 설명
머신러닝과 생성형 AI 모델을 브라우저에서 실행하려면 복잡한 웹 개발이 필요할까요? Gradio의 Interface, Blocks, 컴포넌트, 이벤트, State, Queue, Progress, 스트리밍, ChatInterface, API, Hugging Face Spaces 배포까지 실습 중심으로 알아봅니다.
지난 시간에는 Streamlit을 이용해 Python 데이터와 머신러닝 모델을 웹 애플리케이션으로 만들었습니다.
버튼, 입력창, 차트, 데이터프레임을 배치하고 다음과 같은 웹앱까지 완성했습니다.
CSV 파일 업로드
→ 데이터 분석
→ 머신러닝 예측
→ 결과 차트 표시
→ 예측 파일 다운로드그런데 우리가 만든 것이 데이터 대시보드가 아니라 AI 모델 데모라면 어떨까요?
이미지를 업로드하면
어떤 물체인지 분류한다.
마이크에 말하면
음성을 문자로 변환한다.
문장을 입력하면
감정을 분석한다.
질문을 입력하면
AI가 답변을 생성한다.
그림을 올리면
객체의 위치를 표시한다.이런 모델을 다른 사람에게 보여주기 위해 일반적인 웹사이트를 만든다면 화면, 서버, API를 모두 연결해야 합니다.
프런트엔드 화면 작성
→ 파일 업로드 구현
→ 서버 API 작성
→ 모델 연결
→ 결과 직렬화
→ 로딩 화면 구현
→ 오류 처리
→ 배포AI 모델 하나를 보여주려다 웹 개발 프로젝트 하나가 새로 태어날 수 있습니다.
개발자:
“이미지 분류 모델을
잠깐 보여드리겠습니다.”
일주일 후:
“현재 로그인 화면을 개발 중입니다.”이때 유용한 도구가 Gradio입니다.
Gradio는 Python 함수의 입력과 출력을 웹 컴포넌트에 연결해 머신러닝 모델, API, 일반 Python 함수를 빠르게 웹 데모로 만들 수 있는 오픈소스 Python 패키지입니다. JavaScript나 복잡한 프런트엔드 코드를 작성하지 않고도 실행 가능한 인터페이스를 구성할 수 있습니다.
다음 Python 함수가 있다고 가정해 보겠습니다.
def greet(name: str) -> str:
return f"{name}님, 반갑습니다!"Gradio를 연결하면 다음과 같습니다.
import gradio as gr
def greet(name: str) -> str:
return f"{name}님, 반갑습니다!"
demo = gr.Interface(
fn=greet,
inputs=gr.Textbox(
label="이름"
),
outputs=gr.Textbox(
label="인사 결과"
)
)
demo.launch()실행하면 브라우저에 입력창과 실행 버튼, 결과창이 나타납니다.
Python 함수
+ 입력 컴포넌트
+ 출력 컴포넌트
= 웹 데모AI 모델에게 정장을 입히고 웹 무대에 올리는 시간이 찾아왔습니다. 🤖🎤
1. Gradio란?
Gradio는 Python 함수, 머신러닝 모델 또는 API 주위에 웹 기반 사용자 인터페이스를 빠르게 구성할 수 있도록 도와주는 오픈소스 Python 패키지입니다.
Gradio가 담당하는 부분은 다음과 같습니다.
입력창 생성
버튼 생성
파일 업로드
카메라·마이크 입력
함수 호출
결과 화면 표시
작업 대기열
진행 상태 표시
출력 스트리밍
API 엔드포인트
공유 링크개발자는 주로 다음 두 가지에 집중할 수 있습니다.
어떤 입력을 받을 것인가?
Python 함수가 무엇을 반환할 것인가?2026년 7월 30일 기준 공식 GitHub의 최신 릴리스는 Gradio 6.21.0이며, 2026년 7월 29일 공개되었습니다. Gradio 6은 현재 유지보수되는 주요 버전 계열로 안내되고 있습니다.
버전은 계속 변경될 수 있으므로 실제 프로젝트에서는 다음 명령어로 설치 버전을 확인합니다.
python -m pip show gradioPython에서도 확인할 수 있습니다.
import gradio as gr
print(
gr.__version__
)2. Gradio 6의 현재 구조
Gradio에는 웹 데모를 만드는 여러 수준의 API가 있습니다.
Interface
Python 함수 하나를 빠르게 웹 화면으로 감쌉니다.
함수 하나
입력 컴포넌트
출력 컴포넌트
→ 빠른 모델 데모Blocks
컴포넌트의 배치, 이벤트, 데이터 흐름을 직접 구성합니다.
복수의 입력
여러 버튼
탭
상태 관리
연결된 이벤트
→ 사용자 정의 웹앱ChatInterface
챗봇 UI를 빠르게 구성합니다.
사용자 메시지
대화 기록
AI 응답 함수
→ 챗봇 완성공식 문서에서 `Interface`는 함수 주위에 웹 기반 GUI를 만드는 고수준 클래스이며, `Blocks`는 레이아웃과 이벤트, 데이터 흐름을 더 세밀하게 제어할 수 있는 저수준 API로 설명됩니다. `ChatInterface`는 챗봇 UI를 위한 고수준 추상화입니다.
Interface:
빠른 조립 키트
Blocks:
부품을 직접 배치하는 작업대
ChatInterface:
챗봇 전용 완제품 조립 키트3. Streamlit과 Gradio의 차이
Streamlit과 Gradio는 모두 Python으로 웹 인터페이스를 만들 수 있습니다.
하지만 출발점이 조금 다릅니다.
Streamlit의 중심
페이지를 위에서 아래로 작성
데이터 분석
대시보드
필터
지표
차트
업무용 데이터 앱Gradio의 중심
Python 함수
머신러닝 모델
입력과 출력
이벤트
생성형 AI
빠른 웹 데모비교
| 구분 | Streamlit | Gradio |
|---|---|---|
| 중심 개념 | Python 스크립트 | 함수와 이벤트 |
| 빠른 모델 데모 | 편리함 | 매우 편리함 |
| 데이터 대시보드 | 매우 강함 | 구성 가능 |
| 챗봇 UI | 구성 가능 | ChatInterface 제공 |
| 오디오·영상 AI | 구성 가능 | 전문 컴포넌트 풍부 |
| 이벤트 연결 | 재실행 구조 | 이벤트 리스너 구조 |
| 상태 관리 | Session State | State·BrowserState |
| API 제공 | 별도 구성이 필요할 수 있음 | 앱 이벤트가 API로 노출 가능 |
| 공유 링크 | 배포 필요 | 임시 공유 링크 지원 |
| 대표 활용 | 데이터 앱 | AI 모델 쇼케이스 |
둘 중 하나가 무조건 더 좋은 것은 아닙니다.
데이터 분석 대시보드
→ Streamlit이 자연스러운 경우가 많음
이미지·음성·LLM 모델 데모
→ Gradio가 자연스러운 경우가 많음두 도구는 라이벌이라기보다 서로 다른 종류의 무대를 담당합니다.
4. Gradio를 사용하기 좋은 상황
Gradio는 다음 상황에서 특히 유용합니다.
머신러닝 모델 시연
표 데이터 분류
이미지 분류
객체 검출
음성 인식
번역
감정 분석생성형 AI 데모
LLM 챗봇
이미지 생성
음성 생성
텍스트 요약
문서 질의응답
AI 에이전트연구 결과 공유
동료에게 모델 테스트 화면 제공
논문 모델 데모
고객 POC
내부 모델 비교
하이퍼파라미터 실험빠른 프로토타입
백엔드 함수는 완성됨
하지만 웹 화면은 아직 없음
→ Gradio로 즉시 연결다음과 같은 서비스에는 추가적인 아키텍처가 필요할 수 있습니다.
복잡한 결제 시스템
대규모 회원 서비스
정교한 사용자 권한
매우 복잡한 프런트엔드
수천 개 화면의 업무 시스템Gradio는 대형 백화점을 짓는 건설 장비라기보다 모델을 빠르게 공개하는 팝업스토어에 가깝습니다.
5. Gradio 설치하기
Gradio 6의 공식 빠른 시작 문서는 Python 3.10 이상을 요구하며 가상환경에서 `pip`로 설치할 것을 권장합니다.
Windows
python -m venv venv
venv\Scripts\activate
python -m pip install --upgrade pip
python -m pip install --upgrade gradiomacOS 또는 Linux
python3 -m venv venv
source venv/bin/activate
python -m pip install --upgrade pip
python -m pip install --upgrade gradio머신러닝 실습 패키지 포함
python -m pip install \
gradio \
numpy \
pillow \
pandas \
scikit-learnWindows 명령 프롬프트에서는 한 줄로 실행할 수 있습니다.
python -m pip install gradio numpy pillow pandas scikit-learn설치 확인
python -c "import gradio; print(gradio.__version__)"6. 첫 번째 Gradio 앱
`app.py` 파일을 만듭니다.
import gradio as gr
def greet(
name: str,
intensity: int
) -> str:
cleaned_name = name.strip()
if not cleaned_name:
return "이름을 입력해 주세요."
exclamation_marks = (
"!"
* intensity
)
return (
f"{cleaned_name}님, "
f"반갑습니다"
f"{exclamation_marks}"
)
demo = gr.Interface(
fn=greet,
inputs=[
gr.Textbox(
label="이름",
placeholder="이름을 입력하세요"
),
gr.Slider(
minimum=1,
maximum=10,
value=3,
step=1,
label="반가움 강도"
)
],
outputs=gr.Textbox(
label="인사 결과"
),
title="🎉 AI보다 먼저 만드는 인사 앱",
description=(
"이름과 반가움 강도를 입력하면 "
"Python 함수가 인사를 생성합니다."
),
api_name="greet"
)
if __name__ == "__main__":
demo.launch()`Interface`에는 세 가지 핵심 정보가 필요합니다.
fn
→ 실행할 Python 함수
inputs
→ 함수에 전달할 입력 컴포넌트
outputs
→ 함수 반환값을 표시할 출력 컴포넌트이 구조는 공식 `Interface` API의 핵심 설계입니다.
7. Gradio 앱 실행하기
터미널에서 다음 명령을 실행합니다.
python app.py일반적으로 로컬 서버가 실행되고 브라우저에서 앱에 접속할 수 있습니다.
코드를 수정할 때 자동 재실행 기능을 사용하려면 다음 명령을 활용할 수 있습니다.
gradio app.pyGradio 공식 개발 가이드는 코드 변경을 빠르게 반영하는 reload 모드를 제공합니다.
외부 접속 허용
서버에서 실행한다면 다음처럼 설정할 수 있습니다.
demo.launch(
server_name="0.0.0.0",
server_port=7860
)클라우드 환경에서는 추가로 다음 항목을 확인해야 합니다.
운영체제 방화벽
클라우드 보안 그룹
Docker 포트 연결
Nginx 리버스 프록시
HTTPS 인증서8. Python 함수와 컴포넌트 연결
다음 함수는 숫자 두 개를 더합니다.
def add_numbers(
number_a: float,
number_b: float
) -> float:
return number_a + number_bGradio에 연결합니다.
import gradio as gr
def add_numbers(
number_a: float,
number_b: float
) -> float:
return number_a + number_b
demo = gr.Interface(
fn=add_numbers,
inputs=[
gr.Number(
label="첫 번째 숫자"
),
gr.Number(
label="두 번째 숫자"
)
],
outputs=gr.Number(
label="계산 결과"
)
)
demo.launch()입력 컴포넌트의 순서는 함수 인수의 순서와 연결됩니다.
첫 번째 Number
→ number_a
두 번째 Number
→ number_b함수가 값을 하나 반환하면 출력 컴포넌트도 하나를 지정합니다.
9. Interface 완벽 이해하기
`Interface`는 모델이나 함수 하나를 빠르게 시연할 때 적합합니다.
demo = gr.Interface(
fn=predict,
inputs=input_component,
outputs=output_component
)title
title="고양이·강아지 분류기"description
description=(
"사진을 업로드하면 "
"AI가 고양이와 강아지를 분류합니다."
)examples
examples=[
["examples/cat.jpg"],
["examples/dog.jpg"]
]api_name
api_name="classify_image"이벤트에 API 이름을 지정하면 Gradio Client나 외부 프로그램이 해당 기능을 호출할 수 있습니다.
allow_flagging 대신 현재 플래그 설정 확인
Gradio 주요 버전이 바뀌면서 일부 매개변수 이름과 위치가 변경될 수 있습니다.
Gradio 6에서는 앱 전체에 적용되는 테마와 CSS 같은 설정이 `Blocks` 생성자에서 `launch()`로 이동하는 등 호환성 변경이 있었습니다. 이전 버전의 예제를 그대로 복사하기보다 현재 버전의 문서를 확인해야 합니다.
10. 입력·출력 컴포넌트
Gradio 컴포넌트는 입력, 출력 또는 두 역할 모두로 사용될 수 있습니다.
문자열
gr.Textbox()숫자
gr.Number()슬라이더
gr.Slider(
minimum=0,
maximum=100,
value=50
)체크박스
gr.Checkbox(
label="고급 분석 사용"
)선택 메뉴
gr.Dropdown(
choices=[
"빠른 모델",
"정확한 모델"
],
value="빠른 모델",
label="모델 선택"
)`Dropdown`은 하나 또는 여러 항목을 선택하는 입력·출력 컴포넌트입니다.
라디오 버튼
gr.Radio(
choices=[
"긍정",
"부정",
"중립"
],
label="감정"
)이미지
gr.Image(
type="pil"
)파일
gr.File()표
gr.Dataframe()JSON
gr.JSON()분류 결과
gr.Label(
num_top_classes=5
)11. 컴포넌트의 전처리와 후처리
Gradio는 브라우저의 입력을 Python 함수가 사용하기 좋은 형태로 변환합니다.
이를 전처리라고 합니다.
브라우저에서 이미지 업로드
→ Gradio 전처리
→ PIL 이미지 또는 NumPy 배열
→ Python 함수Python 함수가 반환한 값은 브라우저에 표시할 형태로 다시 변환됩니다.
이를 후처리라고 합니다.
Python 함수가 PIL 이미지 반환
→ Gradio 후처리
→ 브라우저 이미지 표시Gradio 컴포넌트는 입력 데이터를 Python 객체로 변환하는 `preprocess`, 함수 결과를 화면이 사용할 형태로 바꾸는 `postprocess` 단계를 갖습니다.
Image의 type
gr.Image(
type="pil"
)함수에 PIL 이미지 객체가 전달됩니다.
gr.Image(
type="numpy"
)함수에 NumPy 배열이 전달됩니다.
gr.Image(
type="filepath"
)임시 파일 경로가 전달됩니다.
공식 Image 컴포넌트 문서도 설정에 따라 NumPy 배열, PIL 이미지 또는 파일 경로를 함수에 전달한다고 설명합니다.
12. 여러 입력과 출력 연결하기
다음 함수는 텍스트를 분석해 글자 수와 단어 수를 반환합니다.
def analyze_text(
text: str
) -> tuple[int, int, str]:
cleaned_text = text.strip()
character_count = len(
cleaned_text
)
word_count = len(
cleaned_text.split()
)
summary = (
f"총 {character_count}글자이며 "
f"{word_count}개 단어입니다."
)
return (
character_count,
word_count,
summary
)Gradio 앱:
import gradio as gr
def analyze_text(
text: str
) -> tuple[int, int, str]:
cleaned_text = text.strip()
character_count = len(
cleaned_text
)
word_count = len(
cleaned_text.split()
)
summary = (
f"총 {character_count}글자이며 "
f"{word_count}개 단어입니다."
)
return (
character_count,
word_count,
summary
)
demo = gr.Interface(
fn=analyze_text,
inputs=gr.Textbox(
lines=8,
label="분석할 문장"
),
outputs=[
gr.Number(
label="글자 수"
),
gr.Number(
label="단어 수"
),
gr.Textbox(
label="분석 결과"
)
]
)
demo.launch()반환 순서와 출력 컴포넌트 순서가 일치해야 합니다.
character_count
→ 첫 번째 Number
word_count
→ 두 번째 Number
summary
→ Textbox13. Examples 추가하기
사용자가 입력 형식을 쉽게 이해하도록 예제를 제공할 수 있습니다.
demo = gr.Interface(
fn=analyze_text,
inputs=gr.Textbox(
label="문장"
),
outputs=[
gr.Number(),
gr.Number(),
gr.Textbox()
],
examples=[
[
"Python은 배우기 즐거운 언어입니다."
],
[
"Gradio로 AI 데모를 만들어 봅시다."
]
]
)예제를 클릭하면 입력값이 채워지고 설정에 따라 함수가 실행됩니다.
빈 입력창:
“무엇을 넣어야 하지?”
Examples:
“이런 값을 넣으면 됩니다.”예제는 설명서보다 빠른 사용법 안내판이 될 수 있습니다.
14. Interface의 한계
Interface는 빠르지만 복잡한 화면에는 한계가 있습니다.
버튼을 여러 개 만들고 싶다.
입력과 출력을 원하는 위치에 배치하고 싶다.
탭을 만들고 싶다.
여러 함수를 연결하고 싶다.
특정 입력이 바뀔 때만 실행하고 싶다.
결과를 State에 저장하고 싶다.이럴 때 `Blocks`를 사용합니다.
Interface:
한 함수의 빠른 데모
Blocks:
여러 컴포넌트와 이벤트로 만든 웹앱15. Blocks란?
`Blocks`는 Gradio 컴포넌트의 레이아웃, 이벤트, 데이터 흐름을 세밀하게 구성하는 API입니다.
공식 문서에 따르면 Blocks는 Interface보다 화면 배치, 함수 실행 이벤트, 컴포넌트 사이의 데이터 흐름을 더 유연하게 제어할 수 있습니다.
기본 구조:
import gradio as gr
def greet(
name: str
) -> str:
return f"{name}님, 반갑습니다!"
with gr.Blocks() as demo:
gr.Markdown(
"# 🎤 Gradio 인사 무대"
)
name_input = gr.Textbox(
label="이름"
)
greet_button = gr.Button(
"인사하기",
variant="primary"
)
result_output = gr.Textbox(
label="결과"
)
greet_button.click(
fn=greet,
inputs=name_input,
outputs=result_output
)
demo.launch()컴포넌트 생성
name_input = gr.Textbox()이벤트 연결
greet_button.click(
fn=greet,
inputs=name_input,
outputs=result_output
)이벤트 리스너는 다음 관계를 정의합니다.
어떤 일이 발생하면
→ 어떤 함수를 실행하고
→ 무엇을 입력으로 전달하며
→ 어느 컴포넌트에 결과를 표시할 것인가16. Row와 Column으로 화면 배치하기
기본적으로 컴포넌트는 위에서 아래로 배치됩니다.
가로 배치
with gr.Blocks() as demo:
with gr.Row():
name_input = gr.Textbox(
label="이름"
)
age_input = gr.Number(
label="나이"
)세로 영역
with gr.Blocks() as demo:
with gr.Row():
with gr.Column():
name_input = gr.Textbox()
age_input = gr.Number()
with gr.Column():
result_output = gr.Textbox()scale
with gr.Row():
with gr.Column(
scale=2
):
main_content = gr.Textbox()
with gr.Column(
scale=1
):
settings = gr.Dropdown(
choices=[
"빠름",
"정확함"
]
)개념적인 비율:
메인 영역:
2
설정 영역:
1화면의 약 2:1 비율로 배치됩니다.
17. Tab과 Accordion 활용하기
Tab
with gr.Blocks() as demo:
with gr.Tab(
"텍스트 분석"
):
text_input = gr.Textbox()
with gr.Tab(
"이미지 분석"
):
image_input = gr.Image()
with gr.Tab(
"모델 정보"
):
gr.Markdown(
"모델 설명"
)Accordion
with gr.Accordion(
"고급 설정",
open=False
):
temperature = gr.Slider(
minimum=0.0,
maximum=2.0,
value=0.7,
label="Temperature"
)
max_tokens = gr.Slider(
minimum=16,
maximum=2048,
value=512,
step=16,
label="최대 토큰"
)탭은 서로 다른 기능을 구분하고 Accordion은 자주 사용하지 않는 설정을 접어 둘 때 유용합니다.
모든 옵션을 화면에 펼침
→ 조종석 폭발
고급 옵션을 Accordion에 보관
→ 평온 회복18. 이벤트 리스너 이해하기
Blocks에서 컴포넌트는 사용자 행동에 반응합니다.
버튼 클릭
텍스트 입력
값 변경
파일 업로드
Enter 입력
페이지 로딩대표적인 이벤트 연결:
button.click(
fn=process,
inputs=input_component,
outputs=output_component
)이벤트 리스너의 기본 구성은 다음과 같습니다.
이벤트 발생 컴포넌트
이벤트 종류
실행 함수
입력 컴포넌트
출력 컴포넌트19. click·change·input·submit 이벤트
click
버튼을 클릭할 때 실행합니다.
run_button.click(
fn=analyze,
inputs=text_input,
outputs=result_output
)submit
Textbox에서 Enter를 누를 때 실행합니다.
text_input.submit(
fn=analyze,
inputs=text_input,
outputs=result_output
)change
컴포넌트 값이 변경되었을 때 실행합니다.
사용자 입력뿐 아니라 함수가 컴포넌트 값을 변경해도 발생할 수 있습니다.
model_dropdown.change(
fn=show_model_description,
inputs=model_dropdown,
outputs=model_description
)input
사용자가 직접 값을 입력하거나 조작했을 때 실행합니다.
temperature_slider.input(
fn=show_temperature,
inputs=temperature_slider,
outputs=temperature_text
)실시간으로 입력할 때마다 무거운 모델을 실행하면 요청이 지나치게 많아질 수 있습니다.
사용자가 한 글자 입력
→ 대형 LLM 실행
한 글자 추가
→ 대형 LLM 다시 실행
GPU:
“문장을 다 쓰고 불러주세요.”20. 이벤트 연결과 then·success·failure
이벤트가 끝난 뒤 다른 이벤트를 연속 실행할 수 있습니다.
first_event = run_button.click(
fn=preprocess,
inputs=input_data,
outputs=processed_data
)
first_event.then(
fn=predict,
inputs=processed_data,
outputs=result_output
)then
앞 이벤트의 성공 여부와 관계없이 후속 이벤트를 실행합니다.
success
앞 이벤트가 성공한 경우에만 실행합니다.
first_event.success(
fn=show_success_message,
outputs=status_output
)failure
앞 이벤트가 실패한 경우에만 실행합니다.
first_event.failure(
fn=restore_previous_state,
outputs=status_output
)Gradio 공식 이벤트 가이드는 `.then()`, `.success()`, `.failure()`를 이용해 연속 실행과 성공·실패 분기를 구성할 수 있다고 설명합니다.
21. 컴포넌트 속성 동적으로 변경하기
함수에서 새 컴포넌트 설정을 반환해 화면 속성을 바꿀 수 있습니다.
def update_output_mode(
mode: str
):
if mode == "자세히":
return gr.Textbox(
label="상세 결과",
lines=10,
visible=True
)
return gr.Textbox(
label="간단 결과",
lines=2,
visible=True
)연결:
mode_dropdown.change(
fn=update_output_mode,
inputs=mode_dropdown,
outputs=result_output
)간단한 속성 업데이트에는 `gr.update()`를 사용하는 이전 예제도 많이 검색되지만, 주요 버전에 따라 권장 방식이 달라질 수 있습니다.
현재 버전의 컴포넌트 반환 방식을 확인하는 것이 좋습니다.
22. 사용자 알림과 오류 처리
Gradio 함수 안에서 사용자에게 알림을 표시할 수 있습니다.
정보
gr.Info(
"분석을 시작합니다."
)경고
gr.Warning(
"입력 문장이 너무 짧습니다."
)오류
raise gr.Error(
"이미지를 먼저 업로드해 주세요."
)`gr.Error`는 예외로 발생시켜 실행을 중단하며, `gr.Info`와 `gr.Warning`은 알림을 표시하고 실행을 계속할 수 있습니다.
예:
def analyze_image(
image
):
if image is None:
raise gr.Error(
"이미지를 업로드해 주세요."
)
gr.Info(
"이미지 분석을 시작합니다."
)
return run_model(
image
)23. State가 필요한 이유
다음과 같은 값을 여러 이벤트에서 유지해야 할 수 있습니다.
대화 기록
예측 기록
장바구니
선택한 설정
업로드된 문서 목록
게임 점수
사용자별 모델 상태일반 전역 변수에 저장하면 모든 사용자가 같은 값을 공유할 수 있습니다.
prediction_history = []사용자 A의 예측이 사용자 B에게 보일 수 있습니다.
사용자 A:
고양이 사진 분석
사용자 B:
“제 기록에 왜 고양이가 있죠?”사용자별 값을 관리하려면 `gr.State`를 사용할 수 있습니다.
24. 전역·세션·브라우저 상태
Gradio는 상태를 크게 세 종류로 구분합니다.
전역 상태
앱이 실행되는 동안 모든 사용자가 공유합니다.
visitor_count = 0서버 전체에서 공유할 모델 객체나 공용 통계에 사용할 수 있습니다.
동시 접근 시 스레드 안전성을 고려해야 합니다.
세션 상태
현재 사용자의 페이지 세션에서만 유지합니다.
history_state = gr.State(
[]
)이벤트 입력과 출력에 포함합니다.
def add_history(
new_item: str,
history: list[str]
):
updated_history = (
history
+ [
new_item
]
)
return (
updated_history,
updated_history
)연결:
add_button.click(
fn=add_history,
inputs=[
item_input,
history_state
],
outputs=[
history_state,
history_output
]
)브라우저 상태
브라우저 저장소에 값을 유지할 수 있습니다.
user_settings = gr.BrowserState({
"theme": "light",
"language": "ko"
})브라우저 상태는 페이지 새로고침이나 브라우저 종료 후에도 유지할 사용자 설정에 활용할 수 있습니다.
민감한 인증정보를 브라우저 상태에 무심코 저장해서는 안 됩니다.
25. Queue와 동시 실행 제어
머신러닝 추론은 시간이 오래 걸리거나 GPU를 사용할 수 있습니다.
사용자 여러 명이 동시에 요청하면 다음 문제가 발생할 수 있습니다.
GPU 메모리 부족
서버 과부하
응답 순서 혼란
모델 동시 실행 충돌
사용자가 진행 상태를 알 수 없음Gradio는 이벤트 요청을 관리하는 Queue 기능을 제공합니다. Queue를 사용하면 대기 중인 작업을 순서대로 처리하고 동시 실행 제한을 설정할 수 있습니다.
demo.queue(
default_concurrency_limit=2,
max_size=50
)실행:
demo.launch()이벤트별 동시 실행 제한
predict_button.click(
fn=predict,
inputs=image_input,
outputs=result_output,
concurrency_limit=1
)GPU 모델을 한 번에 하나씩만 실행하도록 제한할 수 있습니다.
사용자 1:
모델 실행 중
사용자 2:
대기열 1번
사용자 3:
대기열 2번모델도 번호표가 필요합니다.
26. Progress 표시하기
오래 걸리는 함수에는 진행률을 표시할 수 있습니다.
함수 매개변수에 `gr.Progress()` 기본값을 추가합니다.
import time
import gradio as gr
def long_process(
step_count: int,
progress=gr.Progress()
) -> str:
for index in progress.tqdm(
range(step_count),
desc="AI가 열심히 계산 중"
):
time.sleep(
0.1
)
return "작업 완료"Gradio 공식 가이드에 따르면 `gr.Progress` 객체를 함수 매개변수로 주입하고 0부터 1 사이 값이나 `tqdm()` 방식으로 진행 상황을 갱신할 수 있습니다.
직접 비율을 지정할 수도 있습니다.
def process_files(
files,
progress=gr.Progress()
):
total_count = len(
files
)
for index, file in enumerate(
files
):
progress(
(
index + 1
) / total_count,
desc=(
f"{index + 1}/"
f"{total_count} 처리 중"
)
)
analyze_file(
file
)
return "완료"27. Generator와 출력 스트리밍
일반 함수는 최종 결과를 한 번 반환합니다.
def generate_text(
prompt: str
) -> str:
return (
"최종 생성 문장"
)Generator는 중간 결과를 여러 번 전달합니다.
import time
def stream_text(
prompt: str
):
response = (
f"입력한 문장은 "
f"'{prompt}'입니다."
)
partial_text = ""
for character in response:
partial_text += character
time.sleep(
0.04
)
yield partial_textGradio에 연결하면 글자가 조금씩 출력됩니다.
demo = gr.Interface(
fn=stream_text,
inputs=gr.Textbox(),
outputs=gr.Textbox()
)
demo.queue()
demo.launch()Gradio는 함수가 `return` 대신 여러 번 `yield`하는 Generator일 때 순차적으로 출력을 갱신할 수 있습니다.
return:
완성품 한 번 배송
yield:
제작 과정을 조금씩 생중계LLM의 토큰 스트리밍과 이미지 생성 중간 결과 표시에 활용할 수 있습니다.
28. 오디오·영상 스트리밍
Gradio는 Generator에서 오디오나 영상 조각을 순차적으로 전달하는 스트리밍 출력을 지원합니다.
출력 컴포넌트에 `streaming=True`를 설정하고 함수가 미디어 조각을 `yield`하도록 구성합니다.
오디오 출력
audio_output = gr.Audio(
streaming=True,
autoplay=True
)영상 출력
video_output = gr.Video(
streaming=True,
autoplay=True
)실시간 입력도 지원됩니다.
마이크 음성 조각
→ 음성 인식 모델
→ 부분 인식 결과
웹캠 프레임
→ 객체 검출 모델
→ 박스가 그려진 영상Gradio 공식 스트리밍 입력 가이드는 카메라 이미지와 마이크 오디오 조각을 이벤트 함수에 전달하는 구조를 제공합니다.
실시간 처리는 일반 파일 업로드보다 복잡합니다.
처리 속도
네트워크 지연
프레임 누락
시간 제한
동시 사용자
GPU 처리량처음에는 단일 파일 방식으로 기능을 확인한 뒤 스트리밍으로 확장하는 편이 좋습니다.
29. ChatInterface로 챗봇 만들기
`ChatInterface`는 챗봇 UI를 빠르게 구성합니다.
함수는 일반적으로 두 값을 받습니다.
message
→ 현재 사용자 메시지
history
→ 이전 대화 기록기본 예:
import gradio as gr
def echo_chat(
message: str,
history: list[dict]
) -> str:
return (
f"방금 입력하신 메시지는 "
f"'{message}'입니다."
)
demo = gr.ChatInterface(
fn=echo_chat,
title="💬 메아리 챗봇",
description=(
"아직 AI는 아니지만 "
"당신의 말을 매우 성실하게 반복합니다."
),
examples=[
"안녕하세요",
"Gradio란 무엇인가요?",
"Python 함수를 설명해 주세요"
],
api_name="chat"
)
demo.launch()현재 ChatInterface 함수의 `history`에는 OpenAI 스타일의 역할·내용 딕셔너리가 전달되며, 함수는 문자열, 메시지 딕셔너리, 파일이나 Gradio 컴포넌트 등을 반환할 수 있습니다.
30. 스트리밍 챗봇 만들기
import time
import gradio as gr
def stream_chat(
message: str,
history: list[dict]
):
response = (
"Gradio는 Python 함수에 "
"웹 인터페이스를 연결하는 "
"도구입니다."
)
partial_response = ""
for character in response:
partial_response += character
time.sleep(
0.03
)
yield partial_response
demo = gr.ChatInterface(
fn=stream_chat,
title="🤖 스트리밍 AI 데모",
save_history=True,
api_name="stream_chat"
)
demo.queue()
demo.launch()`ChatInterface`는 Generator가 반환하는 부분 문자열을 이용해 스트리밍 응답을 표시할 수 있습니다. 또한 `save_history=True`를 설정하면 브라우저에 대화 기록을 보관하고 이전 대화를 선택할 수 있습니다.
실제 LLM 연결 위치
def chat(
message,
history
):
response = llm.generate(
message=message,
history=history
)
return responseGradio는 화면과 이벤트를 담당합니다.
모델 호출과 프롬프트, RAG, 도구 실행은 별도의 Python 로직에서 처리합니다.
31. 멀티모달 챗봇
텍스트뿐 아니라 이미지나 파일도 받는 챗봇을 만들 수 있습니다.
import gradio as gr
def multimodal_chat(
message: dict,
history: list[dict]
) -> str:
text = message.get(
"text",
""
)
files = message.get(
"files",
[]
)
return (
f"텍스트: {text}\n\n"
f"첨부 파일: {len(files)}개"
)
demo = gr.ChatInterface(
fn=multimodal_chat,
multimodal=True,
title="📎 멀티모달 챗봇"
)
demo.launch()`multimodal=True`일 때 첫 번째 함수 인수는 `text`와 `files`를 포함한 딕셔너리로 전달됩니다.
활용 예:
사진 + 질문
→ 이미지 질의응답
PDF + 질문
→ 문서 분석
음성 + 지시
→ 음성 비서
코드 파일 + 질문
→ 코드 리뷰32. 이미지·오디오·비디오 컴포넌트
Image
gr.Image(
sources=[
"upload",
"webcam",
"clipboard"
],
type="pil",
image_mode="RGB",
label="이미지 입력"
)`Image`는 이미지 업로드 입력 또는 이미지 출력으로 사용할 수 있습니다.
Audio
gr.Audio(
sources=[
"upload",
"microphone"
],
type="filepath",
label="음성 입력"
)오디오를 NumPy 배열과 샘플링 주파수 형태로 받으려면 다음과 같이 설정할 수 있습니다.
gr.Audio(
type="numpy"
)Video
gr.Video(
sources=[
"upload",
"webcam"
],
label="영상 입력"
)브라우저 재생을 위해서는 지원되는 컨테이너와 코덱 조합이 필요하며, Gradio가 가능한 경우 재생 가능한 형식으로 변환을 시도할 수 있습니다.
ImageEditor
gr.ImageEditor(
type="pil",
label="이미지 편집"
)ImageEditor는 브러시, 자르기, 레이어 등 간단한 편집 기능을 제공할 수 있습니다.
33. 파일 업로드와 다운로드
파일 입력
file_input = gr.File(
file_types=[
".pdf",
".txt",
".csv"
],
file_count="single",
label="분석할 파일"
)`File` 컴포넌트는 하나 또는 여러 개의 일반 파일을 업로드하는 입력이나 파일 다운로드 출력으로 사용할 수 있습니다.
파일 처리 함수
from pathlib import Path
def inspect_file(
file_path: str
) -> dict:
path = Path(
file_path
)
return {
"file_name": path.name,
"extension": path.suffix,
"size_bytes": path.stat().st_size
}파일 출력
def create_result_file(
text: str
) -> str:
output_path = Path(
"result.txt"
)
output_path.write_text(
text,
encoding="utf-8"
)
return str(
output_path
)출력 컴포넌트:
gr.File(
label="결과 다운로드"
)서버 파일을 사용자에게 노출할 때는 허용 경로와 파일 접근 보안을 확인해야 합니다. Gradio는 임의의 서버 파일이 공개되지 않도록 파일 접근 검사를 수행하며 필요할 때 `allowed_paths`를 명시할 수 있습니다.
34. Dataframe·JSON·Gallery
Dataframe
gr.Dataframe(
headers=[
"이름",
"점수",
"상태"
],
datatype=[
"str",
"number",
"str"
],
interactive=True
)Dataframe은 표 데이터를 입력받거나 출력하는 스프레드시트형 컴포넌트입니다.
JSON
gr.JSON(
label="API 결과"
)Python 딕셔너리나 리스트를 구조화해 표시할 때 편리합니다.
Gallery
gr.Gallery(
label="생성 이미지",
columns=3,
rows=2
)Gallery는 이미지와 영상을 격자로 표시하며 캡션을 함께 제공할 수 있습니다.
AnnotatedImage
객체 검출 박스나 세그멘테이션 마스크를 이미지 위에 표시할 수 있습니다.
gr.AnnotatedImage(
label="객체 검출 결과"
)AnnotatedImage는 기본 이미지와 사각형 또는 마스크 주석을 함께 표시합니다.
35. 모델과 무거운 자원 로딩하기
모델을 이벤트 함수 안에서 매번 불러오면 매우 느립니다.
좋지 않은 코드
def predict(
image
):
model = load_large_model()
return model.predict(
image
)요청할 때마다 모델을 로딩합니다.
사용자 요청
→ 모델 로딩
→ 예측
다음 사용자 요청
→ 모델 다시 로딩앱 시작 시 한 번 로딩
model = load_large_model()
def predict(
image
):
return model.predict(
image
)함수 캐시
from functools import lru_cache
@lru_cache(
maxsize=1
)
def get_model():
return load_large_model()
def predict(
image
):
model = get_model()
return model.predict(
image
)주의
여러 사용자가 같은 모델 객체를 공유할 수 있습니다.
모델이 추론 중 내부 상태를 수정한다면 스레드 안전성을 검토해야 합니다.
PyTorch 모델은 추론 전에 다음과 같이 설정합니다.
model.eval()그리고 기울기 계산을 비활성화합니다.
with torch.inference_mode():
output = model(
input_tensor
)36. Gradio 캐시 사용하기
Gradio 6은 함수 결과를 저장하는 캐시 기능을 제공합니다.
자동 캐시
import gradio as gr
@gr.cache
def classify_image(
image
):
return model.predict(
image
)입력 내용이 같으면 이전 결과를 재사용할 수 있습니다.
사용자별 캐시
@gr.cache(
per_session=True,
max_size=128
)
def analyze_document(
document
):
return run_analysis(
document
)캐시가 적합한 작업
이미지 분류
음성 전사
임베딩 계산
문서 구조 추출
결정적인 전처리캐시가 주의되는 작업
무작위 이미지 생성
Temperature가 있는 LLM 생성
현재 시간이 포함된 결과
외부 데이터가 계속 바뀌는 함수Gradio는 자동 `@gr.cache`와 직접 제어하는 `gr.Cache()`를 제공하며, 입력 내용 해시, 크기 제한, 메모리 제한, 사용자 세션 분리 등을 설정할 수 있습니다.
민감한 사용자 데이터를 캐시한다면 사용자별 분리와 보관 정책을 반드시 검토해야 합니다.
37. Gradio 앱을 API로 사용하기
Gradio 앱의 이벤트는 웹 UI뿐 아니라 API로 호출할 수 있습니다.
이벤트에 `api_name`을 지정합니다.
predict_button.click(
fn=predict,
inputs=image_input,
outputs=result_output,
api_name="predict"
)또는 Interface에 지정합니다.
demo = gr.Interface(
fn=predict,
inputs=gr.Image(),
outputs=gr.Label(),
api_name="predict"
)Gradio 앱의 API 페이지에서 입력과 출력 구조, Python Client 예제를 확인할 수 있습니다.
Gradio 공식 가이드는 앱 이벤트를 HTTP 요청, Python Client 또는 JavaScript Client로 호출하는 방법을 제공합니다.
Gradio 화면:
사람이 사용
Gradio API:
다른 프로그램이 사용웹 데모를 만들었는데 API도 함께 생기는 작은 보너스 상자입니다.
38. gradio_client 활용하기
`gradio_client`를 이용하면 다른 Python 프로그램에서 Gradio 앱을 호출할 수 있습니다.
설치
최근 Gradio를 설치했다면 의존성으로 포함될 수 있습니다.
별도로 설치하려면 다음 명령을 사용합니다.
python -m pip install --upgrade gradio_client로컬 앱 호출
from gradio_client import Client
client = Client(
"http://127.0.0.1:7860"
)
result = client.predict(
"홍길동",
3,
api_name="/greet"
)
print(
result
)파일 입력
from gradio_client import (
Client,
handle_file
)
client = Client(
"사용할 Gradio 앱 주소"
)
result = client.predict(
handle_file(
"sample.jpg"
),
api_name="/predict"
)`handle_file()`은 파일을 서버에 업로드하고 Gradio 컴포넌트가 처리할 수 있도록 준비합니다.
비동기 작업
job = client.submit(
"분석할 문장",
api_name="/analyze"
)
result = job.result()`submit()`은 Job 객체를 반환하며 나중에 결과를 가져올 수 있습니다.
39. 임시 공유 링크
로컬 앱을 잠시 외부에 공유할 수 있습니다.
demo.launch(
share=True
)공개된 임시 링크를 다른 사람에게 전달하면 로컬에서 실행 중인 앱에 접속할 수 있습니다.
Gradio의 공유 링크는 로컬 서버로 연결되는 임시 접근 경로이며, 장기간 운영하는 영구 배포 대신 데모나 테스트에 적합합니다.
주의사항
컴퓨터를 종료하면 앱도 종료
Python 프로세스가 멈추면 접속 불가
임시 URL
로컬 파일 접근 주의
민감한 데이터 입력 금지
서버 자원은 내 컴퓨터 사용임시 링크는 모델 시연을 위한 팝업 부스입니다.
회사 핵심 서비스를 팝업 부스에서 운영하면 안 됩니다.
40. 비밀번호 인증
간단한 사용자명과 비밀번호 인증을 설정할 수 있습니다.
demo.launch(
auth=(
"demo_user",
"strong_password"
)
)여러 계정:
demo.launch(
auth=[
(
"user1",
"password1"
),
(
"user2",
"password2"
)
]
)환경변수 사용:
import os
username = os.environ[
"GRADIO_USERNAME"
]
password = os.environ[
"GRADIO_PASSWORD"
]
demo.launch(
auth=(
username,
password
)
)Gradio 내장 인증은 간단한 접근 제어에 적합하지만 다중 인증, 로그인 실패 잠금, 세밀한 권한 관리 같은 강력한 보안 기능을 완전히 제공하지는 않습니다. 엄격한 보안이 필요한 서비스는 별도의 인증 시스템을 검토해야 합니다.
41. Hugging Face Spaces 배포
Gradio 앱을 영구 주소로 공개하려면 Hugging Face Spaces를 활용할 수 있습니다.
공식 공유 가이드에서는 `gradio deploy` 명령이나 브라우저 업로드 방식으로 앱을 Spaces에 배포할 수 있다고 안내합니다.
프로젝트 구조
digit_gradio_app/
├─ app.py
├─ requirements.txt
└─ README.mdrequirements.txt
gradio
numpy
pillow
pandas
scikit-learn터미널 배포
gradio deployCLI가 저장소와 앱 정보를 확인한 뒤 현재 폴더의 파일을 Spaces에 업로드합니다.
브라우저 배포
Hugging Face 계정 생성
→ 새 Space 생성
→ SDK로 Gradio 선택
→ app.py 업로드
→ requirements.txt 업로드
→ 자동 빌드비밀정보
API 키는 코드나 GitHub 저장소에 작성하지 않습니다.
Spaces 설정에서 Secret으로 등록하고 Python에서는 환경변수로 읽습니다.
import os
api_key = os.environ[
"MY_API_KEY"
]42. Docker 배포
자체 서버나 클라우드 VM에 배포하려면 Docker를 활용할 수 있습니다.
Gradio 공식 가이드는 앱과 Python 환경을 컨테이너로 패키징해 자체 서버에서 실행하는 방법을 제공합니다.
Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install \
--no-cache-dir \
-r requirements.txt
COPY . .
EXPOSE 7860
CMD [
"python",
"app.py"
]app.py 실행 설정
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
server_port=7860
)이미지 생성
docker build \
-t digit-gradio-app .컨테이너 실행
docker run \
--rm \
-p 7860:7860 \
digit-gradio-app실제 운영 환경에서는 다음 요소도 필요합니다.
Nginx 리버스 프록시
HTTPS
도메인
로그 수집
프로세스 재시작
CPU·메모리 제한
GPU 컨테이너 설정
인증
모니터링43. 실전 프로젝트 준비
이번에는 scikit-learn의 손글씨 숫자 데이터셋을 이용해 손글씨 숫자 AI 판독소를 만들어 보겠습니다.
프로젝트 기능
손글씨 숫자 이미지 업로드
이미지 흑백 변환
밝은 배경 자동 반전
숫자 영역 자르기
8×8 이미지 변환
머신러닝 숫자 예측
클래스별 확률 표시
전처리 이미지 확인
사용자별 예측 기록
모델 정확도 표시
API 엔드포인트 제공
Queue와 Progress 적용사용 기술
Gradio
→ 웹 화면과 이벤트
Pillow
→ 이미지 전처리
NumPy
→ 픽셀 배열
scikit-learn
→ 숫자 분류 모델
Pandas
→ 예측 기록 표프로젝트 구조
digit_gradio_app/
├─ app.py
└─ requirements.txt44. 손글씨 숫자 분류 모델
scikit-learn의 `load_digits()` 데이터는 0부터 9까지 손글씨 숫자의 8×8 이미지 특성을 제공합니다.
다음 모델을 사용하겠습니다.
입력:
8 × 8 이미지
특성:
64개 픽셀값
모델:
표준화 + 로지스틱 회귀
출력:
숫자 0~9모델 학습 함수:
from functools import lru_cache
from sklearn.datasets import (
load_digits
)
from sklearn.linear_model import (
LogisticRegression
)
from sklearn.metrics import (
accuracy_score
)
from sklearn.model_selection import (
train_test_split
)
from sklearn.pipeline import (
make_pipeline
)
from sklearn.preprocessing import (
StandardScaler
)
@lru_cache(
maxsize=1
)
def train_digit_model() -> dict:
digits = load_digits()
(
X_train,
X_test,
y_train,
y_test
) = train_test_split(
digits.data,
digits.target,
test_size=0.2,
random_state=2026,
stratify=digits.target
)
model = make_pipeline(
StandardScaler(),
LogisticRegression(
max_iter=3000,
random_state=2026
)
)
model.fit(
X_train,
y_train
)
predictions = model.predict(
X_test
)
accuracy = accuracy_score(
y_test,
predictions
)
return {
"model": model,
"accuracy": accuracy,
"sample_count": (
len(digits.data)
),
"feature_count": (
digits.data.shape[1]
)
}`lru_cache`를 사용해 모델을 한 번 학습한 뒤 재사용합니다.
45. 업로드 이미지 전처리
학습 데이터는 8×8 크기이며 어두운 배경에 밝은 숫자가 그려져 있습니다.
사용자가 올린 이미지는 다음처럼 다를 수 있습니다.
흰 종이에 검은 숫자
검은 화면에 흰 숫자
숫자가 한쪽에 치우침
여백이 많음
사진 크기가 매우 큼전처리 과정:
PIL 이미지 입력
→ 흑백 변환
→ 대비 조정
→ 배경이 밝으면 반전
→ 숫자 영역 탐색
→ 정사각형 여백 추가
→ 8×8 축소
→ 0~16 범위 변환코드:
import numpy as np
from PIL import (
Image,
ImageOps
)
def prepare_digit_image(
image: Image.Image
) -> tuple[
np.ndarray,
Image.Image
]:
grayscale_image = ImageOps.grayscale(
image
)
grayscale_image = ImageOps.autocontrast(
grayscale_image
)
image_array = np.asarray(
grayscale_image,
dtype=np.float32
)
if image_array.mean() > 127:
image_array = (
255
- image_array
)
threshold_value = max(
20.0,
float(
image_array.max()
) * 0.15
)
foreground_mask = (
image_array
>= threshold_value
)
coordinates = np.argwhere(
foreground_mask
)
if coordinates.size == 0:
raise ValueError(
"숫자 영역을 찾지 못했습니다."
)
(
minimum_y,
minimum_x
) = coordinates.min(
axis=0
)
(
maximum_y,
maximum_x
) = coordinates.max(
axis=0
)
cropped_array = image_array[
minimum_y:maximum_y + 1,
minimum_x:maximum_x + 1
]
cropped_image = Image.fromarray(
cropped_array.astype(
np.uint8
)
)
width, height = (
cropped_image.size
)
square_size = max(
width,
height
)
padding = max(
2,
square_size // 5
)
canvas_size = (
square_size
+ padding * 2
)
square_canvas = Image.new(
mode="L",
size=(
canvas_size,
canvas_size
),
color=0
)
paste_x = (
canvas_size - width
) // 2
paste_y = (
canvas_size - height
) // 2
square_canvas.paste(
cropped_image,
(
paste_x,
paste_y
)
)
resized_image = square_canvas.resize(
(
8,
8
),
Image.Resampling.LANCZOS
)
resized_array = np.asarray(
resized_image,
dtype=np.float32
)
model_input = (
resized_array
/ 255.0
* 16.0
).reshape(
1,
-1
)
preview_image = resized_image.resize(
(
256,
256
),
Image.Resampling.NEAREST
)
return (
model_input,
preview_image
)`NEAREST` 방식으로 확대하면 8×8 픽셀 구조를 선명하게 확인할 수 있습니다.
46. 숫자 예측 함수
from datetime import datetime
import gradio as gr
import pandas as pd
HISTORY_COLUMNS = [
"시간",
"예측 숫자",
"예측 확률"
]
def predict_digit(
image,
history: list,
progress=gr.Progress()
):
if image is None:
raise gr.Error(
"손글씨 숫자 이미지를 "
"업로드해 주세요."
)
progress(
0.15,
desc="이미지를 확인하고 있습니다."
)
try:
(
model_input,
preview_image
) = prepare_digit_image(
image
)
except ValueError as error:
raise gr.Error(
str(
error
)
) from error
progress(
0.6,
desc="숫자 특징을 분석하고 있습니다."
)
model_bundle = train_digit_model()
model = model_bundle[
"model"
]
prediction = int(
model.predict(
model_input
)[0]
)
probabilities = (
model.predict_proba(
model_input
)[0]
)
prediction_scores = {
str(
digit
): float(
probability
)
for digit, probability in enumerate(
probabilities
)
}
confidence = float(
probabilities[
prediction
]
)
progress(
0.9,
desc="결과표를 정리하고 있습니다."
)
summary = (
f"## 예측 숫자: **{prediction}**\n\n"
f"- 예측 확률: **{confidence:.2%}**\n"
f"- 입력 특성: 8×8 픽셀\n"
f"- 참고: 업로드 이미지와 "
f"학습 데이터 형태가 다르면 "
f"정확도가 낮아질 수 있습니다."
)
updated_history = list(
history
)
updated_history.append({
"시간": (
datetime.now()
.strftime(
"%H:%M:%S"
)
),
"예측 숫자": prediction,
"예측 확률": confidence
})
history_dataframe = pd.DataFrame(
updated_history,
columns=HISTORY_COLUMNS
)
progress(
1.0,
desc="예측 완료"
)
return (
prediction_scores,
preview_image,
summary,
updated_history,
history_dataframe
)`gr.Label`에 클래스 이름과 확률의 딕셔너리를 반환하면 순위와 확률을 표시할 수 있습니다.
47. Blocks 기반 화면 구성
import gradio as gr
with gr.Blocks(
title="손글씨 숫자 AI 판독소",
fill_width=True
) as demo:
gr.Markdown(
"""
# 🔢 손글씨 숫자 AI 판독소
숫자 하나가 크게 보이는 이미지를 업로드하면
머신러닝 모델이 0부터 9 중 어떤 숫자인지 예측합니다.
"""
)
history_state = gr.State(
[]
)
with gr.Row():
with gr.Column(
scale=1
):
image_input = gr.Image(
type="pil",
image_mode="RGB",
sources=[
"upload",
"webcam",
"clipboard"
],
label="손글씨 숫자 이미지"
)
with gr.Row():
predict_button = gr.Button(
"🔍 숫자 판독하기",
variant="primary"
)
clear_button = gr.Button(
"입력 지우기"
)
with gr.Column(
scale=1
):
prediction_output = gr.Label(
label="숫자별 예측 확률",
num_top_classes=10
)
processed_output = gr.Image(
label="모델이 본 8×8 이미지",
image_mode="L"
)
summary_output = gr.Markdown()
with gr.Tab(
"📜 예측 기록"
):
history_output = gr.Dataframe(
headers=HISTORY_COLUMNS,
datatype=[
"str",
"number",
"number"
],
interactive=False,
label="현재 사용자 세션 기록"
)
clear_history_button = gr.Button(
"기록 초기화"
)
with gr.Tab(
"🧠 모델 정보"
):
model_bundle = train_digit_model()
gr.Markdown(
f"""
### 모델 구성
- 알고리즘: 표준화 + 로지스틱 회귀
- 학습 데이터: {model_bundle["sample_count"]:,}개
- 입력 특성: {model_bundle["feature_count"]}개
- 내부 평가 정확도: {model_bundle["accuracy"]:.2%}
이 정확도는 scikit-learn의 원본 숫자 데이터에서
분리한 평가 데이터에 대한 결과입니다.
사용자가 촬영하거나 그린 이미지에서는
배경, 글씨체, 크기에 따라 성능이 달라질 수 있습니다.
"""
)48. 예측 기록 State
이벤트를 연결합니다.
predict_event = predict_button.click(
fn=predict_digit,
inputs=[
image_input,
history_state
],
outputs=[
prediction_output,
processed_output,
summary_output,
history_state,
history_output
],
api_name="predict_digit",
concurrency_limit=2
)이미지 입력 초기화:
clear_button.click(
fn=lambda: (
None,
None,
None,
""
),
inputs=None,
outputs=[
image_input,
prediction_output,
processed_output,
summary_output
]
)기록 초기화 함수:
def clear_prediction_history():
empty_dataframe = pd.DataFrame(
columns=HISTORY_COLUMNS
)
return (
[],
empty_dataframe
)이벤트 연결:
clear_history_button.click(
fn=clear_prediction_history,
inputs=None,
outputs=[
history_state,
history_output
]
)예측 기록은 `gr.State`에 저장되므로 다른 사용자의 기록과 분리됩니다.
페이지를 새로고침하면 현재 세션 기록이 초기화될 수 있습니다.
49. 전체 프로젝트 코드
다음 내용을 `app.py`에 저장합니다.
from datetime import datetime
from functools import lru_cache
import gradio as gr
import numpy as np
import pandas as pd
from PIL import (
Image,
ImageOps
)
from sklearn.datasets import (
load_digits
)
from sklearn.linear_model import (
LogisticRegression
)
from sklearn.metrics import (
accuracy_score
)
from sklearn.model_selection import (
train_test_split
)
from sklearn.pipeline import (
make_pipeline
)
from sklearn.preprocessing import (
StandardScaler
)
HISTORY_COLUMNS = [
"시간",
"예측 숫자",
"예측 확률"
]
@lru_cache(
maxsize=1
)
def train_digit_model() -> dict:
"""손글씨 숫자 분류 모델을 학습합니다."""
digits = load_digits()
(
X_train,
X_test,
y_train,
y_test
) = train_test_split(
digits.data,
digits.target,
test_size=0.2,
random_state=2026,
stratify=digits.target
)
model = make_pipeline(
StandardScaler(),
LogisticRegression(
max_iter=3000,
random_state=2026
)
)
model.fit(
X_train,
y_train
)
predictions = model.predict(
X_test
)
accuracy = accuracy_score(
y_test,
predictions
)
return {
"model": model,
"accuracy": accuracy,
"sample_count": (
len(digits.data)
),
"feature_count": (
digits.data.shape[1]
)
}
def prepare_digit_image(
image: Image.Image
) -> tuple[
np.ndarray,
Image.Image
]:
"""업로드 이미지를 숫자 모델 입력으로 변환합니다."""
grayscale_image = ImageOps.grayscale(
image
)
grayscale_image = ImageOps.autocontrast(
grayscale_image
)
image_array = np.asarray(
grayscale_image,
dtype=np.float32
)
if image_array.size == 0:
raise ValueError(
"빈 이미지입니다."
)
if image_array.mean() > 127:
image_array = (
255
- image_array
)
maximum_pixel = float(
image_array.max()
)
if maximum_pixel <= 0:
raise ValueError(
"숫자를 확인할 수 없습니다."
)
threshold_value = max(
20.0,
maximum_pixel * 0.15
)
foreground_mask = (
image_array
>= threshold_value
)
coordinates = np.argwhere(
foreground_mask
)
if coordinates.size == 0:
raise ValueError(
"숫자 영역을 찾지 못했습니다."
)
(
minimum_y,
minimum_x
) = coordinates.min(
axis=0
)
(
maximum_y,
maximum_x
) = coordinates.max(
axis=0
)
cropped_array = image_array[
minimum_y:maximum_y + 1,
minimum_x:maximum_x + 1
]
cropped_image = Image.fromarray(
cropped_array.astype(
np.uint8
)
)
width, height = (
cropped_image.size
)
square_size = max(
width,
height
)
padding = max(
2,
square_size // 5
)
canvas_size = (
square_size
+ padding * 2
)
square_canvas = Image.new(
mode="L",
size=(
canvas_size,
canvas_size
),
color=0
)
paste_x = (
canvas_size - width
) // 2
paste_y = (
canvas_size - height
) // 2
square_canvas.paste(
cropped_image,
(
paste_x,
paste_y
)
)
resized_image = square_canvas.resize(
(
8,
8
),
Image.Resampling.LANCZOS
)
resized_array = np.asarray(
resized_image,
dtype=np.float32
)
model_input = (
resized_array
/ 255.0
* 16.0
).reshape(
1,
-1
)
preview_image = resized_image.resize(
(
256,
256
),
Image.Resampling.NEAREST
)
return (
model_input,
preview_image
)
def predict_digit(
image: Image.Image | None,
history: list,
progress=gr.Progress()
):
"""이미지의 손글씨 숫자를 예측합니다."""
if image is None:
raise gr.Error(
"손글씨 숫자 이미지를 "
"업로드해 주세요."
)
progress(
0.15,
desc="이미지를 확인하고 있습니다."
)
try:
(
model_input,
preview_image
) = prepare_digit_image(
image
)
except ValueError as error:
raise gr.Error(
str(
error
)
) from error
progress(
0.6,
desc="숫자 특징을 분석하고 있습니다."
)
model_bundle = train_digit_model()
model = model_bundle[
"model"
]
prediction = int(
model.predict(
model_input
)[0]
)
probabilities = (
model.predict_proba(
model_input
)[0]
)
prediction_scores = {
str(
digit
): float(
probability
)
for digit, probability in enumerate(
probabilities
)
}
confidence = float(
probabilities[
prediction
]
)
progress(
0.85,
desc="결과를 정리하고 있습니다."
)
summary = (
f"## 예측 숫자: **{prediction}**\n\n"
f"- 예측 확률: **{confidence:.2%}**\n"
f"- 모델 입력: 8×8 픽셀\n"
f"- 분류 대상: 0부터 9\n\n"
f"> 촬영 조건과 글씨체에 따라 "
f"예측 결과가 달라질 수 있습니다."
)
updated_history = list(
history
)
updated_history.append({
"시간": (
datetime.now()
.strftime(
"%H:%M:%S"
)
),
"예측 숫자": prediction,
"예측 확률": confidence
})
history_dataframe = pd.DataFrame(
updated_history,
columns=HISTORY_COLUMNS
)
progress(
1.0,
desc="예측 완료"
)
return (
prediction_scores,
preview_image,
summary,
updated_history,
history_dataframe
)
def clear_inputs():
"""입력과 현재 예측 결과를 초기화합니다."""
return (
None,
None,
None,
""
)
def clear_prediction_history():
"""현재 세션의 예측 기록을 초기화합니다."""
empty_dataframe = pd.DataFrame(
columns=HISTORY_COLUMNS
)
return (
[],
empty_dataframe
)
model_bundle = train_digit_model()
with gr.Blocks(
title="손글씨 숫자 AI 판독소",
fill_width=True
) as demo:
gr.Markdown(
"""
# 🔢 손글씨 숫자 AI 판독소
숫자 하나가 크게 보이는 이미지를 업로드하면
머신러닝 모델이 **0부터 9** 중 어떤 숫자인지 예측합니다.
흰 종이에 진한 색으로 숫자를 크게 적어 촬영하면
비교적 좋은 결과를 얻을 수 있습니다.
"""
)
history_state = gr.State(
[]
)
with gr.Row():
with gr.Column(
scale=1
):
image_input = gr.Image(
type="pil",
image_mode="RGB",
sources=[
"upload",
"webcam",
"clipboard"
],
label="손글씨 숫자 이미지"
)
with gr.Row():
predict_button = gr.Button(
"🔍 숫자 판독하기",
variant="primary"
)
clear_button = gr.Button(
"입력 지우기"
)
with gr.Column(
scale=1
):
prediction_output = gr.Label(
label="숫자별 예측 확률",
num_top_classes=10
)
processed_output = gr.Image(
label="모델이 본 8×8 이미지",
image_mode="L"
)
summary_output = gr.Markdown(
"""
이미지를 업로드하고
**숫자 판독하기** 버튼을 누르세요.
"""
)
with gr.Tab(
"📜 예측 기록"
):
history_output = gr.Dataframe(
headers=HISTORY_COLUMNS,
datatype=[
"str",
"number",
"number"
],
interactive=False,
label="현재 사용자 세션 기록"
)
clear_history_button = gr.Button(
"기록 초기화"
)
with gr.Tab(
"🧠 모델 정보"
):
gr.Markdown(
f"""
### 모델 구성
- 알고리즘: 표준화 + 로지스틱 회귀
- 학습 데이터: {model_bundle["sample_count"]:,}개
- 입력 특성: {model_bundle["feature_count"]}개
- 내부 평가 정확도: {model_bundle["accuracy"]:.2%}
### 주의사항
내부 평가 정확도는 원본 숫자 데이터에서 분리한
평가 데이터에 대한 결과입니다.
사용자가 촬영한 이미지에는 글씨체, 그림자,
배경, 카메라 각도 차이가 있으므로
실제 정확도는 달라질 수 있습니다.
"""
)
predict_button.click(
fn=predict_digit,
inputs=[
image_input,
history_state
],
outputs=[
prediction_output,
processed_output,
summary_output,
history_state,
history_output
],
api_name="predict_digit",
concurrency_limit=2
)
clear_button.click(
fn=clear_inputs,
inputs=None,
outputs=[
image_input,
prediction_output,
processed_output,
summary_output
]
)
clear_history_button.click(
fn=clear_prediction_history,
inputs=None,
outputs=[
history_state,
history_output
]
)
if __name__ == "__main__":
demo.queue(
default_concurrency_limit=2,
max_size=30
)
demo.launch(
theme=gr.themes.Soft(),
footer_links=[
"api",
"settings"
]
)Gradio 6에서는 테마, CSS 등 앱 전체에 적용되는 설정을 `launch()`에 전달하는 방식이 사용됩니다.
50. 실행 및 배포 방법
requirements.txt
gradio
numpy
pillow
pandas
scikit-learn가상환경
Windows
python -m venv venv
venv\Scripts\activatemacOS·Linux
python3 -m venv venv
source venv/bin/activate설치
python -m pip install \
-r requirements.txt실행
python app.py임시 공유
demo.launch(
share=True
)전체 코드에서는 `launch()` 부분을 다음처럼 변경할 수 있습니다.
demo.launch(
share=True,
theme=gr.themes.Soft()
)Hugging Face Spaces
프로젝트 폴더에서 다음 명령을 실행합니다.
gradio deploy또는 Space를 생성한 뒤 `app.py`와 `requirements.txt`를 업로드합니다.
51. 성능과 보안 주의사항
모델을 요청마다 로딩하지 않는다
앱 시작 시 한 번 로딩
→ 요청마다 추론만 실행GPU 모델의 동시 실행을 제한한다
concurrency_limit=1GPU 메모리와 모델 특성에 맞게 설정합니다.
Queue 크기를 무한히 늘리지 않는다
대기 사용자가 너무 많으면 응답 시간이 지나치게 길어집니다.
demo.queue(
max_size=50
)업로드 파일을 신뢰하지 않는다
확인할 내용:
확장자
MIME 유형
파일 크기
이미지 해상도
압축 폭탄
악성 파일
파일명서버 내부 파일을 노출하지 않는다
사용자에게 반환할 파일 경로를 제한하고 `allowed_paths` 설정을 주의해서 사용합니다.
API 키를 코드에 작성하지 않는다
import os
api_key = os.environ[
"MODEL_API_KEY"
]기본 인증을 완전한 보안 시스템으로 간주하지 않는다
중요한 서비스에는 OAuth, 세션 관리, 접근 권한, 속도 제한을 별도로 설계합니다.
사용자별 데이터가 전역 변수에 섞이지 않게 한다
공용 모델:
전역 공유 가능
사용자 대화 기록:
gr.State 또는 외부 DB공유 링크에 민감한 기능을 올리지 않는다
임시 공유 URL도 인터넷에서 접근할 수 있습니다.
생성형 AI 출력에 검증 절차를 둔다
잘못된 정보
유해한 출력
개인정보 노출
프롬프트 인젝션
도구 오작동Gradio는 UI를 제공하지만 모델 출력의 안전성과 정확성을 보장하지는 않습니다.
52. 자주 발생하는 오류
오류 1. gradio를 찾을 수 없음
ModuleNotFoundError:
No module named 'gradio'설치:
python -m pip install --upgrade gradio확인:
python -m pip show gradio현재 Python:
python -c "import sys; print(sys.executable)"오류 2. Python 버전이 낮음
Gradio 6은 Python 3.10 이상을 요구합니다.
확인:
python --version낮은 버전이라면 새 Python 환경을 구성합니다.
오류 3. 인터넷의 오래된 코드가 실행되지 않음
증상:
unexpected keyword argument
attribute not found
parameter has been removed원인:
예제는 Gradio 3 또는 4 기준
현재 환경은 Gradio 6버전 확인:
import gradio as gr
print(
gr.__version__
)Gradio 6 마이그레이션 문서에서 제거되거나 이동한 매개변수를 확인합니다.
오류 4. 함수 입력 개수가 맞지 않음
함수:
def predict(
text,
temperature
):
...잘못된 연결:
inputs=[
text_input
]수정:
inputs=[
text_input,
temperature_slider
]오류 5. 함수 반환값 개수가 맞지 않음
출력 컴포넌트가 두 개라면 함수도 두 값을 반환해야 합니다.
return (
prediction,
confidence
)오류 6. 이미지 자료형이 예상과 다름
gr.Image(
type="pil"
)함수는 PIL 이미지 객체를 받습니다.
gr.Image(
type="numpy"
)함수는 NumPy 배열을 받습니다.
자료형을 출력해 확인합니다.
print(
type(image)
)오류 7. 버튼이 작동하지 않음
이벤트 연결이 `Blocks` 내부에서 정의되었는지 확인합니다.
with gr.Blocks() as demo:
button = gr.Button()
output = gr.Textbox()
button.click(
fn=run,
outputs=output
)오류 8. State가 사용자 사이에 섞임
전역 리스트를 사용했을 수 있습니다.
history = []수정:
history_state = gr.State(
[]
)오류 9. 페이지를 새로고침하자 State가 사라짐
`gr.State`는 현재 페이지 세션을 위한 상태입니다.
새로고침 후에도 유지하려면 다음 중 하나를 검토합니다.
gr.BrowserState
데이터베이스
Redis
사용자 계정 기반 저장오류 10. GPU 메모리 부족
CUDA out of memory대응:
동시 실행 수 줄이기
입력 이미지 크기 줄이기
배치 크기 줄이기
작은 모델 사용
모델 양자화 검토
사용하지 않는 모델 해제concurrency_limit=1오류 11. 요청이 계속 대기함
확인할 내용:
앞 작업이 종료되지 않음
Queue 동시 실행 수가 너무 낮음
Generator가 끝나지 않음
외부 API 응답 지연
GPU 작업 멈춤Queue 크기와 이벤트 실행 로그를 확인합니다.
오류 12. 공유 링크가 열리지 않음
로컬 Python 프로세스 종료
네트워크 차단
공유 터널 문제
앱 시작 오류터미널 로그를 먼저 확인합니다.
임시 공유 대신 Hugging Face Spaces나 자체 서버 배포를 검토합니다.
오류 13. 다운로드 파일 접근이 차단됨
Gradio가 허용하지 않은 서버 경로일 수 있습니다.
사용자 업로드 파일, 현재 작업 디렉터리의 생성 파일 또는 명시적으로 허용된 경로인지 확인합니다.
오류 14. ChatInterface 함수 오류
기본 ChatInterface 함수는 다음 구조를 사용합니다.
def chat(
message,
history
):
return "응답"멀티모달 모드에서는 `message`가 딕셔너리입니다.
def chat(
message: dict,
history
):
text = message["text"]
files = message["files"]오류 15. 스트리밍이 한 번에 출력됨
함수가 Generator인지 확인합니다.
잘못된 예:
def generate():
result = ""
for token in tokens:
result += token
return result스트리밍:
def generate():
result = ""
for token in tokens:
result += token
yield resultQueue도 활성화합니다.
demo.queue()53. 연습 문제
문제 1
이름과 직업을 입력받아 자기소개 문장을 반환하는 Interface를 만드세요.
문제 2
숫자 두 개와 연산자를 입력받는 계산기 앱을 만드세요.
더하기
빼기
곱하기
나누기0으로 나누는 경우 `gr.Error`를 발생시키세요.
문제 3
Textbox에서 Enter를 누르면 문장 길이를 분석하도록 `submit` 이벤트를 연결하세요.
문제 4
Blocks와 Row를 사용해 입력창과 결과창을 좌우로 배치하세요.
문제 5
다음 세 개의 탭을 만드세요.
텍스트 분석
이미지 분석
모델 정보문제 6
버튼 클릭 이벤트가 끝난 뒤 `.success()`로 완료 메시지를 표시하세요.
문제 7
예측 결과를 `gr.State`에 저장하고 Dataframe으로 출력하세요.
문제 8
현재 사용자의 설정값을 `gr.BrowserState`에 저장하세요.
문제 9
5초 동안 진행되는 가상 작업에 Progress 표시를 추가하세요.
문제 10
Generator를 이용해 문장을 한 글자씩 출력하세요.
문제 11
`ChatInterface`로 사용자의 메시지를 거꾸로 반환하는 챗봇을 만드세요.
문제 12
멀티모달 ChatInterface에서 첨부 파일 개수와 입력 문장을 출력하세요.
문제 13
이미지를 입력받아 흑백 이미지로 변환하는 앱을 만드세요.
문제 14
CSV 파일을 업로드하고 행 수, 열 수, 결측값 수를 JSON으로 반환하세요.
문제 15
예측 버튼에 다음 옵션을 적용하세요.
api_name:
predict
concurrency_limit:
1문제 16
Gradio 앱을 `gradio_client`로 호출하세요.
문제 17
앱에 간단한 사용자명·비밀번호 인증을 추가하세요.
문제 18
손글씨 숫자 판독소에 다음 기능을 추가하세요.
예측 결과 CSV 다운로드
확률 50% 미만 경고
최근 기록 10개만 유지
숫자별 예측 횟수 차트
사용자 피드백 버튼54. 핵심 요약
설치
python -m pip install --upgrade gradioInterface
demo = gr.Interface(
fn=predict,
inputs=gr.Textbox(),
outputs=gr.Textbox()
)실행
demo.launch()Blocks
with gr.Blocks() as demo:
input_component = gr.Textbox()
button = gr.Button()
output_component = gr.Textbox()
button.click(
fn=process,
inputs=input_component,
outputs=output_component
)여러 입력
inputs=[
text_input,
slider_input
]여러 출력
outputs=[
result_output,
score_output
]State
history_state = gr.State(
[]
)Queue
demo.queue(
default_concurrency_limit=2
)Progress
def process(
value,
progress=gr.Progress()
):
progress(
0.5,
desc="처리 중"
)스트리밍
def stream():
result = ""
for token in tokens:
result += token
yield resultChatInterface
demo = gr.ChatInterface(
fn=chat
)멀티모달 채팅
demo = gr.ChatInterface(
fn=chat,
multimodal=True
)임시 공유
demo.launch(
share=True
)인증
demo.launch(
auth=(
"username",
"password"
)
)Gradio Client
from gradio_client import Client
client = Client(
"앱 주소"
)
result = client.predict(
"입력값",
api_name="/predict"
)Spaces 배포
gradio deploy55. 마무리
이번 시간에는 Gradio를 이용해 Python 함수와 머신러닝·생성형 AI 모델을 웹 데모로 만드는 방법을 알아보았습니다.
Gradio의 가장 기본적인 원리는 매우 단순합니다.
Python 함수
+ 입력 컴포넌트
+ 출력 컴포넌트
= 실행 가능한 웹 데모빠른 데모에는 `Interface`를 사용했습니다.
gr.Interface(
fn=predict,
inputs=input_component,
outputs=output_component
)복잡한 화면에는 `Blocks`를 사용했습니다.
Row와 Column
Tab과 Accordion
여러 버튼
이벤트 연결
State
Queue챗봇에는 `ChatInterface`를 사용했습니다.
gr.ChatInterface(
fn=chat
)Gradio의 핵심 구성 요소를 다시 정리해 보겠습니다.
Component
→ 데이터를 입력하고 결과를 표시
Event
→ 사용자 행동과 Python 함수 연결
Interface
→ 함수 하나를 빠르게 웹 데모로 변환
Blocks
→ 복잡한 화면과 데이터 흐름 구성
State
→ 사용자별 값 유지
Queue
→ 요청 순서와 동시 실행 관리
Progress
→ 작업 진행 상황 표시
Generator
→ 중간 결과 스트리밍
Client
→ Gradio 앱을 API로 호출Gradio를 이용하면 모델 코드를 거의 변경하지 않고도 사용자가 직접 체험할 수 있는 화면을 만들 수 있습니다.
개발 전:
model.predict(image)
개발 후:
이미지 업로드
→ 분석 버튼
→ 확률 차트
→ 결과 다운로드
→ API 제공같은 모델이지만 사용 가능성은 크게 달라집니다.
터미널 속 모델은 개발자만 사용할 수 있습니다.
웹 인터페이스를 얻은 모델은 동료, 고객, 연구자, 사용자와 대화할 수 있습니다.
모델:
“정확도는 97%입니다.”
사용자:
“그래서 어디에 이미지를 넣나요?”
Gradio:
“입력창과 버튼을 준비했습니다.”다만 Gradio가 모델 자체를 더 정확하게 만들어 주는 것은 아닙니다.
좋은 UI
≠
좋은 모델
화려한 확률 표시
≠
검증된 예측
AI라는 제목
≠
지능적인 결과데이터 품질, 모델 평가, 오류 분석, 보안, 개인정보 보호는 여전히 개발자의 책임입니다.
Gradio는 AI 모델을 포장하는 상자입니다.
상자가 아무리 예뻐도 안에 든 모델이 틀린 답을 내놓으면 사용자는 예쁘게 포장된 오답을 받게 됩니다.
그러므로 AI 데모를 만들 때는 다음 질문을 함께 던져야 합니다.
모델은 어떤 데이터로 학습했는가?
어떤 상황에서 잘못 예측하는가?
확률을 어떻게 해석해야 하는가?
사용자 데이터는 어디에 저장되는가?
동시 사용자 요청을 감당할 수 있는가?
오류가 발생했을 때 무엇을 보여줄 것인가?이 질문에 답할 수 있을 때 Gradio 데모는 단순한 장난감에서 실제 검증 도구로 성장합니다.
이제 머신러닝 모델이 완성되었다면 터미널 결과만 캡처하지 마세요.
입력 컴포넌트를 연결하고, 버튼을 달고, 결과를 스트리밍해 보세요.
당신의 Python 함수가 브라우저 무대에서 첫 공연을 시작할 차례입니다. 🎭🤖
다음 편 예고
[Python 완전정복 시리즈 #35] Hugging Face Transformers 완벽 이해하기 | 사전 학습 AI 모델을 Python에서 사용하는 방법
다음 시간에는 Hugging Face 생태계와 Transformers 라이브러리를 이용해 사전 학습된 AI 모델을 불러오고 실행하는 방법을 알아봅니다.
Pipeline, Tokenizer, Model, 감정 분석, 문장 분류, 텍스트 생성, 요약, 번역, 이미지 분류, 모델 저장, GPU 실행, Gradio 데모 연결까지 실습해 보겠습니다.
#Python #파이썬 #Python강좌 #파이썬기초 #Gradio #그라디오 #머신러닝 #딥러닝 #생성형AI #AI웹앱 #웹데모 #Interface #Blocks #ChatInterface #AI챗봇 #멀티모달 #이미지분류 #음성인식 #LLM #State #Queue #Streaming #HuggingFace #HuggingFaceSpaces #scikitlearn #모델배포 #Python웹개발 #코딩공부 #프로그래밍
