Closed6

Vertex AI で Gemini 1.5を使う 4: リテール向けのマルチモーダルユースケース

kun432kun432

以下の続き

https://zenn.dev/kun432/scraps/b123dbb69dbbd3

マルチモーダルのリテール向けレコメンデーションでの Gemini の使用

以下のnotebookを進める。前回同様、ここまでやってきたようなことをリテール向けのユースケースでいろいろやってみるというもの。

事前準備

セットアップ回りは前回の記事を参考に、ということで詳細は割愛。

パッケージインストール

!pip install --upgrade --user --quiet google-cloud-aiplatform

ColaboratoryからGCPを使えるように認証を行う。

from google.colab import auth

auth.authenticate_user()

Vertex AIへの接続

import vertexai

PROJECT_ID="YOUR_PROJECT_ID"
REGION="asia-northeast1"

vertexai.init(
    project=PROJECT_ID,
    location=REGION
)

ライブラリをまとめてインポート

from vertexai.generative_models import GenerativeModel, Image

notebookの簡単のためのヘルパー関数を読み込み

import http.client
import io
import typing
import urllib.request

import IPython.display
from PIL import Image as PIL_Image
from PIL import ImageOps as PIL_ImageOps


def display_image(image: Image, max_width: int = 600, max_height: int = 350) -> None:
    pil_image = typing.cast(PIL_Image.Image, image._pil_image)
    if pil_image.mode != "RGB":
        # RGBAなどのモードは、まだすべてのJupyter環境でサポートされているわけではない
        pil_image = pil_image.convert("RGB")
    image_width, image_height = pil_image.size
    if max_width < image_width or max_height < image_height:
        # notebook上で画像を小さく表示するためにサイズを変更する
        pil_image = PIL_ImageOps.contain(pil_image, (max_width, max_height))
    display_image_compressed(pil_image)


def display_image_compressed(pil_image: PIL_Image.Image) -> None:
    image_io = io.BytesIO()
    pil_image.save(image_io, "jpeg", quality=80, optimize=True)
    image_bytes = image_io.getvalue()
    ipython_image = IPython.display.Image(image_bytes)
    IPython.display.display(ipython_image)


def get_image_bytes_from_url(image_url: str) -> bytes:
    with urllib.request.urlopen(image_url) as response:
        response = typing.cast(http.client.HTTPResponse, response)
        if response.headers["Content-Type"] not in ("image/png", "image/jpeg"):
            raise Exception("Image can only be in PNG or JPEG format")
        image_bytes = response.read()
    return image_bytes


def load_image_from_url(image_url: str) -> Image:
    image_bytes = get_image_bytes_from_url(image_url)
    return Image.from_bytes(image_bytes)


def print_multimodal_prompt(contents: list):
    """
    Geminiに送信されるコンテンツが与えられたら、
    読みやすくするために、完全なマルチモーダルプロンプトを出力する。
    """
    for content in contents:
        if isinstance(content, Image):
            display_image(content)
        else:
            print(content)

モデルの定義。元のnotebookでは、Gemini 1.0 Pro Visionとなっているが、今回はGemini 1.5 Proを使用する。

multimodal_model = GenerativeModel("gemini-1.5-pro")
kun432kun432

Geminiを使った画像の理解

画像

room_image_url = "https://storage.googleapis.com/github-repo/img/gemini/retail-recommendations/rooms/spacejoy-c0JoR_-2x3E-unsplash.jpg"

room_image = load_image_from_url(room_image_url)

prompt = "この部屋に見えるものと全体の雰囲気を説明して:"
contents = [
    prompt,
    room_image,
]

responses = multimodal_model.generate_content(contents, stream=True)

print("-------Prompt--------")
print_multimodal_prompt(contents)

print("\n-------Response--------")
for response in responses:
    print(response.text, end="")
この部屋は居心地が良く、ミニマルなボヘミアンスタイルのリビングルームです。

**家具:**

