towardsdatascience.com/how-to-deploy-ml-solutions-with-fastapi-docker-and-gcp-de1bb8bfc59a

这是关于全栈数据科学的一个更大系列中的第 5 篇文章。在这篇文章中,我介绍了基于机器学习的搜索 API 的部署。虽然我们可以用无数种方式来做这件事,但在这里,我讨论了一种简单的 3 步方法,几乎可以应用于任何机器学习解决方案。示例代码在GitHub 仓库中免费提供。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/6c7f67cb7abb6747a32e70a7faa9901c.png

由Andy Hermawan在Unsplash上的照片


虽然看起来机器学习都是关于训练复杂的模型,但在现实世界中,一个模型(本身)并不能创造价值。要将机器学习模型转化为机器学习解决方案(即有价值的东西),我们必须“部署”它。

这可以采取许多不同的形式。例如,创建一个用户可以与之交互的 Web 界面,将模型集成到现有的软件系统中,或者甚至只是为开发者设置一个 API 来访问模型(想想 OpenAI 之类的)。

我在这里描述的3 步策略与所有这些示例都兼容。它包括:

  1. 使用 FastAPI 创建推理 API

  2. 通过 Docker 容器化 API

  3. 在云平台上运行容器(我在这里使用 GCP)

cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FpJ_nCklQ65w%3Ffeature%3Doembed&display_name=YouTube&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DpJ_nCklQ65w&image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FpJ_nCklQ65w%2Fhqdefault.jpg&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=youtube

FastAPI

部署策略的第一步是将我们的模型包装在一个 API(即应用程序编程接口)中。简单来说,一个 API 允许你以编程方式与应用程序交互。

实现这一第一步的一种方式是通过 FastAPI,这是一个 Python 库,它提供了一种将 Python 函数转换为 API 端点的方法。下面的代码提供了一个具体的示例。

Docker

虽然我们可以直接将我们的推理 API 部署到云上,但通常首先“容器化”它是个好主意。这就是 Docker 发挥作用的地方。

Docker 允许你创建一个轻量级的包装器,捕获我们所有 API 的依赖项,这使得在新机器上启动(得多)容易。

它通过一个两步过程工作。第一步,我们创建一个 Docker 镜像。这本质上是一个告诉系统如何从头开始启动 API 的配方(别担心,制作起来超级简单)。第二步,我们可以在安装了 Docker 的任何系统上运行该镜像。运行的镜像被称为容器,它就像一个坐在更大系统上的小虚拟机。

谷歌云运行

最后,我们需要计算资源来运行我们的 Docker 镜像。当然,一个人可以在笔记本电脑上做这件事(可能不是一个好主意),通过本地服务器或云提供商。

在这里,我使用 Google Cloud Run,这是一个专门用于运行 Docker 容器的 GCP 服务。它还有一个免费层,因此你可以部署项目而无需承担不必要的成本。

示例代码:部署语义搜索 API

现在我们已经对关键概念有了基本理解,让我们看看这个过程在代码中的样子。下面的示例基于本系列之前的文章,这些文章从我的所有 YouTube 视频中提取标题和转录,并将它们转换成了文本嵌入。

简而言之,文本嵌入是 文本的语义上有意义的数值表示,它使一种新的搜索(通常称为语义搜索)成为可能。

在这里,我们将构建和部署一个覆盖我所有 YouTube 视频的语义搜索系统的 API。这个 API 是运行在 Hugging Face spaces 上的实时搜索应用程序的后端。

该应用程序的后端代码库和前端代码库都是免费提供的。


步骤 1:创建 API

FastAPI 使得将现有的 Python 脚本转换成 API 变得非常简单,只需添加几行代码即可。下面是这个过程的示例。

我们首先导入一些有用的库。

from fastapi import FastAPI
import polars as pl
from sentence_transformers import SentenceTransformer
from sklearn.metrics import DistanceMetric
import numpy as np
from app.functions import returnSearchResultIndexes

接下来,我们将定义我们的语义搜索函数的构建块。具体来说,是文本嵌入模型、我们想要搜索的视频的标题和转录嵌入,以及一个距离度量来评估哪些视频与用户的查询最相关。如果你想要深入了解语义搜索,我在一篇过去的文章中讨论了它。

# define model info
model_name = 'all-MiniLM-L6-v2'

# load model
model = SentenceTransformer(model_name)

# load video index
df = pl.scan_parquet('app/data/video-index.parquet')

# create distance metric object
dist_name = 'manhattan'
dist = DistanceMetric.get_metric(dist_name)

现在,我们定义我们的 API 操作。在这里,我们将创建 3 个 GET 请求。第一个在下面的代码块中显示。

