🦄

OpenAIのAPI(GPT-4V・DALL-E3)を使って「絵しりとり」する

2023/11/12に公開

A. OpenAI DevDay

OpenAI DevDay(2023/11/7)の発表で報告されたAPIの一部を使って、簡単なチュートリアルを作ってみたいな~と思い、一例として「絵しりとり」のシステムを構築してみました!
ソースコードはgithubにまとめてます

  • OpenAI DevDayの様子

https://www.youtube.com/live/U9mJuUkhUzk?si=NUifpYrcx6Wmk4On

B. 準備

APIのクレジット設定

APIを使用するにあたって、あらかじめクレジット1$以上の支払いを行う必要がある。
こちらに従って、使用料事前購入を行う。

  1. アカウントのBilling overviewに移動

  1. Add to credit balance」→ 購入するクレジット(最低5$)を指定 → 購入

openaiライブラリ使用準備

  • openaiモジュールのインストール
! pip install --upgrade pip
! pip install openai, requests
  • 事前に発行したAPIキーの環境変数設定
import os
os.environ["OPENAI_API_KEY"] = "sk-xxxxxxx"

C. 各種APIについて

OpenAI公式に書いてあるQuick Startを参考にする

1. Vision (Image2Text)

  • image_urlの値は公開URLの画像ファイルだけでなく、ローカルファイルを読み込んでも使える
  • githubでは複数画像を読み込めるように改良
from openai import OpenAI

# ローカル画像ファイルのエンコーディング
def encode_image(image_path):
    with open(image_path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode('utf-8')

image = encode_image('/path/to/image.jpg')

# gpt-4VのAPI使用:`text`にプロンプト・`image_url`にエンコードした画像を埋め込む
client = OpenAI()
response = client.chat.completions.create(
    model="gpt-4-vision-preview",
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "What are in these images?",
                },
                {
                    "type": "image_url",
                    "image_url": {
                        "url": f"data:image/jpeg;base64, {image}"
                    },
                },
            ],
        }
    ],
    max_tokens=300,
)
print(response.choices[0].message.content)
  • ガビガビな画像でもそれなりに説明してくれる...
Image Prompt GPT (Translated)
What's in this image? 画像は少しボヤけていますが、動いている人物を映しているようです。その人物は赤いTシャツと青いショーツを着ており、サングラスをかけている可能性があります。腕と足の位置から、歩いているか走っていることが示されており、動きがあることを示唆しています。背景には自転車の部分的な見え方から、屋外のエリアである可能性が高く、おそらくは舗装された道路や公共のスペースのようです。自転車の道や一般的に自転車が使用されたり駐輪される場所の近くであることを示唆しています。
  • 以下responseで返ってきた原文

The image is a bit blurry but it appears to show a person in motion. The person is wearing a red t-shirt, blue shorts, and is possibly wearing sunglasses. They seem to be walking or running, indicated by the position of the arms and legs suggesting movement. The setting looks like an outdoor area possibly a street or public space with pavement. There are also partial views of bicycles in the background, which suggests it could be near a bike path or a place where bikes are commonly used or parked.

2. DALL-E3 (Text2Image)

  • prompt引数で生成したい画像の内容を指定
  • 現時点(2023/11/12)では生成枚数はn=1のみ許容されていた
from openai import OpenAI
client = OpenAI()

response = client.images.generate(
    model="dall-e-3",
    prompt="a dog",
    size="1024x1024",
    quality="standard",
    n=1,
)
image_url = response.data[0].url
  • 返り値のimage_urlを参照すると生成された画像を見ることができる。
    • 以下は「a dog」のプロンプトに対する生成画像。かわいい。

3. TTS (Text2Speech)

  • input引数に読み上げさせたいテキストを指定
from openai import OpenAI

client = OpenAI()
response = client.audio.speech.create(
  model="tts-1",
  voice="alloy",
  input="今日も一日頑張るぞい"
)
response.stream_to_file('path/to/speech.mp3')
  • stream_to_file()引数で指定したファイルに読み上げ音声が保存される

4. TextGen

  • こちらは元々あったchatGPTのAPIを利用
response = client.chat.completions.create(
            model="gpt-4-1106-preview",
            messages=[
                {"role": "system", "content": "You are a helpful assistant"},
                {"role": "user", "content": "GPTってなぁに?"}
            ],
        )
result = response.choices[0].message.content
  • messageの内容