* **中央:** リビングスペースの中央には、木製の脚が付いたベージュ色の布張りのソファがあり、白とベージュのクッションがいくつか置かれています。
* **ソファの左側:** 大きな白いオットマンチェアが置かれ、ベージュのクッションが置かれています。
* **ソファの右側:** オットマンチェアの隣には、小さな丸いベージュのオットマンが置かれています。
* **ソファの前:** ソファの前には、木製の脚が付いた小さな丸いコーヒーテーブルが置かれ、本と装飾品が置かれています。
* **右側の角:** 右側の角には、円筒形のシェードが付いた背の高いフロアランプが置かれています。ランプの土台は細く、木目調です。
* **ランプの横:** フロアランプの横には、収納用の大きな籐製のバスケットが置かれています。

**装飾:**

* **壁:** 壁は白く、ミニマルなボヘミアンの雰囲気を演出しています。
* **鏡:** ソファの上の壁には、籐でできた異なるサイズの丸い鏡が 2 つあります。
* **植物:** 左側の角には、背の高い白い花瓶にドライフラワーの大きなアレンジメントが飾られています。
* **テキスタイル:** ソファーには、無地の白とベージュのクッションに加えて、質感のある模様のクッションが置かれています。毛足の長いベージュのブランケットがソファの上に無造作に置かれています。床には、ニュートラルカラーの大きなエリアラグが敷かれており、空間に温かさを加えています。

**雰囲気:**

部屋全体として、明るく風通しが良く、落ち着いたリラックスした雰囲気が漂っています。ニュートラルなカラーパレット、天然素材、ミニマルな装飾が組み合わさり、シンプルでスタイリッシュなリビングスペースを生み出しています。

事前知識に基づいて、オープンなリコメンドの生成

同じ画像を使う。

prompt1 = "この部屋に新しい家具のオススメをお願い:"
prompt2 = "その理由も詳しく説明してほしい"
contents = [prompt1, room_image, prompt2]

responses = multimodal_model.generate_content(contents, stream=True)

print("-------Prompt--------")
print_multimodal_prompt(contents)

print("\n-------Response--------")
for response in responses:
    print(response.text, end="")
このリビングに新しい家具をいくつかお勧めします。その理由も詳しく説明します。

1. **大きくて大胆な植物か木の鉢植え**
   - **理由:** この部屋は、すでに柔らかい色使いと自然な風合いを取り入れた、美しくニュートラルな基盤を持っています。大きくて存在感のある植物を追加すると、空間に彩りと活力が加わり、居心地の良さがさらに高まります。鉢植えの材質やスタイルは、部屋の既存の美意識を引き立てるものにすることができます。例えば、籐やセラミックの鉢植えは、有機的で素朴な雰囲気をさらに強調するでしょう。

2. **ソファの後ろの壁に飾るアート作品かギャラリーウォール**
   - **理由:** ソファの上の壁のスペースが少し寂しい感じがします。目を引くアート作品や、互いに調和のとれた複数の額装作品を飾ると、個性を表現し、空間により洗練された印象を与えることができます。部屋のカラーパレットに合うものを選んで、統一感を維持しましょう。例えば、抽象画や植物画は、既存の落ち着いた雰囲気を引き立てるのに最適です。

3. **収納を兼ねたスタイリッシュなオットマンかコーヒーテーブル**
   - **理由:** オットマンやコーヒーテーブルは、リビングに機能性とスタイルをプラスするのに最適です。収納スペース付きのものを選べば、毛布、雑誌、その他の身の回り品を収納できます。また、部屋の全体的なデザインテーマを補完するものにすることもできます。例えば、木製の脚とリネン張りのオットマンは、暖かさと洗練された雰囲気をプラスすることができます。

これらの追加アイテムは、リビングの全体的な美観を高め、より居心地の良い、パーソナルでスタイリッシュな空間にするのに役立ちます。
prompt1 = "この部屋について説明して:"
prompt2 = "ここに合うタイプの椅子を紹介して。"
contents = [prompt1, room_image, prompt2]

responses = multimodal_model.generate_content(contents, stream=True)

print("-------Prompt--------")
print_multimodal_prompt(contents)

print("\n-------Response--------")
for response in responses:
    print(response.text, end="")
この部屋には、すでに曲線的でオフホワイト色の印象的なアクセントチェアが置かれているので、いくつかオプションがあります。

