跳转至

Gradio 快速搭建 ML Demo 教程

一句话说明

Gradio 让你三行代码就能把机器学习模型变成一个可交互的网页 Demo,面试时直接甩链接展示项目,比 PPT 酷一百倍。


Gradio 是什么?

白话解释

想象你训练了一个"猫狗分类器"模型,想让别人试用——传统做法是写前端页面+后端 API,至少折腾两天。而 Gradio 就是一个"模型展示柜":你把模型函数丢进去,它自动生成一个漂亮的网页界面,用户上传图片就能看到预测结果。

你的 Python 函数 → Gradio 包装 → 自动生成网页 Demo → 分享链接给任何人

三行代码示例

import gradio as gr                          # 导入 Gradio
demo = gr.Interface(fn=lambda x: x, inputs="text", outputs="text")  # 创建界面
demo.launch()                                # 启动网页

Gradio vs 23 篇的 Streamlit 有什么不同?

维度Gradio(本篇)Streamlit(23 篇已讲)
定位ML 模型 Demo(输入→预测→输出)数据仪表盘/数据 App
核心模式函数式:一个函数搞定脚本式:从上到下写代码
最擅长模型推理演示、API 生成数据探索、图表仪表盘
部署平台HuggingFace Spaces(一键)Streamlit Cloud
学习曲线极低(三行代码上手)低(但需要了解响应式)

简单记忆:要展示模型 → 用 Gradio;要做数据看板 → 用 Streamlit。


核心概念

1. Interface(接口)—— 最简单的方式

Interface 是 Gradio 的高层 API,只需要三个参数:

gr.Interface(fn=你的函数, inputs=输入组件, outputs=输出组件)

白话:Interface 就像一个"自动售货机"——你告诉它"投币口长什么样"(inputs)、"出货口长什么样"(outputs)、"中间怎么处理"(fn),它自动帮你搭好整个机器。

2. Blocks(块)—— 灵活布局

Blocks 是 Gradio 的低层 API,允许你自由控制布局、组合组件:

with gr.Blocks() as demo:      # 用 with 语句创建一个 Blocks
    gr.Markdown("# 标题")       # 放一个标题
    with gr.Row():               # 水平排列
        inp = gr.Textbox()       # 左边放输入框
        out = gr.Textbox()       # 右边放输出框
    btn = gr.Button("提交")      # 放一个按钮
    btn.click(fn=my_func, inputs=inp, outputs=out)  # 绑定事件

白话:Interface 是"自动售货机",Blocks 是"乐高积木"——你可以自由拼出任何形状的界面。

3. Component(组件)—— 输入输出的零件

常用组件一览:

组件用途示例场景
gr.Textbox文本输入/输出情感分析、翻译
gr.Image图片输入/输出图像分类、风格迁移
gr.Audio音频输入/输出语音识别
gr.Slider滑块调节参数(阈值、温度)
gr.Dropdown下拉菜单选择模型/数据集
gr.File文件上传上传 FASTA/CSV
gr.Dataframe表格展示分析结果
gr.PlotMatplotlib/Plotly 图展示可视化
gr.Label分类标签显示预测概率
gr.Chatbot聊天界面对话机器人

4. Event(事件)—— 触发动作

在 Blocks 中,每个组件都可以绑定事件:

btn.click(fn=处理函数, inputs=[输入组件], outputs=[输出组件])     # 点击按钮触发
textbox.change(fn=处理函数, inputs=[输入组件], outputs=[输出组件]) # 内容变化触发
textbox.submit(fn=处理函数, inputs=[输入组件], outputs=[输出组件]) # 按回车触发

安装配置

# 推荐在 bioinfo 环境中安装(截至 2025 年 6 月最新版 6.14.0)
conda activate bioinfo

# 安装 Gradio(需要 Python >= 3.10)
pip install gradio

# 验证安装
python -c "import gradio as gr; print(f'Gradio 版本: {gr.__version__}')"
# 预期输出: Gradio 版本: 6.14.0

# 可选:安装常用 ML 库(后续 Demo 需要)
pip install scikit-learn transformers torch pillow matplotlib

注意:Gradio 6.x 需要 Python >= 3.10,如果你的环境是 3.8/3.9,需要使用 pip install gradio==4.44.1(v4 最后版本)。


实操教程

Demo 1:Hello World(入门)

# === gradio_hello.py ===
# 最简单的 Gradio Demo:输入名字,输出问候语

import gradio as gr  # 导入 Gradio 库

def greet(name):
    """接收用户名字,返回问候语"""
    return f"你好,{name}!欢迎体验 Gradio Demo!"

# 创建 Interface
# fn: 处理函数
# inputs: 输入组件类型("text" 是 gr.Textbox 的简写)
# outputs: 输出组件类型
demo = gr.Interface(
    fn=greet,                    # 处理函数
    inputs=gr.Textbox(           # 输入:文本框
        label="请输入你的名字",    # 显示标签
        placeholder="例如:小明"   # 占位提示
    ),
    outputs=gr.Textbox(          # 输出:文本框
        label="问候语"            # 显示标签
    ),
    title="Hello World Demo",    # 页面标题
    description="输入你的名字,获取一句问候!"  # 页面描述
)

