Jupyter Notebookでリッチな表示:OpenAI APIを使ってみる(その2)

目次
  1. Matplotlibでグラフ
    1. Matplotlibについて
    2. グラフについて
    3. OpenAI APIから取得した結果をグラフで表示
  2. display関数
    1. display関数の利用例
    2. OpenAI APIから取得した結果をdisplay関数で表示

Matplotlibでグラフ

Matplotlibについて

Matplotlibは、Pythonでグラフ描画を行うためのライブラリの一つです
2次元のグラフはもちろん、一部の3次元グラフも描くことが可能
多様なグラフを生成する機能を持っています(ラインプロット・バーチャート・エラーバー・ヒストグラム・パイチャート・スキャッタープロットなど)

Matplotlibは。NumPyやPandasを使ってデータを操作し、Matplotlibでその結果を視覚的に表示する、というワークフローが一般的です。
Matplotlibは機能が豊富で複雑なので、「SeabornやPlotly」のような直感的にグラフを描けるライブラリもあります。

グラフについて

ラインプロット
時間の経過とともに変化するデータ(時系列データ)を視覚化するのに最もよく使用されます。
株価の変動、気温の変化、ウェブサイトの訪問者数など

import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y)
plt.show()

バーチャート
バーチャートは、カテゴリごとの数値を比較するのに適しています。
各商品の売上高、各地域の人口、性別や年齢層ごとのアンケート結果など

import matplotlib.pyplot as plt
categories = ['A', 'B', 'C', 'D', 'E']
values = [7, 3, 4, 2, 6]
plt.bar(categories, values)
plt.show()

エラーバー
測定の不確かさや変動を視覚化するのに用いられます。
平均値や中央値といった統計量を中心に、その周囲にエラーバーを描くことで、データの分散や標準偏差を表現します。
科学実験の結果を報告なで

import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [2, 3, 5, 7, 9]
y_err = [0.5, 0.4, 0.5, 0.2, 0.3]
plt.errorbar(x, y, yerr=y_err, fmt='o')
plt.show()

ヒストグラム
データの分布を視覚化するために使用されます。
ある特定の数値がデータセット内で何回出現するかを示すため、データの分布が正規分布(ベルカーブ)に従っているのか、あるいは他の形状をしているのかを示すのに役立ちます

import matplotlib.pyplot as plt
import numpy as np
data = np.random.randn(1000)
plt.hist(data, bins=30)
plt.show()

パイチャート
全体に対する各部分の割合を視覚化するために使用されます。
全体を100%としたときの、各カテゴリの占める割合を示すのに適しています。ただし
*3~4つ以上のカテゴリを比較するときには視覚的に解釈しにくくなるため、使用は限定的です

import matplotlib.pyplot as plt
sizes = [25, 35, 30, 10]
labels = ['A', 'B', 'C', 'D']
plt.pie(sizes, labels=labels, autopct='%1.1f%%')
plt.show()

スキャッタープロット(散布図)
2つの変数間の関係性を視覚化するために使用されます。

import matplotlib.pyplot as plt
import numpy as np
x = np.random.randn(100)
y = np.random.randn(100)
plt.scatter(x, y)
plt.show()

OpenAI APIから取得した結果をグラフで表示

より例が思いつかないので、異なるプロンプトに対するAPIの応答長さを比較して、その結果を棒グラフで表示するだけです
日本語を表示するために、システムにあるフォントを利用しています

import openai
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
from dotenv import dotenv_values

# APIキーの設定
config = dotenv_values(".env")
openai.api_key = config["OPENAI_API_KEY"]

# 日本語フォントの設定 フォントのパスは環境によります
font_path = '/System/Library/Fonts/ヒラギノ明朝 ProN.ttc' 
font_prop = fm.FontProperties(fname=font_path)
# Matplotlibの全体的な設定を変更
plt.rcParams['font.family'] = font_prop.get_name()

# いくつかのプロンプトを用意します
prompts = ["フランス語でこんにちは", "イタリア語でこんにちは", "スペイン語でこんにちは"]

# 各プロンプトの結果の長さを格納するためのリスト
response_lengths = []

for prompt in prompts:
    response = openai.Completion.create(
        model="text-davinci-003",
        prompt=prompt,
        max_tokens=100
    )
    # 応答の長さをリストに追加します
    response_lengths.append(len(response.choices[0].text.strip()))

# 応答の長さを棒グラフで表示します
plt.bar(prompts, response_lengths)
plt.title('Response lengths for different prompts')
plt.xlabel('Prompts')
plt.ylabel('Response Length')
plt.show()

*APIキーはハードコートしていません。詳細はこちらで

display関数

Pythonで値を表示するためには通常print関数を使用しますが、Jupyter Notebookではさらにdisplay関数も利用できます。

displayは、print関数と同じくあらゆるPythonオブジェクトを出力しますが、対応するフォーマットが定義されていればリッチな出力(HTML、画像など)を表示することができます。
結果を視覚化する際などに特に有用です。

