見出し画像

GPT-4 Vision API で遊ぶ


はじめに

おはこんにちばんは、えるです❢
OpenAI DevDayでなんだかいろいろ発表されたので遊ぼうシリーズ(?)の第2弾は GPT-4 Vision APIです✨

ちなみに第1弾noteは以下
DALLE3 APIを使用した ふわっと画像生成|える (note.com)

ひとまずは上記noteの実装をベースに、画像を取り込んだ際はGPT-4Vを呼ぶような実装にしてみます

ただ、GPT-4Vは現在のところ制約もあるみたいです、そのあたりにまずは触れてみようかと思います

GPT-4V の現状

まずは素直に公式まにあるを頑張って読みます

Vision - OpenAI API

えるさんは英語ワカンナイ人なので、ところどころ日本語に翻訳しつつ読みます。。

こんな感じ

割と大事なことがさらっと書いてあります
・Assistants APIは画像入力をサポートしていない
・functions/toolsには対応していない

とかがひとまず気になった点でしょうか
というか、えるさんは第1弾noteをベースに作っていたのでfunctions/tools未対応の部分でもしっかり躓きました。。

転んでも可愛いエリちゃんの図

functions/toolsとはFunction callingのことです、たぶん。

Function calling - OpenAI API

確かにFunction Callingのサポートモデルにも入ってないんですよね

さらっと仲間外れのGPT-4V

なんでだろう、将来的には入るとは思うんですが何か理由があるのかな?

未対応といえば、systemメッセージにも現状未対応みたいです

将来的に対応なのかなあ

あとはLimitationとかも読んでおきます


実装方法とか結果

①初期化

import asyncio
import json
import aiohttp
import os
import discord
import openai
import requests
import base64
import random
import io

from PIL import Image, PngImagePlugin
from openai import OpenAI

bot_token = os.environ['KEMI_BOT_KEY']
openai_key = os.environ['GPT_KEY']

ここは第一弾noteと変わらないですね、urlは不要だったので削ったくらいです

②Discord bot記述部

### discord initial
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)

message_chunks = []

@client.event
async def on_ready():
    print(f'{client.user.name} has connected to Discord!')

@client.event
async def on_message(message):
    img_url = []
    print(message.content)
    if message.author == client.user:
        return

    if message.attachments:
      for attachment in message.attachments:
          img_url.append(attachment.url)

    state,data = gpt_msg(message.content,img_url)
    await message.channel.send(state)
    await message.channel.send(data)

client.run(bot_token)

第一弾noteと比較して削り忘れた記述の削除と、discordの添付ファイルを画像と解釈し画像のURLを覚えておく処理を追加しています
そして画像のURLをGPT-4Vのinputにしているので、現状だと他の添付ファイルはエラーになるはずです(試してませんが恐らく)

③gpt_msg部

def gpt_msg(question,img_url):
    img_cnt=0
    img_description = ""
    for images in img_url:
      print(images)
      img_cnt+=1
      model_name = "gpt-4-vision-preview"
      client = OpenAI()
      response = client.chat.completions.create(
          model="gpt-4-vision-preview",
          messages=[
             {
                "role": "user",
                "content": [
                    {"type": "text", "text": "どのような画像か詳細を説明してください"},
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": images,
                        },
                    },
                ],
             }
          ],
          max_tokens=800,
      )
      img_description = str(img_cnt)+"枚目の画像は"+response.choices[0].message.content+"です。"

    model_name = "gpt-4-1106-preview"
    client = OpenAI()
    if img_cnt > 0:
        question = img_description+question

    messages = [{"role": "user", "content": question}]

    response = client.chat.completions.create(

        model=model_name,
        messages=[
            {"role": "user", "content": question},
        ],
        tools=tools,
        tool_choice="auto",
    )

    response_message = response.choices[0].message
    send_message = response.choices[0].message.content

    tool_calls = response_message.tool_calls

    if tool_calls:

        available_functions = {
            "generate_image": generate_image,
        }
        messages.append(response_message)
        for tool_call in tool_calls:
            function_name = tool_call.function.name
            function_to_call = available_functions[function_name]
            function_args = json.loads(tool_call.function.arguments)
            prompt = function_args.get("prompt")

        second_response = client.chat.completions.create(
            model=model_name,
            messages=[
                {"role": "user", "content": prompt+"がモチーフの画像を作りたいので、その詳細な描写を画像生成AIに伝えるためのプロンプトを提案してください。 回答は英語が嬉しいです。プロンプトのみの回答が嬉しいです。"},
            ],
        )
        input_text = second_response.choices[0].message.content
        input_text = dalle_api(input_text)
        return "--generate--", input_text
    else:
        return "----", send_message

