AB
AiBoss
チュートリアル

Gradio

チュートリアル

用 Gradio 搭建并托管机器学习交互界面

从安装 Gradio 开始,用 Interface 与 Blocks 两种方式搭建机器学习交互界面,掌握常用组件、按钮事件与行列布局,最后把应用托管到 Hugging Face Space,并借助推理端点让大模型对话跑得更快。

Gradio 是一个面向机器学习的轻量级 Python 库,它把模型函数直接包装成可交互的网页界面:用户在页面上输入文本、图片、音频或文件,函数返回的结果再渲染回页面。它适合需要快速把模型、脚本或数据处理流程展示给他人的人,也适合想给内部工具加一个可用前端、但不想写 HTML 和 JavaScript 的开发者。本文要讲的是从零搭出一个能用的 Gradio 界面,并把它托管到 Hugging Face Space 上。如果你更习惯在浏览器里直接调试前端交互,也可以配合 Gradio 相关的工具页一起看。

整篇内容按四块展开:环境准备、组件与事件、布局排版、托管与推理端点。每一块都会给出可以直接运行的代码,最后用一个完整示例把各部分串起来。

准备工作

Gradio 是纯 Python 包,前置条件只有一条:本机有一个可用的 Python 环境,并且能执行 pip 安装命令。素材中演示所用的版本为 gradio 4.44.0、gradio_client 1.3.0,但版本迭代较快,安装时不必锁定这个版本号,具体可用版本与依赖要求以官网当前信息为准。

安装命令:

pip install gradio

安装完成后可以确认一下版本:

pip list | grep gradio

输出中应当能看到 gradio 与 gradio_client 两个包。前者是主库,后者是配套的客户端库,通常随主库一起装上。

关于运行环境还有两点需要提前知道:

  • 应用启动后默认监听 7860 端口,浏览器访问本机对应地址即可看到界面。
  • 如果要在容器、远程服务器或局域网内访问,需要显式指定监听地址与端口,否则默认只对本机开放。

操作步骤

第一步:用 Interface 跑通最小应用

Gradio 最直接的入口是 gr.Interface,它把「输入组件、处理函数、输出组件」三件事声明清楚,页面就自动生成。新建 app.py,写入:

import gradio as gr

def greet(name, intensity):
    return "Hello, " + name + "!" * int(intensity)

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

demo.launch()

运行 python app.py,服务启动后打开浏览器访问本机 7860 端口,就能看到界面。页面上体现的是 Gradio 的三个基本要素:

  • 输入:由 inputs 指定,这里是一个文本框和一个滑动条。
  • 动作:由 fn 指定,输入组件的数据会按顺序传给 greet 函数。
  • 输出:由 outputs 指定,函数返回值渲染到结果区域。输出只有一个时可以直接写字符串,也可以写成 ["text"] 这样的列表形式。

需要改端口或允许其他机器访问时,给 launch 传参:

demo.launch(server_port=7866, server_name='0.0.0.0')

其中 server_port 换端口,server_name 设为 0.0.0.0 表示接受所有网络接口的连接。这一步在远程服务器上部署时基本是必需的。

第二步:用组件对象做精细设置

上一步的 inputs=["text", "slider"] 是字符串简写。要控制标签、输入来源、数据格式等细节,需要换成组件对象。以音频为例:

import gradio as gr

demo = gr.Interface(
    fn=lambda i: i,
    inputs=gr.Audio(sources=["microphone", "upload"], type="numpy", label="Audio File"),
    outputs="text",
)

demo.launch()

这里几个参数的作用分别是:

  • label:组件左上角显示的文本标签。
  • sources:允许的输入方式,示例中同时开放麦克风录音与文件上传。
  • type:输入数据的形态。numpy 表示以音频数组形式交给函数;输出端也可以设为 filepath,此时以文件路径形式返回,文件保存在 /tmp/gradio/ 目录下。

组件参数数量不少,逐个背下来意义不大。实际开发中通常是在 IDE(例如 VSCode)里借助参数提示与说明来选,需要确认某个参数的确切行为时再查文档。

第三步:组合多个常用组件

下面这段代码把文本、文件、图像、下拉菜单、音频、单选、复选、滑动条放在一起,输入与输出一一对应,并把 live 设为 True 开启实时渲染——左侧输入一变化,右侧输出立即更新,不需要点提交按钮:

import gradio as gr

input_list = [
    gr.Textbox(label="Textbox", lines=3, max_lines=7, placeholder="Placeholder"),
    gr.File(label="File", type="binary"),
    gr.Image(sources=["webcam", "upload"], label="Image"),
    gr.Dropdown(choices=["option 1", "option 2", "option 3"], label="Dropdown"),
    gr.Audio(sources=["microphone", "upload"], type="numpy", label="Audio File"),
    gr.Radio(choices=["option 1", "option 2", "option 3"], label="Radio"),
    gr.Checkbox(label="Checkbox"),
    gr.Slider(minimum=0, maximum=10, label="Slider", step=5),
]

