Fastapi学习笔记之使用 Amazon ec2 部署图像检测模型-2

前言

接着上一篇博客,这一节开始介绍使用 Docker 部署 FastAPI,以及使用带 UI 的端到端应用程序。

1、使用 Docker 部署 FastAPI

到目前为止,我们已经创建了一个 AWS 实例,我们还创建了一个 FastAPI,它将图像的 base64 字符串表示形式作为输入并返回边界框和关联的类。 但是所有的 FastAPI 代码仍然驻留在我们的本地机器上。 我们如何将它放在ec2服务器上? 并在云端运行预测。

(1)安装docker

我们将按照 fastAPI 创建者本人的建议,使用 docker 部署我们的应用程序。 我将尝试解释 docker 是如何工作的。 下面的部分可能看起来令人生畏,但它只是一系列命令和步骤。 所以留在我身边。

我们可以从安装 docker 开始,使用:

sudo apt-get update
sudo apt install docker.io

然后我们使用以下命令启动 docker 服务:

sudo service docker start

(2)为 docker 创建文件夹结构

└── dockerfastapi
    ├── Dockerfile
    ├── app
    │   └── main.py
    └── requirements.txt

这里 dockerfastapi 是我们项目的主文件夹。 这是此文件夹中的不同文件:

  • requirements.txt:Docker 需要一个文件,告诉它运行我们的应用程序需要哪些库。 在这里,我列出了我在 Fastapi API 中使用的所有库。

numpy
opencv-python
matplotlib
torchvision
torch
fastapi
pydantic
  • Dockerfile:第二个文件是Dockerfile。

FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7

COPY ./app /app
COPY requirements.txt .
RUN pip --no-cache-dir install -r requirements.txt

dockerfile 可以被认为是类似于 sh 文件的东西,它包含创建可以在容器中运行的 docker 镜像的命令。 可以将 docker 镜像视为安装了 Python 和 Python 库等所有内容的环境。 容器是一个单元,它只是我们系统中使用 dockerimage 的一个孤立的盒子。 使用docker的好处是我们可以创建多个docker镜像,在多个容器中使用。 例如,一个图像可能包含 python36,而另一个图像可能包含 python37。 我们可以在单个 Linux 服务器中生成多个容器。

我们的 Dockerfile 包含一些东西:

FROM 命令:第一行 FROM 指定我们从 tiangolo(FastAPI 创建者)的 Docker 镜像开始。 根据他的网站:“这个图像包含一个“自动调整”机制,这样你就可以添加你的代码并自动获得相同的高性能。 而且没有做出牺牲”。 我们正在做的只是从一个为我们安装 python3.7 的映像开始,同时为 uvicorn 和 gunicorn ASGI 服务器添加了一些配置,并为 ASGI 服务器自动添加了一个 start.sh 文件。 对于喜欢冒险的人来说,特别是命令集 1 和命令集 2 通过一种菊花链命令来执行。

COPY 命令:我们也可以将 docker 镜像视为包含文件等的文件夹。 在这里,我们将之前创建的应用程序文件夹和 requirements.txt 文件复制到我们的 docker 镜像中。

运行命令:我们运行 pip install 命令来使用现在位于 docker 映像上的 requirements.txt 文件安装我们所有的 python 依赖项。

  • main.py:此文件包含我们之前创建的 fastapiapp.py 代码。 请记住仅保留文件 main.py 的名称。

(3)Docker 创建

我们已经获得了所需结构中的所有文件,但我们还没有使用任何 docker 命令。 我们首先需要使用 Dockerfile 构建一个包含所有依赖项的镜像。

我们可以简单地通过以下方式做到这一点:

sudo docker build -t myimage .

这会从 tiangolo 的镜像下载、复制和安装一些文件和库,并创建一个名为 myimage 的镜像。 这个 myimage 有 python37 和一些由 requirements.txt 文件指定的 python 包。

然后我们只需要启动一个运行这个图像的容器。 我们可以使用:

sudo docker run -d --name mycontainer -p 80:80 myimage

这将创建一个名为 mycontainer 的容器,它运行我们的 docker 镜像 myimage。 80:80 部分将我们的 docker 容器端口 80 连接到我们的 Linux 机器端口 80。

