将 Imagen 部署到 Cloud Run

1. 关于此 Codelab

上次更新时间:2024 年 10 月 11 日

撰写者:Laurie White

图片生成

说实话,使用大语言模型 (LLM) 生成图片很有趣。当然,根据提示生成图片有很多商业应用,从定制广告到富有吸引力的演示文稿,不一而足。(Google Cloud 网站列出了使用广告客户经理的公司的一些具体用例。)不过,查看系统在您搜索“田野中快乐的绿色狗狗”时返回的结果还是很有趣的。

无论您是出于专业还是休闲(或两者兼有)的原因而对图片生成感兴趣,在使用图片生成程序和将其部署到 Web 应用之间都会遇到一些挑战。本实验将帮助您克服这些挑战。

构建内容

在此 Codelab 中,您将构建一个应用,该应用会接受文本提示,并返回一个网页,其中包含使用该提示生成的图片。

学习内容

在本实验中,您将学习:

  • 如何在笔记本环境中使用 Google Imagen 根据文本提示创建图片
  • 将 Imagen 代码从记事本迁移到 Web 应用时遇到的困难
  • 如何部署使用 Imagen 生成图片的 Cloud Run 应用
  • 如何在 HTML 中添加 Imagen 中的图片

此 Codelab 重点介绍 Imagen 和部署。对于不相关的概念,我们仅会略作介绍,但是会提供相应代码块供您复制和粘贴。

所需条件

如需获取此 Codelab 的完整代码,请访问 https://github.com/Annie29/imagen-deployment

2. 启用 API

选择要用于此 Codelab 的项目。建议您创建一个新项目,以便在完成后更轻松地移除所有工作。

您需要先启用一些 API,然后才能开始使用 Imagen。

  1. 转到 Google Cloud Console。
  2. 前往 Vertex AI 信息中心。
  3. 选择“启用所有推荐的 API”

a8f336f7380a9eab.png

3. 探索 Google Imagen(可选)

如果您熟悉 Imagen,可以跳过此部分。

在尝试创建使用 Imagen 的 Web 应用之前,了解 Imagen 的功能非常有用。幸运的是,有许多笔记本可以运行简单的 Imagen 代码,因此我们先从其中一个笔记本开始。

  1. 前往 https://github.com/GoogleCloudPlatform/generative-ai/blob/main/vision/getting-started/image_generation.ipynb 中的记事本。
  2. 选择“在 Colab 中打开”以在 Google 的笔记本服务器中打开笔记本。
  3. 依次选择“文件”-“在云端硬盘中保存副本”,或点击页面顶部的“复制到云端硬盘”,以创建此记事本的副本。
  4. 关闭原始副本(以免在错误的副本中进行操作!)。
  5. 您需要点击右上角的“连接”按钮,才能连接到运行时。2afdc8fa660a89bd.png
  6. 开始处理笔记本中的每个单元。
  7. 如需运行单元,您可以点击单元左侧的方括号 [] 或箭头,也可以使用“运行时”菜单中的“运行所选内容”选项(或其快捷键):dfec032ef6c31296.png
  8. 重启当前运行时后,您会收到系统崩溃的消息。别担心。这很正常。
  9. 您需要对笔记本环境进行身份验证。
  10. 您可以在代码右侧的框中输入项目 ID(而非名称)和位置(如果您尚未设置位置,则可以使用 us-central1),让 Colab 为您将这些信息插入代码中。
  11. 当您看到“生成图片”时,便有机会了解 Imagen 的功能。您可以随意更改问题并重新运行该单元格,看看能获得哪些不同类型的图片。
  12. 至此,您应该已经大致了解了 Imagen 如何根据笔记本创建图片。您可以随时完成此 Notebook,详细了解图片参数。

4. 开始构建用于显示图片的 Web 应用

我们将使用 Python 在 Cloud Run 上使用 Flask 框架构建应用。

Python Flask 应用在文件夹中设置如下:

app-folder
    templates
        template.html
        (etc.)
        anothertemplate.html
    main.py
    requirements.txt

模板是包含 HTML 的文件,通常包含命名占位符,程序会在这些占位符中插入生成的文本。main.py 是 Web 服务器应用本身,而 requirements.txtmain.py 使用的所有非标准库的列表。

该应用将包含两个页面:第一个页面用于获取提示,第二个页面用于显示图片并允许用户输入其他提示。

首先创建项目框架。

创建文件结构

此 Codelab 假定您的项目位于 imageapp 文件夹中。如果您使用其他名称,请务必相应地更新命令。

选择屏幕右上角的提示图标,进入 Cloud Shell。

28135f700c5b12b0.png

您可以使用 shell 窗口顶部的箭头将 shell 移至新标签页,从而获得更多工作空间:

310422ac131813e1.png

在 Cloud Shell 的主目录中,创建 imageapp 文件夹,切换到该文件夹,然后创建 templates 文件夹。您可以通过命令行或 Cloud Shell 编辑器执行此操作。

创建模板

