ベクトルDBサービスPineconeを試す

# 目次
- ベクトル埋め込みとは
- 使い方
- 感想
- 参考

# ベクトル埋め込みとは
文章や画像のデータを数ベクトル化すること。ベクトル同士を用いて類似性を図れるようになるため、セマンティック検索やレコメンドに応用されています。ベクトルなので専用のDBが必要ですが、Pineconeはそれが可能なサービスの一種です。ここではその使い方を紹介します。
# 使い方
### 登録
[Pinecone](https://www.pinecone.io/)からサインアップしてください。メールアドレスだけで簡単に使えるようになります。コンソール画面からAPIキーを確認できたらOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3077345/4acb9760-84ed-151e-ec58-bdf2f13a9927.png)
- Starter planでもAPIキーを複数保有できますが、インデックスは1つしか作成できません
### インデックスを作成
今回はローカルから操作したいので、まずは`pinecone-client`をインストールします。
```
pip install pinecone-client
```
完了したら上で確認したAPIキーを使ってPineconeを初期化します。
```py
from pinecone import Pinecone, PodSpec
pc = Pinecone(api_key='<APIキー>')
```
- PodSpec：インデックスを設定する際に必要

ここまでできたら次でインデックスを作成します。
```py
pc.create_index(
    name="command-test-cosine", 
    dimension=3, 
    metric="cosine",
    spec=PodSpec(
        environment='gcp-starter', 
        pod_type='starter'
    )
)
```
- `name`：インデックスの名前
- `dimension`：格納するデータ（ベクトル）の次元のこと。ここでは3にしていますがもっと巨大にもできます
- `metric`：ベクトルDBではクエリ（ベクトルをリクエストする）との類似度が高いデータを返します。Pineconeでは３種類の設定ができます
    - `euclidean`：リクエストしたベクトルとのユークリッド距離を計算し、近いものから返します
    - `dotproduct`：リクエストしたベクトルとの内積を計算し、近いものから返します
    - `cosine`：リクエストしたベクトルとのコサイン類似度を計算し、近いものから返します
- `spec`：serverlessかpodを選択できます。今回はpodで作りますが、Starter planの場合は上で書いた環境しか使えないようです

### データの挿入・取得
DBに接続して使えるようにします。
```py
index = pc.Index(name="command-test-cosine")
```
まだDBには何も入っていない状態なので、データを挿入しましょう。数値は`float`で入力してください。
```py
index.upsert([
    ("X", [1.0, 0.0, 0.0]),
    ("Y", [0.0, 1.0, 0.0]),
    ("Z", [0.0, 0.0, 1.0]),
    ("A", [1.0, 1.0, 1.0])
])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3077345/63136bd6-5eed-8adc-3a41-1478aaf1dc03.png)

これで4つのベクトルが格納されました。以下コードでも確認できます。
```py
index.describe_index_stats()
```
```
{'dimension': 3,
 'index_fullness': 4e-05,
 'namespaces': {'': {'vector_count': 4}},
 'total_vector_count': 4}
```
それではベクトルを取得してみます。まずは原点をリクエストしてみましょう。
```py
index.query(
    vector=[0.0, 0.0, 0.0],
    top_k=4,
    include_values=True
)
```
- `vector`：基準となるベクトル
- `top_k`：類似度の高いベクトルをいくつ取得するか設定できる
- `include_values`：レスポンスにベクトルの中身を含めるか設定できる
```
{'matches': [{'id': 'A', 'score': 0.0, 'values': [1.0, 1.0, 1.0]},
             {'id': 'Y', 'score': 0.0, 'values': [0.0, 1.0, 0.0]},
             {'id': 'Z', 'score': 0.0, 'values': [0.0, 0.0, 1.0]},
             {'id': 'X', 'score': 0.0, 'values': [1.0, 0.0, 0.0]}],
 'namespace': '',
 'usage': {'read_units': 6}}
```
コサイン類似度は全て0になりました。そもそも意味のないリクエストなので、0で返すようになってたりするんでしょうか。
次は`(1, 0, 0)`をリクエストしてみます。
```py
index.query(
    vector=[1.0, 0.0, 0.0],
    top_k=4,
    include_values=True
)
```
```
{'matches': [{'id': 'X', 'score': 1.0, 'values': [1.0, 0.0, 0.0]},
             {'id': 'A', 'score': 0.577350259, 'values': [1.0, 1.0, 1.0]},
             {'id': 'Z', 'score': 0.0, 'values': [0.0, 0.0, 1.0]},
             {'id': 'Y', 'score': 0.0, 'values': [0.0, 1.0, 0.0]}],
 'namespace': '',
 'usage': {'read_units': 6}}
```
類似するベクトルが降順にレスポンスされています。
`(-1, 0, 0)`もリクエストしてみます。
```py
index.query(
    vector=[-1.0, 0.0, 0.0],
    top_k=4,
    include_values=True
)
```
```
{'matches': [{'id': 'Y', 'score': 0.0, 'values': [0.0, 1.0, 0.0]},
             {'id': 'Z', 'score': 0.0, 'values': [0.0, 0.0, 1.0]},
             {'id': 'A', 'score': -0.577350259, 'values': [1.0, 1.0, 1.0]},
             {'id': 'X', 'score': -1.0, 'values': [1.0, 0.0, 0.0]}],
 'namespace': '',
 'usage': {'read_units': 6}}
```
コサイン類似度の下で類似性の高いベクトルが返ってくることがわかりました。

### インデックスの削除
使い終わったらインデックスを削除することができます。
```py
pc.delete_index("command-test-cosine")
```

# 感想
ベクトルDBを簡単に体験できました。次はセマンティック検索を実装してみようと思います。

# 参考
- [Pinecone Documentation](https://docs.pinecone.io/)