…長い!! ので第一弾noteと比較して追加や変更した部分をまずは抜粋します

    img_cnt=0
    img_description = ""
    for images in img_url:
      print(images)
      img_cnt+=1
      model_name = "gpt-4-vision-preview"
      client = OpenAI()
      response = client.chat.completions.create(
          model="gpt-4-vision-preview",
          messages=[
             {
                "role": "user",
                "content": [
                    {"type": "text", "text": "どのような画像か詳細を説明してください"},
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": images,
                        },
                    },
                ],
             }
          ],
          max_tokens=800,
      )
      img_description = str(img_cnt)+"枚目の画像は"+response.choices[0].message.content+"です。"

    model_name = "gpt-4-1106-preview"
    client = OpenAI()
    if img_cnt > 0:
        question = img_description+question

概要としては、GPT-4Vにどんな画像か聞いて、その結果を「1枚目の画像は~、2枚目の画像は~」と繋げています
上記の結果をユーザー入力に繋いでいるので、「画像の説明と、画像に関連する質問」をユーザーがしている感じにしています。

④tool部(旧function部)

tools=[
    {
        "type": "function",
        "function": {
            "name": "generate_image",
            "description": "イラスト生成を指示する文の場合、イラスト生成用のキーワードを作成する。",
            "parameters": {
                "type": "object",
                "properties": {
                    "prompt": {
                        "type": "string",
                        "description": "生成用キーワードなど",
                    },
                },
                "required": ["prompt"],
            },
        },
    }
]

ここは第一弾noteと変わらないです


⑤DALLE3 API部

### DALLE setting
def dalle_api(input_text):
  client = OpenAI()

  response = client.images.generate(
    model="dall-e-3",
    prompt=input_text,
    size="1024x1024",
    quality="standard",
    n=1,
  )

  image_url = response.data[0].url

  return image_url

ここも第一弾noteと変わらないです


結果

以下の感じで、画像が解釈可能になりました
私の実装の場合画像の説明でDALLE3を呼びに行ってしまうのですが、
「イラストは作成しないで」というと作成しませんでした、それでいけちゃうんだ。。

イラストからイラスト生成に繋ぐことも一応できちゃいます


現在の実装方法の問題点

1) discordの画像以外の添付ファイル未対応

現状の私の使い方ならいいかーと思ってますが、これって対応できるのかな? ファイル名拡張子で弾くとか??

2)複数枚の画像の読み込み

現状は1枚ずつ読み込んでますが、GPT-4Vはマルチにも読めるので、本来はそちらのほうが良い気がします

マルチ対応GPT-4V

これは単に1枚読みのほうが取り合えずの実装が容易だったのが理由なので、今後試してみたいですね

3)複数枚画像の解釈失敗

例えば2枚画像を読み、「1枚目の説明をお願い」とした場合、なぜか2枚目の画像を説明してくれました
この辺りは複数枚読み込みにするとむしろ安定するのかな?

4)GPT4の呼び出し回数

現状は画像ありの場合で例えば「画像説明をして」の場合GPT-4V→GPT4の具合に必ず2回呼びますが、
本来だとGPT-4Vを1回呼べば良いですよねえ、、とか考えつつ実装の容易性で現状になってます

このあたりはGPT-4Vがfunction callingに対応すれば解決するので、待ちかなあ






この記事が気に入ったらサポートをしてみませんか?