output_list = [
    "text",   # Textbox
    "text",   # File
    "image",  # Image
    "text",   # Dropdown
    "audio",  # Audio
    "text",   # Radio
    "text",   # Checkbox
    "text",   # Slider
]

demo = gr.Interface(
    fn=lambda *i: i,
    inputs=input_list,
    outputs=output_list,
    live=True
)

demo.launch()

注意 fn=lambda *i: i 只是把输入原样返回,用来观察每个组件的数据形态。真实场景里把它换成自己的处理函数即可。

常用组件的用途与方向可以按下表对照选择:

组件用途简介类型
gr.Textbox文本输入输出,适合字符串或描述性文字输入 + 输出
gr.File文件上传与下载,适合文件处理、结果保存输入 + 输出
gr.Image上传或显示图像,适合图像生成、分类、处理输入 + 输出
gr.Audio音频上传与播放,适合语音生成与处理输入 + 输出
gr.Video视频上传与播放,适合视频处理与生成输入 + 输出
gr.Dataframe展示表格数据或 Pandas DataFrame,适合数据分析结果输入 + 输出
gr.Dropdown下拉选项,适合分类或选项选择输入
gr.Slider数值范围选择,适合参数或范围输入输入
gr.Checkbox单个复选框,适合布尔型选择输入
gr.Radio单选按钮组,从多个选项中选一个输入
gr.Number数字输入框,适合数值数据输入
gr.Label显示分类标签或模型预测结果输出
gr.Plot展示 Matplotlib 或 Plotly 图表输出
gr.JSON展示 JSON 结构化数据输出
gr.HighlightedText高亮文本片段,适合 NLP 分词与分类输出
gr.Gallery多图轮播或画廊视图输出
gr.Chatbot显示聊天记录,适合对话式应用输出
gr.Code高亮显示代码输出
gr.MarkdownMarkdown 格式文本输出
gr.HTML自定义 HTML 内容输出

第四步:绑定按键与交互事件

事件绑定同样遵循「输入、输出、动作」三要素,只是写法从 Interface 的声明式变成了组件方法调用。下面这个例子同时绑定了按钮点击和输入框失焦两个事件:

import gradio as gr

def greet(name):
    return f"你好, {name} !"

with gr.Blocks() as demo:
    name_input = gr.Textbox(label="输入你的名字")
    greet_button = gr.Button("点击问好")
    output_text = gr.Textbox(label="问候语")

    greet_button.click(fn=greet, inputs=[name_input], outputs=[output_text])
    name_input.blur(fn=greet, inputs=[name_input], outputs=[output_text])

demo.launch()

用户点按钮会生成问候语,把光标移出输入框也会触发同样的处理。常见事件与触发时机如下:

组件类型常用事件说明
Buttonclick用户点击按钮时触发
Textboxsubmit用户按回车键时触发
Textboxblur输入框失去焦点时触发
Dropdownchange用户选择项改变时触发
Sliderrelease拖动并释放滑块时触发
Filedrag文件被拖到上传区域时触发
Filedrop文件在上传区域被释放时触发

第五步:用 Blocks、Row、Column 控制布局

gr.Interface 适合单输入单输出的简单场景。要做更自由的排版,就用 gr.Blocks 配合 gr.Row 与 gr.Column。下面是一个带比例控制的布局示例:

import gradio as gr

def process(name, age):
    return f"你好,{name}!你 {age} 岁了。"

with gr.Blocks() as demo:
    gr.Markdown("# Gradio 布局示例")
    gr.Markdown("这是一个简单的 Gradio 示例,展示了如何使用 Blocks API 构建界面。")
    gr.Markdown("前几行可以写[链接](#yourlink),介绍等内容。")

    with gr.Row():
        with gr.Column(scale=2):
            name_input = gr.Textbox(label="输入你的名字", placeholder="请输入名字")
        with gr.Column(scale=1):
            age_input = gr.Number(label="输入你的年龄")

    with gr.Row():
        submit_button = gr.Button("提交", scale=1)
        output_text = gr.Textbox(label="输出结果", scale=3)

    submit_button.click(fn=process, inputs=[name_input, age_input], outputs=output_text)

demo.launch()

理解这段排版只需要记住一条规则:先按行排,再在行内按列细分。在 Blocks 内部,各个子块按行依次排列,上面这段代码一共是三行 Markdown 加两行 Row;在 Row 内部,子块按列排列,第四行和第五行各含两个列;Column 内部同样按行排列子块。scale 参数控制同一行内各列的宽度比例,示例中名字输入框与年龄输入框是 2:1,按钮与输出框是 1:3。

还有一个容易忽略的细节:如果某个子块的布局很简单、不需要额外参数,外层的 with 可以省略。上面连续三行 gr.Markdown(...) 等价于给每一行都套一个 with Row():。只有在需要调整比例或自定义样式时,才需要显式写出 with Row():。

第六步:把界面嵌入已有网页

Gradio 应用除了单独访问,也可以放进现有页面的 iframe 中:

<iframe src="http://127.0.0.1:7860" width="100%" height="600px" frameborder="0"></iframe>