该应用将包含两个页面:第一个页面(我们将其称为 home.html)用于获取提示,第二个页面(我们将其称为 display.html)用于显示图片并允许用户输入其他提示。

使用 Cloud Shell 编辑器或您选择的 Linux 编辑器,创建两个模板。在 imageapp/templates 文件夹中,创建用户将看到的初始页面 home.html。它使用变量 prompt 返回用户输入的说明。

templates/home.html

<!DOCTYPE html>
<html>
   <head>
       <title>Let's draw a picture</title>
   </head>
   <body>
       <h1>Let's draw a picture</h1>
       <form  action="/" method="post" >
           <input type="text" id="prompt" name="prompt">
           <input type="submit" value="Send">
       </form>
   </body>
</html>

然后,创建 display.html,用于显示图片。请注意,图片的位置将位于 image_url 中。

templates/display.html

<!DOCTYPE html>
<html>
   <head>
       <title>Let's draw a picture</title>
   </head>
   <body>
       <h1>Let's draw a picture</h1>

       <div>
           <form  action="/" method="post" >
               <input type="text" id="prompt" name="prompt">
               <input type="submit" value="Send">
           </form>

           <p></p>
       </div>

       <div id="picture">
           <img id="pict" name="pict" alt="The created image" src="{{image_uri}}" style="width:100%;">
       </div>

   </body>
</html>

5. 启动代码

您需要创建文件 requirements.txt,以确保您的程序需要的所有库都已可用。目前,只需在 requirements.txt 文件中添加 flask 即可。

main.py 文件包含用于处理 Web 请求的代码。我们只需处理两项请求:用于获取首页的 GET 请求,以及用于提交描述我们要生成的图片的表单的 POST 请求。

使用 Cloud Shell 编辑器或您选择的 Linux 编辑器,在 imageapp 文件夹中创建 main.py 文件。我们将从以下框架开始:

main.py

import flask

app = flask.Flask(__name__)

@app.route("/", methods=["GET"])
def home_page():
    return flask.render_template("home.html")

@app.route("/", methods=["POST"])
def display_image():
    # Code to get the prompt (called prompt) from the submitted form
    # Code to generate the image
    # Code to create a URL for the image (called image_url)

    return flask.render_template("display.html", prompt=prompt, image_url=image_url)

# Initialize the web server app when the code locally (Cloud Run handles it in that environment)
if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=8080)

实际上,这几乎就是整个应用。display_image 中有三个注释需要使用 Python 代码来补充,就这样了。

我们来填补缺失的部分。Flask 可让您轻松检索提示。在注释后面添加一行,如下所示:

# Code to get the prompt (called prompt) from the submitted form
prompt = flask.request.form["prompt"]

如果您想立即测试应用,可以在 display_image 中的 return 语句前添加一行代码,为 image_url(指向图片的有效网址)赋值。

例如:image_url="<your url here>"

您可以通过 Cloud Shell 在本地运行该程序(使用命令 python main.py),并使用屏幕右上角的“在端口 8080 上预览”功能预览该程序。

a80b4abd28cb7eed.png

目前,您始终会在您提供的网址中看到图片。接下来,我们来看看如何从应用中获取该值。请务必移除用于为 image_url 提供静态值的行。

6. 创建映像

Google Cloud 提供了一个适用于 Vertex AI 上的生成式 AI 的 Python API。如需使用它,我们必须在程序顶部附近添加一行代码来导入它,并将其与其他导入内容放在一起:

from vertexai.vision_models import ImageGenerationModel

并在 requirements.txt 文件中添加 vertexai

ImageGenerationModel 的文档介绍了如何使用该模型。我们将创建一个模型,然后根据提示从中生成图片。在 main.py 中添加第二步的代码,用于创建图片并将其存储在 response 中:

# Code to generate the image
model = ImageGenerationModel.from_pretrained("imagegeneration@006")
response = model.generate_images(prompt=prompt)[0]

一次最多可以创建 4 张图片,具体取决于发送到 generate_images 的参数,因此返回的值将是 GeneratedImage 的列表,即使只返回一张图片(如本例所示)也是如此。

现在,我们需要在 WWW 页面上显示该图片。GeneratedImage 确实有一种用于 show 图片的方法,但它仅适用于笔记本环境。不过,您可以通过一种方法保存图片。我们会保存图片,并在渲染模板时发送已保存图片的网址。

这有点棘手,而且有很多方法可以实现。我们来逐步了解其中一种更简单的方法。(如果您更喜欢通过直观的方式学习,请参阅下方显示的步骤图片。)

首先,我们需要保存图片。但它将被命名为什么?使用静态名称可能会出现问题,因为许多人可以同时使用该程序。虽然我们可以为每位用户创建单独的图片名称(使用 UUID 之类的名称),但更简单的方法是使用 Python 的 tempfile 库,该库会创建一个具有唯一名称的临时文件。以下代码将创建一个临时文件,获取其名称,并将图片生成步骤的响应写入临时文件。我们暂时不会在代码中输入它,因为我们需要先获取网址。

with tempfile.NamedTemporaryFile("wb") as f:
    filename = f.name
    response.save(filename, include_generation_parameters=False)
    # process the saved file here, before it goes away