# 启动 Demo(会在浏览器自动打开 http://127.0.0.1:7860)
demo.launch()

运行:

python gradio_hello.py
# 浏览器打开 http://127.0.0.1:7860 即可看到界面

Demo 2:文本分类(情感分析)

# === gradio_text_clf.py ===
# 用 scikit-learn 做一个简单的中文情感分析 Demo

import gradio as gr                     # 导入 Gradio
import re                               # 正则表达式,用于简单分词

# 定义一个简单的基于关键词的情感分析器
# (实际项目中会用训练好的模型,这里为了演示简化)
POSITIVE_WORDS = {"好", "棒", "优秀", "喜欢", "开心", "不错", "满意", "推荐", "赞", "优质"}
NEGATIVE_WORDS = {"差", "烂", "糟糕", "难用", "讨厌", "失望", "垃圾", "坑", "后悔", "恶心"}

def sentiment_analysis(text):
    """
    对输入文本做情感分析
    返回:字典格式 {标签: 概率},Gradio 的 Label 组件会自动显示为概率条
    """
    if not text.strip():                    # 如果输入为空
        return {"请输入文本": 1.0}

    words = set(text)                       # 简单按字拆分(中文场景)
    pos_count = len(words & POSITIVE_WORDS) # 统计正面词数量
    neg_count = len(words & NEGATIVE_WORDS) # 统计负面词数量
    total = pos_count + neg_count           # 总匹配数

    if total == 0:                          # 没有匹配到任何情感词
        return {"正面": 0.5, "负面": 0.5}   # 返回中性

    pos_score = pos_count / total           # 计算正面比例
    neg_score = neg_count / total           # 计算负面比例

    return {"正面": pos_score, "负面": neg_score}  # 返回概率字典

# 创建 Demo
demo = gr.Interface(
    fn=sentiment_analysis,                  # 处理函数
    inputs=gr.Textbox(                      # 输入:文本框
        label="输入评论文本",
        placeholder="例如:这个产品真的很棒,推荐购买!",
        lines=3                             # 文本框高度为 3 行
    ),
    outputs=gr.Label(                       # 输出:标签(自动显示概率条)
        label="情感分析结果",
        num_top_classes=2                   # 显示前 2 个类别
    ),
    title="中文情感分析 Demo",
    description="输入一段中文评论,分析其情感倾向(正面/负面)",
    examples=[                              # 预设示例(用户可以直接点击)
        ["这个产品真的很棒,推荐购买!"],
        ["太差了,用了两天就坏了,非常失望"],
        ["还行吧,没什么特别的感觉"]
    ]
)

demo.launch()

Demo 3:图像分类

# === gradio_image_clf.py ===
# 用预训练的 MobileNet 做图像分类 Demo

import gradio as gr                     # 导入 Gradio
import torch                            # PyTorch
from torchvision import transforms, models  # 图像变换和预训练模型
import json                             # 解析标签文件
import urllib.request                   # 下载标签文件

# ---------- 模型加载(只执行一次) ----------
model = models.mobilenet_v2(pretrained=True)  # 加载预训练的 MobileNet V2
model.eval()                                   # 切换到推理模式(关闭 dropout 等)

# 下载 ImageNet 标签(1000 个类别名称)
url = "https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt"
try:
    labels = urllib.request.urlopen(url).read().decode("utf-8").splitlines()
except Exception:
    labels = [f"class_{i}" for i in range(1000)]  # 下载失败时用占位标签

# 图像预处理管道(和 MobileNet 训练时一致)
preprocess = transforms.Compose([
    transforms.Resize(256),               # 短边缩放到 256
    transforms.CenterCrop(224),           # 中心裁剪 224x224
    transforms.ToTensor(),                # 转为 Tensor(0-1)
    transforms.Normalize(                 # ImageNet 标准化
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225]
    ),
])

def classify_image(image):
    """
    对上传的图像做分类预测
    image: PIL.Image 对象(Gradio 自动转换)
    返回: {类别名: 概率} 字典
    """
    if image is None:                     # 未上传图像
        return {}

    img_tensor = preprocess(image).unsqueeze(0)  # 预处理 + 增加 batch 维度
    with torch.no_grad():                        # 不计算梯度(省内存)
        outputs = model(img_tensor)              # 前向传播
        probs = torch.nn.functional.softmax(outputs[0], dim=0)  # Softmax 得概率

    top5_probs, top5_indices = torch.topk(probs, 5)  # 取概率最高的 5 个
    result = {}
    for prob, idx in zip(top5_probs, top5_indices):
        result[labels[idx.item()]] = prob.item()     # {类别名: 概率}

    return result

