使用 Django 为深度学习模型提供服务

发布: (2025年12月28日 GMT+8 15:42)
8 分钟阅读
原文: Dev.to

Source: Dev.to

(请提供您希望翻译的正文内容,我将按照要求保留源链接、格式和代码块,仅翻译文本部分。)

引言

深度学习并不是在大语言模型出现时才突然出现的。该领域已经发展了数十年,始于20世纪末的早期神经网络研究,并随着计算能力、数据量和算法的进步逐步提升。如今,深度学习系统已被用于图像识别、自然语言处理、推荐系统以及许多其他实际应用中。然而,训练模型只是工作流的一部分。要让模型真正有用,必须对其进行部署。模型需要接受输入并以可靠的方式返回预测。本文将通过 Django 和 PyTorch,演示一种实用的深度学习模型部署方法。

注意: 此处侧重于清晰和正确性,而非生产级别的优化(我们将在后续文章中讨论)。

Part 1: 设置 Django 项目

  1. 创建并激活虚拟环境,然后安装 Django

    python -m venv venv
    source venv/bin/activate
    pip install django
  2. 创建一个新的 Django 项目

    django-admin startproject sample_project
    cd sample_project
  3. 创建一个将包含模型服务逻辑的应用

    python manage.py startapp my_app
  4. 项目目录结构

    sample_project/
    ├── sample_project/
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── my_app/
    │   ├── views.py
    │   ├── apps.py
    │   └── templates/
    └── manage.py
  5. sample_project/settings.py 中将 my_app 添加到 INSTALLED_APPS。此阶段尚未涉及深度学习代码。

第 2 部分:训练深度学习模型

模型训练特意放在 Django 之外。训练通常计算量大,最好在单独的脚本或 notebook 中完成。Django 以后只负责推理,而不是优化。

下面是一个训练函数,执行带验证的监督学习并跟踪常用指标。此示例使用 PyTorch 模型。

def train_model(
    model: nn.Module,
    loss_fn,
    optim,
    lr: float,
    train_dl,
    val_dl,
    num_classes: int,
    epochs: int = 20,
    device: str = "cuda",
) -> dict[str, list[float]]:
    """
    Train a PyTorch model.

    - Moves data and the model to the chosen device (CPU or GPU)
    - Computes loss using cross‑entropy (appropriate for multi‑class classification)
    - Tracks accuracy and macro‑averaged F1 score using torchmetrics
    - Separates training and validation phases
    """
    # implementation goes here …

关键要点

  • 该函数完全独立于 Django,确保训练与服务之间的清晰分离。
  • 您可以根据任务自行训练模型,或使用预训练模型并进行适配。

第3部分:保存和加载模型权重

在训练完成后,保存模型学习到的参数,以便 Django 在推理时加载它们。

torch.save(
    {"model_state_dict": model.state_dict()},
    "resnet18_model.pth"
)

在部署模型时,完全按照训练时的架构重新构建,然后加载已保存的权重:

model = ResNet18_CustomHead(num_classes=5).to(device)
ckpt = torch.load("resnet18_model.pth", map_location=device)
model.load_state_dict(ckpt["model_state_dict"])
model.eval()   # 禁用 dropout、batch‑norm 更新等

这种显式的方法在不同环境下更不易出错。

第4部分:创建用于推理的 Django 视图

现在我们把所有内容连接起来。下面的视图接受上传的图像,加载已训练好的模型,执行推理,并以 JSON 形式返回预测结果。

import os
import uuid
import shutil
from pathlib import Path

from django.http import JsonResponse
from django.shortcuts import render
from django.conf import settings

import torch
from torchvision import transforms

# Assume these utilities are defined elsewhere in the project
# from .utils import load_images_from_path, get_default_test_transforms, infer_on_unknown_data
# from .models import ResNet18_CustomHead

TEMP_DIR = Path(settings.BASE_DIR) / "temp"
MODEL_WEIGHTS_PATH = Path(settings.BASE_DIR) / "model_weights"


