Embeddingで作成したベクトルデータから、類似度を高いものを検索する

# はじめに
OpenAIの[Embedding](https://platform.openai.com/docs/api-reference/embeddings)を使うことで、文章を定量化（ベクトル化）できます。
今回は、その機能を使って、

1. データベースの文章をベクトル化しデータ構築
1. 入力された文章をベクトル化
1. データベースから最も近いベクトルを抽出

ということをします。
これを応用して、コールセンターの自動化などができるようです。

今回は、Google Colabratory上でやってみます。

# Google Colabのノートブックを作成する
まず、Google Colabのノートブックを新規作成しましょう。

https://colab.research.google.com/drive/1ShJGecoBiJXG66fBldLLr4GJ8cSOVCu7#scrollTo=OIPOs0CD_4G1

# OpenAIのインストール
Google Colabには、様々なライブラリが初めからインストールされていますが、OpenAIはインストールされていないようです。
インストールしましょう。
```bash
!pip install openai
```

# OpenAIをインポートし、APIキーを設定
インストールできたら、インポートして、APIキーを設定しましょう。
```python
import openai

openai.api_key = "ご自身のAPIキー"
```
これでEmbeddingとChatGPTが使えます。

# データベース構築
まずは、データベースを構築しましょう。
今回は、企業情報をベクトル化します。
```
data = [
    {
        "company" : "A社",
        "text" : "A社は、大手IT企業であり、常に最先端の技術開発に取り組んでいます。本社は東京にありますが、リモートワークも積極的に導入しており、勤務地については柔軟な対応をしています。従業員数は5,000人以上で、AI、クラウド、ブロックチェーンなど、多岐に渡る領域で事業を展開しています。厚生労働大臣賞を受賞した福利厚生制度は、従業員のワークライフバランスを大切にしています。"
    },
    {
        "company" : "B社",
        "text" : "B社は、グローバルに展開する食品製造会社で、健康と栄養に配慮した商品開発で知られています。従業員数は3,000人以上で、本社は大阪にあります。オフィス内には健康診断ルームやフィットネスジムが完備されており、従業員の健康を重視した福利厚生が充実しています。"
    },
    {
        "company" : "C社",
        "text" : "C社は、スタートアップのフィンテック企業で、スピーディーな決済システムの開発と提供で注目を集めています。東京に本社を構え、若くて活気のあるチームが働いています。福利厚生はスタートアップらしく、フレックスタイム制度やリモートワーク対応、ストックオプション制度などがあります。"
    },
    {
        "company" : "D社",
        "text" : "D社は、教育業界に革新をもたらすEdTech企業で、子供たちの未来をデジタル技術で支えています。本社は京都にあり、国内外から高度な教育理念を収集し、新たな学びの形を提供しています。従業員には、教育の現場に触れる機会を提供し、教育現場のニーズを深く理解する文化があります。"
    },
    {
        "company" : "E社",
        "text" : "E社は、自動車産業のリーディングカンパニーで、エコカー開発の先駆けとして世界中から注目されています。本社は名古屋にあり、従業員数は10,000人以上です。社員教育に力を入れており、エンジニアからマネージメントまで多様なキャリアパスを描くことが可能です。"
    },
    {
        "company" : "F社",
        "text" : "F社は、リーダブルなAIソリューションを提供するスタートアップ企業です。AIのブラックボックス化を解消し、説明可能なAIを開発しています。小規模なチームではありますが、その製品は国内外の大手企業から高い評価を受けています。社員の自主性を重視したフラットな組織文化が特徴です。"
    },
    {
        "company" : "G社",
        "text" : "G社は、映画製作会社で、大衆の心をつかむストーリーテリングで知られています。従業員のクリエイティビティを重視し、豊かな表現力と個性を活かすことができる職場環境を提供しています。本社は東京ですが、映画の製作地により、一時的な海外赴任もあります。"
    },
    {
        "company" : "H社",
        "text" : "H社は、家具・インテリアの小売業で、そのオリジナリティとデザイン性の高さで評価されています。社内には、デザイナーや職人、マーケティングスタッフなど、多様なバックグラウンドを持つメンバーがいます。本社は福岡にあり、福利厚生として社員割引制度や健康支援制度が充実しています。"
    },
    {
        "company" : "I社",
        "text" : "I社は、医療業界向けのITソリューションを提供する企業で、医療現場の問題解決に貢献しています。本社は神戸にあり、医療従事者やエンジニアが協力して、使いやすく、実効性のあるソリューションを開発しています。福利厚生として、健康診断や医療保険の補完サービスが充実しています。"
    },
    {
        "company" : "J社",
        "text" : "J社は、ホテルチェーンを全国に展開する企業で、そのおもてなしの精神と、地域の文化を尊重したサービスが高く評価されています。本社は札幌にあり、全国各地のホテルでの勤務が可能です。以下、10件の企業情報をサンプルとして提供します。"
    }
]
```
ChatGPTに適当に作らせました。

これらの文章をベクトル化しましょう。
```index```というリストに、会社名と説明、ベクトルを格納していきます。
```python
index = []
for datum in data:
    # ベクトル化
    vec = openai.Embedding.create(
      model='text-embedding-ada-002',
      input=datum["text"]
    )

    # ベクトルをデータベースに追加
    index.append({
        'title': datum['company'],
        'body': datum['text'],
        'embedding': vec['data'][0]['embedding']
    })
```
これでデータベース構築は完了です。

# 入力された文章をベクトル化
次に、入力された文章をベクトル化します。
```python
INPUT = "私は教育事業に興味があり、関西方面での勤務を希望しています。"

vec = openai.Embedding.create(
    model='text-embedding-ada-002',
    input=INPUT
)
vec = vec['data'][0]['embedding']
```

# 検索
次に、入力と近いベクトルをデータベース内で探していきます。
近いベクトルを探すには、ベクトルの内積を使用します。
cosθの値が1に近いほど、近いベクトルということです。

まずは、データベースのベクトルとの類似度を計算します。
結果は、```results```という新しい変数に格納していきます。
総当たりの計算には```map```と```lambda```関数を使っています。
```python
# 総当りで類似度を計算
from openai.embeddings_utils import cosine_similarity
results = map(
        lambda i: {
            'title': i['title'],
            'body': i['body'],
            # ここで入力とデータベースのコサイン類似度を計算
            'similarity': cosine_similarity(i['embedding'], vec)
            },
        index
)
```

入力された文章とデータベースの各文章の類似度が計算できたので、コサイン類似度でソートします。
```reverse=True```なので、大きい順に並び替えてます。
```python
results = sorted(results, key=lambda i: i['similarity'], reverse=True)
```

# 結果を出力
並び替えが終わったので、一番類似度が高かったデータを出力しましょう。
```python
print(f'title: {results[0]["title"]}')
print(f'body: {results[0]["body"]}')
```
今回の例ですと、D社が出てくれば正解です。

# 問題点
今回作成したプログラムには問題点があります。
それは、「私は教育事業に興味があり、関西方面での勤務を希望しています。どのような企業がおすすめですか？」など、余計な文章が入っていると、うまく類似度を計算できないことです。

実際、INPUTのところを変えて試してみると、C社が一番となりました。
C社は東京の決済システムの開発をしている会社なので、失敗していることがわかります。

# おわりに
問題点を解決するプログラムは、別の記事で紹介します。
今回は、類似度を高いものを検索できたのでOKです。

本記事がどなたかのお役に立てるとうれしいです。
参考になったと感じたら、いいねとストックお願いします！！