您可以通过多种方式处理已保存的文件,但最简单、最安全的方法之一是使用数据网址

借助数据网址,您可以在网址中发送实际数据,而不仅仅是数据的路径。数据网址的语法如下:

data:[image/png][;base64],<data>

如需获取图片的 base64 编码,我们需要打开由 tempfile 保存的文件,并将其读取到变量中。是的,这将是一个大型字符串,但对于现代浏览器和服务器来说应该没问题。然后,我们将使用 base64 库将其编码为字符串,以便在数据网址中发送。

用于执行第三步(创建网址)的最终代码将如下所示:

# Code to create a URL for the image (called image_url)
with tempfile.NamedTemporaryFile("wb") as f:
    filename = f.name
    response.save(filename, include_generation_parameters=False)
    # process the saved file here, before it goes away
    with open(filename, "rb") as image_file:
        binary_image = image_file.read()
        base64_image = base64.b64encode(binary_image).decode("utf-8")
        image_url = f"data:image/png;base64,{base64_image}"

您可以在下图中看到所有这些步骤:

268876579dc02376.png

您需要在程序开头导入 tempfile 和 base64。

import tempfile
import base64

请尝试从 Cloud Shell 运行您的程序,方法是确保您位于包含 main.py 的文件夹中,然后运行以下命令:

python main.py

然后,您可以使用屏幕右上角的“在端口 8080 上预览”功能进行预览。

a80b4abd28cb7eed.png

7. 常见错误

在某些时候,您可能会注意到,在运行程序(测试期间或部署后)时,您会收到类似以下内容的消息:

2366c3bba6273517.png

这很可能是由违反 Google 的 Responsible AI 实践的提示导致的。就算是简单的“小猫玩彩色球”这样的提示,也可能会导致此问题。(不过别担心,您可以获得“小猫玩彩色玩具”的图片。)

为了解决此错误,我们将添加代码来捕获尝试生成图片时引发的异常。如果存在,我们会再次渲染 home.html 模板,并显示一条消息。

首先,在 home.html 模板中,在第一个表单后面添加一个 div,该表单在出现错误时会显示:

<!DOCTYPE html>
<html>
   <head>
       <title>Let's draw a picture</title>
   </head>
   <body>
       <h1>Let's draw a picture</h1>
       <form  action="/" method="post" >
           <input type="text" id="prompt" name="prompt">
           <input type="submit" value="Send">
       </form>
       {% if mistake %}
       <div id="warning">
       The prompt contains sensitive words that violate
       <a href=\"https://ai.google/responsibility/responsible-ai-practices\">
           Google's Responsible AI practices</a>.
       Try rephrasing the prompt."</div>

       {% endif %}

   </body>
</html>

然后,在 main.py 中添加代码,以便在 display_image 中调用 generate_images 代码时捕获可能的异常。如果存在异常,代码将渲染包含消息的 home.html 模板。

# Code to generate the image
   model = ImageGenerationModel.from_pretrained("imagegeneration@006")
   try:
       response = model.generate_images(prompt=prompt)[0]   
   except:
       #  This is probably due to a questionable prompt
       return flask.render_template("home.html", warning=True)

这并不是 Imagen 的唯一 Responsible AI 功能。我们提供了多项功能来保护生成的人脸和儿童,以及图片的常规滤镜。您可以点击此处详细了解这些功能。

8. 将应用部署到 Web

您可以使用 Cloud Shell 中 imageapp 文件夹中的命令将应用部署到 Web 上。请务必在命令中使用您的实际项目 ID。

gcloud run deploy imageapp \
  --source . \
  --region us-central1 \
  --allow-unauthenticated \
  --project your-project-id

您应该会看到如下所示的响应,告知您在哪里可以找到申请:

Service [imageapp] revision [imageapp-00001-t48] has been deployed and is serving 100 percent of traffic.
Service URL: https://imageapp-708208532564.us-central1.run.app```

9. 清理

虽然 Cloud Run 不会对未在使用中的服务计费,但您可能仍然需要为将容器映像存储在 Artifact Registry 中而产生的相关费用付费。为避免产生费用,您可以删除代码库或删除 Cloud 项目。删除 Cloud 项目后,系统即会停止对该项目中使用的所有资源计费。

如需删除容器映像代码库,请执行以下操作

gcloud artifacts repositories delete cloud-run-source-deploy \
  --location $REGION

如需删除 Cloud Run 服务,请执行以下操作

gcloud run services delete imageapp \
  --platform managed \
  --region $REGION

如需删除 Google Cloud 项目,请执行以下操作

  1. 检索当前项目 ID:
PROJECT_ID=$(gcloud config get-value core/project)
  1. 确保这是您要删除的项目:
echo $PROJECT_ID
  1. 删除项目:
gcloud projects delete $PROJECT_ID

10. 恭喜

恭喜,您已成功构建一个 Web 应用,该应用将显示由 Imagen 创建的图片。如何在应用中使用此功能?

后续操作

查看下列 Codelab…

深入阅读