📢 转载信息
原文链接:https://machinelearningmastery.com/the-complete-guide-to-docker-for-machine-learning-engineers/
原文作者:Bala Priya C
在本文中,您将学习如何使用Docker来打包、运行和交付一个完整的机器学习预测服务,涵盖从训练模型到将其作为API提供服务以及将其分发为容器镜像的整个工作流程。
我们将涵盖的主题包括:
- 适用于机器学习工作的核心Docker概念(镜像、容器、层、缓存)。
- 训练一个简单的分类器并使用FastAPI提供预测服务。
- 编写高效的Dockerfile,在本地运行容器,并将镜像推送到Docker Hub。
让我们开始吧。
机器学习工程师 Docker 完整指南
图片来源:作者
引言
机器学习模型在不同环境中往往表现不同。由于版本不匹配、缺少依赖项或系统级别的差异,一个在您的笔记本电脑上运行良好的模型可能会在同事的机器或生产环境中失败。这使得协作和部署变得不必要的复杂。
Docker通过将您的整个机器学习应用程序——模型、代码、依赖项和运行时环境——打包到一个标准化的容器中来解决这些问题,该容器可以在任何地方以相同的方式运行。这样您就可以一次构建,随处运行,而无需担心配置不匹配或依赖冲突。
本文将通过一个简单的示例向您展示如何容器化机器学习模型。您将学习:
- 面向机器学习的Docker基础知识
- 构建和部署机器学习模型
- 使用Docker容器化机器学习应用程序
- 为机器学习应用程序编写优化的Dockerfile
让我们迈出第一步,构建可以在任何地方都能正常工作的模型。
先决条件
在学习如何使用Docker容器化机器学习模型之前,请确保您具备以下条件。
必需条件:
- 您的机器上安装了 Python 3.11(或最新版本)
- 已安装 FastAPI 及所需依赖项(不用担心,我们会边学边安装!)
- 基本的命令行/终端知识
- 已安装 Docker Desktop(点击此处下载)
- 一个文本编辑器或IDE
有帮助但非必需条件:
- 对机器学习概念有基本了解
- 熟悉 Python 虚拟环境
- REST API 经验
检查您的Docker安装情况:
|
1
2
|
docker --version docker run hello-world
|
如果这两个命令都能正常运行,您就准备好了!
面向机器学习工程师的Docker基础知识
在构建第一个机器学习容器之前,让我们先理解基本概念。Docker乍一看可能很复杂,但一旦掌握了这些核心思想,一切都会迎刃而解。
什么是Docker,为什么机器学习工程师应该关心?
Docker 是一个平台,它将您的应用程序及其所有依赖项打包到一个称为容器的标准化单元中。对于机器学习工程师来说,Docker解决了开发和部署中的几个相关挑战。
机器学习工作流程中一个常见问题是代码在不同机器上的行为不一致,这是由于 Python 或库版本不匹配造成的。Docker通过封装整个运行时环境消除了这种可变性,确保在任何地方都能保持一致的行为。
机器学习项目通常依赖于复杂的软件堆栈,具有严格的版本要求,例如与特定 CUDA 版本绑定的 TensorFlow,或与某些 NumPy 版本冲突的 PyTorch。Docker容器可以干净地隔离这些依赖项,防止版本冲突并简化设置。
可重现性是机器学习研究和生产的基石。通过将代码、库和系统依赖项打包到单个镜像中,Docker可以精确地重建实验和结果。
部署模型通常涉及在不同机器或云平台之间重新配置环境。使用Docker,一次构建的环境可以在任何地方运行,从而最大限度地减少设置时间和部署风险。
Docker 镜像 vs 容器
这是最需要理解的概念。许多初学者混淆了镜像和容器,但它们在根本上是不同的。
Docker 镜像就像一个蓝图或食谱。它是一个只读模板,包含:
- 操作系统(通常是轻量级的Linux发行版)
- 您的应用程序代码
- 所有依赖项和库
- 配置文件
- 运行应用程序的指令
可以将其视为编程中的类定义。它定义了细节,但本身不执行任何操作。
Docker 容器是镜像的运行实例。它就像从类实例化的对象。您可以从同一个镜像创建多个容器,就像可以从同一个类创建多个对象一样。
这是一个例子:
|
1
2
3
4
5
6
7
|
# This is an IMAGE - a template docker build -t my-ml-model:v1 . # These are CONTAINERS - running instances docker run --name experiment-1 my-ml-model:v1 docker run --name experiment-2 my-ml-model:v1 docker run --name experiment-3 my-ml-model:v1
|
我们还没有介绍Docker命令。但目前,要知道您可以使用docker build命令构建一个镜像,并使用docker run命令从镜像启动容器。您创建了一个镜像,但启动了三个独立的运行容器。每个容器都以自己的内存和进程独立运行,但它们都起源于同一个镜像。
Dockerfile
Dockerfile是您编写构建镜像指令的地方。它是一个纯文本文件(字面上就是Dockerfile,没有扩展名),Docker会从上到下读取它。
Docker以层的形式构建镜像。Dockerfile中的每条指令都会在您的镜像中创建一个新层。Docker 会缓存这些层,如果内容没有改变,重新构建会更快。
使用卷(Volumes)持久化数据
容器是易失性的。也就是说,当您删除一个容器时,其中的所有内容都会消失。这对需要保存训练日志、模型检查点和实验结果的机器学习工程师来说是个问题。
卷通过将主机机器上的目录挂载到容器中来解决这个问题:
|
1
|
docker run -v /path/on/host:/path/in/container my-model
|
现在写入/path/in/container的文件实际上存储在主机上的/path/on/host。即使您删除了容器,它们也会保留下来。
对于机器学习工作流程,您可能需要挂载:
|
1
2
3
4
5
|
docker run \ -v $(pwd)/data:/app/data \ -v $(pwd)/models:/app/models \ -v $(pwd)/logs:/app/logs \ my-training-container
|
这样,您的训练好的模型、数据集和日志就会保存在容器外部。
网络和端口映射
当您运行一个容器时,它拥有自己的网络命名空间。要访问容器内部运行的服务,您需要映射端口:
|
1
|
docker run -p 8000:8000 my-api
|
这会将您机器上的8000端口映射到容器中的8000端口。格式是host_port:container_port。
对于机器学习API,这使您可以同时运行多个模型版本:
|
1
2
3
4
|
# Run two versions side by side docker run -d -p 8000:8000 --name wine-api-v1 yourusername/wine-predictor:v1 docker run -d -p 8001:8000 --name wine-api-v2 yourusername/wine-predictor:v2 # v1 served at http://localhost:8000, v2 at http://localhost:8001
|
为什么选择Docker而不是虚拟环境?
您可能会问:“为什么不直接使用 venv 或 conda 呢?” Docker优于机器学习环境的原因如下:
虚拟环境只隔离 Python 包。它们不隔离系统库(如 CUDA 驱动程序)、操作系统差异(Windows 与 Linux)或系统级依赖项(如 libgomp,libgfortran)。
Docker 隔离了一切。您的容器在 MacBook、队友的 Windows PC 和云中的 Linux 服务器上运行方式完全相同。此外,Docker 可以轻松地同时运行不同版本的 Python,这在虚拟环境中使用起来很麻烦。
使用Docker容器化机器学习应用
现在我们了解了Docker基础知识,让我们来构建一些实用的东西。我们将使用scikit-learn 的葡萄酒数据集创建一个葡萄酒质量预测模型,并将其作为生产级的API进行部署。我们将涵盖以下内容:
- 构建和训练一个随机森林分类器
- 创建一个FastAPI应用程序来提供预测服务
- 编写一个高效的Dockerfile
- 在本地构建和运行容器
- 测试API端点
- 将镜像推送到Docker Hub以供分发
让我们开始吧!
步骤 1: 设置项目
首先,创建一个具有以下推荐结构的项目目录:
|
1
2
3
4
5
6
|
wine-predictor/ ├── train_model.py ├── app.py ├── requirements.txt ├── Dockerfile └── .dockerignore
|
接下来,创建并激活一个虚拟环境:
|
1
2
|
python3 -m venv v1 source v1/bin/activate
|
然后安装所需的包:
|
1
|
pip install fastapi uvicorn pandas scikit-learn
|
步骤 2: 构建机器学习模型
首先,我们需要创建机器学习模型。我们将使用scikit-learn内置的葡萄酒数据集。这是包含13种葡萄酒化学特征的数据集。
创建一个名为train_model.py的文件:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
import pickle from sklearn.datasets import load_wine from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.preprocessing import StandardScaler # Load the wine dataset wine = load_wine() X, y = wine.data, wine.target # Split the data X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # Scale features scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # Train the model model = RandomForestClassifier(n_estimators=100, random_state=42) model.fit(X_train_scaled, y_train) # Evaluate accuracy = model.score(X_test_scaled, y_test) print(f"Model accuracy: {accuracy:.2f}") # Save both the model and scaler with open('model.pkl', 'wb') as f: pickle.dump(model, f) with open('scaler.pkl', 'wb') as f: pickle.dump(scaler, f) print("Model and scaler saved successfully!")
|
这段代码的作用是:我们加载了包含13种葡萄酒化学特征的葡萄酒数据集。在将数据划分为训练集和测试集后,我们使用StandardScaler对特征进行标准化。我们训练了一个随机森林分类器,并保存了模型和缩放器。为什么要保存缩放器?因为在稍后进行预测时,我们需要以与训练数据完全相同的方式对新数据进行缩放。
运行此脚本以训练并保存您的模型:
|
1
|
python3 train_model.py
|
您应该会看到输出显示模型的准确性以及文件已保存的确认信息。
步骤 3: 创建FastAPI应用程序
现在让我们使用FastAPI创建一个API,该API加载我们训练好的模型并提供预测服务。
创建一个名为app.py的文件:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
from fastapi import FastAPI, HTTPException from pydantic import BaseModel import pickle import numpy as np app = FastAPI(title="Wine Quality Predictor") # Load model and scaler at startup with open('model.pkl', 'rb') as f: model = pickle.load(f) with open('scaler.pkl', 'rb') as f: scaler = pickle.load(f) # Wine class names for better output wine_classes = ['Class 0', 'Class 1', 'Class 2'] class WineFeatures(BaseModel): alcohol: float malic_acid: float ash: float alcalinity_of_ash: float magnesium: float total_phenols: float flavanoids: float nonflavanoid_phenols: float proanthocyanins: float color_intensity: float hue: float od280_od315_of_diluted_wines: float proline: float # Pydantic v2-compatible schema example model_config = { "json_schema_extra": { "example": { "alcohol": 13.2, "malic_acid": 2.77, "ash": 2.51, "alcalinity_of_ash": 18.5, "magnesium": 96.0, "total_phenols": 2.45, "flavanoids": 2.53, "nonflavanoid_phenols": 0.29, "proanthocyanins": 1.54, "color_intensity": 5.0, "hue": 1.04, "od280_od315_of_diluted_wines": 3.47, "proline": 920.0 } } } @app.get("/"): def read_root(): return { "message": "Wine Quality Prediction API", "endpoints": { "/predict": "POST - Make a prediction", "/health": "GET - Check API health", "/docs": "GET - API documentation" } } @app.get("/health") def health_check(): return {"status": "healthy", "model_loaded": model is not None, "scaler_loaded": scaler is not None} @app.post("/predict") def predict(features: WineFeatures): try: # Convert input to array input_data = np.array([ features.alcohol, features.malic_acid, features.ash, features.alcalinity_of_ash, features.magnesium, features.total_phenols, features.flavanoids, features.nonflavanoid_phenols, features.proanthocyanins, features.color_intensity, features.hue, features.od280_od315_of_diluted_wines, features.proline ]) input_data = input_data.reshape(1, -1) # Scale the input input_scaled = scaler.transform(input_data) # Make prediction prediction = model.predict(input_scaled) probabilities = model.predict_proba(input_scaled)[0] pred_index = int(prediction[0]) return { "prediction": wine_classes[pred_index], "prediction_index": pred_index, "confidence": float(probabilities[pred_index]), "all_probabilities": { wine_classes[i]: float(p) for i, p in enumerate(probabilities) } } except Exception as e: raise HTTPException(status_code=500, detail=str(e))
|
🚀 想要体验更好更全面的AI调用?
欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。
评论区