手把手带你走一遍:机器学习模型如何用FastAPI和Docker部署

你大概用过不少健康管理类的App------输入年龄、性别、BMI、血压,再填几项血液指标,它就能给出一个风险评分或健康提示。

这个评分背后,往往是一个训练好的机器学习模型在实时计算。

但模型在Jupyter Notebook里跑通,和让它变成成千上万用户都能调用的服务,中间隔着一条不小的鸿沟。

今天我们就以"乳腺肿瘤风险筛查"这个具体场景为例(选这个是因为 scikit-learn 库中自带了真实的数据集),手把手走一遍从模型训练到API封装,再到Docker容器化、最终发布上线的完整流程。

读完希望你能把自己的模型变成一个随时可调用的Web服务。

一、先规划好项目结构

动手之前,先想清楚项目怎么组织。

一个清晰的结构能让后续开发、调试和部署都顺心很多。

我们创建一个名为 health-risk-api 的文件夹,里面分成几个部分:

  • src 目录放FastAPI应用代码
  • artifacts 目录存放训练好的模型文件
  • 根目录下放训练脚本、依赖清单和Dockerfile。

具体长这样:

plain 复制代码
health-risk-api/
├── src/
│   ├── __init__.py
│   └── api.py              # FastAPI 应用
├── artifacts/
│   └── breast_cancer_model.joblib  # 训练好的模型
├── build_model.py          # 模型训练脚本
├── requirements.txt        # Python 依赖
└── Dockerfile              # 容器配置

这个结构不复杂,但把训练、服务、依赖和容器配置都分开了,后面每一步该做什么一目了然。

二、准备开发环境

接下来需要一台装有Python 3.9+和Docker的机器。

Python用来训练模型和写API,Docker用来打包运行环境。

为了避免污染系统环境,我们先建一个虚拟环境,再安装必要的库:scikit-learn、pandas、fastapi和uvicorn。

bash 复制代码
python -m venv health-env
source health-env/bin/activate   # Windows 用 health-env\Scripts\activate
pip install scikit-learn pandas fastapi uvicorn joblib

这几行命令执行完,开发环境就齐了。

这里多装了一个 joblib,后面保存模型会用到它,比pickle更适合存储scikit-learn模型。

三、训练一个肿瘤风险分类模型

我们直接使用scikit-learn内置的乳腺癌数据集。它包含569条样本,每条有30个细胞核特征(半径、纹理、周长、面积等),目标是把肿瘤分为恶性或良性。

这个数据集规模适中,特征丰富,用来演示部署流程再合适不过。

我们选逻辑回归作为分类器,因为它训练快、可解释性好,而且输出概率值,方便我们包装成"风险评分"。

在 build_model.py 中,我们这样写:

python 复制代码
import joblib
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, roc_auc_score
from sklearn.pipeline import Pipeline

# 加载数据
data = load_breast_cancer()
X, y = data.data, data.target

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# 构建包含标准化和逻辑回归的管道
pipe = Pipeline([
    ('scaler', StandardScaler()),
    ('clf', LogisticRegression(max_iter=1000, random_state=42))
])

pipe.fit(X_train, y_train)

# 评估
y_pred = pipe.predict(X_test)
y_prob = pipe.predict_proba(X_test)[:, 1]
print(f"准确率: {accuracy_score(y_test, y_pred):.4f}")
print(f"AUC: {roc_auc_score(y_test, y_prob):.4f}")

# 保存整个管道
joblib.dump(pipe, 'artifacts/breast_cancer_model.joblib')

运行这个脚本,你会在 artifacts 目录下得到 breast_cancer_model.joblib。

bash 复制代码
$ python build_model.py
准确率: 0.9825
AUC: 0.9954

这个文件里既包含标准化器,也包含分类器,后面API直接加载它就能完成端到端的预测。

四、用FastAPI把模型包装成API

模型有了,但总不能要求每个用户都装Python和scikit-learn吧?这时候FastAPI就派上用场了。

它轻量、性能好,还能自动生成交互式文档。

我们在 src/api.py 里做三件事:定义输入数据格式、加载模型、提供预测接口。

先用Pydantic定义输入结构。

乳腺癌数据集一共有30个特征,分别是10个均值特征、10个误差特征和10个最差值特征。

Pydantic的模型字段顺序很重要,因为我们在后面拼装numpy数组时必须严格按训练时的特征顺序来,否则模型会用错数据,预测结果就完全不可信了。

python 复制代码
from pydantic import BaseModel

