Gradio 学习

Gradio 做一些展示页面真的非常方便,非常有必要学习的一个库!!!以此记录

Quickstrat

先决条件 及 下载

python3.8.0 及以上

pip install gradio -i https://pypi.tuna.tsinghua.edu.cn/simple

一个快速开始的例子

import gradio as gr

def gradio_welcome(name):
    return f"我是Gradio, 欢迎你:{name}!"

app = gr.Interface(
    fn=gradio_welcome,
    inputs=['text'],
    outputs=['text']
)

app.launch()

当然对上面的控件,是简写方式

app = gr.Interface(
    fn=gradio_welcome,
    inputs=[gr.Textbox(label='请输入您的名字', value='张三')],
    outputs=[gr.Textbox(label='输出')]
)

1. Interface

a.介绍

第一部分都是围绕着Interface这个高级抽象类展开的,利用Interface这个抽象类建立一个页面,初始化Interface需要三个必要参数fninputs, outputs

  • fn: 对Interface(UI)的绑定

  • inputs: 输入,输入的数量要取决于绑定函数的形参数量

  • outputs: 输出,输出的数量要取决于绑定函数的返回数量

官方对此的描述: The gr.Interface class is a high-level abstraction in Gradio that allows you to quickly create a demo for any Python function simply by specifying the input types and the output types. Revisiting our first demo:

b.输入,输出

输入也是多元的,TextBox的字符串,CheckBox的布尔值,Slider的数字

输出可以输出 不仅是输出TextBox文字 Image, DataFrame, VideoHtmlMarkdown

一个输入图片,加一层黄色蒙版的案例

import numpy as np
import gradio as gr

def sepia(input_img):
    sepia_filter = np.array([
        [0.393, 0.769, 0.189], 
        [0.349, 0.686, 0.168], 
        [0.272, 0.534, 0.131]
    ])
    sepia_img = input_img.dot(sepia_filter.T)
    sepia_img /= sepia_img.max()
    return sepia_img

demo = gr.Interface(sepia, gr.Image(), "image")
demo.launch()

c. 其他参数

import gradio as gr


def calculator(name, cls):
    return name + '是' + cls

demo = gr.Interface(
    calculator,
    [
        "text",
        gr.Radio(["一班", "二班", "三班"]),
    ],
    "text",
    examples=[
        ['张三', '三班'],
        ['李四', '二班'],
    ],
    title="班级表格",
    description="这是一个班级表格参照",
)

demo.launch()

必要3个参数,不在此再次赘述了

  • title: app的一个标题

  • label: 输入输出空间都有次属性,一个空间的标签

  • description:进一步说明

  • examples:列子 也可以传入csv文件examples='/demo/calculator/examples' 确保目录下有csv文件

    • cache_examples:True则对例子进行缓存,即使数据更新了,也不会进行更新了,只能用开启新的launch重新加载。也可以为lazy开启懒加载,启动程序不会进行缓存,当第一个用户访问用到时才会开始缓存。相对于前者启动Gradio的速度会更快

d. Interface的四种情况

  1. Standard Demo:有输入有输出

  2. Outputs-only Demo: 没有输入仅仅只有输出

  3. Inputs-only Demo: 只有输入没有输出

  4. Unified demos: 拥有输入和输出,但是输入和输出都是通一控件,输出覆盖掉输出。比如翻译模型,输入你好,输出hello 覆盖你好

e. Interface State

1.Globla

在全局创建一个变量,保存历史过来的数据,进行显示。如果是多用户使用,这个方案则会导致所有用户都会出现不属于他们的输出,引用官方的一个案例

import gradio as gr

scores = []

def track_score(score):
    scores.append(score)
    top_scores = sorted(scores, reverse=True)[:3]
    return top_scores

demo = gr.Interface(
    track_score, 
    gr.Number(label="Score"), 
    gr.JSON(label="Top Scores")
)
demo.launch()

2.Session

利用session可以区分用户,解决了globla带来的串联问题。我自己的一个案例,模拟下载输出进度。

import time

import gradio as gr

def store_message(file: str, history: list[str]):
    for i in range(1, 101):
        time.sleep(0.01)
        history.append(f'{file}文件正在下载,进度[{i}%]')
        print(history)
        yield '\n'.join(history), history
    history.append(f'{file}文件下载完成')
    return '\n'.join(history), history

demo = gr.Interface(
    fn=store_message,
    inputs=["textbox", gr.State(value=[])],
    outputs=[gr.Textbox(max_lines=5, autoscroll=True, show_copy_button=True, label='日志输出'), gr.State()]
)

demo.launch()

f. Live 和 Streaming

  • live : 布尔值,如果为True,则自动监听,输入参数输入完成后,无需点击按钮,自动在输出控件显示输出结果

  • streaming: 流式传输,摄像头,或者麦克风之类的需要一直传输的

先跳过,后面在学习,暂时没有这个需求

2. Blocks

类似于组件的开发,一个最直观的例子

  1. 上面说的Interface四种情况,都是单数据量,而Blocks则不限制

import gradio as gr

with gr.Blocks() as demo:
    name = gr.Textbox(label="Name")
    output = gr.Textbox(label="Output Box")
    greet_btn = gr.Button("Greet")


    @greet_btn.click(inputs=name, outputs=output)
    def greet(name):
        return "Hello " + name + "!"

demo.launch()
import gradio as gr


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


with gr.Blocks() as demo:
    name = gr.Textbox(label="Name")
    output = gr.Textbox(label="Output Box")
    greet_btn = gr.Button("Greet")
    greet_btn.click(fn=greet, inputs=name, outputs=output, api_name="greet")

demo.launch()

a. 时间监听和交互 (Event Listeners and Interactivity)

  • interactivity: 其中Textbox当作为输出时是不能交互的, 通过此参数输出控件也变得可被交互,当然有输出内容的时候交互输入的内容就会被覆盖掉

    output = gr.Textbox(label="Output Box", interactive=True)

b. 多个输入,字典或列表

上面的案例都是一个控件的输入,此案例是两个输入控件的传递,分别用字典和列表

import gradio as gr

with gr.Blocks() as demo:
    a = gr.Number(label="a")
    b = gr.Number(label="b")
    with gr.Row():
        add_btn = gr.Button("Add")
        sub_btn = gr.Button("Subtract")
    c = gr.Number(label="sum")

    def add(num1, num2):
        return num1 + num2
    add_btn.click(add, inputs=[a, b], outputs=c)

    def sub(data):
        return data[a] - data[b]
    sub_btn.click(sub, inputs={a, b}, outputs=c)


demo.launch()

c. 输出 retrun 字典或列表

之前默认案例的return都是用的列表,输出两个,当其中一个不用的时候,也要用0或者None占位。

而字典可以允许跳过某些控件的更新。但是传递的时候key必须是控件名称

列表

with gr.Blocks() as demo:
    food_box = gr.Number(value=10, label="Food Count")
    status_box = gr.Textbox()
    def eat(food):
        if food > 0:
            return food - 1, "full"
        else:
            return 0, "hungry"
    gr.Button("EAT").click(
        fn=eat,
        inputs=food_box,
        outputs=[food_box, status_box]
    )

字典

with gr.Blocks() as demo:
    food_box = gr.Number(value=10, label="Food Count")
    status_box = gr.Textbox()
    def eat(food):
        if food > 0:
            return {food_box: food - 1, status_box: "full"}
        else:
            return {status_box: "hungry"}
    gr.Button("EAT").click(
        fn=eat,
        inputs=food_box,
        outputs=[food_box, status_box]
    )