機械学習において、アンサンブルという用語は、複数のモデルを並行して組み合わせることを指します。その考え方は、グループの知恵を活用して、最終的な答えに対するより良い合意を形成することです。 このタイプのアプローチは、教師あり学習の分野で、特に分類問題において、RandomForest のような非常に成功したアルゴリズムとともに広く研究され、適用されてきました。通常、各個別のモデルの出力を、より堅牢で一貫性のある最終的な出力に結合するために、何らかの投票/重み付けシステムが適用されます。 教師なし学習の分野では、このタスクはさらに困難になります。まず、この研究は分野自体の課題を網羅しているため、データに関する事前知識がなく、自分たちをいかなるターゲットとも比較することができません。第二に、すべてのモデルからの情報を組み合わせる適切な方法を見つけることが依然として問題であり、これをどのように行うかについてコンセンサスが得られていないためです。 この記事では、このトピックに関する最良のアプローチ、つまり類似性マトリックスのクラスタリングについて説明します。 この方法の主な考え方は、データセット X が与えられたときに、Si が xi と xj の類似度を表すように行列 S を作成することです。マトリックスは、いくつかの異なるモデルからのクラスタリング結果に基づいて構築されます。 二変量共起行列モデルを構築する最初のステップは、入力間のバイナリ共起マトリックスを作成することです。 2 つの入力 i と j が同じクラスターに属しているかどうかを示すために使用されます。 import numpy as np from scipy import sparse def build_binary_matrix( clabels ): data_len = len(clabels) matrix=np.zeros((data_len,data_len)) for i in range(data_len): matrix[i,:] = clabels == clabels[i] return matrix labels = np.array( [1,1,1,2,3,3,2,4] ) build_binary_matrix(labels)
KMeans を使用した類似度行列の構築クラスターを 2 値化する関数を構築したので、類似性マトリックスの構築に進むことができます。 ここでは、M つの異なるモデルによって生成された M 個の共起行列間の平均値を計算するだけの最も一般的な方法を紹介します。定義: このように、同じクラスターに属するエントリの類似度値は 1 に近くなり、異なるグループに属するエントリの類似度値は 0 に近くなります。 K-Means モデルによって作成されたラベルに基づいて類似度マトリックスを構築します。これは MNIST データセットを使用して行われました。簡潔さと効率性のため、PCA を使用して縮小された 10,000 枚の画像のみを使用します。 from sklearn.datasets import fetch_openml from sklearn.decomposition import PCA from sklearn.cluster import MiniBatchKMeans, KMeans from sklearn.model_selection import train_test_split mnist = fetch_openml('mnist_784') X = mnist.data y = mnist.target X, _, y, _ = train_test_split(X,y, train_size=10000, stratify=y, random_state=42 ) pca = PCA(n_components=0.99) X_pca = pca.fit_transform(X)
モデル間の多様性を可能にするために、各モデルはランダムな数のクラスターでインスタンス化されました。 NUM_MODELS = 500 MIN_N_CLUSTERS = 2 MAX_N_CLUSTERS = 300 np.random.seed(214) model_sizes = np.random.randint(MIN_N_CLUSTERS, MAX_N_CLUSTERS+1, size=NUM_MODELS) clt_models = [KMeans(n_clusters=i, n_init=4, random_state=214) for i in model_sizes] for i, model in enumerate(clt_models): print( f"Fitting - {i+1}/{NUM_MODELS}" ) model.fit(X_pca)
次の関数は類似度行列を作成する。 def build_similarity_matrix( models_labels ): n_runs, n_data = models_labels.shape[0], models_labels.shape[1] sim_matrix = np.zeros( (n_data, n_data) ) for i in range(n_runs): sim_matrix += build_binary_matrix( models_labels[i,:] ) sim_matrix = sim_matrix/n_runs return sim_matrix この関数を呼び出します: models_labels = np.array([ model.labels_ for model in clt_models ]) sim_matrix = build_similarity_matrix(models_labels)
最終結果は次のとおりです。 類似性マトリックスからの情報は、対数、多項式などの変換を適用するなど、最後のステップの前に後処理することができます。 私たちの場合、変更は行いません。 Pos_sim_matrix = sim_matrix クラスタリング類似度マトリックス類似性マトリックスは、すべてのクラスタリング モデルが連携して構築した知識を表す方法です。 これにより、どのエントリが同じクラスターに属する可能性が高いか、またどのエントリがそうでないかを直感的に確認できます。しかし、この情報はまだ実際のクラスターに変換する必要があります。 これは、類似性マトリックスをパラメータとして受け取ることができるクラスタリング アルゴリズムを使用して行われます。ここでは SpectralClustering を使用します。 from sklearn.cluster import SpectralClustering spec_clt = SpectralClustering(n_clusters=10, affinity='precomputed', n_init=5, random_state=214) final_labels = spec_clt.fit_predict(pos_sim_matrix)
標準KMeansモデルとの比較私たちの方法が有効かどうかを確認するために、KMeans と比較してみましょう。 NMI、ARI、クラスター純度、クラス純度のメトリックを使用して、標準 KMeans モデルを評価し、それをアンサンブル モデルと比較します。また、各クラスターにどのクラスが属しているかを視覚化するために、分割行列をプロットします。 from seaborn import heatmap import matplotlib.pyplot as plt def data_contingency_matrix(true_labels, pred_labels): fig, (ax) = plt.subplots(1, 1, figsize=(8,8)) n_clusters = len(np.unique(pred_labels)) n_classes = len(np.unique(true_labels)) label_names = np.unique(true_labels) label_names.sort() contingency_matrix = np.zeros( (n_classes, n_clusters) ) for i, true_label in enumerate(label_names): for j in range(n_clusters): contingency_matrix[i, j] = np.sum(np.logical_and(pred_labels==j, true_labels==true_label)) heatmap(contingency_matrix.astype(int), ax=ax, annot=True, annot_kws={"fontsize":14}, fmt='d') ax.set_xlabel("Clusters", fontsize=18) ax.set_xticks( [i+0.5 for i in range(n_clusters)] ) ax.set_xticklabels([i for i in range(n_clusters)], fontsize=14) ax.set_ylabel("Original classes", fontsize=18) ax.set_yticks( [i+0.5 for i in range(n_classes)] ) ax.set_yticklabels(label_names, fontsize=14, va="center") ax.set_title("Contingency Matrix\n", ha='center', fontsize=20) from sklearn.metrics import normalized_mutual_info_score, adjusted_rand_score def purity( true_labels, pred_labels ): n_clusters = len(np.unique(pred_labels)) n_classes = len(np.unique(true_labels)) label_names = np.unique(true_labels) purity_vector = np.zeros( (n_classes) ) contingency_matrix = np.zeros( (n_classes, n_clusters) ) for i, true_label in enumerate(label_names): for j in range(n_clusters): contingency_matrix[i, j] = np.sum(np.logical_and(pred_labels==j, true_labels==true_label)) purity_vector = np.max(contingency_matrix, axis=1)/np.sum(contingency_matrix, axis=1) print( f"Mean Class Purity - {np.mean(purity_vector):.2f}" ) for i, true_label in enumerate(label_names): print( f" {true_label} - {purity_vector[i]:.2f}" ) cluster_purity_vector = np.zeros( (n_clusters) ) cluster_purity_vector = np.max(contingency_matrix, axis=0)/np.sum(contingency_matrix, axis=0) print( f"Mean Cluster Purity - {np.mean(cluster_purity_vector):.2f}" ) for i in range(n_clusters): print( f" {i} - {cluster_purity_vector[i]:.2f}" ) kmeans_model = KMeans(10, n_init=50, random_state=214) km_labels = kmeans_model.fit_predict(X_pca) data_contingency_matrix(y, km_labels) print( "Single KMeans NMI - ", normalized_mutual_info_score(y, km_labels) ) print( "Single KMeans ARI - ", adjusted_rand_score(y, km_labels) ) purity(y, km_labels) data_contingency_matrix(y, final_labels) print( "Ensamble NMI - ", normalized_mutual_info_score(y, final_labels) ) print( "Ensamble ARI - ", adjusted_rand_score(y, final_labels) ) purity(y, final_labels)
上記の値から、アンサンブル法によってクラスタリング品質が実際に向上できることがわかります。また、コンティンジェンシー マトリックスでは、分布クラスが改善され、「ノイズ」が減り、より一貫した動作が見られるようになりました。 |