class TumorFeatures(BaseModel):
    # 10 个均值特征
    mean_radius: float
    mean_texture: float
    mean_perimeter: float
    mean_area: float
    mean_smoothness: float
    mean_compactness: float
    mean_concavity: float
    mean_concave_points: float
    mean_symmetry: float
    mean_fractal_dimension: float

    # 10 个误差特征
    radius_error: float
    texture_error: float
    perimeter_error: float
    area_error: float
    smoothness_error: float
    compactness_error: float
    concavity_error: float
    concave_points_error: float
    symmetry_error: float
    fractal_dimension_error: float

    # 10 个最差特征
    worst_radius: float
    worst_texture: float
    worst_perimeter: float
    worst_area: float
    worst_smoothness: float
    worst_compactness: float
    worst_concavity: float
    worst_concave_points: float
    worst_symmetry: float
    worst_fractal_dimension: float

接着初始化FastAPI应用,并用 lifespan 在启动时加载模型。

这样比在模块顶层直接加载更规范,也方便后面做测试。

python 复制代码
import joblib
import numpy as np
from contextlib import asynccontextmanager
from fastapi import FastAPI

ml_model = {}

@asynccontextmanager
async def lifespan(app: FastAPI):
    ml_model["pipe"] = joblib.load('artifacts/breast_cancer_model.joblib')
    yield
    ml_model.clear()

app = FastAPI(title="乳腺肿瘤风险预测 API", lifespan=lifespan)

然后写预测端点。用户POST过来一组特征,我们按顺序转成numpy数组,调用模型推理,返回恶性概率和风险等级:

python 复制代码
@app.post("/v1/predict")
def predict_risk(features: TumorFeatures):
    input_array = np.array([[
        features.mean_radius,
        features.mean_texture,
        features.mean_perimeter,
        features.mean_area,
        features.mean_smoothness,
        features.mean_compactness,
        features.mean_concavity,
        features.mean_concave_points,
        features.mean_symmetry,
        features.mean_fractal_dimension,
        features.radius_error,
        features.texture_error,
        features.perimeter_error,
        features.area_error,
        features.smoothness_error,
        features.compactness_error,
        features.concavity_error,
        features.concave_points_error,
        features.symmetry_error,
        features.fractal_dimension_error,
        features.worst_radius,
        features.worst_texture,
        features.worst_perimeter,
        features.worst_area,
        features.worst_smoothness,
        features.worst_compactness,
        features.worst_concavity,
        features.worst_concave_points,
        features.worst_symmetry,
        features.worst_fractal_dimension
    ]])
    prob = ml_model["pipe"].predict_proba(input_array)[0, 1]
    risk_level = "高风险" if prob > 0.7 else ("中风险" if prob > 0.3 else "低风险")
    return {
        "malignancy_probability": round(float(prob), 4),
        "risk_level": risk_level
    }

再补一个根路径的健康检查,方便确认服务是否活着:

python 复制代码
@app.get("/")
def health_check():
    return {"status": "ok", "service": "乳腺肿瘤风险预测"}

五、在本地把服务跑起来

代码写好了,先在本地验证一下。用uvicorn启动开发服务器:

bash 复制代码
uvicorn src.api:app --reload --port 8000

看到"Uvicorn running on http://127.0.0.1:8000"就说明服务起来了。

打开浏览器访问 http://localhost:8000/docs,FastAPI会自动生成Swagger文档,你可以直接在里面点"Try it out"发请求。

当然也可以用curl:

bash 复制代码
curl -X POST "http://localhost:8000/v1/predict" \
  -H "Content-Type: application/json" \
  -d '{
    "mean_radius": 17.99, "mean_texture": 10.38, "mean_perimeter": 122.8,
    "mean_area": 1001.0, "mean_smoothness": 0.1184, "mean_compactness": 0.2776,
    "mean_concavity": 0.3001, "mean_concave_points": 0.1471,
    "mean_symmetry": 0.2419, "mean_fractal_dimension": 0.07871,
    "radius_error": 1.095, "texture_error": 0.9053, "perimeter_error": 8.589,
    "area_error": 153.4, "smoothness_error": 0.006399,
    "compactness_error": 0.04904, "concavity_error": 0.05373,
    "concave_points_error": 0.01587, "symmetry_error": 0.03003,
    "fractal_dimension_error": 0.006193,
    "worst_radius": 25.38, "worst_texture": 17.33, "worst_perimeter": 184.6,
    "worst_area": 2019.0, "worst_smoothness": 0.1622,
    "worst_compactness": 0.6656, "worst_concavity": 0.7119,
    "worst_concave_points": 0.2654, "worst_symmetry": 0.4601,
    "worst_fractal_dimension": 0.1189
  }'

如果返回类似 {"malignancy_probability": 0.9823, "risk_level": "高风险"},说明本地服务已经通了。

顺便说一句,这条样本恰好是数据集里标签为恶性的第一条,所以高风险的结果也是符合预期的------这也能反过来验证你的API没有把特征拼错。

六、用Docker把服务装进容器

本地跑通只是第一步。

