こんにちは、サイオステクノロジーの遠藤です。
今回は、生成AI開発でよく登場する「マルチモーダル」について解説します。このブログでは、まず概要を理解し、その後アプリケーションの作成を通じてより深い理解を目指していきます。それでは始めましょう!
マルチモーダルとは?
マルチモーダルとは 複数の形式(モード)を組み合わせて理解する仕組み のことです。
例えば、人間は普段からマルチモーダルな理解をしています。道を歩いていて信号機を見るとき、目で「赤信号だ」と認識し、同時に耳で信号機の音を聞いて、「今は渡っちゃダメだな」と判断します。これが「視覚」と「聴覚」という複数のモードを使った理解、つまりマルチモーダルな理解になります。
この考え方が生成AIにも使われています。
例えば、画像をアップロードして「これ何?」って質問すると、生成AIがその画像を見て説明してくれます。これは、生成AIが「画像」と「テキスト」という複数の情報を同時に扱うからこそできることとなっています。
マルチモーダルなアプリケーションを作成する
実際にマルチモーダルなアプリケーションを作成するには各社が出している生成AIモデルを利用することが必要となります。
生成AIのモデルは Google社が出しているGeminiやAnthropicが出しているClaudeなどなどいろいろな会社から出ていますが、今回はOpenAI社の提供するサービスを利用します。OpenAI社が出しているサービスのなかでも様々なモデルがあり、自然言語から画像を生成できるDALL・Eや音声からテキストへ変換できるWhisperなど、モデルごとにできることが大きく異なります。
今回のブログでは高性能なチャットモデルを利用できるChatGPTモデルを使用していきます。また、ChatGPTモデルは2022年から様々なバージョンが出ていて、進化を遂げてきています。
今回作成するアプリケーションの概要
概要
今回は以下の画像のようなアプリケーションの作成を行い、「テキスト」「画像」「動画」を理解して対話できるマルチモーダルアプリケーションを作成していきます。また、簡単にWebアプリケーションを構築できるStreamlitというフレームワークを利用して作成を行っていきます。
構築にあたって必要となるもの
今回、アプリケーションの構築をまでご自身で行いたい場合には、事前に以下の環境をご準備して頂く必要があります
まずAzureが提供しているOpenAIのAPIを利用するため
- Azure Open AI のリソース
- GPT-4o-mini モデルのデプロイ
が必要となります。
今回のブログで紹介するコードではAzure OpenAI用のクライアント作成するようなコードになっていますが、そこを書き換えていただければ、OpenAIが出しているAPIを叩く形にカスタマイズしていただくことも可能になっていると思われます。
また今回のコードはStremlitを利用していますので
- Python実行環境
が必要となります
段階を分けて理解する
アプリケーションの実装をこれから行っていくのですが、
【STEP1】テキストを理解させる
【STEP2】画像を理解させる
【STEP3】動画を理解させる
の三段階に分けて実装を行うことで理解を深めていきたいと思います。
【STEP1】テキストを理解させる
テキストを理解させるための実装を行っていく前に、まずはAzure OpenAIのAPIを利用したリクエストの送り方について確認してみましょう。ChatGPTのAPIを使用するときには、生成AIに尋ねる内容をmessagesという配列に入れて上げる必要があります。
messagesは役割を表すroleと、具体的な尋ねる内容であるcontentから構成されています。生成AIにこちらの正しい意図を認識させるにはこのroleというものが大切になってきます。
roleにはsystem, user, assistantの3種類があります。
systemでは、モデルの振る舞いや性格を決定します。例えば、「タメ口で話して」とrole: systemで設定してあれば、タメ口で話すようになります。
今回のアプリケーションでは、例に設定してある「あなたは画像やテキストにも対応したアシスタントAIです」といった設定をrole : systemでしています。
userはユーザーからの指示や質問を表す役割があります。このroleで設定したcontentの中で尋ねたい内容を設定したり、画像を指定して渡したりといったことが出来ます。
assistantは今回はアプリケーションでは利用していませんが、モデルの応答を表す役割があります。使用方法としてはユーザーと生成AI間の対話履歴も理解させたい場合に、生成AI側の発言であることを示すために使用されます。
では実際に
system: あなたは画像やテキストにも対応したチャットアシスタントです。すべての質問に日本語で返答してください。
user : マルチモーダルについて教えて
を設定してリクエストを送ってみます。
そうするとレスポンスとして
マルチモーダル(Multimodal)は、複数の種類のデータや入力方法を組み合わせて処理・解析・生成するアプローチを指します。
と返ってきました。これでテキストを理解できるアプリケーションの実装が完了しました。
【STEP2】画像を理解させる
続いて画像を扱えるようにアプリケーションを拡張していきます。ChatGPTAPIを利用して画像をリクエストとして送るには、
- 画像のリンクを渡す
- Base64でエンコードされた値を渡す
の2つの方法があり、今回は2つ目の「Base64でエンコードされた値を渡す」を利用して実装していきます。
ここからはpythonコードベースで実装を確認していきます。具体的な実装は以下のようなコードになります。
# 画像をbase64にエンコードする
def encode_image(image):
return base64.b64encode(image.read()).decode("utf-8")
...
# "type":"image_url"でbase64変換した画像を送信する
base64_image = encode_image(uploaded_image)
messages = [
{"role": "system", "content": "あなたは画像やテキストにも対応したチャットアシスタントです。すべての質問に日本語で返答してください。"},
{"role": "user", "content": [
{"type": "text", "text": user_input},
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{base64_image}"}}
]}
手順としては、まずは、画像をbase64形式に変換を行います。そしてmessages配列のなかに、base64のデータを入れて渡して上げることでAPIで画像を扱うことができるようになります。注意事項としては、先程までのテキストの扱いではcontentの中でそのままテキストを扱っていたのですが、画像を送る場合にはtype付けを行い、image_urlというtypeを指定する必要がある点が注意点となります。
では画像を含めたリクエストについて確認してみましょう。以下のリクエストを送ってみます。
system : あなたは画像やテキストにも対応したチャットアシスタントです。すべての質問に日本語で返答してください。
role :user この画像をもとに物語を考えて + ファンタジーチックな空に浮いている島の画像
そうするとしっかりファンタジーチックな空に浮いている島を認識して以下のようなレスポンスが返ってきました!
物語: 空中の王国 かつて、 天空に浮かぶ壮大な王国 「アエリオン」 が存在しました。 この王国は、 雲の上に築かれた切り立った岩の上にあり、透き通る滝が流れ落ちる美しい景色が広がっています。 王国の人々は、その美しさを愛し、力強く繁栄していました。 しかし、彼らの幸せは、ある日突然の危機にさらされます。 ある夜、暗い影が空を覆い、不気味な雲が王国を包み込みました。 恐ろしいドラゴン、 「ヴォルクラ」が現れ、王国の秘宝を求めて攻撃を仕掛けました。 人々は恐怖に震え、 滝の音すら聞こえなくなりました。 王国の王子、アレンは、勇気を奮い立たせ、王国を救う決意をしました。 彼は祖父から授けられた古の剣を手に取り、ドラゴンに立ち向かうための旅に出ることにしました。 旅の途中で、アレンは不思議な生き物たちと出会い、彼らから知恵や力を授かります。 その中には、天空を舞う幻の鳥「フェアリーブレード」 がおり、 彼女はアレンに、ドラゴンの弱点を教えてくれました。ドラゴンは月の光を浴びると力を増し、昼間には眠っていることが多いという情報です。 アレンは仲間たちと共に、 計画を練り、 ドラゴンが月明かりで力を失う瞬間を狙うことにしまし た。夜が訪れ、彼は果敢に陰に隠れ、 ドラゴンが居る巣へと近づいていきます。 激しい戦いの末、アレンは見事にドラゴンを打ち倒しました。 平和が戻ったアエリオンでは、王子の勇気を称える祭りが開かれました。人々は再び笑顔を取り戻 し、空中の王国はさらなる繁栄を迎えました。 アレンは、自分の冒険を通じて友情や勇気の大切さを学び、未来の王として王国を支えていくことを誓いました。 そして、王国の上空には、いつまでも流れる壮麗な滝と青空が広がり、 アエリオンは永遠に美しく輝き続けるのでした。
【STEP3】動画を理解させる
動画をリクエストで扱う方法を… と進んで行きたいのですが、実は今回使用しているGTP-4o-miniのAPIでは直接動画を扱うことが出来ません。だからと言って動画を理解させれないわけではありません。要素に分割することで生成AIに理解させることが出来ます。
動画を構成する要素
動画は、連続した静止画が高速で切り替わることで動いているように見える要素である「映像」と、環境音や話し声とした「音声」から構成されています。
より細かく分解していくと、映像は連続した静止画が高速で切り替わっていると言えるので、複数の画像に分割できると言えます。
また、音声についてですが例えば、人の話している内容等であれば、文字起こしでテキストにすることが出来ます。また環境音など文字起こしできないものであっても、一部の生成AIを利用すれば状況を書き起こし、シチュエーションをテキストにすることが出来ます。
このように動画を要素に分割させて行くことで生成AIに動画を理解させることができるようになります!ちなみにOpenAI社的には今後直接音声や動画をAPIで扱えるようにしていくつもりのようですので、このような分割したりの作業は将来的には必要なくなるかもしれません。
ここまで説明させていただきましたが、今回は準備の関係で音声をテキスト変換して渡す機能までは入れていません。ブログの最後にコードの紹介もするので、例えば、OpenAI社のWhisperモデルを使用して文字起こしを行ったものも渡す機能を追加する変更を加えるなどして音声も認識できるように対応箇所を広げるなどしてさらにマルチモーダルアプリケーションへの理解を深めていただければと思います。
実装を確認
では画像で行ったのと同じようにコードベースでの実装を確認してみましょう。
まず、extract_frames関数を利用してbase64変換したフレーム(静止画)の配列にしています。
そして、map関数で動画を200フレームごとに受け渡しするようにしています。
# 動画をフレームに分割する
def extract_frames(video_bytes):
with tempfile.NamedTemporaryFile(delete=False) as temp_video_file:
temp_video_file.write(video_bytes)
temp_video_file_path = temp_video_file.name
video = cv2.VideoCapture(temp_video_file_path)
base64_frames = []
while video.isOpened():
success, frame = video.read()
if not success:
break
_, buffer = cv2.imencode(".jpg", frame)
base64_frames.append(base64.b64encode(buffer).decode("utf-8"))
video.release()
print(len(base64_frames), "frames read.")
return base64_frames
...
# AOAIへ200フレームごとにbase64変換した画像を渡す
base64_frames = extract_frames(uploaded_video.read())
messages = [
{"role": "system", "content": "あなたは画像やテキストにも対応したチャットアシスタントです。すべての質問に日本語で返答してください。"},
{"role": "user", "content": [
{"type": "text", "text": user_input},
*map(lambda x: {"image": x, "resize": 240}, base64_frames[0::200]),
]}
]
ここで、動画や画像を扱うときの注意点です
生成AIを利用するときにはテキストを分割した最小単位であるトークン数に応じてお金がかかるようになるのですが、動画や画像を行っているbase64変換を行ったデータはデータ数が大きいためたくさんのトークンが必要となります。例えば、この後実際に送ってみる動画のリクエストには一リクエストあたり11.5円ほどかかります。コードの中で200フレームごとに受け渡しする処理を入れている理由は、このトークン数への対策になっていて、動画を理解させるためには必ずしも全てのフレームを送って理解させる必要はなく、少し飛ばし飛ばしで見ても生成AIは理解できるようなっています。
では実際に動画を含めたリクエストを送ってみます。
system : あなたは画像やテキストにも対応したチャットアシスタントです。すべての質問に日本語で返答してください。
user : この動画を説明してください + ゴールデンレトリバーが飼い主と遊んでいる動画
返ってきたレスポンス
この動画では、緑豊かな公園の中で、犬と人間が一緒に散歩している様子が映し出されています。犬はゴールデンレトリバーのようで、飼い主のそばで楽しそうに歩いています。周囲の自然や明るい日差しが感じられ、ほのぼのとした雰囲気が伝わってきます。また、飼い主が犬とコミュニケーションをとりながら進んでいる様子も見られます。全体的に、愛情と絆を感じさせるシーンです。
無事にゴールデンレトリーバーを認識したりだとか、飼い主とコミュニケーションを取っているなど動画を認識できることが確認できました!
今回作成したアプリケーションのリポジトリ
今回作成したアプリケーションですが、以下のリポジトリで公開しております。
https://github.com/Fumiya-Endo-sti/multi-modal-application
READMEに構築方法等が乗っているので自分で動かしてみたい方はその手順に沿って動かして見ていただければと思います。
まとめ
今回は、マルチモーダルを理解するために、アプリケーションの作成を通して理解を深めていきました。今後もマルチモーダルはより多くの場面で採用されて行くと思われますので、これらの進歩に注目し常に最新の情報を取り入れていくことが大切だと感じています。
最後に宣伝になりますが、弊社では生成AIのアプリケーションの開発や導入などのサポートを行っております。提供できるソリューションについて以下のサイトに記載されておりますので、ご興味がございましたらご一読いただければと思います。
https://nextech-solutions.sios.jp/genai/
ではまた~