把 src 换成应用实际可访问的地址即可。这种方式适合把演示嵌进博客、文档或内部系统页面。

一个完整示例

下面把前面的内容串成一个可运行的应用:用 Blocks 布局,左侧一列放输入组件,右侧一列放输出,绑定按钮点击事件,并保留实时渲染。

import gradio as gr

def analyze(text, image, level):
    summary = f"文本长度:{len(text or '')},等级:{level}"
    if image is not None:
        summary += f",图像尺寸:{image.shape}"
    return summary

with gr.Blocks(title="模型演示") as demo:
    gr.Markdown("# 模型演示界面")
    gr.Markdown("在左侧填写输入,点击提交后在右侧查看结果。")

    with gr.Row():
        with gr.Column(scale=1):
            text_input = gr.Textbox(label="文本", lines=3, placeholder="输入一段文字")
            image_input = gr.Image(sources=["upload"], label="图像")
            level_input = gr.Slider(minimum=0, maximum=10, step=1, label="等级")
            submit = gr.Button("提交")
        with gr.Column(scale=1):
            output = gr.Textbox(label="结果", lines=6)

    submit.click(fn=analyze, inputs=[text_input, image_input, level_input], outputs=output)

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

运行 python app.py 后,本机访问 7860 端口即可看到左右两栏的界面;在服务器上部署时,server_name="0.0.0.0" 让外部可以连进来。把 analyze 换成自己的模型推理函数,就是一个可用的演示页面。

托管到 Hugging Face Space

Hugging Face 提供集成的托管平台,上面有大量模型、数据集和演示空间。Gradio 与 Hugging Face Hub 上的模型和空间可以直接配合使用,发布流程因此简化不少。托管有两种入口:在 Space 页面手动创建,或者用 huggingface_hub 的 Python 包在代码里创建。

在 Space 页面创建应用

在 Space 页面点击创建新空间,然后在空间里新建 app.py,填入:

import gradio as gr

demo = gr.load("Helsinki-NLP/opus-mt-en-es", src="models")
demo.launch()

gr.load 会直接加载 Hub 上的模型并生成对应界面,这段代码得到的是一个英译西的应用。Qwen 系列模型同样支持推理端点,也可以用类似方式通过 gr.load 导入。

这里有一个必须注意的限制:免费空间只提供 CPU 资源。对于参数量较大的模型,例如 Qwen/Qwen2.5-7B-Instruct,在免费空间上直接跑会非常慢。可行的做法是改为调用 Hugging Face 的 Inference API,借用其 GPU 资源来加速。

用推理端点驱动大模型对话

Hugging Face 为许多模型提供 OpenAI 风格的接口,标记为 Inference Endpoint 的模型即可直接调用。下面两个函数分别实现非流式与流式返回:

import gradio as gr
from chattool import Chat
import chattool

model = 'Qwen/Qwen2.5-3B-Instruct'
chattool.api_base = f'https://api-inference.huggingface.co/models/{model}/v1'
chattool.model = model

def bot_resp(message, history):
    chat = Chat(history)
    chat.user(message)
    return chat.getresponse().content

def bot_streaming(message, history):
    chat = Chat(history)
    chat.user(message)
    msg = ''
    for char in chat.stream_responses():
        msg += char
        yield msg

把这两个函数分别交给 gr.ChatInterface,注意设置 type="messages",这样 history 会以 OpenAI 风格的字典格式传入:

with gr.Blocks() as demo:
    gr.ChatInterface(
        fn=bot_resp,
        title="Qwen Series",
        description="Try Qwen 2.5",
        fill_height=True,
        type="messages"
    )
    gr.ChatInterface(
        fn=bot_streaming,
        title="Qwen Series with Streaming",
        type='messages',
        description="Try Qwen 2.5",
        fill_height=True,
        stop_btn='Stop'
    )

demo.launch()

流式版本里 stop_btn 提供了一个中断按钮,长回答时比较实用;fill_height=True 让聊天区域撑满可用高度。

密钥不要明文写进代码。在 Space 中把它设置为仓库的私密变量,例如变量名 OPENAI_API_KEY,值填你的 Hugging Face 令牌,代码里从环境变量读取即可。

注意事项

  • 端口与监听地址:默认端口是 7860,默认只对本机开放。远程部署必须设置 server_name='0.0.0.0',否则外部访问不到。
  • 音频输出路径:音频组件输出 type="filepath" 时,文件落在 /tmp/gradio/ 目录,需要持久保存的结果要自行转存。
  • 实时渲染的代价:live=True 会在每次输入变化时触发函数,处理逻辑较重时容易造成频繁调用,按需开启。
  • 免费空间只有 CPU:大模型在免费空间上直接推理会非常慢,应改用 Inference API 借用 GPU 资源。
  • 密钥管理:令牌通过 Space 的私密变量传入,不要写死在代码或公开仓库里。
  • 版本与配额:Gradio 版本、Hugging Face 空间资源规格与推理端点配额都可能调整,具体以官网当前信息为准。