# create FastAPI object
app = FastAPI()

# API operations
@app.get("/")
def health_check():
    return {'health_check': 'OK'}

在上面的代码块中,我们使用 FastAPI() 类初始化一个新的 FastAPI 应用程序,然后创建一个 “健康检查” 端点。

要做到这一点,我们定义了一个 Python 函数,它不接受任何输入,但返回一个包含键 “health_check” 和值 “OK” 的字典。要将这个函数转换为 API 端点,我们只需给它添加一个装饰器并指定端点的路径。在这里,我们使用根路径,即 “/”。

让我们看看另一个例子。在这里,我们将有一个名为 info 的端点,它返回有关 API 的更多信息。

@app.get("/info")
def info():
    return {'name': 'yt-search', 'description': "Search API for Shaw Talebi's YouTube videos."}

我们可以看到,这个端点与健康检查非常相似。然而,这个端点位于 “/info”。

最后,让我们创建搜索端点,它根据用户查询返回最相关视频的标题和 ID。

@app.get("/search")
def search(query: str):
    idx_result = returnSearchResultIndexes(query, df, model, dist)
    return df.select(['title', 'video_id']).collect()[idx_result].to_dict(as_series=False)

对于这个端点,我们需要一个输入:用户的查询。然后它被传递到另一个在另一个脚本中定义的 Python 函数,该函数执行搜索背后的所有数学运算。虽然我不会在这里详细介绍,但好奇的读者可以在 GitHub 上查看代码,或者在 YouTube 上查看搜索函数的代码演示。

由于这个函数只返回搜索结果在 df 数据框中的行号,我们需要使用这个输出抓取我们关心的标题和视频 ID,然后将它们作为 Python 字典返回。重要的是,我们 API 端点的所有输出都应该是字典,因为它们遵循 API 的标准 JSON 格式。

注意,上面的代码块引用了两个外部文件:app/functions.py 和 app/data/video-index.parquet,这暗示了以下目录结构。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/5ddcfb54ce20dec7dcd022f497feaff9.png

FastAPI API 的目录结构。图片由作者提供。

要在本地运行此 API,我们可以导航到根目录并运行以下命令。

uvicorn app.main:app --host 0.0.0.0 --port 8080

uvicorn 是一个 Python 库,允许我们运行使用 FastAPI 创建的 Web 应用程序。此命令将在 0.0.0.0:8080 本地运行 API。我们使用此主机和端口的理由将在我们部署到 Google Cloud Run 时变得明显。

步骤 2:创建 Docker 镜像

哇,我们的 API 在本地运行了!让我们继续下一步,在云中运行它。

我们通过为我们的 API 创建一个 Docker 镜像来实现这一点。这需要我们创建 3 个额外的文件:Dockerfile、requirements.txt 和 app/init.py。以下是我们的目录应该看起来像什么。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/459837f50d8bcbcbbf3a873ce4818f9c.png

创建 Docker 镜像的目录结构。图片由作者提供(无意中打趣)。

Dockerfile 包含运行我们的 Docker 镜像的逐步指令。requirements.txt 指定了运行我们的 API 所需的 Python 库(及其版本)。最后,app/init.py 将 app 文件夹标记为 Python 包,确保 Python 可以在容器中运行时找到并正确导入我们的 API 代码。

下面是 Dockerfile 的内部结构。

# start from python base image
FROM python:3.10-slim

# change working directory
WORKDIR /code

# add requirements file to image
COPY ./requirements.txt /code/requirements.txt

# install python libraries
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt

# add python code
COPY ./app/ /code/app/

# specify default commands
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]

第一行在我们的 Python 3.10 已安装的现有镜像上启动我们的镜像。接下来,我们将工作目录从 root 更改为 /code。

然后,我们将代码库中的 requirements.txt 文件复制到 Docker 镜像中。有了这个,我们就可以使用 pip 安装我们的需求。

接下来,我们将 API 的所有代码复制过来。

注意:我们在其他所有事情之前复制并安装了 Python 包,因为这允许我们缓存需求的安装。这在开发期间快速运行 Docker 镜像时很有帮助,因为它避免了等待几分钟来安装依赖项。

最后,我们指定默认命令,这个命令与我们本地运行 API 时使用的是同一件事。

步骤 3:部署到 Google Cloud

到这一步,我们可以构建我们的 Docker 镜像并将其推送到Docker Hub,这样我们就可以轻松地将它部署到许多不同的云服务中。然而,在这里我将采用一种替代策略。

相反,我将把所有代码推送到 GitHub,然后我们可以直接将容器部署到 Google Cloud Run。这有两个关键优势。

