LoginSignup
10
1
記事投稿キャンペーン 「2024年!初アウトプットをしよう」

Livebook から Gemini Pro Vision を呼んで画像についての質問に答える

Last updated at Posted at 2024-01-06

はじめに

Google の生成 AI Gemini Pro Vision を Livebook から呼んでみます

Gemini Pro Vision はいわゆる「マルチモーダル」で、 文章と画像を合わせて認識 することができます

例えば AWS の Rekognition は画像認識用の AI サービスなので、画像を投げると、その画像に含まれる物の情報が得られます
画像 |> テキスト

OpenAI の ChatGPT 3.5 だと、文章を投げると、それに対する返答が文章で得られます
テキスト |> テキスト

Stable Difusion だと文章から画像を生成します
テキスト |> 画像

これらに対して Gemini Pro Vision は 与えた画像に対して文章で質問ができる サービスです
(画像 + テキスト) |> テキスト

ちなみに ChatGPT 4 Vsion も同じことが可能です

いまいちピンとこないかもしれませんが、とりあえず Livebook から呼んでみましょう

実装したノートブックはこちら

Gemini Pro の API キー取得

色々手段はあると思いますが、私がやった方法です

Gemini Pro の料金説明ページに行きます

左下の Get API をクリックします

スクリーンショット 2024-01-06 9.40.37.png

初回は認証等を求められるので、指示に従います

Google AI Studio の画面に遷移したら左メニューの先頭 Get API Key をクリックし、 API Key の画面に遷移します

特別理由がなければ Create API key in new project をクリックしましょう

スクリーンショット 2024-01-06 9.43.10.png

少し待つと API キーの値が表示されるので、その値を Livebook で使用します

左メニューの Create new から、 Google AI Studio 内で Gemini Pro が試せます

とりあえず気軽に試したい場合はこっちが便利です

セットアップ

Livebook で新しいノートブックを作成し、セットアップセルに以下の内容を入力します

Mix.install([
  {:kino, "~> 0.12"},
  {:req, "~> 0.4"}
])

入出力用の UI に Kino を使います

REST API として Gemini Pro にアクセスするため Req をインストールします
(Gemini Pro 用の Elixir モジュールは見当たりませんでした)

接続情報の準備

API キーの入力 UI を作ります

api_key_input = Kino.Input.text("API_KEY")

スクリーンショット 2024-01-06 9.51.13.png

作成した UI に、取得しておいた Gemini Pro の API キーを入力します

API キーの値を UI から取得し、 REST API の URL とリクエストヘッダーを用意します

api_key = Kino.Input.read(api_key_input)

base_url = "https://generativelanguage.googleapis.com/v1beta/models"

headers = %{
  "Content-Type" => "application/json"
}

文章だけの入力

まずは文章だけで質問してみます

request_body = %{
  contents: [
    %{
      parts: [
        %{
          text: "開発言語の Elixir について教えてください"
        }
      ]
    }
  ]
}

Req を使い、 POST メソッドでアクセスします

文章だけなので、単なる Gemini Pro で事足りるため、 URL は /gemini-pro になっています

response =
  "#{base_url}/gemini-pro:generateContent?key=#{api_key}"
  |> Req.post!(
    json: request_body,
    headers: headers
  )
  |> Map.get(:body)

実行結果

%{
  "candidates" => [
    %{
      "content" => %{
        "parts" => [
          %{
            "text" => "Elixir は、高並行性と堅牢性を備えた、汎用的な関数型プログラミング言語です。..."
          }
        ],
        "role" => "model"
      },
      "finishReason" => "STOP",
      "index" => 0,
      "safetyRatings" => [
        %{"category" => "HARM_CATEGORY_SEXUALLY_EXPLICIT", "probability" => "NEGLIGIBLE"},
        %{"category" => "HARM_CATEGORY_HATE_SPEECH", "probability" => "NEGLIGIBLE"},
        %{"category" => "HARM_CATEGORY_HARASSMENT", "probability" => "NEGLIGIBLE"},
        %{"category" => "HARM_CATEGORY_DANGEROUS_CONTENT", "probability" => "NEGLIGIBLE"}
      ]
    }
  ],
  "promptFeedback" => %{
    "safetyRatings" => [
      %{"category" => "HARM_CATEGORY_SEXUALLY_EXPLICIT", "probability" => "NEGLIGIBLE"},
      %{"category" => "HARM_CATEGORY_HATE_SPEECH", "probability" => "NEGLIGIBLE"},
      %{"category" => "HARM_CATEGORY_HARASSMENT", "probability" => "NEGLIGIBLE"},
      %{"category" => "HARM_CATEGORY_DANGEROUS_CONTENT", "probability" => "NEGLIGIBLE"}
    ]
  }
}

色々な項目が返ってきていますが、返答のテキストは text に入っています

テキストをマークダウンとして表示しましょう

response
|> Map.get("candidates")
|> hd()
|> Map.get("content")
|> Map.get("parts")
|> hd()
|> Map.get("text")
|> Kino.Markdown.new()

実行結果
スクリーンショット 2024-01-06 9.57.37.png

ちゃんと Elixir がモバイルアプリに利用されている ことまで知っていますね