实际上就是这样。 此时,您应该能够在浏览器中打开以下 URL。

# <IPV4 public IP>/docs
URL: 18.237.28.174/docs

我们可以使用以下方式以编程方式检查我们的应用程序:

payload = json.dumps({
  "base64str": base64str,
  "threshold": 0.5
})

response = requests.put("[http://18.237.28.174/predict](http://18.237.28.174/predict)",data = payload)
data_dict = response.json()
print(data_dict)

(4)可能出现的问题

以上所有内容都很好,如果您按照确切的说明进行操作,就会开箱即用,但现实世界并非如此。 您肯定会在此过程中遇到一些错误,并且需要调试您的代码。 因此,为了帮助您解决这个问题,一些 docker 命令可能会派上用场:

  • 当我们使用 sudo docker run 运行我们的容器时,我们没有得到很多信息,这在调试时是个大问题。 您可以使用以下命令查看实时日志。 如果您在此处看到错误,则需要更改代码并重新构建映像。

sudo docker logs -f mycontainer
  • 启动和停止 Docker:有时,重新启动 Docker 可能会有所帮助。 在这种情况下,您可以使用:

sudo service docker stop
sudo service docker start
  • 列出图像和容器:使用 docker,您最终会创建图像和容器,但您将无法在工作目录中看到它们。 您可以使用以下方式列出您的图像和容器:

sudo docker container ls
sudo docker image ls
  • 删除未使用的 docker 镜像或容器:您可能需要删除一些镜像或容器,因为它们会占用大量系统空间。

 # the prune command removes the unused containers and images
    sudo docker system prune

    # delete a particular container
    sudo docker rm mycontainer

    # remove myimage
    sudo docker image rm myimage

    # remove all images
    sudo docker image prune — all
  • **Checking localhost:**Linux服务器没有浏览器,但我们仍然可以看到浏览器输出,虽然有点难看:

 curl localhost
  • 无需一次又一次地重新加载图像即可开发:对于开发而言,能够仅更改我们机器上的代码内容并实时测试它是很有用的,而不必每次都构建图像。 在这种情况下,在每次代码更改时自动运行带有实时自动重新加载的服务器也很有用。 在这里,我们使用 Linux 机器上的应用程序目录,并在开发过程中将默认目录 (/start.sh) 替换为开发备选目录 /start-reload.sh。 一切正常后,我们可以再次构建我们的镜像,在容器中运行它。

sudo docker run -d -p 80:80 -v $(pwd):/app myimage /start-reload.sh

2、带 UI 的端到端应用程序

我们在这里完成了 API 创建,但我们还可以使用我们的 FastAPI API 使用 Streamlit 创建基于 UI 的应用程序。 这不是您在生产环境中的做法(您可能让开发人员使用 React、node.js 或 javascript 制作应用程序),但主要是检查如何使用图像 API 的端到端流程。 我将在本地而不是 ec2 服务器上托管这个准系统 Streamlit 应用程序,它将从 ec2 上托管的 FastAPI API 获取边界框信息和类。

如果您需要了解更多关于 streamlit 的工作原理,您可以查看这篇文章。 此外,如果您还想将此 streamlit 应用程序部署到 ec2,这里又是一个教程。

这是 ec2 上带有 UI 和 FastAPI API 的整个应用程序的流程:

我们需要在 streamlit 应用程序中解决的最重要的问题是:

如何使用 Streamlit 从用户那里获取图像文件?

(1)使用文件上传器:我们可以使用文件上传器:

bytesObj = st.file_uploader(“Choose an image file”)

下一个问题是,我们从 streamlit 文件上传器得到的这个 bytesObj 是什么? 在 streamlit 中,我们将从 file_uploader 获取一个 bytesIO 对象,我们需要将其转换为 base64str 以用于我们的 FastAPI 应用程序输入。 这可以使用以下方法完成:

def bytesioObj_to_base64str(bytesObj):
   return base64.b64encode(bytesObj.read()).decode("utf-8")

base64str = bytesioObj_to_base64str(bytesObj)

