[Gradio] Gradio 기초

2024. 3. 25. 23:43Developers 공간 [Shorts]/Software Basic

728x90
반응형
<분류>
A. 수단
- OS/Platform/Tool : Linux, Kubernetes(k8s), Docker, AWS
- Package Manager : node.js, yarn, brew, 
- Compiler/Transpillar : React, Nvcc, gcc/g++, Babel, Flutter

- Module Bundler  : React, Webpack, Parcel

B. 언어
- C/C++, python, Javacsript, Typescript, Go-Lang, CUDA, Dart, HTML/CSS

C. 라이브러리 및 프레임워크 및 SDK
- OpenCV, OpenCL, FastAPI, PyTorch, Tensorflow, Nsight

 


1. What? (현상)

 

빠르게 Interactive Web App을 python으로 구축하기 위해 Streamlit, Dash, Gradio 등을 활용하곤 합니다. StreamlitGradio 모두 직관적으로 쉽게 설계할 수 있지만, Dash는 React와 웹개발에 대한 지식이 다소 필요해 Learning Curve가 상대적으로 steep합니다. 하지만 Dash가 개인화된 인터페이스를 구축하는데 유리하며 Streamlit과 함께 활발한 Community를 형성하고 있습니다.

** Learning Curve : 학습 곡선으로, 배우기에 점점 어려울 수록 가파르다 혹은 높다고 합니다.

** Ecosystem : Software 및 Platform 간의 compatibility 및 comprehensive solution을 보장하는 네트워크입니다.

** Community : collaboration을 위한 discussion이나 problem report 등이 형성된 네트워크입니다.

 

이 중 이번 글에서는 Gradio를 다뤄볼 것이며, Gradio는 Machine Learning 모델을 위한 Web Demo를 만들기 위해 주로 사용하는 python기반의 오픈소스 Front-end 프레임워크 및 패키지입니다. 주로 Generative AI 분야에서 conversational interface를 제공하기위해 인기를 얻기 시작했으며, LLM이나 Diffusion같은 모델의 Demo를 보일 때 사용되고 있습니다.

 

설치 부터 시작해서 네번의 단계를 통해 발전시키며, 어떤 함수들이 있는지 간단히 살펴보겠습니다.


2. Why? (원인)

  • X

3. How? (해결책)

 

0. 설치

 

먼저 아래와 같은 명령어로 gradio를 쉽게 설치할 수 있습니다. 

** Gradio에서 활용한 __future__라는 annotations가 python3.7버전부터 사용가능하므로(3.6도 불가능), 반드시 python 버전을 미리 확인하시길 추천드립니다.

pip3 install gradio

 


설치를 해보았으니 gradio demo를 띄우기 위한 가장 쉽게 구할 수 있는 toy example들을 네번의 단계에 나눠서 보이겠습니다. 

 

1. 첫번째 단계 : 서버를 띄우기

 

Interface() 클래스를 활용해 GUI를 구현했습니다. 이처럼 web-based GUI Demo를 만들기 위한 high-level class에는 Interface, ChatInterface, TabbedInterface, Blocks 가 있습니다.

import gradio as gr

def greet(name):
    return "Hello " + name + "!"

demo = gr.Interface(fn=greet, inputs="text", outputs="text")

demo.launch(server_port=45555, server_name="0.0.0.0")

 

 

이 코드를 돌린 나의 서버의 IP가 11.12.13.14라고 가정하겠습니다. 위와 같이 실행한다면 서버 내부에서는 0.0.0.0:45555, 서버 외부에서는 11.12.13.14:45555를 통해 들어가 확인할 수 있습니다. 아래는 띄워본 결과입니다.

[첫번째 단계 결과]

 


 

2. 두번째 단계 : Layout 만들어보기

 

위에서 Interface 클래스를 활용한 것과 같이 이번엔 Blocks() 클래스를 활용해 레이아웃을 만들어 보겠습니다. title과 css를 아래와 같이 줄 수 있습니다.

 