画像と文章の入力

画像と文章の組み合わせを Gemini Pro Vision に投げてみましょう

画像入力の UI を作成します
今回は JPEG 形式の画像を使うため、明示的に形式を指定しています

image_input = Kino.Input.image("IMAGE", format: :jpeg)

スクリーンショット 2024-01-06 10.04.56.png

UI から質問を投げたい画像を選択します

選択した画像を UI から読み込みます

image_binary =
  image_input
  |> Kino.Input.read()
  |> Map.get(:file_ref)
  |> Kino.Input.file_path()
  |> File.read!()

実行結果
スクリーンショット 2024-01-06 10.06.28.png

質問する文章と、 BASE64 エンコーディングした画像をリクエストボディーに入れます

request_body = %{
  contents: [
    %{
      parts: [
        %{
          text: "首輪は何色ですか"
        },
        %{
          inline_data: %{
            mime_type: "image/jpeg",
            data: Base.encode64(image_binary)
          }
        }
      ]
    }
  ]
}

Gemini Pro Vision にリクエストを投げます

今回は画像を含むので URL が /gemini-pro-vision になっています

response =
  "#{base_url}/gemini-pro-vision:generateContent?key=#{api_key}"
  |> Req.post!(
    json: request_body,
    headers: headers
  )
  |> Map.get(:body)

実行結果

%{
  "candidates" => [
    %{
      "content" => %{"parts" => [%{"text" => " 首輪は青色です。"}], "role" => "model"},
      "finishReason" => "STOP",
      "index" => 0,
      "safetyRatings" => [
        %{"category" => "HARM_CATEGORY_SEXUALLY_EXPLICIT", "probability" => "NEGLIGIBLE"},
        %{"category" => "HARM_CATEGORY_HATE_SPEECH", "probability" => "NEGLIGIBLE"},
        %{"category" => "HARM_CATEGORY_HARASSMENT", "probability" => "NEGLIGIBLE"},
        %{"category" => "HARM_CATEGORY_DANGEROUS_CONTENT", "probability" => "NEGLIGIBLE"}
      ]
    }
  ],
  "promptFeedback" => %{
    "safetyRatings" => [
      %{"category" => "HARM_CATEGORY_SEXUALLY_EXPLICIT", "probability" => "NEGLIGIBLE"},
      %{"category" => "HARM_CATEGORY_HATE_SPEECH", "probability" => "NEGLIGIBLE"},
      %{"category" => "HARM_CATEGORY_HARASSMENT", "probability" => "NEGLIGIBLE"},
      %{"category" => "HARM_CATEGORY_DANGEROUS_CONTENT", "probability" => "NEGLIGIBLE"}
    ]
  }
}

text の部分だけ取り出してマークダウンとして取得します

response
|> Map.get("candidates")
|> hd()
|> Map.get("content")
|> Map.get("parts")
|> hd()
|> Map.get("text")
|> Kino.Markdown.new()

スクリーンショット 2024-01-06 10.10.10.png

ちゃんと画像内の首輪の色を答えてくれました

もっと難しい質問をしてみます

request_body = %{
  contents: [
    %{
      parts: [
        %{
          text: "写真内の犬はどういう状態になっていますか"
        },
        %{
          inline_data: %{
            mime_type: "image/jpeg",
            data: Base.encode64(image_binary)
          }
        }
      ]
    }
  ]
}


"#{base_url}/gemini-pro-vision:generateContent?key=#{api_key}"
|> Req.post!(
  json: request_body,
  headers: headers
)
|> Map.get(:body)
|> Map.get("candidates")
|> hd()
|> Map.get("content")
|> Map.get("parts")
|> hd()
|> Map.get("text")
|> Kino.Markdown.new()

実行結果

写真内の犬は、人の腕の中に抱えられていて、大人しくしています。

これも正解しています!

もっと抽象的な質問をしてみましょう

request_body = %{
  contents: [
    %{
      parts: [
        %{
          text: "写真内の犬はどんなことを考えているように見えますか"
        },
        %{
          inline_data: %{
            mime_type: "image/jpeg",
            data: Base.encode64(image_binary)
          }
        }
      ]
    }
  ]
}


"#{base_url}/gemini-pro-vision:generateContent?key=#{api_key}"
|> Req.post!(
  json: request_body,
  headers: headers
)
|> Map.get(:body)
|> Map.get("candidates")
|> hd()
|> Map.get("content")
|> Map.get("parts")
|> hd()
|> Map.get("text")
|> Kino.Markdown.new()

実行結果

写真内の犬は、カメラに興味津々で、何だろうと不思議に思っているように見えます。また、少し緊張しているようにも見えます。

単なる画像認識を超えているように見えますね

まとめ

Gemini Pro Vision は画像と文章を組み合わせて認識することで、かなり幅広い認識ができるようになっています

今までの画像 AI では答えられなかったような汎用的なタスクに対応できるので、色々な応用ができそうです

ただし、他の生成 AI と同様、分からない問いにも「一見正しそうなデタラメ」を返してきます

また、他の画像 AI のような物体の位置情報は返してくれません(質問すれば返ってくるけどデタラメになっています)

用途、用法には気をつけましょう

10
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
1