😎

OpenAIの text embeddingsで取得できるベクトルを調査する

2023/09/05に公開

概要

OpenAIの text embeddingsを使うとテキストをベクトルに変換することができます。
https://platform.openai.com/docs/guides/embeddings

このベクトルはどういう特徴があるのか、これを使うと何が出来るのかなど気になったので調査しました。

解説

google colabで実行します。

ライブラリのインストール

!pip install openai japanize_matplotlib

ライブラリのimport

import openai

import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import japanize_matplotlib

from openai.embeddings_utils import cosine_similarity, get_embeddings

api keyのセット

import os
openai.api_key = "sk-から始まるopenaiのシークレットキー"

embeddingするテキスト

TEXTS = [
    '焼肉は美味しい',
    '焼肉は美味しくない',
    '刺し身は美味しい',
    '刺し身は美味しくない',
    'ジュゲムジュゲム',
]

各テキストの埋め込みを取得

embeddings = get_embeddings(TEXTS, engine="text-embedding-ada-002")

ベクトルの形状をチェック

# 次元数
print('--- len(embeddings[0])')
print(len(embeddings[0]))

# ベクトルを何次元か出力
print('--- embeddings[0][:3]')
print(embeddings[0][:3])

# ノルム
norms = [np.linalg.norm(np.array(embedding)) for embedding in embeddings]
print('--- norms')
print(norms)
--- len(embeddings[0])
1536
--- embeddings[0][:3]
[-0.00018179217295255512, -0.0057162633165717125, -0.002747262828052044]
--- norms
[1.0000000089330063, 1.0000000193200298, 0.9999999591016167, 0.999999979812748, 1.0000000512624498]

1536次元のベクトルに変換されることが分かります。
ベクトルのノルム(長さ)はほぼ1になります。

各テキスト同士のコサイン類似度を表示

# 空のDataFrameを作成
df = pd.DataFrame(index=TEXTS, columns=TEXTS)

# コサイン類似度を計算してDataFrameに格納
for i, vec_a in enumerate(TEXTS):
    for j, vec_b in enumerate(TEXTS):
        similarity = cosine_similarity(
            np.array(embeddings[i]),
            np.array(embeddings[j])
        )
        df.iloc[i, j] = similarity

# Matplotlibでの表示
fig, ax = plt.subplots()
cax = ax.matshow(df.astype(float), cmap="coolwarm")
fig.colorbar(cax)

ax.set_xticks(np.arange(len(df.columns)))
ax.set_yticks(np.arange(len(df.index)))

ax.set_xticklabels(list(df.columns), rotation=45)
ax.set_yticklabels(list(df.index))

# 各セルに数値を描画
for i in range(len(df.index)):
    for j in range(len(df.columns)):
        c = df.iloc[i, j]
        ax.text(j, i, str(round(c, 2)), va='center', ha='center')

plt.show()

「焼肉は美味しい」「焼肉は美味しくない」
「刺し身は美味しい」「刺し身は美味しくない」
はそれぞれ意味的に逆の事を言っているので、コサイン類似度は小さくなるのではと思ったのですが、そうはならないようです。

コサイン類似度が近い => 近いジャンルのテキスト
位に思っておけば良さそうです。

一般的にembeddingは文章検索に使われることが多そうですが、意味的には逆のテキストでも検索順位としては上に来るということがわかります。

embeddingで何ができるのか

ただ、embeddingには意味情報が入っているので、そこから分析や学習が出来るようです。

https://platform.openai.com/docs/guides/embeddings/use-cases
「Classification using the embedding features」

にある通りembeddingを特徴量にして分類問題をとくモデルを作ってみます。

各テキストを「ポジティブ」「ネガティブ」「ニュートラル」に分類するモデルを学習します。

from sklearn.ensemble import RandomForestClassifier

# 各テキストがポジティブかネガティブか学習させる
# 2 => positive
# 1 => negative
# 0 => neutral
rfr = RandomForestClassifier(n_estimators=100)
rfr.fit(embeddings, [
    2, # '焼肉は美味しい'
    1, # '焼肉は美味しくない'
    2, # '刺し身は美味しい'
    1, # '刺し身は美味しくない'
    0  # 'ジュゲムジュゲム'
])

preds = rfr.predict(get_embeddings([
    '野菜は美味しい',
    '野菜は美味しくない',
    'ラーメンうめー',
    'ラーメンまずい',
    'ごこうのすりきれ',
], engine="text-embedding-ada-002"))
print(preds)
[2 1 2 1 2]

最後だけ0を期待していましたが間違えてしまいました。
(訓練データ量が少なすぎ?)
ただ、応用すれば色々できそうな気がします。

まとめ

embeddingsの挙動がなんとなく理解できました。

LLMはファインチューニングだけだとハルシネーションがのリスクが無くならない思うので、プロンプトに埋め込む情報が重要になってくると思います。

その際こういったテクニックが生きてくる場合もあるのではと思いました。

Discussion