用Streamlit+FastAPI打造机器学习应用:从本地调试到Docker部署全流程
用Streamlit+FastAPI打造机器学习应用:从本地调试到Docker部署全流程
在数据科学和机器学习领域,快速构建和部署应用一直是个挑战。传统方法需要前端开发、后端API和部署运维的多方面技能,而Streamlit和FastAPI的组合彻底改变了这一局面。本文将带你从零开始,通过一个汽车评估案例,完整演示如何构建前后端分离的机器学习应用,并最终通过Docker容器化部署。
1. 技术栈选择与项目架构设计
Streamlit和FastAPI的组合为数据科学家提供了全栈开发能力。Streamlit以其极简的Python API著称,能让开发者用几行代码构建交互式Web界面。FastAPI则是高性能的Python Web框架,特别适合构建机器学习API服务。
我们的汽车评估应用将采用前后端分离架构:
- 前端:Streamlit构建用户界面,收集用户输入并展示预测结果
- 后端:FastAPI提供RESTful API,处理模型推理请求
- 部署:Docker容器化,使用Docker Compose编排服务
这种架构的优势在于:
- 前后端开发完全解耦,可独立迭代
- 容器化部署确保环境一致性
- 易于扩展为微服务架构
2. 环境准备与依赖安装
开始编码前,我们需要设置开发环境。推荐使用Python 3.8+和虚拟环境:
# 创建并激活虚拟环境
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
# 安装核心依赖
pip install streamlit fastapi uvicorn pandas scikit-learn
对于汽车评估案例,我们将使用UCI的Car Evaluation数据集。可以通过以下代码快速加载:
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
# 加载数据集
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/car/car.data"
columns = ["buying", "maint", "doors", "persons", "lug_boot", "safety", "class"]
data = pd.read_csv(url, names=columns)
# 数据预处理
X = pd.get_dummies(data.drop("class", axis=1))
y = data["class"].astype("category").cat.codes
# 训练简单模型
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model = RandomForestClassifier().fit(X_train, y_train)
3. 构建FastAPI后端服务
FastAPI后端负责模型推理,我们将创建一个标准的RESTful API:
# api/main.py
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List
import pandas as pd
import joblib
app = FastAPI()
# 定义请求体模型
class CarFeatures(BaseModel):
buying: str
maint: str
doors: str
persons: str
lug_boot: str
safety: str
# 加载预训练模型
model = joblib.load("model.pkl")
@app.post("/predict")
async def predict(features: List[CarFeatures]):
# 转换输入为DataFrame
df = pd.DataFrame([f.dict() for f in features])
df = pd.get_dummies(df)
# 确保所有训练时的列都存在
for col in model.feature_names_in_:
if col not in df.columns:
df[col] = 0
# 预测并返回结果
predictions = model.predict(df[model.feature_names_in_])
return {"predictions": predictions.tolist()}
启动API服务:
uvicorn api.main:app --reload --port 8001
提示:在生产环境中,应该添加适当的错误处理、日志记录和输入验证
4. 开发Streamlit前端界面
Streamlit前端提供用户友好的交互界面:
# app.py
import streamlit as st
import requests
# 配置页面
st.set_page_config(page_title="汽车评估系统", layout="wide")
st.title("🚗 汽车评估系统")
# 定义评估等级映射
class_values = {
0: "不可接受",
1: "可接受",
2: "良好",
3: "优秀"
}
# 创建表单收集用户输入
with st.form("car_evaluation"):
st.subheader("请输入汽车特征")
col1, col2 = st.columns(2)
with col1:
buying = st.radio("购买价格", ["vhigh", "high", "med", "low"])
maint = st.radio("维护成本", ["vhigh", "high", "med", "low"])
doors = st.select_slider("车门数量", options=["2", "3", "4", "5more"])
with col2:
persons = st.select_slider("载客量", options=["2", "4", "more"])
lug_boot = st.select_slider("行李厢大小", options=["small", "med", "big"])
safety = st.select_slider("安全等级", options=["low", "med", "high"])
submitted = st.form_submit_button("评估")
# 处理表单提交
if submitted:
inputs = {
"inputs": [{
"buying": buying,
"maint": maint,
"doors": doors,
"persons": persons,
"lug_boot": lug_boot,
"safety": safety
}]
}
try:
response = requests.post(
"http://localhost:8001/predict",
json=inputs
)
prediction = class_values[response.json()["predictions"][0]]
st.success(f"评估结果: {prediction}")
except Exception as e:
st.error(f"评估失败: {str(e)}")
启动前端应用:
streamlit run app.py
5. Docker容器化部署
为了确保应用在不同环境中的一致性,我们使用Docker进行容器化。首先为每个服务创建Dockerfile。
前端Dockerfile:
# frontend/Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8501
CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]
后端Dockerfile:
# backend/Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8001
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001"]
docker-compose.yml:
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "8501:8501"
depends_on:
- backend
environment:
- BACKEND_URL=http://backend:8001
backend:
build: ./backend
ports:
- "8001:8001"
启动所有服务:
docker-compose up --build
6. 性能优化与扩展
随着应用规模增长,我们需要考虑以下优化:
-
API性能:
- 添加缓存层(如Redis)
- 实现异步端点
- 启用Gzip压缩
-
前端体验:
- 使用Streamlit缓存减少重复计算
- 添加加载状态指示器
- 实现表单验证
-
部署架构:
- 添加Nginx反向代理
- 配置负载均衡
- 设置健康检查
# 示例:带缓存的FastAPI端点
from fastapi import FastAPI, Depends
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from redis import asyncio as aioredis
app = FastAPI()
@app.on_event("startup")
async def startup():
redis = aioredis.from_url("redis://redis")
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")
@app.get("/expensive-operation")
@cache(expire=60) # 缓存60秒
async def expensive_operation():
# 耗时计算
return {"result": "data"}
7. 监控与日志
生产环境需要完善的监控系统:
- Prometheus:收集指标数据
- Grafana:可视化监控数据
- ELK Stack:集中日志管理
FastAPI内置了Prometheus指标端点:
from prometheus_fastapi_instrumentator import Instrumentator
Instrumentator().instrument(app).expose(app)
对于Streamlit,可以添加自定义日志:
import logging
from streamlit.logger import get_logger
logger = get_logger(__name__)
logger.setLevel(logging.INFO)
def some_function():
try:
# 业务逻辑
logger.info("操作成功")
except Exception as e:
logger.error(f"操作失败: {str(e)}")
raise
8. 安全最佳实践
确保应用安全的关键措施:
-
API安全:
- 启用CORS限制
- 实现认证/授权
- 使用HTTPS
-
输入验证:
- 严格校验所有用户输入
- 防范SQL注入
- 限制请求大小
-
依赖安全:
- 定期更新依赖
- 扫描漏洞
- 锁定依赖版本
FastAPI安全配置示例:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://yourdomain.com"],
allow_credentials=True,
allow_methods=["GET", "POST"],
allow_headers=["*"],
)
Streamlit安全提示:
# 禁用危险功能
st.set_option("server.enableCORS", False)
st.set_option("server.enableXsrfProtection", True)
9. 持续集成与部署
自动化部署流程能显著提高效率:
- GitHub Actions示例:
name: Deploy to Production
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build and push Docker images
run: |
docker-compose -f docker-compose.prod.yml build
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
docker-compose -f docker-compose.prod.yml push
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
docker-compose -f docker-compose.prod.yml pull
docker-compose -f docker-compose.prod.yml up -d
- 部署检查清单:
- 测试覆盖率 >80%
- 通过所有CI流水线
- 完成负载测试
- 备份现有部署
10. 实际案例:汽车评估系统增强版
让我们扩展基础功能,添加更多实用特性:
# 增强版app.py
import streamlit as st
import requests
import pandas as pd
import plotly.express as px
# 添加多页面支持
st.sidebar.title("导航")
page = st.sidebar.radio("", ["评估", "数据分析", "关于"])
if page == "评估":
# 原有评估逻辑...
elif page == "数据分析":
st.title("汽车数据洞察")
# 加载数据集
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/car/car.data"
columns = ["buying", "maint", "doors", "persons", "lug_boot", "safety", "class"]
data = pd.read_csv(url, names=columns)
# 交互式可视化
st.subheader("数据分布")
col = st.selectbox("选择分析维度", columns[:-1])
fig = px.histogram(data, x=col, color="class", barmode="group")
st.plotly_chart(fig)
# 相关性分析
st.subheader("特征相关性")
numeric_data = pd.get_dummies(data)
fig = px.imshow(numeric_data.corr())
st.plotly_chart(fig)
elif page == "关于":
st.title("关于汽车评估系统")
st.markdown("""
### 技术栈
- 前端: Streamlit
- 后端: FastAPI
- 部署: Docker + Compose
### 数据集
使用UCI机器学习仓库的[Car Evaluation Dataset](https://archive.ics.uci.edu/ml/datasets/Car+Evaluation)
""")
这个增强版展示了如何利用Streamlit快速构建数据分析功能,为用户提供更深入的洞察。
更多推荐


所有评论(0)