# 创建 Demo
demo = gr.Interface(
    fn=classify_image,                   # 分类函数
    inputs=gr.Image(                     # 输入:图像
        type="pil",                      # 传入 PIL.Image 格式
        label="上传图片"
    ),
    outputs=gr.Label(                    # 输出:分类标签
        label="分类结果",
        num_top_classes=5                # 显示 Top 5
    ),
    title="图像分类 Demo(MobileNet V2)",
    description="上传一张图片,MobileNet V2 模型会预测它是什么",
)

demo.launch()

Demo 4:聊天机器人

# === gradio_chatbot.py ===
# 用 Gradio 的 ChatInterface 搭建一个简单聊天机器人

import gradio as gr  # 导入 Gradio
import random        # 随机数(模拟回复)

def chatbot_response(message, history):
    """
    聊天机器人的回复函数
    message: 用户当前输入的消息(字符串)
    history: 历史对话记录(Gradio 自动管理)
    返回: 机器人的回复(字符串)
    """
    # 简单的规则回复(实际项目中替换为 LLM API 调用)
    message_lower = message.lower()

    if "你好" in message or "hello" in message_lower:
        return "你好!我是生信小助手,有什么可以帮你的?"
    elif "基因" in message:
        return "基因(Gene)是编码蛋白质的 DNA 片段。你想了解基因的哪方面?比如基因表达、基因突变、基因组学?"
    elif "宏基因组" in message:
        return "宏基因组(Metagenomics)是直接从环境样本(如粪便、土壤)提取所有微生物的 DNA 进行测序分析。常用工具有 MetaPhlAn、HUMAnN、Kraken2 等。"
    elif "帮助" in message or "help" in message_lower:
        return "我可以回答生信相关问题,试试问我:\n- 什么是宏基因组?\n- 基因是什么?\n- 推荐学习资源"
    else:
        responses = [
            f"这是个好问题!关于'{message}',你可以查看相关文献了解更多。",
            f"'{message}'是一个有趣的话题,建议在 PubMed 上搜索相关论文。",
            "抱歉,这个问题超出了我的知识范围,建议查阅专业教材。"
        ]
        return random.choice(responses)  # 随机选一个回复

# 用 ChatInterface(聊天专用的高层 API)
demo = gr.ChatInterface(
    fn=chatbot_response,                 # 回复函数
    title="生信小助手",                    # 标题
    description="一个简单的生信问答聊天机器人",
    examples=["你好", "什么是宏基因组?", "基因是什么?"],  # 预设问题
    theme="soft"                         # 使用柔和主题
)

demo.launch()

Demo 5:多输入多输出

# === gradio_multi_io.py ===
# 多输入多输出示例:BMI 计算器 + 健康建议

import gradio as gr       # 导入 Gradio
import matplotlib          # 导入 matplotlib
matplotlib.use("Agg")     # 使用非交互后端(服务器环境必须)
import matplotlib.pyplot as plt  # 绑定图表绘制
import numpy as np        # 数值计算

def calculate_bmi(height_cm, weight_kg, age, gender):
    """
    多输入函数:接收身高、体重、年龄、性别
    多输出:BMI 文本、健康等级标签、BMI 分布图
    """
    if height_cm <= 0 or weight_kg <= 0:
        return "请输入有效的身高和体重", {}, None

    height_m = height_cm / 100              # 厘米转米
    bmi = weight_kg / (height_m ** 2)       # BMI = 体重 / 身高²

    # 判断 BMI 等级
    if bmi < 18.5:
        category = "偏瘦"
        advice = "建议适当增加营养摄入"
    elif bmi < 24:
        category = "正常"
        advice = "继续保持健康的生活方式"
    elif bmi < 28:
        category = "偏胖"
        advice = "建议适当控制饮食并增加运动"
    else:
        category = "肥胖"
        advice = "建议咨询医生制定健康计划"

    # 文本输出
    text_result = f"身高: {height_cm}cm | 体重: {weight_kg}kg | 性别: {gender}\n"
    text_result += f"BMI: {bmi:.1f} | 等级: {category}\n"
    text_result += f"建议: {advice}"

    # 标签输出(概率条形式展示各等级)
    label_result = {
        "偏瘦": max(0, 1 - abs(bmi - 16) / 10),
        "正常": max(0, 1 - abs(bmi - 21) / 10),
        "偏胖": max(0, 1 - abs(bmi - 26) / 10),
        "肥胖": max(0, 1 - abs(bmi - 32) / 10),
    }

    # 绘制 BMI 分布图(matplotlib)
    fig, ax = plt.subplots(figsize=(6, 3))          # 创建图
    categories = ["偏瘦\n<18.5", "正常\n18.5-24", "偏胖\n24-28", "肥胖\n>28"]
    colors = ["#3498db", "#2ecc71", "#f39c12", "#e74c3c"]
    ranges = [18.5, 24, 28, 40]
    ax.barh(categories, ranges, color=colors, alpha=0.6)   # 水平条形图
    ax.axvline(x=bmi, color="red", linewidth=2, label=f"你的 BMI: {bmi:.1f}")  # 标记线
    ax.set_xlabel("BMI 值")
    ax.legend()
    ax.set_title(f"BMI 评估结果 ({gender}, {age}岁)")
    plt.tight_layout()

    return text_result, label_result, fig    # 返回三个输出