코드를 살펴보면 Row()Cloumn()을 활용해 레이아웃을 꾸며줄 수 있습니다. Row() 이하에 있는 Element들은 Row로 정렬되며, Column() 이하는 Element들이 Column으로 정렬됩니다.

 

이외에 사용한 Element들은 아래와 같습니다.

  • Markdown : Markdown Output을 만들어내기 위해 사용하는 component입니다.
  • File : 파일을 업로드하기 위한 component입니다.
  • Image : 이미지를 업로드하고 display하기 위한 component입니다.
  • Textbox : Text를 받아 display하기 위한 component입니다.

 

import gradio as gr

css=".gradio-container{background-color:white}"
with gr.Blocks(title="My Demo", css=css) as demo:
    gr.Markdown("""
            # The title here
            The description here
    """)
    with gr.Row():
        with gr.Row(): # Row-wise align after this
        #with gr.Column(): # Column-wise align after this
            gr.File(type="filepath", label="This is gr.File()")
            gr.Image(label="This is gr.Image()")
        #with gr.Column():
        with gr.Row():
            gr.Image(label="This is gr.Image()")
            gr.Textbox(label="This is gr.Textbox()", visible=True)
            
demo.launch(server_port=45555, server_name="0.0.0.0")

 

위 내용을 보면 RowColumn의 차이를 보이기 위해 Column이 주석처리 되어있습니다. 아래는 Row로 구현되었을 때의 결과입니다.

[두번째 단계 결과 : Row를 활용한 Layout]

 

다음으로 아래는 위 주석처리된 Row 대신에 Column을  사용했을 때의 결과입니다.

[두번째 단계 결과 : Column을 활용한 Layout]

 


 

3. 세번째 단계 : Event Listener 만들어보기

 

이번엔 어떤 component의 변화에 대해 Event Listener를 추가하는 내용을 만들어보겠습니다.

 

아래는 .psd 파일을 받아 이미지 layer 여러개로 나누기 위해 callback 함수들을 만들어준 결과입니다.

** PSD(Photoshop Document) 파일 :  Adobe Photoshop의 독점 파일 포맷으로, Raster 포맷을 가지고 있습니다. 손실형 압축을 하지 않아 용량이 크지만 포토샵에서 작업한 결과 그대로의 정보를 가지고 있기 때문에 레이어 정보 및 다양한 편집 설정 정보를 가지고 있습니다. 

** 포토샵에서 문자나 모양은 벡터(Vector) 또는 비트맵(Bitmap 혹은 Raster) 이미지로 표현되는데, 포토샵에서 편집을 하기 위해서는 해당 레이어의 벡터 이미지를 Rasterize하는 것이 필요합니다.

** Vector File format : AI, CDR, SVG.

** Raster File format : JPG, GIF, PNG, TIF, BMP, PSD

 

먼저 psd_tools라는 패키지를 사용할 것이기 때문에 설치해줍니다.

pip3 install psd_tools

 

 

아래 코드를 보시면 Button이라는 component를 클릭했을 때, click()이라는 함수를 통해 clicked라는 Event Listener를 추가해주었으며, Radio라는 component에 변화가 생겼을 때, change()라는 함수를 통해 changed라는 Event Listener를 추가해주었습니다. clickedchanged 함수에 대해서는 코드 아래 더보기를 참조하세요

기타로 활용한 Element들은 아래와 같습니다.

  • Button : 클릭이 가능한 component입니다.
  • Gallery : 이미지를 여러개 grid로 display하기 위한 component입니다.
  • Radio : 선택이 가능한 여러개의 Radio button을 만들어주는 component입니다.
  • Slider : 유동적으로 게이지를 조절할 수 있는 slider component입니다.
  • Tab : 여러개 선택이 가능한 탭을 만들어주는 Layout Element 입니다. 
import gradio as gr