(2)使用 URL:我们还可以使用 text_input 从用户那里获取图像 URL。

url = st.text_input(‘Enter URL’)

然后我们可以使用请求模块和 base64 编码和 utf-8 解码从 URL 以 base64 字符串格式获取图像:

def ImgURL_to_base64str(url):
    return base64.b64encode(requests.get(url).content).decode("utf-8")

base64str = ImgURL_to_base64str(url)

这是我们 Streamlit 应用程序的完整代码。 您已经看到了这篇文章中的大部分代码。

import streamlit as st
import base64
import io
import requests,json
from PIL import Image
import cv2
import numpy as np
import matplotlib.pyplot as plt
import requests
import random

# use file uploader object to recieve image
# Remember that this bytes object can be used only once
def bytesioObj_to_base64str(bytesObj):
    return base64.b64encode(bytesObj.read()).decode("utf-8")

# Image conversion functions

def base64str_to_PILImage(base64str):
    base64_img_bytes = base64str.encode('utf-8')
    base64bytes = base64.b64decode(base64_img_bytes)
    bytesObj = io.BytesIO(base64bytes)
    img = Image.open(bytesObj)
    return img

def PILImage_to_cv2(img):
    return np.asarray(img)

def ImgURL_to_base64str(url):
    return base64.b64encode(requests.get(url).content).decode("utf-8")

def drawboundingbox(img, boxes,pred_cls, rect_th=2, text_size=1, text_th=2):
    img = PILImage_to_cv2(img)
    class_color_dict = {}

    #initialize some random colors for each class for better looking bounding boxes
    for cat in pred_cls:
        class_color_dict[cat] = [random.randint(0, 255) for _ in range(3)]

    for i in range(len(boxes)):
        cv2.rectangle(img, (int(boxes[i][0][0]), int(boxes[i][0][1])),
                      (int(boxes[i][1][0]),int(boxes[i][1][1])),
                      color=class_color_dict[pred_cls[i]], thickness=rect_th)
        cv2.putText(img,pred_cls[i], (int(boxes[i][0][0]), int(boxes[i][0][1])),  cv2.FONT_HERSHEY_SIMPLEX, text_size, class_color_dict[pred_cls[i]],thickness=text_th)
    plt.figure(figsize=(20,30))
    plt.imshow(img)
    plt.xticks([])
    plt.yticks([])
    plt.show()

st.markdown("<h1>Our Object Detector App using FastAPI</h1><br>", unsafe_allow_html=True)

bytesObj = st.file_uploader("Choose an image file")

st.markdown("<center><h2>or</h2></center>", unsafe_allow_html=True)

url = st.text_input('Enter URL')

if bytesObj or url:
    # In streamlit we will get a bytesIO object from the file_uploader
    # and we convert it to base64str for our FastAPI
    if bytesObj:
        base64str = bytesioObj_to_base64str(bytesObj)

    elif url:
        base64str = ImgURL_to_base64str(url)

    # We will also create the image in PIL Image format using this base64 str
    # Will use this image to show in matplotlib in streamlit
    img = base64str_to_PILImage(base64str)

    # Run FastAPI
    payload = json.dumps({
      "base64str": base64str,
      "threshold": 0.5
    })

    response = requests.put("http://18.237.28.174/predict",data = payload)
    data_dict = response.json()


    st.markdown("<center><h1>App Result</h1></center>", unsafe_allow_html=True)
    drawboundingbox(img, data_dict['boxes'], data_dict['classes'])
    st.pyplot()
    st.markdown("<center><h1>FastAPI Response</h1></center><br>", unsafe_allow_html=True)
    st.write(data_dict)

我们可以使用以下方法在本地运行这个 streamlit 应用程序:

streamlit run streamlitapp.py

我们可以看到我们的应用程序在 localhost:8501 上运行。 适用于用户上传的图像以及基于 URL 的图像。 这也是一些猫爱好者的猫图片。

3、总结

我们在这里创建了一个完整的工作流程,以通过 ec2 上的 FastAPI 部署图像检测模型,并在 Streamlit 中利用这些结果。 我希望这可以帮助您解决在生产中部署模型的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倾城一少

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值