Ultralytics YOLO27:

使用 Ultralytics 在 Vertex AI 上部署预训练 YOLO 模型进行推理#

本指南将向你展示如何使用 Ultralytics 将预训练 YOLO26 模型容器化,为其构建 FastAPI 推理服务器,并将该模型与推理服务器部署到 Google Cloud Vertex AI。示例实现将涵盖 YOLO26 的目标检测用例,但相同的原理也适用于使用其他 YOLO 模式

开始之前,你需要创建一个 Google Cloud Platform (GCP) 项目。作为新用户,你可以免费获得价值 $300 的 GCP 额度,这笔额度足以测试一个正在运行的配置,之后还可以将其扩展到其他 YOLO26 用例,包括训练以及批量和流式推理。

你将学到什么#

  1. 使用 FastAPI 为 Ultralytics YOLO26 模型创建推理后端。
  2. 创建一个 GCP Artifact Registry 仓库来存储 Docker 镜像。
  3. 构建包含模型的 Docker 镜像并将其推送到 Artifact Registry。
  4. 将模型导入 Vertex AI。
  5. 创建 Vertex AI 端点并部署模型。
为什么要部署容器化模型?
  • 使用 Ultralytics 完全控制模型:你可以使用自定义推理逻辑,完全控制预处理、后处理和响应格式。
  • Vertex AI 处理其余工作:它会自动扩缩容,同时让你能够灵活配置计算资源、内存和 GPU 配置。
  • 原生 GCP 集成与安全性:可无缝配置 Cloud Storage、BigQuery、Cloud Functions、VPC 控制、IAM 策略和审计日志。

前置条件#

  1. 在你的计算机上安装 Docker
  2. 安装 Google Cloud SDK,并进行身份验证以使用 gcloud CLI
  3. 强烈建议你阅读 Ultralytics Docker 快速入门指南,因为按照本指南操作时,你需要扩展一个官方 Ultralytics Docker 镜像。

1. 使用 FastAPI 创建推理后端#

首先,你需要创建一个 FastAPI 应用,用于处理 YOLO26 模型的推理请求。该应用将负责模型加载、图像预处理和推理(预测)逻辑。

Vertex AI 合规基础#

Vertex AI 要求你的容器实现两个特定端点:

  1. Health 端点(/health):服务就绪时必须返回 HTTP 状态 200 OK

  2. Predict 端点(/predict):接受包含 base64 编码图像和可选参数的结构化预测请求。根据端点类型的不同,需遵守负载大小限制

    /predict 端点的请求负载应遵循以下 JSON 结构:

    {
        "instances": [{ "image": "base64_encoded_image" }],
        "parameters": { "confidence": 0.5 }
    }

项目文件夹结构#

大部分构建工作将在 Docker 容器内部完成,并且 Ultralytics 还会加载预训练 YOLO26 模型,因此你可以保持简单的本地文件夹结构:

YOUR_PROJECT/
├── src/
│   ├── __init__.py
│   ├── app.py              # Core YOLO26 inference logic
│   └── main.py             # FastAPI inference server
├── tests/
├── .env                    # Environment variables for local development
├── Dockerfile              # Container configuration
├── LICENSE                 # AGPL-3.0 License
└── pyproject.toml          # Python dependencies and project config
重要许可说明

Ultralytics YOLO26 模型和框架根据 AGPL-3.0 授权,其中包含重要的合规要求。请务必阅读 Ultralytics 文档,了解如何遵守许可条款

创建包含依赖项的 pyproject.toml#

为了方便管理项目,请创建一个 pyproject.toml 文件,并添加以下依赖项:

[project]
name = "YOUR_PROJECT_NAME"
version = "0.0.1"
description = "YOUR_PROJECT_DESCRIPTION"
requires-python = ">=3.10,<3.13"
dependencies = [
   "ultralytics>=8.3.0",
   "fastapi[all]>=0.89.1",
   "uvicorn[standard]>=0.20.0",
   "pillow>=9.0.0",
]