def analysisPageView(request):
    if request.method == "POST":
        # ---- Prepare a clean temporary directory ----
        os.makedirs(TEMP_DIR, exist_ok=True)

        # Remove any leftover sub‑directories
        for p in TEMP_DIR.iterdir():
            if p.is_dir():
                shutil.rmtree(p)

        req_dir = TEMP_DIR / uuid.uuid4().hex
        req_dir.mkdir(parents=True, exist_ok=True)

        # ---- Save uploaded images to the temp folder ----
        uploaded_files = request.FILES.getlist("images")
        for f in uploaded_files:
            file_name = Path(f.name).name
            ext = Path(file_name).suffix.lower()
            final_file_name = f"image-{uuid.uuid4().hex[:8]}{ext}"

            with open(req_dir / final_file_name, "wb") as dest:
                for chunk in f.chunks():
                    dest.write(chunk)

        # ---- Load images and preprocessing transforms ----
        images = load_images_from_path(req_dir)
        test_tfms = get_default_test_transforms()
        device = "cuda" if torch.cuda.is_available() else "cpu"

        # ---- Load the model (once per request – OK for demos) ----
        model = ResNet18_CustomHead(num_classes=5).to(device)
        ckpt = torch.load(
            MODEL_WEIGHTS_PATH / "resnet18_model.pth",
            map_location=device,
        )
        model.load_state_dict(ckpt["model_state_dict"])
        model.eval()

        # ---- Run inference ----
        pred_labels = infer_on_unknown_data(
            images, model, device, test_tfms
        )

        return JsonResponse({"labels": pred_labels})

    # GET request – render the upload page
    return render(request, "app/analysis.html")

重要说明

  • 在请求处理函数内部加载模型 在演示场景下是可以接受的,但在高并发系统中效率低下。生产环境下应在服务器启动时加载一次模型,并在各请求之间复用。
  • model.eval() 会关闭训练专用的层,如 dropout 和 batch‑norm 的更新。
  • 预处理必须 完全匹配 训练时使用的 transforms,否则预测结果会不可靠。

通过上述步骤,你现在拥有一个最小但可用的流水线,能够训练 PyTorch 模型、保存权重,并通过 Django Web 应用进行服务。

第5部分:测试 API

一旦视图在 urls.py 中配置完成,你可以通过以下方式进行测试:

  • 在 HTML 表单中提交图片
  • 使用 Postmancurl 发送 POST 请求
  • 从前端应用调用该端点

如果一切设置正确,Django 将返回结构化的 JSON 预测结果,便于与其他服务集成。

模型推理示例视图

(前面的视图已经涵盖了此内容;下面的代码片段仅作参考。)

def ModelInferenceView():
    ...
    images = load_images_from_path(req_dir)

    # ⚠️ Warning: not recommended for production
    model = ResNet18_CustomHead(num_classes=5).to(device)

    ckpt = torch.load(MODEL_WEIGHTS_PATH / "resnet18_model.pth",
                     map_location=device)
    state_dict = ckpt["model_state_dict"]
    model.load_state_dict(state_dict)
    ...

    pred_labels = infer_on_unknown_data(images, model, device, test_tfms)

    return JsonResponse({'labels': pred_labels})

回顾

在本文中,我们详细介绍了使用 Django 部署深度学习模型的完整生命周期:

  • 创建了一个 Django 项目和应用
  • 在 Web 层之外训练了一个 PyTorch 模型
  • 正确地保存和加载模型权重
  • 为推理公开了一个 Django 视图
  • 通过 HTTP 请求测试了模型

Django 并非为大规模模型服务而设计,但它是原型、内部工具以及对简洁性和灵活性有需求的工作负载的实用选择。

今天就到这里。参考代码(相关但用于不同项目)可在此处获取。如果你觉得这篇文章有帮助,请在LinkedIn上关注我,并给仓库点星。

Back to Blog

相关文章

阅读更多 »