將 Imagen 部署至 Cloud Run

1. 關於本程式碼研究室

上次更新時間:2024 年 10 月 11 日

撰文:Laurie White

圖像生成

說實話,大型語言模型 (LLM) 的圖像生成可能很有趣,當然,從量身打造的廣告到引人入勝的簡報,有許多業務應用程式可透過提示生成圖片。(Google Cloud 網站上列出許多使用 Creative Agents 的公司案例)。不過,要求「在田野中快樂的綠色狗狗」的圖片,看看會得到什麼結果,還是相當有趣。

無論您是基於專業或娛樂目的 (或兩者皆是) 對圖像生成感興趣,使用圖像生成程式與將程式部署至網頁應用程式之間,都存在一些挑戰。本實驗室將協助您克服這些挑戰。

建構項目

在本程式碼研究室中,您將建構一個應用程式,該應用程式會接收文字提示,並傳回網頁,其中包含使用該提示生成的圖片。

課程內容

本實驗室的學習內容包括:

  • 如何在筆記本環境中使用 Google Imagen,根據文字提示詞建立圖片
  • 將 Imagen 程式碼從筆記本移至網頁應用程式的困難之處
  • 如何部署使用 Imagen 生成圖片的 Cloud Run 應用程式
  • 如何在 HTML 中加入 Imagen 圖片

本程式碼研究室著重於 Imagen 和部署作業。我們不會對與主題無關的概念和程式碼多做介紹,但會事先準備好這些程式碼區塊,屆時您只要複製及貼上即可。

軟硬體需求

本程式碼實驗室的完整程式碼位於 https://github.com/Annie29/imagen-deployment

2. 啟用 API

選取要用於本程式碼研究室的專案。建議您建立新專案,方便在完成後移除所有工作。

開始使用 Imagen 前,請先啟用部分 API。

  1. 前往 Google Cloud 控制台。
  2. 前往 Vertex AI 資訊主頁。
  3. 選取「啟用所有建議的 API」

a8f336f7380a9eab.png

3. 探索 Google Imagen (選用)

如果您熟悉 Imagen,可以略過本節。

嘗試建立使用 Imagen 的網頁應用程式前,不妨先瞭解 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. 開始建構網頁應用程式,顯示圖片

我們將在 Cloud Run 上使用 Flask 框架的 Python 建構應用程式。

Python Flask 應用程式會設定在資料夾中,如下所示:

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

範本是含有 HTML 的檔案,通常會有名稱的預留位置,程式會在這些位置插入產生的文字。main.py 是網路伺服器應用程式本身,而 requirements.txt 則是 main.py 使用的所有非標準程式庫清單。

應用程式會有兩個頁面,第一個頁面會取得提示,第二個頁面則會顯示圖片,並允許使用者輸入其他提示。

請先建立專案架構。

建立檔案結構

本程式碼研究室假設專案位於 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 檔案包含用於處理網頁要求的程式碼。我們只需要處理兩項要求:首頁的 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_imagereturn 陳述式前新增一行,為 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]

視傳送至 generate_images 的參數而定,一次最多可建立 4 張圖片,因此即使只傳回一張圖片 (如本例),傳回值仍會是 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

請確認您位於含有 main.py 的資料夾中,然後執行下列指令,嘗試從 Cloud Shell 執行程式:

python main.py

然後使用畫面右上方的「透過通訊埠 8080 預覽」預覽。

a80b4abd28cb7eed.png

7. 常見錯誤

您可能會發現,在執行程式時 (測試期間或部署後),會收到類似下列的訊息:

2366c3bba6273517.png

這很可能是因為提示違反 Google 負責任的 AI 做法。即使是「小貓玩彩色球」這類簡單的提示詞,也可能導致這個問題。(但別擔心,你可以取得「小貓玩彩色玩具」的圖片。)

為處理這項錯誤,我們將新增程式碼,以便擷取嘗試生成圖片時引發的例外狀況。如果有的話,我們會再次算繪 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 的負責任 AI 技術不只如此。我們提供多項功能,可保護人物和兒童的生成內容,並對圖片套用一般篩選器。如要進一步瞭解這些功能,請參閱這篇文章

8. 將應用程式部署至網頁

您可以使用 Cloud Shell 中 imageapp 資料夾的指令,將應用程式部署至網路。請務必在指令中使用實際的專案 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. 恭喜

恭喜!您已成功建構網頁應用程式,可顯示 Imagen 建立的圖片。如何在應用程式中使用這項功能?

後續步驟

查看一些程式碼研究室…

其他資訊