# 创建多输入多输出 Demo
demo = gr.Interface(
    fn=calculate_bmi,
    inputs=[                                 # 多个输入组件(列表)
        gr.Slider(                           # 身高滑块
            minimum=100, maximum=220,
            value=170, step=1,
            label="身高 (cm)"
        ),
        gr.Slider(                           # 体重滑块
            minimum=30, maximum=150,
            value=65, step=0.5,
            label="体重 (kg)"
        ),
        gr.Number(                           # 年龄数字框
            value=25,
            label="年龄"
        ),
        gr.Radio(                            # 性别单选
            choices=["男", "女"],
            value="男",
            label="性别"
        ),
    ],
    outputs=[                                # 多个输出组件(列表)
        gr.Textbox(label="计算结果"),          # 文本输出
        gr.Label(label="BMI 等级分布"),        # 标签输出
        gr.Plot(label="BMI 分布图"),           # 图表输出
    ],
    title="BMI 健康计算器",
    description="输入身高、体重、年龄和性别,获取 BMI 分析报告",
)

demo.launch()

Demo 6:自定义主题

# === gradio_theme.py ===
# 自定义 Gradio 主题示例

import gradio as gr  # 导入 Gradio

# ---------- 方式 1:使用内置主题 ----------
# Gradio 内置主题:Base, Default, Origin, Monochrome, Soft, Ocean, Glass
# demo = gr.Interface(..., theme=gr.themes.Soft())
# demo = gr.Interface(..., theme="soft")   # 字符串简写也行

# ---------- 方式 2:自定义主题颜色 ----------
custom_theme = gr.themes.Soft(
    primary_hue="teal",                  # 主色调:青色(默认 orange)
    secondary_hue="blue",               # 次色调:蓝色
    neutral_hue="slate",                # 中性色:灰石色
    font=gr.themes.GoogleFont("Noto Sans SC"),  # 使用思源黑体(支持中文)
)

def echo(text):
    return f"你输入了:{text}"

# 使用自定义主题
demo = gr.Interface(
    fn=echo,
    inputs=gr.Textbox(label="输入"),
    outputs=gr.Textbox(label="输出"),
    title="自定义主题演示",
    description="使用 Teal + Blue 配色方案的自定义主题",
    theme=custom_theme                   # 应用自定义主题
)

demo.launch()

生信实战

实战 1:基因序列分类器 Demo

# === gradio_gene_classifier.py ===
# 生信实战:基因序列 GC 含量分析 + 类型预测

import gradio as gr   # 导入 Gradio

def analyze_gene_sequence(sequence, seq_type):
    """
    分析基因序列:计算 GC 含量、长度、碱基组成,预测序列类型
    sequence: DNA/RNA 序列字符串
    seq_type: 序列类型(DNA/RNA)
    """
    # 清洗序列:去空格、换行,统一大写
    seq = sequence.upper().replace(" ", "").replace("\n", "")
    seq = ''.join(c for c in seq if c in "ATCGUN")  # 只保留有效碱基

    if len(seq) < 10:
        return "序列太短(至少10个碱基)", {}, ""

    length = len(seq)                      # 序列长度

    # 碱基统计
    base_counts = {base: seq.count(base) for base in "ATCGUN"}
    valid_bases = {k: v for k, v in base_counts.items() if v > 0}

    # GC 含量计算
    gc_count = seq.count("G") + seq.count("C")
    gc_content = gc_count / length * 100   # GC 百分比

    # 基于 GC 含量的简单分类预测
    # (真实场景用训练好的分类模型,这里用规则演示)
    if gc_content > 60:
        prediction = {"嗜热菌基因": 0.6, "高GC革兰氏阳性菌": 0.3, "普通基因": 0.1}
    elif gc_content > 40:
        prediction = {"普通基因": 0.6, "人类基因": 0.25, "嗜热菌基因": 0.15}
    else:
        prediction = {"AT富集区": 0.5, "启动子区域": 0.3, "普通基因": 0.2}

    # 格式化结果
    report = f"=== 序列分析报告 ===\n"
    report += f"序列类型: {seq_type}\n"
    report += f"序列长度: {length} bp\n"
    report += f"GC 含量: {gc_content:.1f}%\n"
    report += f"碱基组成: {valid_bases}\n"
    report += f"\n=== AT/GC 比例 ===\n"
    at_count = seq.count("A") + seq.count("T") + seq.count("U")
    report += f"AT(U): {at_count} ({at_count/length*100:.1f}%)\n"
    report += f"GC:    {gc_count} ({gc_content:.1f}%)\n"

    return report, prediction, seq[:50] + "..." if length > 50 else seq