1. **同様のスタイル:** 既存の椅子を引き立てたい場合は、同様の素材や形状の椅子を探しましょう。丸みを帯びたシルエットで、リネンやブークレなどの質感のあるオフホワイトまたはベージュの生地の椅子を選びましょう。これは、統一感と調和を保ちます。

2. **対照的な質感:** 興味深い対照を生み出すには、異なる質感の椅子を選んでみましょう。レザーや木製フレームなどの素材を、クリーンなラインとミニマルなデザインで組み合わせると、既存の家具に心地よい緊張感を与えることができます。部屋の軽やかな雰囲気を維持するために、黒または茶色の革のような明るいニュートラルカラーにこだわることを検討してください。

3. **アクセントカラー:** 部屋に色を加えたい場合は、大胆な色の椅子を選んでみましょう。マスタードイエロー、ダスティローズ、またはティールブルーなど、既存のニュートラルなパレットを引き立てる色を選びましょう。部屋のデザインと調和するように、シンプルで曲線の形状にこだわってください。

最終的に、最良のタイプの椅子は、あなたの個人的な好みと、部屋で達成したい雰囲気に依存します。
kun432kun432

与えられた画像を下にリコメンドを生成

先ほどの画像に加えて、以下の画像を追加。

furniture_image_urls = [
    "https://storage.googleapis.com/github-repo/img/gemini/retail-recommendations/furnitures/cesar-couto-OB2F6CsMva8-unsplash.jpg",
    "https://storage.googleapis.com/github-repo/img/gemini/retail-recommendations/furnitures/daniil-silantev-1P6AnKDw6S8-unsplash.jpg",
    "https://storage.googleapis.com/github-repo/img/gemini/retail-recommendations/furnitures/ruslan-bardash-4kTbAMRAHtQ-unsplash.jpg",
    "https://storage.googleapis.com/github-repo/img/gemini/retail-recommendations/furnitures/scopic-ltd-NLlWwR4d3qU-unsplash.jpg",
]

furniture_images = [load_image_from_url(url) for url in furniture_image_urls]

# 選択されたアイテムの中から1つを推薦するには、プロンプトの中でアイテム番号のラベルを付ける必要がある。
# そうすることで、質問を投げかける際に、モデルが各画像を参照できるようになる。
# プロンプト内の画像にラベルを付けることは、幻覚を減らし、全体的により良い結果を生み出すことにも役立つ。
contents = [
    "次の椅子について考えて:",
    "chair 1:",
    furniture_images[0],
    "chair 2:",
    furniture_images[1],
    "chair 3:",
    furniture_images[2],
    "chair 4:",
    furniture_images[3],
    "room:",
    room_image,
    "あなたはインテリアデザイナーです。それぞれの椅子について、部屋のスタイルにふさわしいかどうかを説明して:",
]

responses = multimodal_model.generate_content(contents, stream=True)

print("-------Prompt--------")
print_multimodal_prompt(contents)

print("\n-------Response--------")
for response in responses:
    print(response.text, end="")
インテリアデザイナーとして、各椅子が部屋のスタイルに合っているかどうかの評価をご紹介します。

全体として、この部屋のデザインは、ニュートラルなカラーパレット、クリーンなライン、天然素材とテクスチャーのミックスを特徴とする、モダンなボヘミアン、またはスカンジナビアの美的感覚を反映しています。椅子を評価する際には、この美的感覚を念頭に置いておくことが重要です。

**椅子1**

この工業用金属製のスツールは、この部屋にはあまり適していません。素朴な魅力はありますが、全体的なデザイン美学にはあまりにもミニマルで厳格です。部屋の暖かさと居心地の良さとは対照的です。

**椅子2**

このタフテッドチェアは、クラシックなシルエットとニュートラルな色合いで、部屋に潜在的に合う可能性があります。ただし、全体的なモダンなボヘミアンの雰囲気に完全に一致するかどうかは、室内装飾の特定の素材と色合いに依存します。たとえば、椅子は、よりテクスチャード加工されたスローまたは枕で補完することができます。椅子自体は少しフォーマルすぎて、リラックスした雰囲気に完全に調和しない可能性があります。

