将 Imagen 部署到 Cloud Run

1. 关于此 Codelab

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

作者: Laurie White

图片生成

说实话,大语言模型 (LLM) 生成图片可能很有趣。 当然,根据提示生成图片有很多商业应用,从自定义广告到引人注目的演示文稿,应有尽有。( Google Cloud 网站 上有很多公司使用 Creative Agents 的具体用例。)不过,当您要求生成“田野里快乐的绿色狗狗”的图片时,看到结果可能会非常有趣。

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

构建内容

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

学习内容

在本实验中,您将学习:

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

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

所需条件

此 Codelab 的完整代码可在 https://github.com/Annie29/imagen-deployment 中找到。

2. 启用 API

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

在开始使用 Imagen 之前,您需要启用一些 API。

  1. 转到 Google Cloud 控制台。
  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 如何从笔记本创建图片有了一个大致的了解。 您可以随时完成此笔记本,详细了解图片参数。

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

我们将使用 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 实践 的提示引起的。像“kittens playing with colorful balls”这样简单的提示也可能会导致此问题。 (但请不要担心,您 可以 获取“kittens playing with colorful toys”的图片。)

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

首先,我们在 home.html 模板中的第一个表单后添加一个 div,如果出现错误,该 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 功能。 有许多功能可以保护人物和儿童的生成,并对图片进行常规过滤。 您可以在此处详细了解这些功能 here

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 项目后,系统即会停止对该项目中使用的所有资源计费。

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

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…

深入阅读