# 创建生信 Demo
demo = gr.Interface(
    fn=analyze_gene_sequence,
    inputs=[
        gr.Textbox(                          # DNA/RNA 序列输入
            label="输入基因序列",
            placeholder="粘贴 DNA/RNA 序列,例如:ATCGATCGATCG...",
            lines=5
        ),
        gr.Radio(                            # 序列类型选择
            choices=["DNA", "RNA"],
            value="DNA",
            label="序列类型"
        ),
    ],
    outputs=[
        gr.Textbox(label="分析报告"),          # 文本报告
        gr.Label(label="序列来源预测"),         # 分类预测
        gr.Textbox(label="清洗后序列预览"),     # 处理后序列
    ],
    title="基因序列分析器",
    description="输入 DNA/RNA 序列,自动分析 GC 含量、碱基组成,并预测序列来源",
    examples=[
        ["ATCGATCGATCGATCGATCGATCG", "DNA"],
        ["GCGCGCGCGCGCGCGCGCGCGCGC", "DNA"],
        ["AUGCUAGCUAGCUAGCUAGCUAGC", "RNA"],
    ],
)

demo.launch()

实战 2:蛋白质结构特征预测 Demo

# === gradio_protein_demo.py ===
# 生信实战:蛋白质序列基本特征分析

import gradio as gr             # 导入 Gradio
import matplotlib               # 导入 matplotlib
matplotlib.use("Agg")          # 非交互后端
import matplotlib.pyplot as plt  # 绑定图表绘制
import numpy as np              # 数值计算

# 氨基酸分子量表(单位:Da)
AA_MW = {
    'A': 89.1, 'R': 174.2, 'N': 132.1, 'D': 133.1, 'C': 121.2,
    'E': 147.1, 'Q': 146.2, 'G': 75.0, 'H': 155.2, 'I': 131.2,
    'L': 131.2, 'K': 146.2, 'M': 149.2, 'F': 165.2, 'P': 115.1,
    'S': 105.1, 'T': 119.1, 'W': 204.2, 'Y': 181.2, 'V': 117.1,
}

# 氨基酸疏水性(Kyte-Doolittle 标度)
AA_HYDRO = {
    'A': 1.8, 'R': -4.5, 'N': -3.5, 'D': -3.5, 'C': 2.5,
    'E': -3.5, 'Q': -3.5, 'G': -0.4, 'H': -3.2, 'I': 4.5,
    'L': 3.8, 'K': -3.9, 'M': 1.9, 'F': 2.8, 'P': -1.6,
    'S': -0.8, 'T': -0.7, 'W': -0.9, 'Y': -1.3, 'V': 4.2,
}

def analyze_protein(sequence, window_size):
    """
    分析蛋白质序列的基本理化特征
    sequence: 氨基酸单字母序列
    window_size: 疏水性滑窗大小
    """
    # 清洗序列
    seq = sequence.upper().replace(" ", "").replace("\n", "")
    seq = ''.join(c for c in seq if c in AA_MW)  # 只保留标准氨基酸

    if len(seq) < 5:
        return "序列太短", None

    # 基本统计
    length = len(seq)
    mw = sum(AA_MW.get(aa, 0) for aa in seq) - (length - 1) * 18.02  # 减去缩合水
    aa_counts = {aa: seq.count(aa) for aa in set(seq)}

    # 疏水性滑窗分析
    window = int(window_size)
    hydro_profile = []                        # 疏水性曲线
    for i in range(len(seq) - window + 1):
        window_seq = seq[i:i + window]        # 取窗口内的氨基酸
        avg_hydro = np.mean([AA_HYDRO.get(aa, 0) for aa in window_seq])
        hydro_profile.append(avg_hydro)

    # 绘制疏水性图谱
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6))

    # 子图1:疏水性曲线
    ax1.plot(hydro_profile, color='steelblue', linewidth=1.2)
    ax1.axhline(y=0, color='red', linestyle='--', alpha=0.5)  # 零线
    ax1.fill_between(range(len(hydro_profile)), hydro_profile, 0,
                     where=[h > 0 for h in hydro_profile],
                     alpha=0.3, color='orange', label='疏水区')
    ax1.fill_between(range(len(hydro_profile)), hydro_profile, 0,
                     where=[h <= 0 for h in hydro_profile],
                     alpha=0.3, color='blue', label='亲水区')
    ax1.set_xlabel('残基位置')
    ax1.set_ylabel('疏水性 (Kyte-Doolittle)')
    ax1.set_title(f'疏水性图谱 (窗口={window})')
    ax1.legend()

    # 子图2:氨基酸组成饼图
    top_aa = sorted(aa_counts.items(), key=lambda x: -x[1])[:8]  # 取前8
    labels = [f"{aa} ({count})" for aa, count in top_aa]
    sizes = [count for _, count in top_aa]
    ax2.pie(sizes, labels=labels, autopct='%1.0f%%', startangle=90)
    ax2.set_title('氨基酸组成(Top 8)')

    plt.tight_layout()

    # 文本报告
    report = f"=== 蛋白质特征分析 ===\n"
    report += f"长度: {length} aa\n"
    report += f"分子量: {mw:.1f} Da ({mw/1000:.1f} kDa)\n"
    report += f"平均疏水性: {np.mean([AA_HYDRO.get(aa, 0) for aa in seq]):.2f}\n"
    report += f"预测类型: {'膜蛋白(富含疏水区)' if np.mean([AA_HYDRO.get(aa,0) for aa in seq]) > 0 else '可溶蛋白(亲水为主)'}\n"

    return report, fig

