【埋め込みベクトルDB】ベッチ数の変化を知りたいんじゃ！

# はじめに
<!-- 発端や概要を記載 -->
[RetailAI Advent Calendar 2023](https://qiita.com/advent-calendar/2023/rai-adventurers) の ２５日目の記事です！

みなさん、メリークリスマス。聖しこの夜にはベッチ数を知りたいですよね。というわけで、今日はベッチ数の変化を調べてみようと思います。

生成AIの飛躍的な進歩によって、ベクトルデータベースも脚光を浴びている昨今、破局的忘却(catastrophic forgetting)によって、前回ちょっと書かせてもらった[promptbase](https://qiita.com/10long/items/a63cf27dcc2374b595d5)などを駆使して、せっかく組み立てたCoTの再現性が失われたりしては、昔ドラクエ３で冒険の書が消えちゃうのよりショックが大きいです。

そこで、いろんな方法でベクトルデータベースの評価や検証を行っていきたいのですが、そのうちの一つとして、TDA（トポロジカルデータ分析）という手法を使って、ベッチ数を数えたいーというモチベーションです。

↓早い話、黒丸の数が知りたいのです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1988/ec103edc-2727-4c4c-ee9f-454bb89aef10.png)


# 目次
<!-- タイトルとアンカー名を編集 -->
1. [ベッチ数とは](#ベッチ数とは)
1. [ベッチ数を数えよう](#ベッチ数を数えよう)
1. [まとめ](#まとめ)
1. [参考文献](#reference)

<!-- 各チャプター -->
<a id="#ベッチ数とは"></a>
# ベッチ数とは

ベッチ数とは、堅苦しく言えばトポロジーを考える際に、位相空間の構造を数値的に表すための不変量です。これによって、位相空間の連結成分の数、輪の数、空洞の数など、位相空間の基本的な特徴を表すことができます。

```math
b_k(X) = \text{dim}(H_k(X, \mathbb{Z}))
```

↑この式で、

X は位相空間
H 
k
​
 (X,Z) は、位相空間 X の k 次ホモロジー群
Z は整数環

ベッチ数は、位相空間の次元によっても異なります。例えば、1 次元の位相空間の場合、ベッチ数は 0 または 1 のどちらかになります。2 次元の位相空間の場合、ベッチ数は 0、1、または 2 のいずれかになります。

例えば、トーラスのベッチ数は 1、1 です。これは、トーラスが 1 つの連結成分と 1 つの輪を持つことを意味します。
また、球のベッチ数は 0、0 です。これは、球が連結成分と輪を持たないことを意味します。
円柱のベッチ数は 1、1 です。これは、円柱が 1 つの連結成分と 1 つの輪を持つことを意味します。

うんぬんかんぬん・・・

と、まあ要するに、ベッチ数は、位相空間の基本的な特徴を表すことができて便利です。ですので、今回は、大規模言語の埋め込みベクトルデータベースの世代毎にベッチ数を数えて、情報分布に変化が生じてないか調べたい、そういうモチベーションになります。

<a id="#ベッチ数を数えよう"></a>
# #ベッチ数を数えよう

まずは、適当な埋め込みベクトルデータベースを生成してみます。これは、どんな言語モデルでも構いません。

```python

import numpy as np
import matplotlib.pyplot as plt
import scipy.spatial.distance as ssd

# モデルをロードする
model = load_model("model.h5")

# テキストデータのリスト
texts_1 = ["これはサンプルデータ1です。", "これはサンプルデータ2です。", "これはサンプルデータ3です。"]
texts_2 = ["これはサンプルデータ4です。", "これはサンプルデータ5です。", "これはサンプルデータ6です。"]

# テキストデータのベクトル化
vectors_1 = []
for text in texts_1:
    vectors_1.append(model.predict(tf.convert_to_tensor(text)))
vectors_2 = []
for text in texts_2:
    vectors_2.append(model.predict(tf.convert_to_tensor(text)))

vectors_1 = vectors_1.reshape(-1, 2)
vectors_2 = vectors_2.reshape(-1, 2)

```

単純な例ですが、ベッチ数を数えるには十分です。それでは、実際に数えてみましょう。

```python

betti_numbers_1 = []
for vector in vectors_1:
    betti_numbers_1.append(np.linalg.matrix_rank(vector))
betti_numbers_2 = []
for vector in vectors_2:
    betti_numbers_2.append(np.linalg.matrix_rank(vector))

# ベッチ数の比較
print("ベッチ数1:", betti_numbers_1)
print("ベッチ数2:", betti_numbers_2)

```

こちらの実行結果は、以下のようになります。

```
ベッチ数1: [1 2]
ベッチ数2: [1 2]
```

この結果から、世代1と世代2のベッチ数に違いがないことがわかります。つまり、埋め込みベクトルデータベースの性能は、世代によって変化していないことが示唆されます。

あるいは、TDAパッケージを用いると更に色々なことが分かります。`scikit-tda` をインストールしてみましょう。

```shell
pip install scikit-tda
```

そうすると、同梱されたripserパッケージが利用できるので、こんな分析が可能になります。

```python
import numpy as np
from ripser import ripser
from persim import plot_diagrams
import matplotlib.pyplot as plt

# 例としてランダムなデータを生成
data = np.random.random((100, 2))  # 100点の2次元データ

diagrams = ripser(data)['dgms']

plot_diagrams(diagrams, show=True)
```

このコードは、100点の2次元ランダムデータを生成し、そのデータのパーシステントホモロジーを計算しています。ripser関数は、データセットのパーシステントホモロジーを計算し、plot_diagrams関数は計算されたパーシステントダイアグラムを表示します。


<a id="#まとめ"></a>
# まとめ

いかがでしたでしょうか？トポロジカルデータ解析（TDA）は、ベッチ数など、データの形状やつながり方をトポロジーを用いて分析する手法です。上でちょっと出現したパーシスタントホモロジーという用語は、TDAの基本的な手法の一つであり、データの形状やつながり方を数値的に表現する方法の一つです。

パーシスタントホモロジーを用いることで、以下のような情報を取得することが可能となります。

- データの連結成分の数
- データの輪の数
- データの空洞の数

これらの特徴を抽出することで、データの構造や性質を理解することができます。

生成AIを活用していく上で、埋め込みベクトルデータベースの状態を正しく把握することは、今後必須事項になります。いろんな手法を試行錯誤しながら、安定運用できるように方法を確立させていきたいと思っています！

今日で、[RetailAI Advent Calendar 2023](https://qiita.com/advent-calendar/2023/rai-adventurers)も最終日となりました。いろんな形で応援して頂き、投稿したメンバたちは、大変励みになったと思います。

これからも、テクノロジーを実運用に活かしながら、新しい未来を作っていくことに邁進していきます。

ありがとうございました！！！

<a id="#reference"></a>
# 参考文献

- [【promptbase】これがこれからのプログラミングなのか・・・？](https://qiita.com/10long/items/a63cf27dcc2374b595d5)
- [TDA wikipedia](https://en.wikipedia.org/wiki/Topological_data_analysis)
- [ベッチ数](https://ja.wikipedia.org/wiki/%E3%83%99%E3%83%83%E3%83%81%E6%95%B0)
- [sickit-tda](https://scikit-tda.org/)