with gr.Blocks(title="My Demo") as demo:
    gr.Markdown("""
            # The title here
            The description here
    """)
    with gr.Row():
        with gr.Column():
            with gr.Tab(label="This is gr.Tab()-1"):
                file = gr.File(type="filepath", label="This is gr.File()")
                btn = gr.Button("This is gr.Button()")
                gallery = gr.Gallery(label="This is gr.Gallery()", show_label=True, elem_id="gallery", columns=[3], rows=[3], object_fit="contain", height="auto")
                btn.click(clicked, inputs=[file], outputs=[gallery])
        with gr.Column():
            with gr.Tab(label="This is gr.Tab()-2"):
                gr.Image(label="This is gr.Image()")
            with gr.Tab(label="This is gr.Tab()-3"):
                with gr.Row():
                    gr.Textbox(label="This is gr.Textbox()", visible=True)
                    radio = gr.Radio(choices=['Option1', 'Option2'], label="This is gr.Radio()")
                # Event Listener
                with gr.Row():
                    slide_a = gr.Slider(minimum=0, maximum=50, value=20, step=1, visible=False,
                                        label="this is gr.Slider()", info="information_a")
                    slide_b = gr.Slider(minimum=0, maximum=50, value=20, step=1, visible=False,
                                        label="this is gr.Slider()", info="information_b")
                    slide_c = gr.Slider(minimum=0, maximum=50, value=20, step=1, visible=False,
                                        label="this is gr.Slider()", info="information_c")
                    slide_d = gr.Slider(minimum=0, maximum=50, value=20, step=1, visible=False,
                                        label="this is gr.Slider()", info="information_d")
                radio.change(fn=changed, inputs=[radio], outputs=[slide_a, slide_b, slide_c, slide_d])

demo.launch(server_port=45555, server_name="0.0.0.0")
더보기

------------------------------------------------------------------------------

 <개인적으로 추가해준 Event Listener함수>

 

먼저, callback 함수인 clicked를 보면 위에서 PSD 패키지를 활용해 layer를 분리하는 과정을 의미합니다. 여기서 살펴볼 것은 Error()라는 함수를 통해 Error 메시지를 줄 수 있으며, Progress()라는 instance와 tqdm method를 통해 progress tracker를 만들어 줄 수 있습니다.

from psd_tools import PSDImage

def clicked(file):
    print( "Clicked!")
    if not file or file.split('.')[-1] != 'psd':
        raise gr.Error("This is gr.Error()")
    psd = PSDImage.open(file)
    progress = gr.Progress()
    progress(0.0,desc="This is gr.Progress()")

    layers = [layer for layer in psd if layer.visible]
    layers_out = []
    for layer in progress.tqdm(layers, desc="this is progress.tqdm()"):
        pil_image = layer.composite()
        layers_out.append(pil_image)
    return layers_out

 

다음으로 callback 함수인 changed를 보면 update() component를 return 함으로써 output에 해당하는 객체에 attribute update를 가해줄 수 있습니다.

def changed(option):
    visible = gr.update(visible=True)
    nonvisible = gr.update(visible=False)
    if option == "Option1":
        return visible, nonvisible, nonvisible, visible
    elif option =="Option2":
        return nonvisible, visible, visible, nonvisible
    else :
        return nonvisible, nonvisible, nonvisible, nonvisible

------------------------------------------------------------------------------

 

결과는 아래와 같습니다. 

[세번째 단계 결과]

 

오른쪽에 Tab에는 두가지가 있네요, Tab을 변경해보겠습니다. Radio component가 두개 만들어져있습니다.

 

[세번째 단계 결과 : Tab을 이동한 결과]

 

 

Radio에는 callback을 만들어주었으므로 Option1을 선택해보겠습니다. 아래와 같이 두개의 Slider가 visible하게 변화되었습니다.

[세번째 단계 결과 : Radio에 change callback을 부르도록 선택한 결과]

 


 