# 创建 Demo
demo = gr.Interface(
    fn=analyze_protein,
    inputs=[
        gr.Textbox(
            label="输入蛋白质序列(氨基酸单字母码)",
            placeholder="例如:MVLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSH",
            lines=4
        ),
        gr.Slider(
            minimum=5, maximum=21, value=9, step=2,
            label="疏水性分析窗口大小"
        ),
    ],
    outputs=[
        gr.Textbox(label="特征分析报告"),
        gr.Plot(label="疏水性图谱 & 氨基酸组成"),
    ],
    title="蛋白质序列特征分析",
    description="输入蛋白质氨基酸序列,分析分子量、疏水性图谱、氨基酸组成等特征",
    examples=[
        ["MVLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSH", 9],
        ["MALWMRLLPLLALLALWGPDPAAAFVNQHLCGSHLVEALYLVCGERGFFYTPKTRREAEDLQVGQVELGGGPGAGSLQPLALEGSLQKRGIVEQCCTSICSLYQLENYCN", 11],
    ],
)

demo.launch()

实战 3:物种丰度可视化 Demo

# === gradio_abundance.py ===
# 生信实战:微生物物种丰度可视化(宏基因组方向)

import gradio as gr             # 导入 Gradio
import matplotlib               # 导入 matplotlib
matplotlib.use("Agg")          # 非交互后端
import matplotlib.pyplot as plt  # 绑定图表绘制
import numpy as np              # 数值计算
import io                       # 字节流

# 模拟物种丰度数据(真实场景从 MetaPhlAn/Kraken2 结果读取)
MOCK_DATA = {
    "健康组": {
        "Bacteroides": 25.3, "Faecalibacterium": 18.7, "Prevotella": 12.1,
        "Roseburia": 8.5, "Bifidobacterium": 7.2, "Akkermansia": 5.8,
        "Eubacterium": 4.3, "Ruminococcus": 3.9, "其他": 14.2,
    },
    "T2D 患者": {
        "Bacteroides": 32.1, "Faecalibacterium": 8.2, "Prevotella": 6.5,
        "Roseburia": 4.1, "Bifidobacterium": 3.8, "Akkermansia": 2.1,
        "Eubacterium": 2.9, "Ruminococcus": 5.7, "其他": 34.6,
    },
}

def plot_abundance(group, chart_type, top_n):
    """
    根据选择的分组和图表类型绘制物种丰度图
    """
    top_n = int(top_n)

    if group == "对比两组":
        # 绘制对比柱状图
        fig, ax = plt.subplots(figsize=(10, 5))
        species = list(MOCK_DATA["健康组"].keys())[:top_n]

        x = np.arange(len(species))
        width = 0.35

        healthy = [MOCK_DATA["健康组"].get(s, 0) for s in species]
        t2d = [MOCK_DATA["T2D 患者"].get(s, 0) for s in species]

        ax.bar(x - width/2, healthy, width, label="健康组", color="#2ecc71")
        ax.bar(x + width/2, t2d, width, label="T2D 患者", color="#e74c3c")

        ax.set_xlabel("物种")
        ax.set_ylabel("相对丰度 (%)")
        ax.set_title("健康组 vs T2D 患者 肠道菌群丰度对比")
        ax.set_xticks(x)
        ax.set_xticklabels(species, rotation=45, ha="right", fontsize=8)
        ax.legend()

        report = "=== 关键差异 ===\n"
        for s in species:
            diff = MOCK_DATA["T2D 患者"].get(s, 0) - MOCK_DATA["健康组"].get(s, 0)
            direction = "升高" if diff > 0 else "降低"
            report += f"{s}: T2D 组{direction} {abs(diff):.1f}%\n"

    else:
        data = MOCK_DATA[group]
        sorted_items = sorted(data.items(), key=lambda x: -x[1])[:top_n]
        species = [item[0] for item in sorted_items]
        abundances = [item[1] for item in sorted_items]

        fig, ax = plt.subplots(figsize=(8, 5))

        if chart_type == "柱状图":
            colors = plt.cm.Set3(np.linspace(0, 1, len(species)))
            ax.bar(species, abundances, color=colors)
            ax.set_ylabel("相对丰度 (%)")
            plt.xticks(rotation=45, ha="right", fontsize=8)
        elif chart_type == "饼图":
            ax.pie(abundances, labels=species, autopct='%1.1f%%', startangle=90)
        else:  # 堆叠条形图
            ax.barh(species[::-1], abundances[::-1],
                    color=plt.cm.Pastel1(np.linspace(0, 1, len(species))))
            ax.set_xlabel("相对丰度 (%)")

        ax.set_title(f"{group} - 肠道菌群 Top{top_n} 物种丰度")

        report = f"=== {group} 物种丰度 ===\n"
        for s, a in sorted_items:
            report += f"{s}: {a:.1f}%\n"

    plt.tight_layout()
    return report, fig