真实部署时,目标服务器可能没有Python环境,或者版本不一致。Docker能把应用和所有依赖打包成一个镜像,到哪都能跑。

我们先写 requirements.txt,把版本固定下来,保证环境一致:

plain 复制代码
fastapi==0.115.12
uvicorn==0.34.2
scikit-learn==1.6.1
pandas==2.2.3
numpy==2.2.6
joblib==1.4.2

然后写Dockerfile。

基于官方 python:3.12-slim 镜像,设置工作目录为 /code,复制依赖并安装,再把应用代码和模型文件复制进去,暴露8000端口,最后用uvicorn启动:

dockerfile 复制代码
FROM python:3.12-slim

WORKDIR /code

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

COPY src/ ./src/
COPY artifacts/ ./artifacts/

EXPOSE 8000

CMD ["uvicorn", "src.api:app", "--host", "0.0.0.0", "--port", "8000"]

在项目根目录执行构建:

bash 复制代码
docker build -t health-risk-api .

构建完成后运行容器:

bash 复制代码
docker run -d -p 8000:8000 health-risk-api

现在无论在哪台机器上,只要装了Docker,拉取这个镜像就能得到一模一样的服务。

七、发布到Docker Hub

镜像在本地构建好了,怎么分享给其他人或者部署到云服务器?最直接的办法是推到Docker Hub。

先登录:

bash 复制代码
docker login

然后给镜像打上你的用户名和版本标签,再推送:

bash 复制代码
docker tag health-risk-api your-username/health-risk-api:v1.0
docker tag health-risk-api your-username/health-risk-api:latest

docker push your-username/health-risk-api:v1.0
docker push your-username/health-risk-api:latest

推送完成后,任何一台机器都可以用 docker pull your-username/health-risk-api:v1.0 拉取,然后 docker run 启动。

之后如果你想把服务部署到阿里云、腾讯云或者AWS ECS,也只需要基于这个镜像配置即可。

八、还能继续做什么

走到这一步,你已经拥有了一个可用的模型API。

但如果要上生产,还可以考虑几件事:

  • 加一层认证(比如API Key),防止接口被滥用;
  • 接入日志和监控,观察预测延迟和请求量;
  • 如果业务需要一次性预测多条记录,可以再加一个批量预测端点。

这些都可以在现有FastAPI应用上逐步扩展。

从训练一个模型,到把它变成Docker镜像并推送到仓库,整个过程其实并不神秘。

关键是把每一步拆开,先让本地跑通,再考虑环境一致性和分发。

你完全可以把今天这套流程套用到自己的模型上------不管是预测房价、销量还是设备故障,思路都是一样的。

相关推荐
瑞兴生物RXBio2 小时前
转录组数据分析踩坑:RNA 结果与预期不符、RNA 和蛋白表达不一致,6 大方向排查方案
数据挖掘·数据分析·生信分析·转录组
Data-Miner2 小时前
做表格数据分析的AI工具怎么选?先对一下这三个真实需求,再看哪些真能落地
人工智能·数据分析·excel
BYSJMG3 小时前
计算机毕业设计选题推荐:基于大数据的快递物流运营数据分析与可视化,Spark与K-Means
大数据·算法·数据分析·spark·课程设计
IT毕设梦工厂6 小时前
计算机毕业设计选题推荐:基于大数据的供应链数据分析与可视化|毕业设计选题|计算机毕设|选题推荐|毕设指导|项目定制|源码|高质量项目
大数据·hive·hadoop·python·数据分析·spark·课程设计
BYSJMG6 小时前
计算机毕业设计选题推荐|【基于深度学习的面部关键特征识别与检测】YOLO目标检测+ONNX推理
大数据·hadoop·数据分析·spark·课程设计
IT毕设梦工厂6 小时前
计算机毕业设计选题推荐:基于大数据的二手车数据可视化分析|毕业设计选题|计算机毕设|选题推荐|毕设指导|项目定制|源码|高质量项目
大数据·hadoop·python·数据挖掘·数据分析·课程设计·数据分析数据可视化
明月_清风7 小时前
一个完整的数据平台是怎么工作的?从数据源到数据分析
大数据·后端·数据分析
IT毕设梦工厂8 小时前
计算机毕业设计选题推荐:基于大数据的广告投放数据可视化分析系统|毕业设计选题|计算机毕设|选题推荐|毕设指导|项目定制|源码|高质量项目
大数据·hadoop·信息可视化·数据分析·spark·毕业设计·课程设计
IT毕设梦工厂8 小时前
计算机毕业设计选题推荐:基于大数据的房地产交易数据分析与可视化|毕业设计选题|计算机毕设|选题推荐|毕设指导|项目定制|源码|高质量项目
大数据·hive·hadoop·python·数据分析·spark·课程设计