OpenAIのEmbeddingについて検証してみた

# はじめに

ChatGPTのアプリケーションを構築する上で、Embeddingの活用は結構多いシナリオかと思います。私もEmbeddingをつかったセマンティック検索をやったりしてるのですが、一方でEmbeddingの特性とか向き不向きをあまり理解しないで、なんとなく検索に使っているなぁと思ったので、幾つかのパターンでEmbeddingについて検証してみました。

以下の3パターンでの検証をしてみたいと思います。なお、つかったモデルは`text-embedding-ada-002`です。[公式推奨モデル](https://platform.openai.com/docs/guides/embeddings/embedding-models)ですね。

1. FAQの検索に使うパターン
1. マニュアルの検索に使うパターン
1. 文章(メールとか)の検索に使うパターン

**ちょっと検証が長いので、最後の考察だけ見たいよという方は[こちらのまとめ](#まとめ)を見てください。**

なお、プログラム的にはこんな処理をFastAPIのサーバサイドでやっています。
```python
@router.post("/embedding")
async def embedding(query: Query):

    embedding_word = encode_content(query.word)
    embedding_target = encode_content(query.target)

    similarity = get_similarity(embedding_word,embedding_target)

    data = {
        "similarity": similarity,
        "embedding" : embedding_target
    }
    return JSONResponse(content=data)
```
encodeとコサイン近似度は以下のメソッドで取得します。とても単純ですね
```python
def encode_content(content):
    embedding = get_embedding(content, settings.AOAI_EMB_MODEL)
    return embedding

def get_similarity(embedding1, embedding2):
    similarity = cosine_similarity(embedding1, embedding2)
    return similarity
```

# 1. FAQの検索
FAQの検索にあたっては、まずQで検索するのかAで検索するのかというポイントもあるかと思います。今回対象としたFAQはBing Chatで適当に作った以下です。
|質問|回答|
|:-:|:-:|
|宇宙旅行の予約はどのようにすればいいですか？|宇宙旅行の予約は、インターネットや電話で行うことができます。インターネットで予約する場合は、各宇宙旅行会社のホームページからお好みのプランを選んで、必要事項を入力して予約依頼を送信します。電話で予約する場合は、お近くの販売店や宇宙旅行予約センターにお問い合わせください。宇宙旅行には、健康診断や訓練などの条件がありますので、事前にご確認ください。|
|宇宙旅行の支払い方法は何がありますか？|宇宙旅行の支払い方法は、クレジットカード、デジタルウォレット、仮想通貨などがあります。インターネットで予約した場合は、クレジットカードやデジタルウォレットなどのオンライン決済が便利です。電話で予約した場合は、仮想通貨での支払いが可能です。宇宙旅行は高額な商品ですので、分割払いやローンなどのサービスもあります。|
|宇宙旅行をキャンセルしたい場合はどうすればいいですか？|宇宙旅行をキャンセルしたい場合は、早めに宇宙旅行会社に連絡してください。キャンセル料が発生する場合があります。キャンセル料の額は、旅行開始日までの日数や旅行代金によって異なります。詳しくは、各宇宙旅行会社のキャンセル規定をご確認ください。また、健康診断や訓練などの費用も返金されない場合があります。|
|宇宙旅行中に緊急事態が発生したらどうすればいいですか？|宇宙旅行中に緊急事態が発生したら、まず現地のスペースホテルやスペースシップなどに対応を求めてください。それでも解決しない場合は、宇宙旅行会社の緊急連絡先に連絡してください。また、宇宙旅行保険に加入している場合は、保険会社にも連絡してください。緊急事態の内容や証拠などを記録しておくと、後日の対応がスムーズになります。|
|宇宙旅行をキャンセルしたのですが、キャンセルを取り消すことはできますか？|宇宙旅行をキャンセルした場合、キャンセル猶予に3時間の待機時間があります。3時間以内でしたらキャンセルを取り消すことができますので、なるべく早く各宇宙旅行会社へご連絡ください。なお、3時間を超えた場合はいかなる理由であってもキャンセルの取消はできません。|

## QからQを探す
まずはQの一覧から、代表的なQに対してのベクトルマッチを検証してみます。サーバサイドだけでやるもの味気ないので、以下のような画面をもとに色々さわっていきます。（画像は「予約したい」をベクトル化してコサイン近似度をとった場合です。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/977156/6eb54dfe-d505-36e6-4e4e-9fa0e7bfaf0c.png)

以下のキーワードで期待通りのQが一番コサイン近似度が高くなっていることを確認しました。良い感じですね。

 - 予約したい
 - トラブルがあったらどうしたらいいの？
 - キャンセルをキャンセルできますか？
 - 支払方法は？
 - 旅程を取り消したい
 - キャンセル方法
 - キャンセル方法についてい知りたい
 - 宇宙旅行したい！
 
しかしいずれのキーワードも近似度0.8x～という高い近似度に見えます。例えば「宇宙旅行したい！」の場合（↓）、予約方法の近似度は0.8939...ですが、キャンセル方法の近似度は0.8849...となっています。どうやらコサイン近似度はある程度近い文脈の場合軒並みそれなりに高い数字が出るみたいですね。0.01違ったら十分違うものという取り扱いが必要そうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/977156/d9ec1975-cc9e-17b0-d963-49504d193a91.png)