# 创建 Demo
demo = gr.Interface(
    fn=plot_abundance,
    inputs=[
        gr.Dropdown(                         # 分组选择
            choices=["健康组", "T2D 患者", "对比两组"],
            value="对比两组",
            label="选择分组"
        ),
        gr.Radio(                            # 图表类型
            choices=["柱状图", "饼图", "水平条形图"],
            value="柱状图",
            label="图表类型"
        ),
        gr.Slider(                           # Top N
            minimum=3, maximum=9, value=7, step=1,
            label="显示 Top N 物种"
        ),
    ],
    outputs=[
        gr.Textbox(label="丰度数据"),
        gr.Plot(label="丰度可视化"),
    ],
    title="肠道菌群物种丰度可视化",
    description="基于 T2D(2型糖尿病)宏基因组数据,可视化肠道菌群物种丰度分布",
)

demo.launch()

Gradio vs Streamlit 详细对比

对比维度GradioStreamlit
定位ML 模型 Demo / API数据应用 / 仪表盘
代码量3-10 行起步10-30 行起步
核心 APIgr.Interface() / gr.Blocks()st.write() / st.sidebar()
输入处理声明式(定义组件类型即可)命令式(手动写 widget)
自动生成 API自动生成 REST API需要额外配置
分享share=True 生成临时公网链接需部署到 Streamlit Cloud
HuggingFace原生支持 Spaces也支持,但不如 Gradio 紧密
状态管理简单(函数输入输出)st.session_state(较复杂)
聊天界面gr.ChatInterface 一行搞定需手动拼 st.chat_message
实时更新事件驱动每次交互重跑整个脚本
学习曲线极低
最适合模型推理、论文 Demo、API数据分析报告、BI 看板

选择建议: - 面试展示模型 → Gradio(三行代码 + HuggingFace 一键部署) - 做数据看板/报告 → Streamlit(23 篇已讲) - 两者都会是加分项


部署到 HuggingFace Spaces

HuggingFace Spaces 是 Gradio 应用的首选部署平台,免费且无需服务器。

步骤 1:创建 Space

# 方式 1:网页创建
# 访问 https://huggingface.co/new-space
# Space name: 输入项目名(如 gene-sequence-classifier)
# SDK: 选择 Gradio
# 点击 Create Space

# 方式 2:命令行(需要先 pip install huggingface_hub)
pip install huggingface_hub
huggingface-cli login  # 输入你的 HuggingFace token

步骤 2:准备文件

你的 Space 仓库至少需要两个文件:

app.py(Gradio 应用主文件):

# === app.py ===
# HuggingFace Spaces 会自动运行这个文件
import gradio as gr

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

demo = gr.Interface(fn=greet, inputs="text", outputs="text")
demo.launch()  # 不需要指定端口,Spaces 会自动处理

requirements.txt(Python 依赖):

gradio>=6.0
scikit-learn
matplotlib

步骤 3:推送部署

# 克隆你的 Space 仓库
git clone https://huggingface.co/spaces/你的用户名/项目名
cd 项目名

# 放入 app.py 和 requirements.txt
# ... 复制文件 ...

# 推送即部署
git add .
git commit -m "Initial commit"
git push

# 几分钟后访问 https://huggingface.co/spaces/你的用户名/项目名 即可看到

也可以用 share=True 快速分享

# 在本地开发时,一行代码生成公网链接(72 小时有效)
demo.launch(share=True)
# 输出类似:Running on public URL: https://xxxxx.gradio.live

常见报错与解决

报错 1:ModuleNotFoundError: No module named 'gradio'

原因:没有安装 Gradio 或环境不对
解决:
  conda activate bioinfo          # 确认激活了正确的 conda 环境
  pip install gradio              # 安装 Gradio
  pip install --upgrade gradio    # 或升级到最新版

报错 2:OSError: Cannot find empty port in range: 7860-7860

原因:端口 7860 被占用(上一个 Gradio 进程没关)
解决:
  # Linux/WSL 下查找并杀掉占用进程
  lsof -i :7860
  kill -9 <PID>

  # 或者指定其他端口
  demo.launch(server_port=7861)

报错 3:ValueError: Unknown component: xxx

原因:使用了不存在的组件名称或 API 变动
解决:
  # Gradio 6.x 中很多组件名做了调整
  # 错误写法(旧版):inputs="textbox"
  # 正确写法(新版):inputs=gr.Textbox()
  # 查看官方文档确认组件名:https://www.gradio.app/docs

报错 4:RuntimeError: CUDA out of memory

原因:加载的模型太大,GPU 显存不够
解决:
  # 方法 1:使用 CPU
  model = model.to("cpu")

  # 方法 2:使用更小的模型
  model = models.mobilenet_v2(pretrained=True)  # 而不是 resnet152

  # 方法 3:在 HuggingFace Spaces 申请 GPU(免费额度有限)