4. 네번째 단계 : 다른 페이지 띄워주기

 

마지막으로, 다른 서버에서 띄워준 HTML 페이지를 Element 내에 넣어보겠습니다. 먼저 가정한 11.12.13.14라는 서버에서 6006이라는 포트로 Tensorboard가 띄워져있다고 가정하겠습니다.

 

아래와 같이 해당 Tensorboard 페이지를 Element 안에 넣어주기 위해 HTML()이라는 Element를 활용했습니다.

import gradio as gr

with gr.Blocks(title="My Demo") as demo:
    gr.Markdown("""
            # The title here
            The description here
    """)
    with gr.Row():
        with gr.Tab(label="This is gr.Tab()-1"):
            with gr.Column():
                file = gr.File(type="filepath", label="This is gr.File()")
                btn = gr.Button("This is gr.Button()")
                gallery = gr.Gallery(label="This is gr.Gallery()", show_label=True, elem_id="gallery", columns=[3], rows=[3], object_fit="contain", height="auto")
        with gr.Tab(label="This is gr.Tab()-2"):
            SERVER_NAME = "http://11.12.13.14"
            TENSORBOARD_IFRAME_URL = f"{SERVER_NAME}:6006/?darkMode=true#timeseries"
            tensorboard_iframe = f"""
            <iframe
              src="{TENSORBOARD_IFRAME_URL}"
              frameborder="0"
              width="100%"
              height="700"
            ></iframe>
            """
            gr.HTML(tensorboard_iframe)
            
            
demo.launch(server_port=45555, server_name="0.0.0.0")
더보기

--------------------------------------------------------------------------

<Tensorboard 실행하기>

 

혹시 Tensorboard를 실행하는 방법에 대해 모르시는 분이 있을 것 같아 추가했습니다. 먼저 아래와 같이 설치해주겠습니다.

pip3 install tensorboard==2.15.2 tensorflow==2.15.0.post1 # Ubuntu 22.04
pip3 install tensorboard==2.13.1 tensorflow==2.13.0.post1 # Ubuntu 20.04

 

학습된 log파일이 필요해 공개된 학습 방법으로 간단하게 학습을 진행해보겠습니다. torch에 존재하는 SummaryWriter를 활용했습니다.

import torch
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter()

x = torch.arange(-5, 5, 0.1).view(-1, 1)
y = -5 * x + 0.1 * torch.randn(x.size())

model = torch.nn.Linear(1, 1)
criterion = torch.nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr = 0.1)

def train_model(iter):
    for epoch in range(iter):
        y1 = model(x)
        loss = criterion(y1, y)
        writer.add_scalar("Loss/train", loss, epoch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

train_model(10)
writer.flush()
writer.close()

 

Tensorboard를 두개의 방법으로 실행해보겠습니다.

 

방법1. 명령어를 활용

 

default 포트번호는 6006이지만 직접 포트번호를 지정해서 실행해보았습니다.

tensorboard --logdir=runs --host 0.0.0.0 --port 6006

 

 

방법2. python내에서 Gradio를 활용해 실행

 

kohya라는 오픈소스에 존재하는 코드를 가져와 실행해보았습니다. 해당 소스에서 tensorboard_gui.py와  custom_logging.py를 가져왔습니다.

https://github.com/bmaltais/kohya_ss/blob/master/kohya_gui/tensorboard_gui.py

https://github.com/bmaltais/kohya_ss/blob/master/kohya_gui/custom_logging.py

 

다음으로 아래와 같은 명령어를 통해 실행했습니다.

from tensorboard_gui import start_tensorboard
kwargs={}
kwargs['label'] = False
start_tensorboard(kwargs, './runs', wait_time=5)

 

--------------------------------------------------------------------------

아래는 실행한 후 해당 Tab으로 이동한 결과입니다.

[네번째 단계 결과 : 다른 페이지를 띄운 결과]


https://archivers.tistory.com/17

728x90
반응형