「GPT」とは「Generative Pre-trained Transformer」の略称で、自然言語処理(NLP)においてテキストを生成するためのAIモデルの一つです。OpenAIによって開発され、それぞれのバ
ージョンアップとともに大きな進歩を遂げています。
GPTの核となる技術は「トランスフォーマー」という注意機構を使用したニューラルネットワークアーキテクチャです。このトランスフォ
ーマーは、特定の単語やフレーズがテキストの中でどのように関連しているかを理解するのに非常に効果的です。
GPTは大規模なデータセットで事前学習され、「ファインチューニング」とい
うプロセスを通じて特定のタスクに合わせてさらに学習されます。この学習プロセスによって、GPTは質問応答、テキスト生成、要約、翻訳など、様々な言語タスクに非常に高い性能を示すことが
できます。
時点の知識に基づくと、最新のGPTモデルは「GPT-3」というもので、非常に多くの言語データを学習し、その結果、幅広いテーマや質問に対して高品質なテキストを生成する能力を
持っています。GPT-3は特に、その大きなパラメータ数と一般化能力で注目されています。

D. 絵しりとりシステムの実装

これまでのAPIの機能を用いて、次のイタレーションフローを実行します。

  1. 単語入力
  2. 単語⇒画像変換
  3. 画像⇒単語変換
  4. 単語⇒単語(しりとり) → 2.へ戻る

1. 各種関数

  • 以下のファイル群をfuncsディレクトリ以下に作成
image2text
image2text.py
import base64
from openai import OpenAI

def encode_image(image_path):
    with open(image_path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode('utf-8')


def image2text(impaths:list, prompt:str, max_tokens:int=300):

    if isinstance(impaths, str):
        impaths = [impaths]

    # 複数画像を読み込ませられるように改良
    encimgs = [encode_image(path) for path in impaths]
    
    # エンコーディング
    image_url_list = [
        {
            "type": "image_url",
            "image_url": {
                "url": f"data:image/jpeg;base64, {imurl}"
            }
        } for imurl in encimgs
    ]


    client = OpenAI()
    response = client.chat.completions.create(
        model="gpt-4-vision-preview",
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": prompt,
                    },
                    *image_url_list
                ],
            }
        ],
        max_tokens=max_tokens,
    )
    return response.choices[0].message.content
text2image
text2image.py
import requests
from openai import OpenAI



def download_image(url:str, file_path:str):

    try:
        response = requests.get(url, stream=True)
        if response.status_code == 200:
            with open(file_path, 'wb') as file:
                for chunk in response:
                    file.write(chunk)
            return True
        else:
            return False
    except Exception as e:
        print(f"An error occurred: {e}")
        return False



def text2image(prompt:str, filename:str='./image.jpg'):
    client = OpenAI()

    response = client.images.generate(
        model="dall-e-3",
        prompt=prompt,
        size="1024x1024",
        quality="standard",
        n=1, # You must provide n=1 for dalle-e-3 model
    )

    return download_image(response.data[0].url, filename)
chat
chat.py
def chat(prompt:str, system_msg:str="You are a helpful assistant."):
    client = OpenAI()
    try:
        response = client.chat.completions.create(
            model="gpt-4-1106-preview",
            messages=[
                {"role": "system", "content": system_msg},
                {"role": "user", "content": prompt}
            ],
        )
        result = response.choices[0].message.content
        return result
    except AttributeError as e:
        result = f"Error: {e}"
        return result

2. mainファイル

  • 画像のカテゴリを動物に絞るようにプロンプト調整 (じゃないとなかなかしりとりが続かない...)
shiritori.py
def shiritori(start_word:str, n:int=5):

    CMD1 = 'What animal is this? Please answer in a single word.'
    CMD2 = lambda word: f"Please answer with the name of an animal that starts \
                        with the last letter of 「{word}, in a single word."
    word = start_word

    for i in range(n):
        path2img = lambda i, word: f'data/image{i}_{word}.jpg'

        funcs.text2image(word, path2img(i, word))
        word = funcs.image2text(path2img(i, word), CMD1)
        word = funcs.chat(CMD2(word))
        print(word)

shiritori('dog')

3. 結果

結果は以下のようになりました!ちゃんと成立してるっぽいです!

  1. Dog (犬)
  2. Goldfish (金魚)
  3. Hawk (鷹)
  4. Kangaroo (カンガルー)
  5. Okapi (オカピ)

※ 最後は「Impala」で返してきました。。。

Discussion