背景
こちらの記事の続きになります。
umapで次元削減 -> hdbscanでクラスタリング
前回の記事で取得した(100, 1536)行列を、まずは(100, 適当な次元数)行列へ次元削減しようと思います。
次元削減を行う理由としては、1536次元は後続のクラスタリング処理にとっては多次元過ぎる気がする、になります。
次元削減のアルゴリズムには、pcaやt-sneなどもありますが、今回は新しそうなumapを使ってみます。
ここで、UMAPメソッドの引数n_componentsでは「何次元に次元削減するか」を指定する形になります。
決め打ちにする事も考えましたが、何かしらの理由を以て「何次元に次元削減するか」を決めたかったので、下記の形にしてみました。
hdbscanで最もクラスター数が多くなる次元
具体的には、
- 引数n_componentsを2で次元削減を行った後にhdbscanのデフォルト設定でクラスタリングを行ってクラスター数を取得
- 引数n_componentsを3で次元削減を行った後にhdbscanのデフォルト設定でクラスタリングを行ってクラスター数を取得
- ・・・
そして、最大のクラスター数の次元を、引数n_componentsの値にします。 何かしらの根拠がある訳ではありませんが、最もクラスター数が多いならば、うまくクラスタリング出来る次元、と割り切ってみました(笑)
では、NotebookインスタンスにPythonコードを書いてみます。
まずは、前回の記事から追加のライブラリーをインポートします。
import umap
import hdbscan
続いて、umapで次元削減を行う関数を作成します。UMAPメソッドの引数n_componentsに設定する値は、この関数の引数reducted_num_dimensionで取得するようにします。
def dimension_reduction(vector, reducted_num_dimension):
umap_ = umap.UMAP(n_components=reducted_num_dimension,
random_state=42,
n_jobs=1, # random_stateを設定してもエラーメッセージが表示されないようにするため
n_neighbors=15,
min_dist=0.1)
two_dimension_vector = umap_.fit_transform(vector)
return two_dimension_vector
次に、hdbscanでクラスタリングを行う関数を作成します。
def clustering_function(reducted_dimension_vector, hdbscan_cluster_min_size):
clustering_data = hdbscan.HDBSCAN(min_cluster_size=hdbscan_cluster_min_size)
clustering_data.fit(reducted_dimension_vector)
return clustering_data
それでは、umapを行う関数と、hdbscanを行う関数を使って、「hdbscanで最もクラスター数が多くなる次元」を探す関数を作ってみます。今回は、2~30次元を対象にして、探す形にしてみます。
途中で呼び出している関数clustering_functionは、先程のhdbscanでクラスタリングを行う関数になります。引数hdbscan_cluster_min_sizeは、hdbscanのHDBSCANメソッドの引数min_cluster_sizeへ設定する値になります。一旦は引数min_cluster_sizeのデフォルト値の5を使いました。
def search_reducted_dimension(vector_all):
reducted_vector_num_list = []
cluster_num_list = []
for reducted_num_dim in range(2, 30):
reducted_dimension_vector_all = dimension_reduction(vector=vector_all,
reducted_num_dimension=reducted_num_dim)
clustering_data = clustering_function(reducted_dimension_vector=reducted_dimension_vector_all,
hdbscan_cluster_min_size=5)
reducted_vector_num_list.append(reducted_num_dim)
cluster_num_list.append(len(np.unique(clustering_data.labels_)))
cluster_num_per_vector_num_df = pd.DataFrame(data={"reducted_vector_num": reducted_vector_num_list,
"cluster_num": cluster_num_list})
cluster_num_index = cluster_num_per_vector_num_df["cluster_num"].idxmax()
cluster_num_vector_dim = cluster_num_per_vector_num_df.iloc[cluster_num_index, 0]
print("クラスター数が最大値になるベクトル次元は", cluster_num_vector_dim)
cluster_num_min = cluster_num_per_vector_num_df.iloc[cluster_num_index, 1]
print("そのベクトル次元でのクラスター数は", cluster_num_min)
return cluster_num_vector_dim
では、search_reducted_dimension関数を使って、「hdbscanで最もクラスター数が多くなる次元」を探してみます。引数vector_allには、その1の記事でのアウトプットである変数text_vectorを設定します。
reducted_dim_num = search_reducted_dimension(vector_all=text_vector)
クラスター数が最大値になるベクトル次元は 24
そのベクトル次元でのクラスター数は 6
2~30次元では、24次元に次元削減した時、最もhdbscanでクラスター数が多かったようなので、1536次元ベクトルをUMAPで24次元ベクトルへ次元削減する事にします。
reducted_vector = dimension_reduction(vector=text_vector,
reducted_num_dimension=reducted_dim_num)
print(reducted_vector.shape)
(100, 24)
そして、hdbscanでクラスタリングを行います。
clustering_data = clustering_function(reducted_dimension_vector=reducted_vector,
hdbscan_cluster_min_size=5)
print(np.unique(clustering_data.labels_))
[-1 0 1 2 3 4]
想定通りの動きをしてくれました。
今回は以上になります。
次回は、同じクラスターに属する文章をclaudeに投げて、話題を取得してみます。
次の記事