跳至主要内容

使用 Python 中的 Gradio 为 AI 应用构建用户界面

学习如何使用 Python 中的 Gradio 将技术模型转换为交互式用户界面。
· 14分钟 阅读

使用 AI 探索

如果没有易用的用户界面,当今的 AI 模型实用性会大打折扣。借助开源的 Python Web UI 库 Gradio,您可以弥合 LLM 与非技术终端用户之间的鸿沟。它允许您为 AI 项目快速构建原型,并简化其向更广泛受众的部署。

本教程面向通常不具备 Web 开发经验的机器学习工程师。内容涵盖 Gradio 的基础与核心概念、针对不同 AI 模型类型的界面创建、面向用户体验与交互的高级功能,以及部署与分享的最佳实践。

让我们开始吧。

Gradio 入门

安装

我们先创建一个虚拟环境(推荐使用 Conda):

$ conda create -n gradio_tutorial python=3.9 -y
$ conda activate gradio_tutorial

然后,使用 PIP 安装 Gradio 及其依赖:

$ pip install gradio ipykernel

我们还安装了 ipykernel 包,以便在 Jupyter 笔记本中直接显示 Gradio 界面。此过程需要将刚创建的虚拟环境添加为 Jupyter Lab 的内核。使用以下命令:

$ ipython kernel install --user --name=gradio_tutorial
$ jupyter lab  # Start the lab

这样,您就可以使用已安装 Gradio 的内核创建笔记本。为验证,按惯例导入并打印其版本:

import gradio as gr
print(gr.__version__)
4.37.1

基本概念与术语

我们通过一个“Hello World”示例来学习 Gradio 的关键概念与术语:

def greet(name):
   return f"Hello, {name}!"
demo = gr.Interface(
   fn=greet,
   inputs=['text'],
   outputs="text",
)
demo.launch()

当您在单元格中运行上述代码时,输出将是一个小型交互式界面,返回定制的问候语:

A GIF that shows Hello World example in Gradio

Gradio 围绕几个关键概念展开:

  1. Interface:创建 UI 的核心类。
  2. 组件:输入与输出元素,如文本框、图像与音频。目前内置组件超过 30 种。
  3. 函数:处理来自输入组件的信息并通过输出组件显示结果的 Python 函数。
  4. Launch:启动 Gradio 应用的方法。

在上面,我们创建了 greet 函数,它接收并返回文本输入。因此,在 Interface 类中,我们将输入与输出组件都指定为 text。

最后,我们调用 launch 方法,它会启动本地服务器。若要让任何人都能访问该 UI,您可以将 share 参数设为 True。这将开启一个 SSH 隧道,并将 Gradio 应用部署到一个可公开分享的网页:

demo.launch(share=True)
Running on public URL: https://d638ed5f2ce0044296.gradio.live
This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run gradio deploy from Terminal to deploy to Spaces (https://huggingface.co/spaces)

Gradio 组件

在构建 Gradio 应用时,您将花费大量时间折腾不同的组件以及如何在页面上布局它们。让我们更近一步看看您手头拥有哪些工具。

输入与输出组件

Gradio 提供了丰富的组件来构建交互式界面。这些组件通常分为两类:输入与输出。

输入组件允许用户向底层处理器(可以是任意 Python 函数)提供数据。一些常见的输入包括:

  • Textbox(文本框)
  • Image(图像)
  • Audio(音频)
  • Slider(滑块)
  • Dropdown(下拉菜单)

下面是一个使用部分上述组件的示例界面:

def process_inputs(text, image, audio, number, option):
   # Process inputs and return results
   return f"Processed: {text}, {number}, {option}"
demo = gr.Interface(
   fn=process_inputs,
   inputs=[
       gr.Textbox(label="Enter text"),
       gr.Image(label="Upload image"),
       gr.Audio(label="Upload audio"),  # Uncomment this line to add audio input
       gr.Slider(0, 100, label="Choose a number"),
       gr.Dropdown(["Streamlit", "Taipy", "Gradio"], label="Select a UI library"),
   ],
   outputs="text",
)
demo.launch()

在此示例中,process_inputs 函数需要五个参数。因此,我们需要创建五个输入组件并传给 inputs。虽然输入组件数量应与函数所需参数数量匹配,但这不是硬性规定。为避免错误与警告,请为不需要通过 UI 提供用户输入的参数设置默认值。

Another Gradio demo that shows the usage of multiple components

注意,我们此处使用 Textbox 类来指定输入组件,而不是像第一个示例那样用简单字符串 text。始终建议使用专用类来指定输入与输出组件,以便进行自定义。例如,所有组件类都有一个实用的 label 属性,而 Slider 和 Dropdown 则提供用于指定取值范围和可选项的参数。

许多输入组件也可用于显示输出。以下是一些常见场景:

  • Label:用于显示文本或分类结果
  • Image:用于展示处理后或生成的图像
  • Audio:用于播放处理后或生成的音频
  • Plot:用于显示图形或图表

与输入类似,输出组件的数量必须与处理函数返回值的数量一致。

自定义组件外观

Gradio 允许您自定义组件的外观以满足需求。下面是一个使用自定义文本框的示例:

demo = gr.Interface(
   fn=lambda x: int(x) ** 7,
   inputs=gr.Textbox(
       lines=5,
       placeholder="Enter any number...",
       label="Custom textbox",
       info="This is a customized textbox component to raise any number to the power of 7.",
   ),
   outputs=gr.Textbox(label="And the number is...", show_copy_button=True),
)
demo.launch()

A Gradio demo that shows how to use a custom textbox

在此示例中,我们通过指定行数、添加占位符与信息文本,并为输出增加复制按钮,来自定义 Textbox 组件。

请尝试不同组件及其属性,创建最符合您 AI 应用需求的界面。若想了解某组件可更改哪些属性,您可以查阅其文档,或更方便地在 Jupyter Lab 中于类名后使用 ? 操作符:

A screenshot that shows the properties of a Gradio component

为 LLM 构建界面

让我们把已学内容融会贯通,创建两个由 LLM 驱动的实用文本与图像界面。

首先,我们将构建一个从英语到土耳其语、西班牙语或中文的翻译器:

import openai  # pip install openai
def translate_text(api_key, text, target_language):
  
   openai.api_key = api_key  # Set openai API key
  
   language_map = {
       "Turkish": "Turkish",
       "Spanish": "Spanish",
       "Chinese": "Chinese (Simplified)",
   }
   prompt = f"Translate the following English text to {language_map[target_language]}:\n\nEnglish: {text}\n\n{target_language} translation:"
   try:
       response = openai.chat.completions.create(
           model="gpt-4",
           messages=[
               {"role": "system", "content": "You are a professional translator."},
               {"role": "user", "content": prompt},
           ],
       )
       translation = response.choices[0].message.content.strip()
       return translation
   except Exception as e:
       return f"Error: {str(e)}"

首先我们定义 translate_text 函数。在函数体内,我们设置 OpenAI API 密钥并创建语言映射。然后构造翻译提示词。接着在 try-except 块中,我们向 ChatCompletion 端点发送带系统提示的请求。最后返回第一条候选结果。

现在,我们可以构建界面:

iface = gr.Interface(
   fn=translate_text,
   inputs=[
       gr.Textbox(
           placeholder="Enter your OpenAI API key",
           type="password",
           label="OpenAI API Key",
       ),
       gr.Textbox(
           lines=4,
           placeholder="Enter English text to translate...",
           label="English Text",
       ),
       gr.Dropdown(choices=["Turkish", "Spanish", "Chinese"], label="Target Language"),
   ],
   outputs=gr.Textbox(label="Translation", show_copy_button=True),
   title="English to Turkish/Spanish/Chinese Translator",
   description="Translate English text to Turkish, Spanish, or Chinese using OpenAI's GPT-4 model. Enter your OpenAI API key, the text you want to translate, and select the target language.",
)
iface.launch(share=True)

这段代码与前面的界面一样简单,但引入了几个新属性:

  • 文本框的 type 参数可将纯文本输入字段改为密码输入,从而隐藏文本。
  • 在 Interface 类中,title 与 description 参数会在页面顶部居中添加一级标题与副标题。

效果如下:

A Gradio app that translates englisht to Turkish/Spanish/chinese

您或许好奇,为什么我们要在应用中让用户输入 API 密钥,而不是自己提供。原因与 Gradio 的部署方式有关。

如果我们将自己的 API 密钥作为环境变量提供(这是常见做法),那么可公开分享的应用版本将无法使用,因为它无法访问我们的环境变量。我们会在部署部分看到如何通过将应用部署到 HuggingFace Spaces 来解决这一问题。

接下来,构建一个用于生成图像的 UI:

def generate_surrealist_art(api_key, prompt):
   surrealist_prompt = f"Create a surrealist artwork based on the following concept: {prompt}. The artwork should be dreamlike, with unexpected juxtapositions and a sense of the uncanny."
   client = OpenAI(api_key=api_key)
   response = client.images.generate(
       model="dall-e-3",
       prompt=surrealist_prompt,
       size="1024x1024",
   )
   image_url = response.data[0].url
   return image_url

我们创建了名为 generate_surrealist_art 的函数,使用超现实主义提示向 dall-e-3 发送请求并返回生成图像的 URL。随后,我们再次将该函数传入 Interface 类:

iface = gr.Interface(
   fn=generate_surrealist_art,
   inputs=[
       gr.Textbox(
           placeholder="Enter your OpenAI API key",
           type="password",
           label="OpenAI API Key",
       ),
       gr.Textbox(
           lines=2,
           placeholder="Describe your surrealist concept...",
           label="Concept Description",
       ),
   ],
   outputs=gr.Image(value="str"),
   title="Surrealist Artwork Generator",
   description="Generate surrealist artwork based on your prompts using DALL-E. Enter your OpenAI API key and describe your concept.",
)
iface.launch(share=True)

我们为 API 密钥和希望用超现实主义风格呈现的概念各指定一个输入。然后使用 Image 类创建一个生成图像的输出组件。若将其 value 参数设为 str,组件即可从 URL 下载并显示图像,正合我们需求。

效果如下:

A gif that shows an image-based Gradio app.

为经典机器学习模型构建界面

现在,让我们为一个经典的表格回归模型构建界面。我们将使用 Seaborn 提供的 Diamonds 数据集。

首先创建一个新的工作目录,并在其中新建名为 app.py 的脚本。然后粘贴 这个 GitHub gist 中的代码,以加载数据、使用 Scikit-learn 的 Pipeline 进行处理,并训练 RandomForestRegression 模型。

A screenshot of a GitHub gist

下一步是创建一个处理函数,接受与 Diamonds 数据集特征数相同数量的输入:

# Create the Gradio interface
def predict_price(carat, cut, color, clarity, depth, table, x, y, z):
  
   input_data = pd.DataFrame(
       {
           "carat": [carat],
           "cut": [cut],
           "color": [color],
           "clarity": [clarity],
           "depth": [depth],
           "table": [table],
           "x": [x],
           "y": [y],
           "z": [z],
       }
   )
  
   prediction = model.predict(input_data)[0]
   return f"Predicted Price: ${prediction:.2f}"

该函数将这些输入转换为 DataFrame,并传递给已训练模型流水线的 .predict() 方法。最后返回包含预测价格的字符串。

现在,Interface 类必须与该函数签名匹配:九个用于处理特征的输入组件,以及一个用于显示预测价格的输出:

iface = gr.Interface(
   fn=predict_price,
   inputs=[
       gr.Slider(
           minimum=diamonds["carat"].min(),
           maximum=diamonds["carat"].max(),
           label="Carat",
       ),
       gr.Dropdown(["Fair", "Good", "Very Good", "Premium", "Ideal"], label="Cut"),
       gr.Dropdown(["D", "E", "F", "G", "H", "I", "J"], label="Color"),
       gr.Dropdown(
           ["I1", "SI2", "SI1", "VS2", "VS1", "VVS2", "VVS1", "IF"], label="Clarity"
       ),
       gr.Slider(
           minimum=diamonds["depth"].min(),
           maximum=diamonds["depth"].max(),
           label="Depth",
       ),
       gr.Slider(
           minimum=diamonds["table"].min(),
           maximum=diamonds["table"].max(),
           label="Table",
       ),
       gr.Slider(minimum=diamonds["x"].min(), maximum=diamonds["x"].max(), label="X"),
       gr.Slider(minimum=diamonds["y"].min(), maximum=diamonds["y"].max(), label="Y"),
       gr.Slider(minimum=diamonds["z"].min(), maximum=diamonds["z"].max(), label="Z"),
   ],
   outputs="text",
   title="Diamond Price Predictor",
   description="Enter the characteristics of a diamond to predict its price.",
)
iface.launch(share=True)

在类中,我们为类别特征创建了三个下拉菜单。选项由各特征中的唯一类别填充。我们还创建了六个滑块组件来接收数值特征。滑块范围由各特征的最小值与最大值决定。

现在只需执行脚本即可运行并部署应用:

$ python app.py

效果如下:

A gif that shows a Gradio demo deployed using a script

有关最佳实践与优化建议,请跳至下方“最佳实践”部分。

部署 Gradio 应用

我们已经看到,只需启用一个参数就能轻松部署 Gradio 应用。当然,这种方式的缺点是演示将在 72 小时内过期。因此,推荐的 Gradio 部署方式是通过 HuggingFace Spaces。HuggingFace 于 2021 年收购了 Gradio,使两者之间的集成非常顺畅。

因此,对于本教程或您未来用 Gradio 创建的任何应用,请先在 huggingface.co 注册免费账号,并前往 Settings > Tokens 以生成访问令牌:

A Gif that shows how to generate access token in hugging face

令牌只显示一次,请务必妥善保存。

有了该令牌,您可以将任意数量的 Gradio 应用部署到 Spaces 上并获得永久托管。作为示例,我们将部署上一节的钻石价格预测模型,您会发现过程出奇简单。

您只需进入包含 UI 脚本的目录,并在终端中运行 gradio deploy:

A screenshot on how to deploy a gradio app using the terminal

终端会引导您将脚本转换为可运行的 HuggingFace Space。它会询问如下信息:

  • 您生成的访问令牌
  • Space 标题:部署后将成为 Space URL 的一部分
  • 包含 Gradio UI 代码的脚本名称(默认 app.py)
  • Space 的硬件;留空仅使用 CPU(免费)
  • 脚本使用的任何环境变量(在此可安全存储 API 密钥与用户机密)
  • 依赖项——逐条输入并按 ENTER

随后终端会给出已部署 Space 的链接。如下所示:

image.png

这种部署方式的另一大优势是,Gradio 会自动将演示转换为可用的 REST API。访问与调用说明始终位于页面底部:

A gif that shows the API version of Gradio app demos

这样,您即可同时拥有面向非技术用户的永久 UI 托管,以及供同事与开发者使用的 REST API。

有关更多部署与分享选项,例如将演示嵌入网页、为应用添加 Google 身份验证等,请参阅 Gradio 文档中的 “分享您的应用” 部分。

Gradio 最佳实践与技巧

在使用 Gradio 开发用户界面时,遵循最佳实践能够显著提升用户体验并提高应用的可维护性。以下是一些关键建议:

1. 使用脚本以便组织与维护

将 Gradio 应用组织在 Python 脚本中,以便进行更好的版本控制、协作与部署。

2. 优化组件的空间分配

使用合适的尺寸与布局工具(如 gr.Column()、gr.Row())确保界面均衡且响应良好。

3. 提供充分的信息提示

利用“info”和“label”属性为各组件提供清晰的说明与上下文。

4. 高效处理大量特征集

对于具有大量特征的模型,使用文件输入(CSV、JSON)以支持批量预测并简化界面。

5. 正确管理环境变量

本地开发使用 python-dotenv,部署时在 Hugging Face Spaces 中设置变量。

6. 实现错误处理与校验

校验输入、提供清晰的错误信息,并使用 try-except 块优雅地处理错误。

7. 优化性能

实现缓存、对大型模型使用惰性加载,并为长时任务使用 gr.LoadingStatus()。

8. 注重无障碍设计

确保高对比度、为图像提供替代文本,并为所有交互元素启用键盘导航。

9. 渐进式展现

使用手风琴或选项卡组织复杂界面,按需展示高级选项。

10. 定期更新与维护

保持依赖项最新、监控缺陷,并根据用户反馈持续改进。

11. 充分利用 HuggingFace 资源

利用 HuggingFace 的工具与资源实现与 Gradio 的无缝集成,包括模型仓库与数据集。

12. 在 HuggingFace Hub 托管大型模型

对于大型表格模型,将其上传至 HuggingFace Hub,并在 Gradio 脚本中直接加载,以提升性能并减少本地存储需求。

13. 使用 HuggingFace 数据集

对于大型数据集,将其上传到 HuggingFace Hub,并在 Gradio 应用中直接访问,以简化数据管理并改善加载时间。

结语与延伸资源

在本文中,我们学习了使用 Gradio 为 AI 应用构建用户界面的基础。我们只是浅尝辄止,Gradio 还提供了许多构建复杂界面的功能。例如, 界面状态 可让您的应用在一次函数调用到下一次之间记住输出。 响应式界面 会在用户输入变化时即时动态更新 UI。借助 Blocks,您可以构建具有自定义布局与设计的应用。

同样地,您还可以查阅以下相关资源以获取更多内容:

主题