QにQを検索させる方法は、最近似値をもつものを選択するとそれなりに精度はでそうですね。

## QからAを探す
次にFAQの回答部分をもとに、上記と同じキーワードで検索してみます。基本的には一番近似の高いものは最適なものだったのですが、「キャンセル方法」というキーワードについては、キャンセルの取り消しを最近似としてしまいました。

 - 予約したい
 - トラブルがあったらどうしたらいいの？
 - キャンセルをキャンセルできますか？
 - 支払方法は？
 - 旅程を取り消したい
 - キャンセル方法 -> **キャンセルの取消を最近似としてしまった(下図)**
 - キャンセル方法についてい知りたい
 - 宇宙旅行したい！

下を見ると、コサイン近似度が、キャンセルにまつわるものが高く出ていて、それ以外は低くなっているものの、最近似は期待とは違う回答を出してしまいましたね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/977156/1502332b-38c8-e47e-71bf-e6c72dbc8e34.png)

なお、QからAを検索する方式の場合、ベクトル近似値が結構離れた結果になりました。以下は、「宇宙旅行したい！」というモノの結果ですが、QからQに比べてコサイン近似度に結構開きがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/977156/c9c359e7-0991-2536-92bd-9d30797847bc.png)

QからAの検索の場合、最近似度から0.02ずれているものは全く無関係として扱うことができそうです。そのため、**複数回答を引っ張ってきてChatGPTに取り込むような分析をする場合は、QからA方式の方が検索結果の取り扱いは楽かもしれません。**

やはりEmbeddingの結果のベクトルには、文脈情報が含まれるため、ある程度の文章と文章をぶつけると良い結果が出そうです。

## FAQ形式のまとめ
今回の検証だけでみると、Q-Q方式の方が精度は高くなりましたが、実際のFAQを取り込む場合、より似たようなQが多く存在することになるかと思います。そのため、Q-Q方式の近似度があまり離れないスタイルはちょっと向かない気がします。

そのためFAQであれば、Q-A方式にしたうえで、コサイン近似度が一番高かったものから0.02くらいの範囲内のAをChatGPTに取り込む形で回答をつくるのがよさそうですね。

（もちろんFAQの内容にとっても依存すると思うので、是非お持ちのFAQで検証してみてください。）

# 2. マニュアルの検索
マニュアルについては、段落単位で架空の製品マニュアルをこれまたBing Chatでつくってみます。以下のような内容です。

=====================================

太陽ボードの製品マニュアルを作成しました。以下にご覧ください。

## 太陽ボードの製品マニュアル

### 製品のスペックやメリット
太陽ボードは、太陽電池で稼働する電動キックボードです。太陽光が当たる場所であれば、どこでも充電することができます。最高速度は時速30km、最大航続距離は100kmです。軽量でコンパクトなデザインなので、持ち運びや収納も簡単です。環境にやさしく、経済的な移動手段としておすすめです。

### 電源を入れる方法
太陽ボードの電源を入れるには、本体の前面にある電源ボタンを押します。電源ボタンの横にある液晶画面に、バッテリー残量や速度などの情報が表示されます。電源を切るには、電源ボタンをもう一度押します。

### ブースターを使った加速方法
太陽ボードには、ブースターという機能があります。ブースターを使うと、一時的に最高速度を時速40kmまで上げることができます。ブースターを使うには、本体の後部にあるブースターボタンを押します。ブースターボタンは、押している間だけ有効です。ブースターを使うと、バッテリーの消費が早くなりますので、注意してください。

### トラブルの際に本体に表示されるエラーコードとそれぞれの内容
太陽ボードは、トラブルが発生した場合に、液晶画面にエラーコードという数字を表示します。エラーコードとそれぞれの内容は以下の通りです。

- EPM-001: 太陽電池の故障。太陽電池が正常に充電できない場合に表示されます。太陽光が当たっているか確認してください。それでも改善しない場合は、修理に出してください。
- EPM-002: バッテリーの故障。バッテリーが過放電や過充電などで損傷した場合に表示されます。バッテリーを交換してください。
- EMM-003: モーターの故障。モーターが異常な温度や振動などを検知した場合に表示されます。速度を落として停止してください。それでも改善しない場合は、修理に出してください。

以上が太陽ボードの製品マニュアルです。ご購入ありがとうございます。

=====================================

キチンと段落分けできていれば、まぁこのくらいの文章なら問題なく検索できます。（下）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/977156/a04755a1-009e-0de4-1d0a-c60f34846ccc.png)