**椅子3**

この木製のスツールは、そのシンプルなデザインとライトな木製仕上げにより、部屋に適しています。そのカジュアルな美学は、モダンなボヘミアンの雰囲気によく合い、追加の座席やサイドテーブルとして機能します。ただし、視覚的なインパクトのために興味深いテクスチャまたはカラーパレットと組み合わせることが重要です。

**椅子4**

この回転椅子は、そのモダンなデザインとニュートラルな色合いにより、部屋によく合います。曲線状のシルエットは、部屋の他の家具の丸みを帯びたエッジを補完し、木製ベースは、モダンなボヘミアンの雰囲気の自然な要素と結びついています。さらに、回転機能は汎用性を追加し、それを部屋に適した選択にします。

要約すると、椅子3と4は、そのスタイルとデザインのために、この特定のリビングルームに最も適しています。

JSONフォーマットでレスポンスを返させる。

contents = [
    "次の椅子について考えて:",
    "chair 1:",
    furniture_images[0],
    "chair 2:",
    furniture_images[1],
    "chair 3:",
    furniture_images[2],
    "chair 4:",
    furniture_images[3],
    "room:",
    room_image,
    "あなたはインテリアデザイナーです。それぞれの椅子について、部屋のスタイルにふさわしいかどうか、と、その説明を返して。回答はJSONで:",
]

responses = multimodal_model.generate_content(contents, stream=True)

print("-------Prompt--------")
print_multimodal_prompt(contents)

print("\n-------Response--------")
for response in responses:
    print(response.text, end="")
ここにJSON形式の回答があります。

```json
[
  {
    "chair": "chair 1",
    "suitable": "false",
    "explanation": "チェア1はインダストリアルスタイルのバースツールで、このリビングルームの柔らかくモダンなボヘミアンな美学には合いません。"
  },
  {
    "chair": "chair 2",
    "suitable": "false",
    "explanation": "チェア2は豪華で装飾的な雰囲気を持つ一方、この部屋はよりリラックスして控えめなスタイルを目指しているので、うまくいきません。"
  },
  {
    "chair": "chair 3",
    "suitable": "false",
    "explanation": "チェア3はシンプルなスタイルですが、このリビングルームの全体的なデザインテーマと比較すると、見た目がカジュアルすぎてシンプルすぎます。"
  },
  {
    "chair": "chair 4",
    "suitable": "true",
    "explanation": "チェア4は、清潔でモダンなデザインと、木製の脚などの自然な質感のブレンドが、このリビングルームの既存の美学によく合っています。その落ち着いた色は、色合いを圧倒することなく空間に調和します。"
  }
]
```
kun432kun432

ちょっとボリュームは少なめだったけど、実際に使えそう。

kun432kun432

Vertex AIのGemini関連のチュートリアル、一通りやってみて、Geminiはやはりマルチモーダルの使いやすさは強みだと感じた。プロンプトに画像や動画などのマルチモーダルコンテンツを埋め込む感じはとても直感的に思える。

試さなかったやつに、マルチモーダルRAGってのがあるのだけども、

https://github.com/GoogleCloudPlatform/generative-ai/tree/main/gemini/use-cases/retrieval-augmented-generation/intro_multimodal_rag.ipynb

PDFや画像などが英語ベースだったり、ヘルパー的にコードを外部から呼んでたりして、フォーカスがぼやける気がして試しにくいのだよな。。。

とりあえず基本的なところは一通り流したので、LangChainやLlamaIndexあたりでマルチモーダルの使い勝手がどうなっているかを確認してみたいと思っている。

kun432kun432

ざっと見てみたけども、LangChainもLlamaIndexもマルチモーダルのほうはあまり対応が進んでないように思える。

https://docs.llamaindex.ai/en/stable/module_guides/models/multi_modal/

https://python.langchain.com/v0.2/docs/how_to/multimodal_inputs/

どちらも画像まで、で、動画や音声はサポートされていないのではないだろうか。

Geminiの場合、大きなデータだとGCSを使うのが基本になるようなので、この辺は他と足並み揃えて抽象化するってのは難しいのかもしれない。

このスクラップは3ヶ月前にクローズされました