65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 NLP 和 Transformers 的 Docker 化大型模型 AI

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2021 年 5 月 19 日

CPOL

5分钟阅读

viewsIcon

8807

downloadIcon

90

在本文中,我们使用 Docker 卷中持久化的模型来运行 NLP 推理模型。

引言

Docker 等容器技术可以简化依赖管理并提高软件的可移植性。在本系列文章中,我们将探讨 Docker 在机器学习 (ML) 场景中的使用。

本系列假定您熟悉 AI/ML、容器化(一般)以及 Docker(具体)。

在本系列的上一篇文章中,我们使用容器化的对象检测 API 环境对 TensorFlow 样本图像进行了推理。在本文中,我们将继续处理大型模型,这次是使用 PyTorchTransformers 来处理自然语言处理 (NLP) 任务。欢迎下载本文使用的代码

在本系列的后续文章中,我们将通过 REST API 提供推理模型。接下来,我们将调试在容器中运行的 REST API 服务。最后,我们将使用 Azure Container Instances 将创建的容器发布到云端。

容器与大型模型

只要处理相对较小的模型,在机器学习中使用容器就相当直接。最简单的方法是将模型视为代码的一部分,并在构建时将其添加到容器中。随着模型变大,这种情况会变得更加复杂。模型的大小往往会达到数百兆字节甚至更多。将如此大的体积添加到镜像中会增加其大小和构建时间。此外,如果您在水平扩展的集群中运行代码,这种额外的体积会乘以运行容器的数量。

作为一般规则,您不应在镜像中包含任何可以轻松在容器实例之间共享的内容。在开发过程中,可以通过将本地文件夹映射到容器卷来处理共享文件。这正是在上一篇文章中为对象检测 API 模型所做的事情。但在生产环境中,可能无法这样做,尤其是在计划在云中运行容器的情况下。

在这种情况下,首选的解决方案是依赖 Docker 卷。然而,这种方法也有一些缺点。持久化和共享容器之间的任何内容都会引入额外的依赖项。同一个容器的行为可能会有所不同,具体取决于持久化卷中已存储的内容。此外,在容器运行之前(而不是在构建过程中),您无法将任何内容存储到卷中。

Transformers 的 Dockerfile

Hugging Face Transformers 是一个非常流行的用于处理自然语言处理 (NLP) 任务的库。它支持许多现代 NLP 模型架构和 ML 框架,包括 TensorFlow 和 PyTorch。我们已经使用过几次 TensorFlow,这次我们选择 PyTorch 版本来换换口味。

我们的 Dockerfile 会相当简单

FROM pytorch/pytorch:1.6.0-cuda10.1-cudnn7-runtime

ENV DEBIAN_FRONTEND=noninteractive

ARG USERNAME=mluser
ARG USERID=1000
RUN useradd --system --create-home --shell /bin/bash --uid $USERID $USERNAME \
 && mkdir /home/$USERNAME/.cache && chown -R $USERNAME /home/$USERNAME/.cache 

COPY requirements.txt /tmp/requirements.txt 
RUN pip install -r /tmp/requirements.txt \
 && rm /tmp/requirements.txt

USER $USERNAME
COPY --chown=$USERNAME ./app /home/$USERNAME/app
WORKDIR /home/$USERNAME/app

ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8

ENTRYPOINT ["python", "nlp.py"]

除了基础镜像,与上一篇文章相比,差别不大。官方 PyTorch 基础镜像提供了我们所需的一切,并且仍然比 TensorFlow-GPU 镜像小。

这次,我们不需要任何额外的系统依赖项,因此可以避免使用 apt-get 命令。请注意第一个 RUN 语句中的 mkdir /home/$USERNAME/.cache 命令。在容器执行期间,我们将把 Docker 卷挂载到此路径,用于下载的 PyTorch 模型。

我们显式创建此文件夹是为了在构建镜像时设置访问权限。这些权限将继承到容器执行期间挂载的卷中。

requirements.txt 文件也非常精简。它包含一行:

transformers==4.3.2

我们解决方案中最复杂的部分存储在 nlp.py 脚本中。它被设置为我们容器的入口点,并且在代码下载存档中可用。

组合容器

为了简化卷的创建和挂载,这次我们将使用 docker-compose 来构建和运行我们的容器。如果您使用 Docker Desktop,应该已经安装了它。如果您使用的是 Linux 上的 Docker Server,可能需要安装它

请注意,在最近的 Docker 版本中,您可以使用 docker compose 命令而不是 docker-compose。然而,令我们惊讶的是,我们注意到这两个命令带来的结果并不总是相同的。因此,在本系列文章中,我们将坚持使用 docker-compose

现在,让我们在 Dockerfile 所在的同一目录中创建一个简短的 docker-compose.yml 文件。

version: '3.7'
volumes:
  mluser_cache:
    name: mluser_cache
services:
  mld07_transformers: 
    build:
      context:  '.'
      dockerfile: 'Dockerfile'
    image: 'mld07_transformers'
    volumes:
      - mluser_cache:/home/mluser/.cache

在这里,我们指示 docker-compose 执行两项操作:创建 mluser_cache 卷,并在容器运行时将其挂载到 /home/mluser/.cache 路径。除此之外,该文件还定义了镜像名称和构建上下文。

构建容器

使用 docker-compose,构建容器的命令将与上一篇文章略有不同。

$ docker-compose build --build-arg USERID=$(id -u)

由于所有基本参数都已包含在 docker-compose.yml 配置中,这里我们只需要传递 USERID 值。因为这次我们不将本地文件夹挂载为容器卷,所以可以安全地忽略 --build-arg USERID 属性。事实上,在 Windows 上运行时,您可以随时忽略它。我们只在此保留它,以确保后续文章中的权限一致性。

运行容器

现在容器已构建完成,让我们检查一下它是否正常工作。我们将从一个问答任务开始。

$ docker-compose run --user $(id -u) mld07_transformers \
--task="qa" \
--document="We have considered many names for my dog, such as: Small, Black or Buster. Finally we have called him Ratchet. It is quite an unusual name, but the dog's owner named Michael picked it." \
--question="What is the name of my dog?"

我们在这里故意设置了一个陷阱:在文档中输入了多个“名字”;然而,模型处理得相当好。

模型首次执行时,相关的模型被下载并存储在映射到我们的 mluser_cache 卷的 .cache 文件夹中。

要检查卷大小,我们可以使用以下命令。

$ docker system df -v

Local Volumes space usage should display

为了确保模型数据已持久化,我们可以运行与之前相同的 docker-compose run 命令,但结果略有不同。

这次模型已经在 .cache 文件夹中,所以没有再次下载。

您可以随意尝试下载的 nlp.py 脚本中包含的其他 NLP 任务模型。请注意,其中每个模型在首次使用时都需要下载,有些模型的大小会超过 1 GB。

摘要

我们的 NLP 任务推理模型正在工作,并且下载的模型已保存到持久化的卷中。在下一篇文章中,我们将修改我们的代码,通过 REST API 服务公开相同的逻辑。敬请关注!

© . All rights reserved.