
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.Markdown | Markdown 格式文本 | 输出 |
| 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()
用户点按钮会生成问候语,把光标移出输入框也会触发同样的处理。常见事件与触发时机如下:
| 组件类型 | 常用事件 | 说明 |
|---|---|---|
| Button | click | 用户点击按钮时触发 |
| Textbox | submit | 用户按回车键时触发 |
| Textbox | blur | 输入框失去焦点时触发 |
| Dropdown | change | 用户选择项改变时触发 |
| Slider | release | 拖动并释放滑块时触发 |
| File | drag | 文件被拖到上传区域时触发 |
| File | drop | 文件在上传区域被释放时触发 |
第五步:用 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 空间资源规格与推理端点配额都可能调整,具体以官网当前信息为准。