[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
  • uvicorn 将用于运行 FastAPI 服务器。
  • pillow 将用于图像处理,但你并不局限于 PIL 图像——Ultralytics 支持许多其他格式

使用 Ultralytics YOLO26 创建推理逻辑#

现在你已经设置好项目结构和依赖项,可以实现 YOLO26 的核心推理逻辑。创建一个 src/app.py 文件,使用 Ultralytics Python API 处理模型加载、图像处理和预测。

# src/app.py

from ultralytics import YOLO

# Model initialization and readiness state
model_yolo = None
_model_ready = False

def _initialize_model():
    """Initialize the YOLO model."""
    global model_yolo, _model_ready

    try:
        # Use pretrained YOLO26n model from Ultralytics base image
        model_yolo = YOLO("yolo26n.pt")
        _model_ready = True

    except Exception as e:
        print(f"Error initializing YOLO model: {e}")
        _model_ready = False
        model_yolo = None

# Initialize model on module import
_initialize_model()

def is_model_ready() -> bool:
    """Check if the model is ready for inference."""
    return _model_ready and model_yolo is not None

容器启动时只会加载一次模型,之后所有请求将共享该模型。如果模型需要处理大量推理负载,建议你在稍后的步骤中将模型导入 Vertex AI 时选择内存更大的机器类型。

接下来,使用 pillow 创建两个用于输入和输出图像处理的实用函数。YOLO26 原生支持 PIL 图像。

def get_image_from_bytes(binary_image: bytes) -> Image.Image:
    """Convert image from bytes to PIL RGB format."""
    input_image = Image.open(io.BytesIO(binary_image)).convert("RGB")
    return input_image
def get_bytes_from_image(image: Image.Image) -> bytes:
    """Convert PIL image to bytes."""
    return_image = io.BytesIO()
    image.save(return_image, format="JPEG", quality=85)
    return_image.seek(0)
    return return_image.getvalue()

最后,实现 run_inference 函数来处理目标检测。在本示例中,我们将从模型预测结果中提取边界框、类别名称和置信度分数。该函数将返回一个包含检测结果和原始结果的字典,以便进一步处理或进行标注。

def run_inference(input_image: Image.Image, confidence_threshold: float = 0.5) -> Dict[str, Any]:
    """Run inference on an image using YOLO26n model."""
    # Check if model is ready
    if not is_model_ready():
        print("Model not ready for inference")
        return {"detections": [], "results": None}

    try:
        # Make predictions and get raw results
        results = model_yolo.predict(
            imgsz=640, source=input_image, conf=confidence_threshold, save=False, augment=False, verbose=False
        )

        # Extract detections (bounding boxes, class names, and confidences)
        detections = []
        if results and len(results) > 0:
            result = results[0]
            if result.boxes is not None and len(result.boxes.xyxy) > 0:
                boxes = result.boxes

                # Convert tensors to numpy for processing
                xyxy = boxes.xyxy.cpu().numpy()
                conf = boxes.conf.cpu().numpy()
                cls = boxes.cls.cpu().numpy().astype(int)

                # Create detection dictionaries
                for i in range(len(xyxy)):
                    detection = {
                        "xmin": float(xyxy[i][0]),
                        "ymin": float(xyxy[i][1]),
                        "xmax": float(xyxy[i][2]),
                        "ymax": float(xyxy[i][3]),
                        "confidence": float(conf[i]),
                        "class": int(cls[i]),
                        "name": model_yolo.names.get(int(cls[i]), f"class_{int(cls[i])}"),
                    }
                    detections.append(detection)

        return {
            "detections": detections,
            "results": results,  # Keep raw results for annotation
        }
    except Exception as e:
        # If there's an error, return empty structure
        print(f"Error in YOLO detection: {e}")
        return {"detections": [], "results": None}

你还可以选择添加一个函数,使用 Ultralytics 内置的绘图方法为图像标注边界框和标签。如果你想在预测响应中返回带标注的图像,这会很有用。

def get_annotated_image(results: list) -> Image.Image:
    """Get annotated image using Ultralytics built-in plot method."""
    if not results or len(results) == 0:
        raise ValueError("No results provided for annotation")

    result = results[0]
    # Use Ultralytics built-in plot method with PIL output
    return result.plot(pil=True)

使用 FastAPI 创建 HTTP 推理服务器#

现在你已经完成 YOLO26 核心推理逻辑,可以创建一个 FastAPI 应用来提供该功能。其中将包括 Vertex AI 所需的健康检查和预测端点。

首先,添加导入项并为 Vertex AI 配置日志记录。由于 Vertex AI 将 stderr 视为错误输出,因此将日志传输到 stdout 是合理的做法。

import sys

from loguru import logger

# Configure logger
logger.remove()
logger.add(
    sys.stdout,
    colorize=True,
    format="<green>{time:HH:mm:ss}</green> | <level>{message}</level>",
    level=10,
)
logger.add("log.log", rotation="1 MB", level="DEBUG", compression="zip")

为完全符合 Vertex AI 的要求,请在环境变量中定义所需端点,并设置请求大小限制。建议在生产部署中使用私有 Vertex AI 端点。这样,你将获得更高的请求负载限制(10 MB,而公共端点为 1.5 MB),以及强大的安全性和访问控制。

# Vertex AI environment variables
AIP_HTTP_PORT = int(os.getenv("AIP_HTTP_PORT", "8080"))
AIP_HEALTH_ROUTE = os.getenv("AIP_HEALTH_ROUTE", "/health")
AIP_PREDICT_ROUTE = os.getenv("AIP_PREDICT_ROUTE", "/predict")

# Request size limit (10 MB for private endpoints, 1.5 MB for public)
MAX_REQUEST_SIZE = 10 * 1024 * 1024  # 10 MB in bytes

添加两个 Pydantic 模型来验证请求和响应:

# Pydantic models for request/response
class PredictionRequest(BaseModel):
    instances: list
    parameters: Optional[Dict[str, Any]] = None

class PredictionResponse(BaseModel):
    predictions: list

添加健康检查端点以验证模型是否就绪。这对 Vertex AI 很重要,因为如果没有专用的健康检查端点,其编排器将不断 ping 随机套接字,并且无法确定模型是否已准备好进行推理。检查成功时必须返回 200 OK,失败时返回 503 Service Unavailable

# Health check endpoint
@app.get(AIP_HEALTH_ROUTE, status_code=status.HTTP_200_OK)
def health_check():
    """Health check endpoint for Vertex AI."""
    if not is_model_ready():
        raise HTTPException(status_code=503, detail="Model not ready")
    return {"status": "healthy"}

现在你已经具备实现预测端点所需的一切,该端点将处理推理请求。它将接收图像文件、执行推理并返回结果。请注意,图像必须进行 base64 编码,这还会使负载大小最多增加 33%。

@app.post(AIP_PREDICT_ROUTE, response_model=PredictionResponse)
async def predict(request: PredictionRequest):
    """Prediction endpoint for Vertex AI."""
    try:
        predictions = []

        for instance in request.instances:
            if isinstance(instance, dict):
                if "image" in instance:
                    image_data = base64.b64decode(instance["image"])
                    input_image = get_image_from_bytes(image_data)
                else:
                    raise HTTPException(status_code=400, detail="Instance must contain 'image' field")
            else:
                raise HTTPException(status_code=400, detail="Invalid instance format")

            # Extract YOLO26 parameters if provided
            parameters = request.parameters or {}
            confidence_threshold = parameters.get("confidence", 0.5)
            return_annotated_image = parameters.get("return_annotated_image", False)

            # Run inference with YOLO26n model
            result = run_inference(input_image, confidence_threshold=confidence_threshold)
            detections_list = result["detections"]

            # Format predictions for Vertex AI
            detections = []
            for detection in detections_list:
                formatted_detection = {
                    "class": detection["name"],
                    "confidence": detection["confidence"],
                    "bbox": {
                        "xmin": detection["xmin"],
                        "ymin": detection["ymin"],
                        "xmax": detection["xmax"],
                        "ymax": detection["ymax"],
                    },
                }
                detections.append(formatted_detection)

            # Build prediction response
            prediction = {"detections": detections, "detection_count": len(detections)}

            # Add annotated image if requested and detections exist
            if (
                return_annotated_image
                and result["results"]
                and result["results"][0].boxes is not None
                and len(result["results"][0].boxes) > 0
            ):
                import base64

                annotated_image = get_annotated_image(result["results"])
                img_bytes = get_bytes_from_image(annotated_image)
                prediction["annotated_image"] = base64.b64encode(img_bytes).decode("utf-8")

            predictions.append(prediction)

        logger.info(
            f"Processed {len(request.instances)} instances, found {sum(len(p['detections']) for p in predictions)} total detections"
        )

        return PredictionResponse(predictions=predictions)

    except HTTPException:
        # Re-raise HTTPException as-is (don't catch and convert to 500)
        raise
    except Exception as e:
        logger.error(f"Prediction error: {e}")
        raise HTTPException(status_code=500, detail=f"Prediction failed: {e}")

最后,添加应用入口点以运行 FastAPI 服务器。

if __name__ == "__main__":
    import uvicorn

    logger.info(f"Starting server on port {AIP_HTTP_PORT}")
    logger.info(f"Health check route: {AIP_HEALTH_ROUTE}")
    logger.info(f"Predict route: {AIP_PREDICT_ROUTE}")
    uvicorn.run(app, host="0.0.0.0", port=AIP_HTTP_PORT)

现在你已经拥有一个完整的 FastAPI 应用,可以处理 YOLO26 推理请求。你可以通过安装依赖项并运行服务器在本地进行测试,例如使用 uv。

# Install dependencies
uv pip install -e .

# Run the FastAPI server directly
uv run src/main.py

要测试服务器,你可以使用 cURL 查询 /health/predict 端点。将测试图像放入 tests 文件夹。然后在 Terminal 中运行以下命令:

# Test health endpoint
curl http://localhost:8080/health

# Test predict endpoint with base64 encoded image
curl -X POST -H "Content-Type: application/json" -d "{\"instances\": [{\"image\": \"$(base64 -i tests/test_image.jpg)\"}]}" http://localhost:8080/predict

你应该会收到包含检测对象的 JSON 响应。首次请求时预计会有短暂延迟,因为 Ultralytics 需要拉取并加载 YOLO26 模型。

2. 使用你的应用扩展 Ultralytics Docker 镜像#

Ultralytics 提供多个 Docker 镜像,你可以将其用作应用镜像的基础镜像。Docker 将安装 Ultralytics 和必要的 GPU 驱动程序。

要使用 Ultralytics YOLO 模型的全部功能,你应选择针对 CUDA 优化的镜像进行 GPU 推理。不过,如果 CPU 推理足以满足你的任务,也可以选择仅支持 CPU 的镜像,以节省计算资源:

  • Dockerfile:用于 YOLO26 单 GPU/多 GPU 训练和推理的 CUDA 优化镜像。
  • Dockerfile-cpu:用于 YOLO26 推理的仅 CPU 镜像。

为你的应用创建 Docker 镜像#

在项目根目录创建一个 Dockerfile 文件,内容如下:

# Extends official Ultralytics Docker image for YOLO26
FROM ultralytics/ultralytics:latest

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

# Install FastAPI and dependencies
RUN uv pip install fastapi[all] uvicorn[standard] loguru

WORKDIR /app
COPY src/ ./src/
COPY pyproject.toml ./

# Install the application package
RUN uv pip install -e .

RUN mkdir -p /app/logs
ENV PYTHONPATH=/app/src

# Port for Vertex AI
EXPOSE 8080

# Start the inference server
ENTRYPOINT ["python", "src/main.py"]

在示例中,官方 Ultralytics Docker 镜像 ultralytics:latest 被用作基础镜像。它已包含 YOLO26 模型和所有必要的依赖项。服务器入口点与我们在本地测试 FastAPI 应用时使用的入口点相同。

构建并测试 Docker 镜像#

现在你可以使用以下命令构建 Docker 镜像:

docker build --platform linux/amd64 -t IMAGE_NAME:IMAGE_VERSION .

IMAGE_NAMEIMAGE_VERSION 替换为你所需的值,例如 yolo26-fastapi:0.1。请注意,如果要在 Vertex AI 上部署,必须为 linux/amd64 架构构建镜像。如果你在 Apple Silicon Mac 或其他非 x86 架构上构建镜像,则必须显式设置 --platform 参数。

镜像构建完成后,你可以在本地测试 Docker 镜像:

docker run --platform linux/amd64 -p 8080:8080 IMAGE_NAME:IMAGE_VERSION

现在,你的 Docker 容器正在 8080 端口上运行 FastAPI 服务器,可以接收推理请求。你可以使用之前相同的 cURL 命令测试 /health/predict 端点:

# Test health endpoint
curl http://localhost:8080/health

# Test predict endpoint with base64 encoded image
curl -X POST -H "Content-Type: application/json" -d "{\"instances\": [{\"image\": \"$(base64 -i tests/test_image.jpg)\"}]}" http://localhost:8080/predict

3. 将 Docker 镜像上传到 GCP Artifact Registry#

要将容器化模型导入 Vertex AI,你需要先将 Docker 镜像上传到 Google Cloud Artifact Registry。如果你还没有 Artifact Registry 仓库,则需要先创建一个。

在 Google Cloud Artifact Registry 中创建仓库#

在 Google Cloud Console 中打开 Artifact Registry 页面。如果你是首次使用 Artifact Registry,系统可能会提示你先启用 Artifact Registry API。

Google Cloud Artifact Registry repository creation

  1. 选择“创建仓库”。
  2. 输入仓库名称。选择所需区域,其他选项使用默认设置,除非你有特定的修改需求。
注意

区域选择可能会影响机器的可用性,以及非 Enterprise 用户的某些计算限制。你可以在 Vertex AI 官方文档中找到更多信息:Vertex AI 配额和限制

  1. 创建仓库后,将你的 PROJECT_ID、Location(区域)和 Repository Name 保存到密钥保管库或 .env 文件中。之后你需要使用它们为 Docker 镜像添加标签,并将镜像推送到 Artifact Registry。

向 Artifact Registry 验证 Docker 身份#

向刚创建的 Artifact Registry 仓库验证 Docker 客户端身份。在终端中运行以下命令:

gcloud auth configure-docker YOUR_REGION-docker.pkg.dev

为镜像添加标签并将其推送到 Artifact Registry#

为 Docker 镜像添加标签,并将其推送到 Google Artifact Registry。

为镜像使用唯一标签

建议每次更新镜像时都使用唯一标签。包括 Vertex AI 在内的大多数 GCP 服务都依赖镜像标签进行自动版本控制和扩缩容,因此最好使用语义化版本或基于日期的标签。

使用 Artifact Registry 仓库 URL 为镜像添加标签。将占位符替换为之前保存的值。

docker tag IMAGE_NAME:IMAGE_VERSION YOUR_REGION-docker.pkg.dev/YOUR_PROJECT_ID/YOUR_REPOSITORY_NAME/IMAGE_NAME:IMAGE_VERSION

将带标签的镜像推送到 Artifact Registry 仓库。

docker push YOUR_REGION-docker.pkg.dev/YOUR_PROJECT_ID/YOUR_REPOSITORY_NAME/IMAGE_NAME:IMAGE_VERSION

等待流程完成。现在你应该可以在 Artifact Registry 仓库中看到该镜像。

有关如何在 Artifact Registry 中处理镜像的更具体说明,请参阅 Artifact Registry 文档:推送和拉取镜像

4. 将模型导入 Vertex AI#

使用刚刚推送的 Docker 镜像,你现在可以将模型导入 Vertex AI。

  1. 在 Google Cloud 导航菜单中,转到 Vertex AI > Model Registry。或者,在 Google Cloud Console 顶部的搜索栏中搜索 "Vertex AI"。

Vertex AI Model Registry import interface

1. Click Import. 1. Select Import as a new model. 1. Select the region. You can choose the same region as your Artifact Registry repository, but your selection should be dictated by the availability of machine types and quotas in your region. 1. Select Import an existing model container.

Vertex AI import model dialog

1. In the Container image field, browse the Artifact Registry repository you created earlier and select the image you just pushed. 1. Scroll down to the Environment variables section and enter the predict and health endpoints, and the port that you defined in your FastAPI application.

Vertex AI environment variables configuration

1. Click Import. Vertex AI will take several minutes to register the model and prepare it for deployment. You will receive an email notification once the import is complete.

5. 创建 Vertex AI 端点并部署模型#

Vertex AI 中的端点与模型

在 Vertex AI 的术语中,端点指的是已部署的模型,因为它们代表你发送推理请求的 HTTP 端点;而模型是存储在 Model Registry 中的已训练 ML 构件。

要部署模型,你需要在 Vertex AI 中创建一个端点。

  1. 在 Vertex AI 导航菜单中,转到 Endpoints。选择导入模型时使用的区域。点击“创建”。

Vertex AI create endpoint interface

1. Enter the Endpoint name. 1. For Access, Vertex AI recommends using private Vertex AI endpoints. Apart from security benefits, you get a higher payload limit if you select a private endpoint, however you will need to configure your VPC network and firewall rules to allow access to the endpoint. Refer to the Vertex AI documentation for more instructions on [private endpoints](https://docs.cloud.google.com/gemini-enterprise-agent-platform/machine-learning/predictions/choose-endpoint-type). 1. Click Continue. 1. On the Model settings dialog, select the model you imported earlier. Now you can configure the machine type, memory, and GPU settings for your model. Allow for ample memory if you are expecting high inference loads to ensure there are no I/O bottlenecks for the proper YOLO26 performance. 1. In Accelerator type, select the GPU type you want to use for inference. If you are not sure which GPU to select, you can start with NVIDIA T4, which is CUDA-supported.
区域和机器类型配额

请记住,某些区域的计算配额非常有限,因此你可能无法在所在区域选择某些机器类型或 GPU。如果这一点对你很关键,请将部署区域更改为配额更大的区域。请在 Vertex AI 官方文档中了解更多信息:Vertex AI 配额和限制

  1. 选择机器类型后,你可以点击“继续”。此时,你可以选择在 Vertex AI 中启用模型监控——这是一项额外服务,用于跟踪模型性能并深入了解其行为。此功能为可选项且会产生额外费用,请根据你的需求进行选择。点击“创建”。

Vertex AI 将花费几分钟时间部署模型(某些区域最多需要 30 分钟)。部署完成后,你会收到电子邮件通知。

6. 测试已部署的模型#

部署完成后,Vertex AI 会提供一个示例 API 接口,用于测试模型。

要测试远程推理,你可以使用提供的 cURL 命令,或创建另一个 Python 客户端库来向已部署的模型发送请求。请记住,在将图像发送到 /predict 端点之前,需要先将其编码为 base64。

Vertex AI endpoint testing with cURL

首次请求预计会有短暂延迟

与本地测试类似,首次请求预计会有短暂延迟,因为 Ultralytics 需要在正在运行的容器中拉取并加载 YOLO26 模型。

你已成功使用 Ultralytics 在 Google Cloud Vertex AI 上部署预训练 YOLO26 模型。

常见问题#

  • 可以;但你首先需要将模型导出为与 Vertex AI 兼容的格式,例如 TensorFlow、Scikit-learn 或 XGBoost。Google Cloud 提供了一份关于在 Vertex 上运行 .pt 模型的指南,其中完整介绍了转换流程:在 Vertex AI 上运行 PyTorch 模型

    请注意,最终配置将仅依赖 Vertex AI 的标准服务层,不支持 Ultralytics 框架的高级功能。由于 Vertex AI 完全支持容器化模型,并且可以根据你的部署配置自动扩缩容,因此你无需将 Ultralytics YOLO 模型转换为其他格式,即可利用其全部功能。

  • FastAPI 可为推理工作负载提供高吞吐量。异步支持可以在不阻塞主线程的情况下处理多个并发请求,这对于提供计算机视觉模型服务非常重要。

    FastAPI 自动验证请求和响应,可减少生产环境推理服务中的运行时错误。这对于目标检测 API 尤其有价值,因为输入格式的一致性至关重要。

    FastAPI 为你的推理流水线增加的计算开销极小,从而为模型执行和图像处理任务保留更多可用资源。

    FastAPI 还支持 SSE(服务器发送事件),这对于流式推理场景非常有用。

  • 这其实是 Google Cloud Platform 的一项灵活性特性:你使用的每项服务都需要选择一个区域。在 Vertex AI 上部署容器化模型时,最重要的区域选择是 Model Registry 所使用的区域。它将决定模型部署可用的机器类型和配额。

    此外,如果你要扩展此设置,并将预测数据或结果存储在 Cloud Storage 或 BigQuery 中,则需要使用与 Model Registry 相同的区域,以尽量减少延迟并确保数据访问的高吞吐量。

评论