文章が長いので、コサイン近似度も結構開きますし、これは扱いがいがありますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/977156/4df7c915-1371-f92a-0294-7c8f0f051e3f.png)

ただ、もし段落分けが微妙だと以下のようにコサイン近似度も微妙ですし、期待した場所も取れなくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/977156/432bfd15-8364-654c-1e27-122f43b4684f.png)

## マニュアル検索のまとめ

もうこれはマニュアルを意味単位でどれだけうまく段落分けできるか次第という感じです。Embeddingはうまく段落分けできていれば期待通り動きますし、そうなければ精度が下がりますね。でもこれは違う話題になるので、段落分けは要注意ということで。。

コサイン近似度についてはある程度の文章の固まりがあると近似度が0.7~0.8xまで開くので、上から複数をとるとかはFAQと同じくやればいい感じそうです。

# 3. メールの検索
これまたBing Chat で以下の3つのメールを作成しました。

1通目
===================================================
件名：動画配信サービスXのネットワーク仕様について

本文：
動画配信サービスXをご利用いただきありがとうございます。
ネットワーク仕様に関するお問い合わせをいただきましたので、回答させていただきます。
動画配信サービスXでは、以下のネットワーク仕様を採用しております。

- 動画ファイルはMP4形式でエンコードされています。
- 動画ファイルは分割されて複数のサーバーに分散して保存されています。
- 動画ファイルはストリーミング再生されますが、ダウンロードも可能です。
- 動画ファイルは自動的にユーザーの回線速度やデバイスに合わせて最適な画質で再生されます。

以上が動画配信サービスXのネットワーク仕様です。
もし、ご不明な点やご要望がございましたら、お気軽にお問い合わせください。
今後とも動画配信サービスXをよろしくお願いいたします。


2通目
===================================================
件名：動画配信サービスXの新サービス発表イベントの開催のお知らせ

本文：
動画配信サービスXをご愛顧いただきありがとうございます。
この度、動画配信サービスXでは新たなサービスを発表するイベントを開催することとなりました。
イベントでは、新サービスの内容や特徴、利用方法などを詳しくご紹介する予定です。
また、参加者には先着順で新サービスの無料体験券やオリジナルグッズなどをプレゼントいたします。
イベントはオンラインで行われますので、どなたでも気軽に参加できます。

イベントの詳細は以下の通りです。

- 日時：2023年6月30日（金）19:00～20:30
- 場所：ウェブ会議システム（参加URLは後日お知らせいたします）
- 参加費：無料
- 申し込み方法：動画配信サービスXの公式サイトからお申し込みください

イベントにご興味がございましたら、ぜひお申し込みください。
皆様のご参加を心よりお待ちしております。


3通目
===================================================
件名：動画配信サービスXの障害について

本文：
動画配信サービスXのカスタマーサポート担当の山田太郎と申します。
先日、動画配信サービスXにおいて障害が発生し、一部の機能が利用できない状況となりましたことをお詫び申し上げます。
現在、復旧作業を行っておりますが、復旧の見込みはまだ立っておりません。
障害の原因は、サーバーの過負荷によるものと推測されております。
復旧の見込みが立ち次第、改めてご連絡いたします。
ご不便をおかけして申し訳ございませんが、何卒ご理解とご協力をお願いいたします。



もうある程度結果が自明ですが、期待する検索結果が出せますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/977156/75ed478d-de2c-2a7a-2db0-44d81631298b.png)

## 3.メール検索の考察

検索対象の文章量が多いので、コサイン近似度もうまくバラけますね。他の結果もそうなんですが、0.8以下はどうやら検索に箸にも棒にも引っかからなかったという結果になっていそうです。また以下のような「お知らせ」を探した場合は最もお知らせっぽいものが0.9を出しているので、このあたりも良さげな結果ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/977156/138e402e-c1b7-f36c-8aee-e06cb370a9a1.png)

# まとめ

Embeddingによるベクトル検索（コサイン近似度）の場合、以下の観点が重要ということが見えてきました。
- 検索対象は、ある程度文章量があったほうがよい（文脈を加味した検索をするので）
    - 検索ワードとしても、単語だけが入力されると真価が発揮できないので、注意が必要そう
- 0.8未満のコサイン近似度は、検索にひっかからなかったを意味しそう
- 文章量が多いと、コサイン近似度がある程度幅広くなるので、最高近似度から-0.01~-0.02くらいまでを検索ヒットとみなせそう

さらに、2のパターンで検証しましたが、文章の分けをどう扱うかが前処理として非常に重要ということも見えました。

今回の検証では全文検索との比較なんかはしておりませんが、ベクトル検索とても魅力的ですね、スピードが速いのもいいです。色々と見えてきた感じなので、あとは本当の検索対象をもとに色々といじってみたいなと思います。（本当ってなんだ）