display関数の利用例

HTMLの例

from IPython.display import display, HTML

# HTMLを作成
html_code = """
<div style="font-size: 20px; color: blue;">
  Hello <span style="color: red;">colorful</span> text!
</div>
"""

# HTMLをdisplay関数で表示
display(HTML(html_code))

Markdown

from IPython.display import display, Markdown

markdown_text = """
# H1 Header
## H2 Header
"""

display(Markdown(markdown_text))

画像

from IPython.display import display, Image

# ウェブからの画像
display(Image(url="https://example.com/image.png"))

# ローカルの画像ファイル
display(Image(filename="/path/to/image.png"))

YouTubeビデオ

from IPython.display import YouTubeVideo

# YouTubeのビデオIDを指定
display(YouTubeVideo('dQw4w9WgXcQ'))

オーディオ

from IPython.display import Audio

# ウェブからのオーディオ
display(Audio(url="https://example.com/audio.mp3"))

# ローカルのオーディオファイル
display(Audio(filename="/path/to/audio.mp3"))

LaTeX
*科学的や技術的な文書を美しく整形するための高度な文書作成システム

from IPython.display import Math

display(Math(r'F(k) = \int_{-\infty}^{\infty} f(x) e^{2\pi i k} dx'))

pandasライブラリを利用してテーブルを表示する
DataFrameは2次元のラベル付きデータ構造

import pandas as pd
from IPython.display import display
# データを作成します
data = {
    'Name': ['John', 'Anna', 'Peter', 'Linda'],
    'Age': [28, 24, 35, 32],
    'City': ['New York', 'Paris', 'Berlin', 'London']
}
# pandas の DataFrame を作成します
df = pd.DataFrame(data)
# テーブルを表示します
display(df)

OpenAI APIから取得した結果をdisplay関数で表示

OpenAIのGPT-3は”few-shot learning”という概念に基づいています。
これはモデルが少数の例(”shots”)を参照にして、特定のタスクを理解し、期待される出力を制御する能力です。

AIに「冬」をイメージする色と指示を出すとします。
“few-shot learning”を用いたプロンプトです。
最初のQ&Aは、AIにタスクを理解させる”shots”として機能します。
最後のQ&Aは未完成で、AIにこの文脈で期待する応答を生成させるためのものです。

Q: 次の説明文をカラーパレットの色のリストに変換してください:地中海
A: ["#006699", "#66CCCC", "#F0E68C", "#008000", "#F08080"]

Q: 次の説明文をカラーパレットの色のリストに変換してください:冬
A:

応答の形式を明示することが重要です。
この例では、期待される応答形式はJSON形式の16進数カラーコードの配列です。
応答形式を明示することで、AIの出力を制御しプログラムから再利用しやすい結果を得ることが可能になります。

import openai
from dotenv import dotenv_values
import json
from IPython.display import Markdown, display

# プロンプトを作成
msg = "冬"
prompt = f"""
あなたは、テキストプロンプトに応じたカラーパレットを生成するアシスタントです。
プロンプト内のテーマ、気分、指示に合ったカラーパレットを生成します。
パレットは2から8色の間です。

Q: 次の説明を色のリストに変換してください:地中海
A: ["#006699", "#66CCCC", "#F0E68C", "#008000", "#F08080"]

希望する形式:16進数のカラーコードのJSON配列

Q: 次の説明を色のリストに変換してください:{msg} 
A:
"""

# APIキーの設定
config = dotenv_values(".env")
openai.api_key = config["OPENAI_API_KEY"]

# APIを呼び出す
response = openai.Completion.create(
    model="text-davinci-003",
    prompt=prompt,
    max_tokens=200,
)

# 結果を取得して表示する
colors = json.loads(response.choices[0].text.strip())
Markdown(" ".join(
    f"<span style='display: inline-block; width: 50px; height: 50px; background-color: {color}'></span>"
    for color in colors
))

Chat Completions APIを使った場合

# APIキーの設定
config = dotenv_values(".env")
openai.api_key = config["OPENAI_API_KEY"]

# メッセージを設定
msg = "冬"
# メッセージリストの初期化
messages = [
    {"role": "system", "content": "あなたは、カラーパレットを生成するアシスタントです。プロンプト内のテーマ、気分、指示に合ったカラーパレットを生成します。パレットは2から8色の間です。希望する形式:16進数のカラーコードのJSON配列"},
    {"role": "user", "content": "次の説明を色のリストに変換してください:地中海"},
    {"role": "assistant", "content": '["#006699", "#66CCCC", "#F0E68C", "#008000", "#F08080"]'},
    {"role": "user", "content": f"次の説明を色のリストに変換してください:{msg}"}
]

# APIを呼び出す
response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=messages
)

# 結果を取得して表示する
colors = json.loads(response['choices'][0]['message']['content'])
Markdown(" ".join(
    f"<span style='display: inline-block; width: 50px; height: 50px; background-color: {color}'></span>"
    for color in colors
))

冬のイメージ