AI模型训练完成只是第一步,真正折磨人的往往是“怎么给别人用”。让模型使用者敲 Python 命令不现实,专门写一套 Vue/React 前端又太耗时,于是 Streamlit 和 Gradio 就成了 Python 生态里把 AI 模型变成 Web 应用的关键工具。EP19 这一个主题的落点很明确:你不一定需要会写 HTML/CSS/JavaScript,只要会用 Python 包装模型函数,就能在几小时内做出一个“能打开、能交互、能批量处理”的模型页面。
这两个库的共同价值是“免前端开发”。Streamlit 更像是一个数据应用框架,适合做 AI 指标看板、参数配置工具、Excel/CSV 批量预测工具;Gradio 则更像是 AI Demo 专用框架,把输入框、上传组件、预测结果组件直接绑定到模型函数,顺便还能提供 API 调用入口。两者都支持本地 CPU 启动,也都能对接 GPU 推理,模型能不能跑取决于模型本身,界面层本身几乎没有硬件门槛。实际部署时,Streamlit 默认端口是 8501,Gradio 默认端口是 7860,都不需要额外编译前端资源。
下面这篇会围绕“把模型变成 Web 应用”这条主线展开:先做环境准备,再分别用 Streamlit 和 Gradio 写两个可运行 Demo,然后演示 Gradio 接口调用和 Streamlit 批量预测,最后给出资源占用观察、常见报错排查和上线前的最佳实践。如果你正在给别人交付模型,或者想快速验证一个算法效果,完全可以按文中顺序把流程跑通。
1. 核心能力速览
先看两个框架的整体定位。
| 维度 | Streamlit | Gradio |
|---|---|---|
| 项目定位 | 数据应用框架,偏向数据看板、内部工具、批量处理 | AI 模型 Demo 框架,偏向快速展示和接口访问 |
| 界面定义方式 | Python 脚本从上到下执行,组件自动渲染 | Interface 快速声明式,Blocks 自定义复杂布局 |
| 常用输入组件 | text_input、text_area、file_uploader、selectbox | Textbox、Image、Audio、Dataframe、File |
| 常用输出组件 | metric、dataframe、chart、markdown | Label、Image、Audio、Dataframe、Chatbot |
| 浏览器访问 | 必须有浏览器访问 Streamlit 服务页面 | 同样以浏览器页面为主,页面内提供可调用接口文档 |
| API 接口能力 | 本身不面向第三方程序提供标准模型 API,需要自己封装模型服务 | 自带接口访问能力,可通过 gradio_client 或 HTTP 请求调用 |
| 身份验证 | 需要借助反向代理或框架自身能力 | launch 时可配置 auth 参数做简单账户密码验证 |
| 批量任务 | 适合 pandas 批量导入预测后导出 | 适合单个/小批量文件级任务,大规模任务需要自行排队 |
| 典型启动方式 | streamlit run app.py | 运行 Python 脚本,如 python gradio_app.py |
| 典型端口 | 8501 | 7860 |
| 最适场景 | 内部数据产品、模型效果汇总页、Excel 批量推理 | 模型 Demo、给算法工程师快速联调、模型验收演示 |
从上表能看出,Streamlit 的强项是“数据流”:用户传一张表上来,页面逐条调用模型,最后把预测结果追加到 DataFrame 再下载回去。Gradio 的强项是“模型输入输出绑定”:上传一张图或一段音频,前端直接展示模型返回值,而且新版页面里能直接看到 API 文档和调用示例。两者并不冲突,同一个模型可以底层共用推理函数,上面分别包一层页面。
2. 适用场景与使用边界
先说适合做什么。算法工程师做模型效果汇报时,用 Gradio 做一个文本分类或图像分类 Demo,业务方在浏览器里直接粘贴几个测试样例就能反馈,比把 notebook 截图放进 PPT 直观得多。数据团队需要给运营提供“预测下月销量”的工具时,用 Streamlit 写一个上传历史数据、选择时间范围、生成预测结果的页面,可以省掉完整的报表系统开发周期。还有一类常见场景是本地模型调试,比如你刚导出一个 ONNX 或 PyTorch 模型,想快速看一下不同提示词、不同参数下的结果,用这两个工具搭一个小页面是最快的路径。
不适合做什么也要有预期。这类 Python Web 框架上线后仍是进程内同步推理模型,面对大量并发请求时需要后台任务队列,不能直接拿它当高并发 B 端产品服务。Gradio 适合典型的前后端联调,但如果要复杂权限管理、多页角色体系、支付或严格审计日志,还是需要专业 Web 框架。Streamlit 的页面状态是“每次交互都重新运行脚本”,大规模数据处理时如果不在缓存和任务队列上下功夫,用户操作会明显卡顿。
使用边界需要重点强调合规。模型页面一旦发布到公网或局域网,就代表外部输入可以访问推理服务,必须有身份验证和访问日志。涉及人脸识别、声音克隆、版权图片生成等能力时,必须确认你有合法授权,不能拿未授权素材做线上演示,也不能在不做审核的情况下直接把生成内容分发出去。本文提到的所有 Demo 仅用于测试环境功能验证,不能满足生产环境的未授权公网部署要求。
3. 环境准备与前置条件
3.1 准备 Python 环境
Windows、macOS、Linux 都能运行这两个框架。建议使用 Python 3.9 或更新的版本,具体以两个库官方安装声明为准。先创建一个独立虚拟环境,避免项目之间依赖冲突:
python -m venv .venv # Windows 激活 .venv\Scripts\activate # macOS/Linux 激活 source .venv/bin/activate如果你的机器还没有安装 Python,需要先安装 Python 并确认 pip 可用:
python --version pip --version3.2 安装依赖
核心依赖只有两个库,再加上后面做批量任务会用到 pandas,建议一次性装好:
pip install -U streamlit gradio pandas如果你计划在 Demo 里直接用 Hugging Face Transformers 模型,还需要额外安装 transformers 和对应的深度学习框架:
pip install -U transformers torch注意,这条命令安装的包体积比较大,建议在真正需要时才安装。机器没有 GPU 也能跑 CPU 推理,只是大模型推理会比较慢。如果离线环境无法从公共模型仓下载模型,要提前把模型文件放到本地目录,并在加载代码中切换到本地模型路径。
3.3 检查端口和目录结构
Gradio 和 Streamlit 启动后分别会在 7860 和 8501 端口监听。如果端口被占用,需要提前确认或换端口。准备以下目录结构,方便模型文件和 Web 入口分离:
ai_web_demo/ ├── .venv/ ├── models/ # 本地模型文件 ├── inputs/ # 测试输入文件 ├── outputs/ # 批量预测导出结果 ├── model_service.py # 统一模型加载和预测函数 ├── streamlit_app.py # Streamlit 页面入口 └── gradio_app.py # Gradio 页面入口把模型、输入、输出分开目录管理,后续批量任务或日志排查会更省事。小规模测试时,甚至可以把历史模型、规则函数、失败日志都放在同一份代码里,但建议至少保留一个明确入口。
4. 安装部署与启动方式
4.1 Streamlit 启动方式
Streamlit 不需要写 run 循环,脚本保存后直接用命令行启动:
streamlit run streamlit_app.py如果 8501 端口已被占用,可以显式指定端口:
streamlit run streamlit_app.py --server.port 8502启动成功后,终端会输出本地访问地址,通常是http://localhost:8501。浏览器打开后即可看到页面。Streamlit 的常见工作方式是:代码保存后页面右上角出现 Rerun 提示,点击后就能看到新配置,适合迭代调试。
4.2 Gradio 启动方式
Gradio 在 Python 脚本里调用demo.launch(),然后直接用 Python 运行脚本:
python gradio_app.pyGradio 默认会使用 7860 端口。如果想固定在内网地址并设置端口,可在代码里写成:
demo.launch(server_name="127.0.0.1", server_port=7860)如果设置server_name="0.0.0.0",同一局域网内其他设备也能通过宿主机 IP 访问,但强烈建议仅在可信内网测试时使用,不要直接暴露到公网。
4.3 Gradio 启动时配置简单身份验证
如果你要在内网给同事做模型验收页面,又不想让任何拿到链接的人都能用,可以给launch()加auth参数:
demo.launch(server_name="0.0.0.0", server_port=7860, auth=("admin", "这里替换成强密码"))设置后,浏览器首次访问会弹出账户密码输入框。需要说明的是,不同版本的 Gradio 对auth的支持和表现会有差异,请以你所安装版本的官方文档为准。这只适合演示环境,生产环境仍建议在前面加一层企业身份网关或反向代理。
5. 功能测试:从模型函数到 Web 页面
测试目标是让一个本地模型函数能被网页调用。这里先做一个统一模型入口model_service.py,Streamlit 和 Gradio 两个页面都调用同一个函数,方便后续替换成真实模型。
5.1 写一个可替换的模型服务函数
下面代码是模板,不是完整真实模型。真实模型加载逻辑,比如joblib.load("models/clf.pkl")、AutoModelForSequenceClassification.from_pretrained(...)、本地 ONNX Runtime 推理,都需要替换到load_model和predict_text中。
# model_service.py # -*- coding: utf-8 -*- def load_model(): """换成你自己的模型加载逻辑。""" # 示例1:scikit-learn 模型 # import joblib # return joblib.load("models/clf.pkl") # 示例2:Hugging Face 本地模型 # from transformers import AutoTokenizer, AutoModelForSequenceClassification # tokenizer = AutoTokenizer.from_pretrained("models/bert_local") # model = AutoModelForSequenceClassification.from_pretrained("models/bert_local") # return {"tokenizer": tokenizer, "model": model} return None def predict_text(text, model=None): """文本预测函数,返回 (label, score)。""" # 真实场景请替换成你自己的推理逻辑,例如: # inputs = model["tokenizer"](text, return_tensors="pt") # logits = model["model"](**inputs).logits # label = logits.argmax().item() # return "positive" if label == 1 else "negative", 0.9 # 下面是无真实模型时的规则兜底,用于让页面先跑通 if text and any(word in text for word in ["好", "棒", "推荐", "满意"]): return "positive", 0.92 return "negative", 0.71这里的关键思路是:页面只负责接收输入和展示输出,真正耗时的模型推理集中在一个函数里。你要接本地大模型时,只要改model_service.py的内容,不需要动页面代码。
5.2 Streamlit 文本分类 Demo 测试
创建streamlit_app.py:
import streamlit as st from model_service import load_model, predict_text @st.cache_resource def get_model(): # 缓存模型,避免每次页面交互都重新加载 return load_model() st.title("Streamlit AI 模型快速 Demo") st.caption("输入一段文本,返回预测标签和置信度。模型函数可替换为本地分类模型或生成模型。") text = st.text_area("输入文本", value="这个产品很不错,推荐大家使用") if st.button("开始预测"): model = get_model() with st.spinner("模型推理中..."): label, score = predict_text(text, model=model) st.success("预测完成") st.metric("预测标签", label) st.metric("置信度", f"{score:.4f}")运行:
streamlit run streamlit_app.py浏览器打开后,修改文本框内容,点击“开始预测”,页面会显示预测标签和置信度。判断成功标准是:没有报错,结果正常展示,重复点击时不会每次都重新下载/加载大模型。这是通过@st.cache_resource实现的。如果不加缓存,每次交互都重新执行 Python 脚本,会带来不必要的模型加载开销。
5.3 Gradio 图像分类 Demo 测试
创建gradio_app.py:
import gradio as gr def classify_image(image): # image 是 PIL.Image 对象,Gradio 会自动完成上传文件的解析。 # 这里需要替换成真实图像分类模型推理,例如: # import onnxruntime as ort # session = ort.InferenceSession("models/image_model.onnx") # result = session.run(None, {"input": preprocess(image)}) # return {labels[i]: float(prob) for i, prob in enumerate(result[0][0])} # 当前为 UI 联通测试:返回一个模拟概率分布。 return {"cat": 0.55, "dog": 0.45} demo = gr.Interface( fn=classify_image, inputs=gr.Image(type="pil", label="上传图片"), outputs=gr.Label(num_top_classes=3, label="预测结果"), title="Gradio 图像分类 Demo", description="上传一张图片,观察预测结果。当前为 UI 联调模板,替换 classify_image 内部逻辑即可接入真实模型。", ) if __name__ == "__main__": demo.launch(server_name="127.0.0.1", server_port=7860)运行:
python gradio_app.py浏览器打开http://localhost:7860,拖动一张本地图片到上传区域,页面会返回类别和概率。判断成功的标准是图片能上传、模型函数被调用、输出区域展示分类结果。
比较有价值的体验点是:Gradio 会自动处理图片上传、格式转换和前端渲染。你不用写任何<input type="file">之类的代码,输入输出组件类型已经帮你完成了大部分工作。
5.4 验证多参数交互
Gradio 的优势在于多输入组件绑定。改造一下文本分类 Demo,增加一个“展示阈值”滑块,可以直观看到参数如何影响页面触发:
import gradio as gr from model_service import predict_text def predict_with_threshold(text, threshold): label, score = predict_text(text) if score < threshold / 100: label = "low_confidence" return label, f"{score:.4f}" demo = gr.Interface( fn=predict_with_threshold, inputs=[ gr.Textbox(label="输入文本", lines=3), gr.Slider(minimum=0, maximum=100, step=1, value=80, label="置信度阈值"), ], outputs=[ gr.Textbox(label="预测标签"), gr.Textbox(label="置信度"), ], title="Gradio 多参数 Demo", ) if __name__ == "__main__": demo.launch()这一步的主要目的是验证 Gradio 的输入映射能力:界面上多个控件对应函数里的多个参数,不用你手动解析请求。真实模型应用里,这种模式很适合做温度参数、阈值、模型版本切换等调试。
6. 接口 API 与批量任务
6.1 Gradio 自带 API 调用
Gradio 的实用价值在于可以当做一个轻量接口服务。启动gradio_app.py后,在浏览器页面底部通常能看到 API 文档入口,里面会列出可用接口名和请求字段。新版 Gradio 推荐使用gradio_client调用:
pip install gradio_clientfrom gradio_client import Client # 假设本地 Gradio 服务已经启动在 7860 端口 client = Client("http://127.0.0.1:7860") # 注意:api_name 需要根据页面 API 文档填写,不同版本可能不同 result = client.predict( "这个产品很不错", api_name="/predict" ) print(result)如果你的服务里定义的是多参数函数,比如predict_with_threshold(text, threshold),对应调用就是:
result = client.predict( "服务效果不错", 80, api_name="/predict" )不同 Gradio 版本的 API 命名规则会有差异,最稳妥的方式是安装gradio_client后先查看服务的接口列表。部分旧版本的 HTTP 路径写法与新版不同,不要死记某一个固定地址。调用前先确认 API 文档,能避免很多升级坑。
6.2 Streamlit 页面调用独立模型 API
Streamlit 本身并没有把页面直接开放成“可供第三方程序调用的 AI API”的设计目标和机制。所以更合理的架构是:模型先被一个 FastAPI 或 Gradio 服务包成接口,Streamlit 页面再去调用这个接口。下面是一个在 Streamlit 内调用本地模型 API 的示意,适合“页面层与模型层分离”的场景。
import requests import streamlit as st st.title("调用模型 API 的 Streamlit Demo") text = st.text_area("输入文本") if st.button("调用接口"): resp = requests.post( "http://127.0.0.1:8000/predict", json={"text": text}, timeout=60, ) st.json(resp.json())这种拆分能避免一个问题:当页面需要大幅改造样式或增加权限时,模型服务不用跟着改。真实场景里,你可以把模型部署在 GPU 机器,把 Streamlit 放在另一台轻量机器上,只要网络可通就能工作。
6.3 Streamlit 批量预测示例
批量任务用 Streamlit 处理非常典型。用户上传一个 CSV,页面逐条调用模型预测函数,最终生成带预测结果的 CSV 下载。继续复用前文的model_service.py,创建streamlit_batch.py:
import pandas as pd import streamlit as st from model_service import load_model, predict_text @st.cache_resource def get_model(): return load_model() st.title("批量预测工具") st.write("上传一个包含 text 列的 CSV 文件,系统逐条预测并导出结果。") uploaded = st.file_uploader("上传 CSV", type=["csv"]) if uploaded is not None: df = pd.read_csv(uploaded) if "text" not in df.columns: st.error("CSV 中缺少 text 列") st.stop() if st.button("开始批量预测"): model = get_model() results = [predict_text(str(row), model) for row in df["text"]] df["label"] = [r[0] for r in results] df["score"] = [r[1] for r in results] st.dataframe(df.head(20)) st.download_button( "下载预测结果", df.to_csv(index=False).encode("utf-8-sig"), file_name="predict_result.csv", mime="text/csv", )运行:
streamlit run streamlit_batch.py这个示例有两个地方值得学习。第一,批量预测集中在一次按钮点击中完成,没有让页面反复刷新。第二,导出使用utf-8-sig编码,Windows 下用 Excel 打开 CSV 不容易出现中文乱码。
但要注意,如果 CSV 行数太多、模型推理很慢,这种同步逐条预测会让页面请求超时。生产级批量任务不应该放在 Web 请求里直接跑,而是应该把任务写入队列,后端异步处理,前端轮询进度。Streamlit 适合演示级批量和中小数据量场景。
6.4 服务部署时的请求控制
上线公网或内网供多人访问前,要加一层请求控制和访问限制。Gradio 自带的队列机制可以应对演示级并发,但不是分布式任务队列。如果模型推理需要几秒到几十秒,而同时访问人数较多,应当设置合理的等待机制或限制并发。常见做法是:在 Web 服务前面加 Nginx 反向代理做访问控制,带上身份认证和请求日志;模型接口单独限制调用频率;大批量离线任务则使用 Celery/Arq 这类后台队列处理,Web 页面只负责提交和展示结果。
7. 资源占用与性能观察
开发者在跑通一个模型 Demo 后,最该关注的是“界面层”和“模型层”到底各占了多少资源。其实 Streamlit 和 Gradio 本身属于轻量 Python Web 框架,内存占用并不高,真正的瓶颈在模型推理。如果你用的是 CPU 版本的 PyTorch,加载一个几 GB 的模型往往就会占掉大量内存;如果有 GPU,需要观察显存是否够用。
查看 GPU 状态常用命令:
nvidia-smi持续观察可以使用:
watch -n 1 nvidia-smiWindows 系统可以在任务管理器或使用nvidia-smi -l 1持续刷新。观察的重点是显存峰值和推理过程中的 GPU 利用率。如果显存不足,会看到 CUDA out of memory 报错,此时常见手段是降低推理 batch size、换用半精度模型、量化模型或切到 CPU 小模型验证流程。
CPU 和内存占用也需要关注。以 Linux 为例:
top也可以看单个 Python 进程:
ps aux | grep python在 Streamlit 页面里,如果每个会话都执行一次load_model(),内存会迅速膨胀。正确做法是使用@st.cache_resource缓存模型对象。Gradio 的launch()一般会在一个进程中持有模型对象,因此更需要确保模型只初始化一次,不要在每次请求里重复加载。
性能表现和这几个因素强相关:模型参数规模、输入文本长度、图像分辨率、批量文件数量、并发请求数。不是框架本身越快越好,而是你的推理链路是否被重复初始化、是否有不必要的显存占用。建议第一次测试时用小输入、低并发,确认整个链路稳定后再逐步加压。
8. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 启动后浏览器页面打不开 | 端口被占用或服务未启动 | 查看终端日志;检查端口 | 更换端口,重启服务 |
| 端口被占用 | 上一个进程未退出 | 查看 8501/7860 端口占用进程 | 结束旧进程或换端口 |
| Gradio 上传图片后页面无反应 | 前端连接断开或模型函数报错 | 查看终端堆栈日志 | 在fn函数内打印日志并捕获异常 |
| Streamlit 每次交互都重新加载模型 | 未使用缓存 | 查看日志中是否有重复 loading 记录 | 使用@st.cache_resource包裹模型加载函数 |
| 访问 Gradio 需要身份验证 | launch 未配置 auth | 查看代码是否遗漏 auth 参数 | 配置auth=("用户名", "强密码"),以官方文档为准 |
| transformers 模型下载失败 | 离线环境或模型仓不可访问 | 查看请求超时日志 | 提前把模型下载到本地,改成加载本地目录 |
| CUDA out of memory | 显存不足或 batch size 过大 | 查看 nvidia-smi 显存占用 | 减小 batch size,使用半精度或量化模型 |
| 批量预测 CSV 乱码 | 编码问题 | 用文本编辑器查看 CSV 编码 | 导出时使用utf-8-sig,或统一输入为 UTF-8 |
| 模型返回结构不符合页面预期 | 真实模型输出和页面解析代码不一致 | 单独打印模型返回结果 | 在model_service.py中统一模型输出为(label, score)等固定结构 |
| API 调用 404 | Gradio 版本升级后接口字段变化 | 打开页面 API 文档对比接口名 | 使用页面文档中的实际 api_name 和参数顺序 |
排错顺序建议先终端日志、再端口占用、再模型输出结构。大部分页面上看起来像“前端坏了”的问题,实际上都是 Python 函数内部抛异常或返回了不兼容结构,先看启动进程的终端输出是最直接的办法。
9. 最佳实践与使用建议
9.1 模型服务和页面代码分离
不要把所有模型推理逻辑都塞进页面文件。页面应该只负责渲染和调用model_service.py的函数。这样替换真实模型、升级模型版本时,不用改动整个 Web 应用,测试成本会明显降低。
9.2 使用虚拟环境和依赖锁定
启动项目前创建独立虚拟环境。团队协作或多环境部署时,推荐把依赖写进requirements.txt:
streamlit gradio gradio_client pandas transformers torch如果一次性把所有依赖装进系统 Python,后期很可能出现包版本冲突。更稳妥的做法是在虚拟环境内固定一个可运行版本集合,并记录到文件。
9.3 模型缓存要确定命中范围
Streamlit 中模型加载用@st.cache_resource缓存资源型对象;计算结果如果计算代价高且重复输入少,可以用@st.cache_data缓存普通 DataFrame 或中间结果。需要注意缓存命中会占用内存,不能无限制缓存大输入和所有历史结果。
9.4 目录和日志要规范
模型文件放models/,测试输入放inputs/,批量输出放outputs/,日志单独记录。推理函数建议加入基本日志,至少记录每次请求的输入长度、推理耗时、异常栈。批量任务如果失败,不要直接丢结果,要把失败行单独保存,避免整批重跑。
9.5 上线前确认授权和权限
任何 Web 化模型应用都要思考三个问题:谁能访问?能不能操作敏感数据?输出内容是否可能违规?Gradiolaunch(auth=...)只能做非常基础的登录验证,公网部署时建议用企业身份认证网关。涉及人脸图像、个人语音、版权文本和图像时,必须确认训练和演示素材都有合法授权,不能把未授权素材直接上传到公网模型服务。
9.6 第一次先小参数测试
第一次启动任何模型 Demo,先用最小参数跑通:小分辨率、短文本、batch size 为 1、单用户测试。整条链路稳定后,再逐步增加输入长度和并发量。模型量化、批处理、并发优化都可以放到功能验证之后再做,先解决“能不能用”,再解决“好不好用”。
10. 总结与下一步
Streamlit 和 Gradio 的最大价值不是替代企业级前端框架,而是把“模型能跑”快速变成“模型能用”。Streamlit 适合构建内部数据处理工具、批量预测页面和 AI 指标看板;Gradio 适合做模型 Demo 和接口联调。两者学习曲线都很短,核心是定义好一个统一的模型调用函数,再让组件去绑定它。
如果你是第一次接触,建议先做两个最小验证:一是用 Streamlit 跑通 CSV 批量导入和结果下载,二是用 Gradio 跑通图像上传和分类结果展示。最容易踩的坑分别是模型重复加载、端口冲突、模型输出结构与页面不匹配。
后续想继续扩展,可以考虑三个方向:把模型层换成 FastAPI 独立服务,用 Docker 部署到服务器;把批量任务改成异步队列,避免页面同步卡死;再往上层做权限、多租户和用户体系。到那时你会发现,Streamlit 和 Gradio 仍然是最合适的前端展示层,而真正支撑生产的是周边的模型服务、任务队列与权限管理。
建议收藏这篇,等你要给模型做页面时直接照命令操作,会省下不少查文档时间。