报错 5:gr.Interface has no attribute 'queue'

原因:Gradio 6.x 中 queue 已默认启用,不再需要手动调用
解决:
  # 旧版写法(Gradio 4.x):
  demo.queue().launch()

  # 新版写法(Gradio 6.x):
  demo.launch()    # queue 默认开启,直接 launch 即可

报错 6:中文显示乱码(matplotlib 图表)

原因:matplotlib 默认字体不支持中文
解决:在代码开头加上:
  import matplotlib
  matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']   # 优先用黑体
  matplotlib.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

  # WSL 中可能需要安装字体:
  sudo apt install fonts-wqy-microhei

速查表

组件速查

组件代码功能
文本框gr.Textbox(label="xx")文本输入/输出
数字gr.Number(value=0)数字输入
滑块gr.Slider(0, 100, value=50)范围选择
下拉框gr.Dropdown(choices=["a","b"])下拉选择
单选gr.Radio(choices=["a","b"])单选按钮
多选gr.CheckboxGroup(choices=["a","b"])多选框
复选框gr.Checkbox(label="同意")单个开关
图片gr.Image(type="pil")图片输入/输出
音频gr.Audio(type="filepath")音频输入/输出
视频gr.Video()视频输入/输出
文件gr.File(label="上传")文件上传
表格gr.Dataframe()表格展示
图表gr.Plot()Matplotlib/Plotly 图
标签gr.Label()分类概率展示
画廊gr.Gallery()图片画廊
聊天gr.Chatbot()聊天记录展示
Markdowngr.Markdown("## 标题")富文本展示
按钮gr.Button("提交")触发操作
代码gr.Code(language="python")代码编辑器
HTMLgr.HTML("<b>粗体</b>")自定义 HTML
JSONgr.JSON()JSON 可视化

常用操作速查

# ---------- 快速启动 ----------
demo.launch()                        # 默认 http://127.0.0.1:7860
demo.launch(share=True)              # 生成公网链接(72小时)
demo.launch(server_port=8080)        # 自定义端口
demo.launch(server_name="0.0.0.0")   # 允许外部访问

# ---------- Interface 常用参数 ----------
gr.Interface(
    fn=my_func,                      # 处理函数
    inputs=[...],                    # 输入组件(列表=多输入)
    outputs=[...],                   # 输出组件(列表=多输出)
    title="标题",                     # 页面标题
    description="描述",               # 页面描述
    examples=[[...]],                # 预设示例
    theme="soft",                    # 主题(soft/default/monochrome等)
    live=True,                       # 输入变化即时触发(不用点按钮)
    flagging_mode="never",           # 关闭 flag 功能
)

# ---------- Blocks 常用布局 ----------
with gr.Blocks() as demo:
    gr.Markdown("# 标题")
    with gr.Tab("标签页1"):           # 标签页
        ...
    with gr.Tab("标签页2"):
        ...
    with gr.Row():                   # 水平排列
        with gr.Column(scale=2):     # 左列(占 2 份宽度)
            ...
        with gr.Column(scale=1):     # 右列(占 1 份宽度)
            ...
    with gr.Accordion("展开详情"):    # 可折叠区域
        ...

# ---------- ChatInterface ----------
gr.ChatInterface(
    fn=chat_func,                    # fn(message, history) -> response
    type="messages",                 # 消息格式(Gradio 6.x 默认)
    examples=["你好", "帮助"],        # 预设消息
    title="聊天机器人",
)

# ---------- 事件绑定(Blocks 中) ----------
btn.click(fn=func, inputs=[a], outputs=[b])      # 点击
textbox.submit(fn=func, inputs=[a], outputs=[b])  # 回车
textbox.change(fn=func, inputs=[a], outputs=[b])  # 内容变化
slider.release(fn=func, inputs=[a], outputs=[b])  # 松开滑块

# ---------- 主题 ----------
gr.themes.Base()                     # 基础(蓝色)
gr.themes.Default()                  # 默认(橙色)
gr.themes.Soft()                     # 柔和
gr.themes.Monochrome()               # 黑白
gr.themes.Origin()                   # 原始
gr.themes.Ocean()                    # 海洋
gr.themes.Glass()                    # 玻璃

延伸资源

资源说明
Gradio 官方文档组件 API、参数详解
Gradio Guides官方教程,从入门到进阶
HuggingFace Spaces浏览别人的 Gradio Demo 学习
Gradio GitHub源码 + Issue 讨论
awesome-demosHF 上最受欢迎的 Demo 合集
本项目 23 篇 Plotly+Streamlit数据可视化方向,与本篇互补
本项目 13 篇 AlphaFold蛋白质结构预测,可结合 Gradio 展示
本项目 49 篇 PyTorch 入门深度学习模型训练,训练完用 Gradio 展示

面试加分技巧:把研究项目(T2D 肠道菌群分类器)用 Gradio 包装成 Demo,部署到 HuggingFace Spaces,面试时直接甩链接。面试官能亲手操作你的模型,比说一百句"我做了XX分析"都有说服力。