
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 默认端口是 8501Gradio 默认端口是 7860都不需要额外编译前端资源。下面这篇会围绕“把模型变成 Web 应用”这条主线展开先做环境准备再分别用 Streamlit 和 Gradio 写两个可运行 Demo然后演示 Gradio 接口调用和 Streamlit 批量预测最后给出资源占用观察、常见报错排查和上线前的最佳实践。如果你正在给别人交付模型或者想快速验证一个算法效果完全可以按文中顺序把流程跑通。1. 核心能力速览先看两个框架的整体定位。维度StreamlitGradio项目定位数据应用框架偏向数据看板、内部工具、批量处理AI 模型 Demo 框架偏向快速展示和接口访问界面定义方式Python 脚本从上到下执行组件自动渲染Interface 快速声明式Blocks 自定义复杂布局常用输入组件text_input、text_area、file_uploader、selectboxTextbox、Image、Audio、Dataframe、File常用输出组件metric、dataframe、chart、markdownLabel、Image、Audio、Dataframe、Chatbot浏览器访问必须有浏览器访问 Streamlit 服务页面同样以浏览器页面为主页面内提供可调用接口文档API 接口能力本身不面向第三方程序提供标准模型 API需要自己封装模型服务自带接口访问能力可通过 gradio_client 或 HTTP 请求调用身份验证需要借助反向代理或框架自身能力launch 时可配置 auth 参数做简单账户密码验证批量任务适合 pandas 批量导入预测后导出适合单个/小批量文件级任务大规模任务需要自行排队典型启动方式streamlit run app.py运行 Python 脚本如 python gradio_app.py典型端口85017860最适场景内部数据产品、模型效果汇总页、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_name127.0.0.1, server_port7860)如果设置server_name0.0.0.0同一局域网内其他设备也能通过宿主机 IP 访问但强烈建议仅在可信内网测试时使用不要直接暴露到公网。4.3 Gradio 启动时配置简单身份验证如果你要在内网给同事做模型验收页面又不想让任何拿到链接的人都能用可以给launch()加auth参数demo.launch(server_name0.0.0.0, server_port7860, auth(admin, 这里替换成强密码))设置后浏览器首次访问会弹出账户密码输入框。需要说明的是不同版本的 Gradio 对auth的支持和表现会有差异请以你所安装版本的官方文档为准。这只适合演示环境生产环境仍建议在前面加一层企业身份网关或反向代理。5. 功能测试从模型函数到 Web 页面测试目标是让一个本地模型函数能被网页调用。这里先做一个统一模型入口model_service.pyStreamlit 和 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(): 换成你自己的模型加载逻辑。 # 示例1scikit-learn 模型 # import joblib # return joblib.load(models/clf.pkl) # 示例2Hugging 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, modelNone): 文本预测函数返回 (label, score)。 # 真实场景请替换成你自己的推理逻辑例如 # inputs model[tokenizer](text, return_tensorspt) # 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.pyimport 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, modelmodel) 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.pyimport 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( fnclassify_image, inputsgr.Image(typepil, label上传图片), outputsgr.Label(num_top_classes3, label预测结果), titleGradio 图像分类 Demo, description上传一张图片观察预测结果。当前为 UI 联调模板替换 classify_image 内部逻辑即可接入真实模型。, ) if __name__ __main__: demo.launch(server_name127.0.0.1, server_port7860)运行python gradio_app.py浏览器打开http://localhost:7860拖动一张本地图片到上传区域页面会返回类别和概率。判断成功的标准是图片能上传、模型函数被调用、输出区域展示分类结果。比较有价值的体验点是Gradio 会自动处理图片上传、格式转换和前端渲染。你不用写任何input typefile之类的代码输入输出组件类型已经帮你完成了大部分工作。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( fnpredict_with_threshold, inputs[ gr.Textbox(label输入文本, lines3), gr.Slider(minimum0, maximum100, step1, value80, label置信度阈值), ], outputs[ gr.Textbox(label预测标签), gr.Textbox(label置信度), ], titleGradio 多参数 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 页面调用独立模型 APIStreamlit 本身并没有把页面直接开放成“可供第三方程序调用的 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}, timeout60, ) st.json(resp.json())这种拆分能避免一个问题当页面需要大幅改造样式或增加权限时模型服务不用跟着改。真实场景里你可以把模型部署在 GPU 机器把 Streamlit 放在另一台轻量机器上只要网络可通就能工作。6.3 Streamlit 批量预测示例批量任务用 Streamlit 处理非常典型。用户上传一个 CSV页面逐条调用模型预测函数最终生成带预测结果的 CSV 下载。继续复用前文的model_service.py创建streamlit_batch.pyimport 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(indexFalse).encode(utf-8-sig), file_namepredict_result.csv, mimetext/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 调用 404Gradio 版本升级后接口字段变化打开页面 API 文档对比接口名使用页面文档中的实际 api_name 和参数顺序排错顺序建议先终端日志、再端口占用、再模型输出结构。大部分页面上看起来像“前端坏了”的问题实际上都是 Python 函数内部抛异常或返回了不兼容结构先看启动进程的终端输出是最直接的办法。9. 最佳实践与使用建议9.1 模型服务和页面代码分离不要把所有模型推理逻辑都塞进页面文件。页面应该只负责渲染和调用model_service.py的函数。这样替换真实模型、升级模型版本时不用改动整个 Web 应用测试成本会明显降低。9.2 使用虚拟环境和依赖锁定启动项目前创建独立虚拟环境。团队协作或多环境部署时推荐把依赖写进requirements.txtstreamlit 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 仍然是最合适的前端展示层而真正支撑生产的是周边的模型服务、任务队列与权限管理。建议收藏这篇等你要给模型做页面时直接照命令操作会省下不少查文档时间。