はじめに
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 をクリックします
初回は認証等を求められるので、指示に従います
Google AI Studio の画面に遷移したら左メニューの先頭 Get API Key をクリックし、 API Key の画面に遷移します
特別理由がなければ Create API key in new project をクリックしましょう
少し待つと 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")
作成した 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()
ちゃんと Elixir がモバイルアプリに利用されている ことまで知っていますね
画像と文章の入力
画像と文章の組み合わせを Gemini Pro Vision に投げてみましょう
画像入力の UI を作成します
今回は JPEG 形式の画像を使うため、明示的に形式を指定しています
image_input = Kino.Input.image("IMAGE", format: :jpeg)
UI から質問を投げたい画像を選択します
選択した画像を UI から読み込みます
image_binary =
image_input
|> Kino.Input.read()
|> Map.get(:file_ref)
|> Kino.Input.file_path()
|> File.read!()
質問する文章と、 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()
ちゃんと画像内の首輪の色を答えてくれました
もっと難しい質問をしてみます
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 のような物体の位置情報は返してくれません(質問すれば返ってくるけどデタラメになっています)
用途、用法には気をつけましょう