首先,它避免了在您的本地系统与 Google Cloud Run 使用的系统架构之间解决差异(这对我来说是一个具体问题,因为 Mac 运行在 ARM64 上)。其次,从 GitHub 仓库部署允许持续部署,因此如果我们想更新我们的 API,我们只需将新代码推送到我们的仓库,就会自动启动一个新的容器。

我们首先创建一个新的 GitHub 仓库。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/62b566c99b1db24e2e7e074c9a0dd317.png

创建 GitHub 仓库。图片由作者提供。

然后,我们克隆仓库,添加我们的代码,并将其推回 GitHub。以下是添加代码后的目录结构。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/1d1f84a07779caac6355dda6a1cbcb7c.png

GitHub 仓库目录结构。图片由作者提供。

有了代码,我们可以创建一个新的 Google Cloud Platform 项目。我们通过访问GCP 控制台,点击我们的项目列表,并选择“新建项目”来完成这个操作。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/1b7ce4adf1fad08036da5a88e0960e96.png

创建新的 GCP 项目。图片由作者提供。

一旦我们的项目创建完成,我们可以打开它,并在搜索栏中输入 “cloud run”。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/f38698f12d7bbb37d57fd1231fdb2b7f.png

搜索 Cloud Run 服务。图片由作者提供。

当它打开后,我们将点击“创建服务”。这将为我们打开一个页面,以便我们配置我们的服务。

首先,我们选择从 GitHub 部署服务的选项。然后点击“使用 CLOUD BUILD 设置”。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/8039b9cf078d63881169040bf0fe342d.png

从 GitHub 设置 Cloud Run 服务的持续部署。图片由作者提供。

对于存储库源,我们将选择 GitHub 并选择我们刚刚创建的仓库。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/65a38be6d56233b603987c88b9e8776c.png

选择我们的存储库源。图片由作者提供。

我们将分支保留为*^main$*,并选择“构建类型”为 Dockerfile。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/cfd37f08d9c6a66195962428673bd2c2.png

接下来,我们返回到服务配置屏幕。我们可以将服务命名为我们喜欢的名称(我保留为自动生成的名称)。对于区域,我保留为us-central1,因为它是一级区域,提供最便宜的计算选项(在这个例子中基本上是免费的)。

为了保持简单,我将“允许未认证调用”。当然,你将希望大多数系统需要认证。然后,将其他所有内容保留为默认设置。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/09ef7a39e685c480d31a851febb0d931.png

配置服务。图片由作者提供。

最后,在“容器、卷、网络、安全”下,将容器内存设置为 1 GiB。我们必须将端口保留为 8080,因为我们已经在 Dockerfile 中进行了配置。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/77a636ae02b64fc96f89484ab2fbb7ff.png

编辑容器。图片由作者提供。

我们可以将其他所有内容保留为默认设置,并在屏幕底部点击“创建”。几分钟后,容器将上线!

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/a616974a114d85e5b9801bfe9effbee7.png

在 Cloud Run 上运行的服务。图片由作者提供。

然后,我们可以使用页面顶部指定的 URL 访问 API。点击链接将打开根端点,即我们的健康检查。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/3a1948fc6c00a8ae730acf8de623c70e.png

健康检查端点。图片由作者提供。

我们可以使用以下 URL 手动运行到搜索 API 的 GET 请求:[你的应用 URL]/search?query=LLMs。这将搜索与 LLMs 相关的视频。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/3f06fa59dbf676e9292246b1aeaa8877.png

向搜索 API 端点发送 GET 请求。图片由作者提供。

附加内容:集成到 UI 中

一旦我们的 API 后端设置完成,我们可以将其连接到一个用户友好的界面。我通过Hugging Face Spaces来实现,它完全免费托管 ML 应用。

下面是同样的搜索通过 UI 界面看起来是什么样子。您可以在这里尝试这个 UI 界面,并在这里查看代码。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/a32bdf4c35494439f290c4feb5dfa10b.png

HF Spaces 上的搜索 UI。Gif 由作者提供。

结论

数据科学不仅仅是训练复杂的模型。它关乎解决问题和创造价值。通常,这需要我们将模型部署到它们能产生最大影响的环境中。在这里,我们介绍了一个简单的 3 步策略,用于使用 FastAPI、Docker 和 GCP 部署 ML 模型。

虽然这标志着全栈数据科学系列的结束,但这些文章配有YouTube 播放列表,其中包括一个额外视频,介绍了创建此搜索工具所涉及的实验。

更多关于全栈数据科学的信息 👇

全栈数据科学


资源

联系: 我的网站 | 预约通话

社交平台: YouTube 🎥 | LinkedIn | Twitter

支持: 买我一杯咖啡 ☕️

免费获取我每篇新故事的访问权限

更多推荐