(LLMを活用した こきゃくセグメンテーションの マスタリング)

『LLMを活用した顧客セグメンテーションのマスタリング』

LLMを使用して高度な顧客セグメンテーション技術を解除し、高度な技術を使用してクラスタリングモデルを改善する

目次

· イントロ· データ· メソッド1:Kmeans· メソッド2:K-Prototype· メソッド3:LLM + Kmeans· 結論

イントロ

顧客セグメンテーションプロジェクトは、複数の方法でアプローチすることができます。この記事では、クラスターを定義するだけでなく、結果を分析するための高度な技術を紹介します。この投稿は、クラスタリングの問題に対処するための複数のツールを持ちたいデータサイエンティストを対象としています。

この記事では何を見ますか?

このタイプのプロジェクトにアプローチするための3つの方法を見てみましょう:

  • Kmeans
  • K-Prototype
  • LLM + Kmeans

小さなプレビューとして、作成された異なるモデルの2D表現(PCA)の次の比較を示します:

Graphic comparison of the three methods (Image by Author).

また、以下のような次元削減技術も学びます:

  • PCA
  • t-SNE
  • MCA

いくつかの結果は次のとおりです:

Graphical comparison of the three dimensionality reduction methods (Image by Author).

プロジェクトはノートブックでこちらで見つけることができますまた、私のGithubもご覧ください。

damiangilgonzalez1995 – 概要

データに情熱を持っている私は、物理学からデータサイエンスへ転身しました。テレフォニカ、HPで働き、現在はCTOです…

github.com

非常に重要な注意点として、これはエンドツーエンドのプロジェクトではありません。このタイプのプロジェクトでは、探索的データ分析(EDA)フェーズや変数の選択など、最も重要な部分がスキップされています。

データ

このプロジェクトで使用される元のデータは、公開KaggleのBanking Dataset — Marketing Targetsから取得したものです。このデータセットの各行には、企業の顧客に関する情報が含まれています。一部のフィールドは数値的なものであり、他のフィールドはカテゴリカルなものであり、これにより問題にアプローチする可能な方法が拡大されます。

最初の8列だけ残します。データセットは次のようになります:

データセットの列の簡単な説明を見てみましょう:

  • age (数値)
  • job : 職業のタイプ(カテゴリカル:「管理」「不明」「失業」「経営」「家政婦」「起業家」「学生」「ブルーカラー」「自営業」「退職」「技術者」「サービス」)
  • marital : 婚姻状況(カテゴリカル:「既婚」「離婚」「単身」;「離婚」は離婚または未亡人を意味する)
  • education (カテゴリカル:「不明」「中等教育」「初等教育」「高等教育」)
  • default: クレジットがデフォルトになっていますか?(バイナリ:「はい」「いいえ」)
  • balance: 平均年間残高、ユーロ(数値)
  • housing: 住宅ローンを持っていますか?(バイナリ:「はい」「いいえ」)
  • loan: 個人ローンを持っていますか?(バイナリ:「はい」「いいえ」)

プロジェクトでは、Kaggleのトレーニングデータセットを使用しました。 プロジェクトリポジトリでは、プロジェクトで使用されたデータセットの圧縮ファイルが格納されている「data」フォルダを見つけることができます。さらに、圧縮ファイルの中には2つのCSVファイルがあります。1つはKaggleが提供するトレーニングデータセット (train.csv)で、もう1つは埋め込みを実行したデータセット (embedding_train.csv)です。後ほど詳しく説明します。

プロジェクトの構造をさらに明確にするために、プロジェクトツリーを表示します:

clustering_llm├─ data│  ├─ data.rar├─ img├─ embedding.ipynb├─ embedding_creation.py├─ kmeans.ipynb├─ kprototypes.ipynb├─ README.md└─ requirements.txt

メソッド1:Kmeans

これは最も一般的なメソッドであり、おそらく既に知っているものです。それでも、このような場合に進んだ分析技術を示すために学習していきます。完全な手順は「kmeans.ipynb」というJupyterノートブックにあります。

前処理済み

変数の前処理が行われます:

  1. カテゴリカル変数を数値変数に変換します。通常はワンホットエンコーダーを適用しますが、この場合は序数エンコーダーを適用します。
  2. 数値変数がガウス分布になるようにします。そのためにパワートランスフォーマーを適用します。

コードでどのように見えるか見てみましょう。

import pandas as pd # データフレーム操作import numpy as np # 線形代数# データの可視化import matplotlib.pyplot as pltimport matplotlib.cm as cmimport plotly.express as pximport plotly.graph_objects as goimport seaborn as snsimport shap# sklearn from sklearn.cluster import KMeansfrom sklearn.preprocessing import PowerTransformer, OrdinalEncoderfrom sklearn.pipeline import Pipelinefrom sklearn.manifold import TSNEfrom sklearn.metrics import silhouette_score, silhouette_samples, accuracy_score, classification_reportfrom pyod.models.ecod import ECODfrom yellowbrick.cluster import KElbowVisualizerimport lightgbm as lgbimport princedf = pd.read_csv("train.csv", sep = ";")df = df.iloc[:, 0:8]pipe = Pipeline([('ordinal', OrdinalEncoder()), ('scaler', PowerTransformer())])pipe_fit = pipe.fit(df)data = pd.DataFrame(pipe_fit.transform(df), columns = df.columns)data

出力:

外れ値

データに外れ値が少ないほど、K-meansアルゴリズムは非常に敏感ですので、外れ値はできるだけ少なくすることが重要です。典型的な方法であるzスコアを使用して外れ値を選択することができますが、この投稿ではもっと高度かつクールな方法を紹介します。

では、この方法とは何でしょうか?それは、Python Outlier Detection (PyOD) ライブラリを使用します。このライブラリは、さまざまなケースで外れ値を検出することに特化しています。より具体的には、「empirical cumulative distribution functions for outlier detection」と呼ばれるECODメソッドを使用します。

この方法は、データの分布を取得し、確率密度が低い値(外れ値)を知ることを目的としています。詳細については、Githubをご覧ください。

from pyod.models.ecod import ECODclf = ECOD()clf.fit(data)outliers = clf.predict(data) data["outliers"] = outliers# 外れ値のないデータdata_no_outliers = data[data["outliers"] == 0]data_no_outliers = data_no_outliers.drop(["outliers"], axis = 1)# 外れ値のあるデータdata_with_outliers = data.copy()data_with_outliers = data_with_outliers.drop(["outliers"], axis = 1)print(data_no_outliers.shape) -> (40691, 8)print(data_with_outliers.shape) -> (45211, 8)

モデリング

Kmeansアルゴリズムを使用する際の欠点の一つは、使用するクラスターの数を選ぶ必要があるということです。この場合、そのデータを取得するために、エルボー法を使用します。これは、クラスターの各点とその重心間の歪みを計算するものです。目的は明確で、できるだけ少ない歪みを得ることです。この場合、以下のコードを使用します。

from yellowbrick.cluster import KElbowVisualizer# クラスタリングモデルとビジュアライザーをインスタンス化km = KMeans(init="k-means++", random_state=0, n_init="auto")visualizer = KElbowVisualizer(km, k=(2,10)) visualizer.fit(data_no_outliers)        # データをビジュアライザーに適合させるvisualizer.show()    

出力結果:

Elbow score for different numbers of clusters (Image by Author).

上記の結果から、k=5から歪みが大きく変化しなくなることが分かります。理想的には、k=5以降の挙動がほぼ平坦になることが望ましいですが、これは稀です。最適なクラスター数を確実にするために、シルエット 可視化など、他の手法も適用することができます。以下はそのコードです。

from sklearn.metrics import davies_bouldin_score, silhouette_score, silhouette_samplesimport matplotlib.cm as cmdef make_Silhouette_plot(X, n_clusters):    plt.xlim([-0.1, 1])    plt.ylim([0, len(X) + (n_clusters + 1) * 10])    clusterer = KMeans(n_clusters=n_clusters, max_iter = 1000, n_init = 10, init = 'k-means++', random_state=10)    cluster_labels = clusterer.fit_predict(X)    silhouette_avg = silhouette_score(X, cluster_labels)    print(        "クラスター数 =", n_clusters,        "平均シルエットスコア :", silhouette_avg,    )# 各サンプルのシルエットスコアを計算    sample_silhouette_values = silhouette_samples(X, cluster_labels)    y_lower = 10    for i in range(n_clusters):        ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]        ith_cluster_silhouette_values.sort()        size_cluster_i = ith_cluster_silhouette_values.shape[0]        y_upper = y_lower + size_cluster_i        color = cm.nipy_spectral(float(i) / n_clusters)        plt.fill_betweenx(            np.arange(y_lower, y_upper),            0,            ith_cluster_silhouette_values,            facecolor=color,            edgecolor=color,            alpha=0.7,        )        plt.text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))        y_lower = y_upper + 10        plt.title(f"クラスター数{n_clusters}のシルエットプロット", fontsize=26)        plt.xlabel("シルエット係数の値", fontsize=24)        plt.ylabel("クラスターラベル", fontsize=24)        plt.axvline(x=silhouette_avg, color="red", linestyle="--")        plt.yticks([])          plt.xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])    range_n_clusters = list(range(2,10))for n_clusters in range_n_clusters:    print(f"クラスター数: {n_clusters}")    make_Silhouette_plot(data_no_outliers, n_clusters)       plt.savefig('Silhouette_plot_{}.png'.format(n_clusters))    plt.close()出力結果: """N cluster: 2クラスター数 = 2 平均シルエットスコア : 0.1775761520337095N cluster: 3クラスター数 = 3 平均シルエットスコア : 0.20772622268785523N cluster: 4クラスター数 = 4 平均シルエットスコア : 0.2038116470937145N cluster: 5クラスター数 = 5 平均シルエットスコア : 0.20142888327171368N cluster: 6クラスター数 = 6 平均シルエットスコア : 0.20252892716996912N cluster: 7クラスター数 = 7 平均シルエットスコア : 0.21185490763840265N cluster: 8クラスター数 = 8 平均シルエットスコア : 0.20867816457291538N cluster: 9クラスター数 = 9 平均シルエットスコア : 0.21154289421300868"""

最高のシルエットスコアはn_cluster=9で得られていることがわかりますが、他のスコアと比較するとスコアの変動は非常に小さいことも事実です。現時点では、前の結果はあまり情報を提供していません。一方、前のコードはシルエットの可視化を作成し、より多くの情報を提供しています:

Graphic representation of the silhouette method for different numbers of clusters (Image by Author).

これらの表現をよく理解することがこの投稿の目的ではないため、最適な数字が明確ではないようです。前の表現を見た後で、K=5またはK=6を選択することができます。これは、異なるクラスタにおいて、シルエットスコアが平均値よりも高く、クラスタのサイズに偏りがないことを意味します。さらに、一部の状況では、マーケティング部門はクラスタ/顧客の種類を最も少なくしたいと考える場合もあります(それが当てはまるかどうかはわかりません)。

最後に、K=5でKmeansモデルを作成することができます。

km = KMeans(n_clusters=5, init='k-means++', n_init=10, max_iter=100, random_state=42)clusters_predict = km.fit_predict(data_no_outliers)"""clusters_predict -> array([4, 2, 0, ..., 3, 4, 3])np.unique(clusters_predict) -> array([0, 1, 2, 3, 4])"""

評価

kmeansモデルの評価方法は他のモデルよりも柔軟です。以下のコードを使用することができます:

  • メトリクス
  • 可視化
  • 解釈(会社にとって非常に重要です)

モデルの評価メトリクスに関して、以下のコードを使用することができます:

from sklearn.metrics import silhouette_scorefrom sklearn.metrics import calinski_harabasz_scorefrom sklearn.metrics import davies_bouldin_score"""ダヴィーズ・ボールディン指標は、各クラスタと最も類似したクラスタの平均類似度を示す指標であり、類似度はクラスタ内距離とクラスタ間距離の比率です.DBインデックスの最小値は0で、より小さい値(0に近い値)はより良いクラスタを生成するモデルを示します。"""print(f"ダヴィーズ・ボールディンスコア:{davies_bouldin_score(data_no_outliers,clusters_predict)}")"""Calinski Harabaz Index -> 分散比率基準。Calinski Harabaz Indexは、クラスタ間の分散の合計とクラスタ内の分散の比率を示す指標です。指数が高いほど、クラスタがより分離していることを意味します。"""print(f"Calinskiスコア:{calinski_harabasz_score(data_no_outliers,clusters_predict)}")"""シルエットスコアはクラスタリングアルゴリズムの適合性を計算するためのメトリックですが、kの最適な値を決定するための方法としても使用できます(詳細はこちらを参照)。その値の範囲は-1から1です。値が0の場合、クラスタが重なっており、データまたはkの値が正しくありません。1は理想的な値で、クラスタが非常に密集しており、きれいに分離していることを示します。"""print(f"シルエットスコア:{silhouette_score(data_no_outliers,clusters_predict)}")OUTPUT:"""ダヴィーズ・ボールディンスコア:1.5480952939773156Calinskiスコア:7646.959165727562シルエットスコア:0.2013600389183821"""

示された範囲では、非常に良いモデルではありません。 ダヴィーズ・スコア はクラスタ間の距離が非常に小さいことを示しています。

これはいくつかの要因によるものかもしれませんが、データのエネルギーはデータそのものです。データが十分な予測力を持っていない場合、優れた結果を期待することはできません。

可視化のために、次元削減の方法であるPCA を使用することができます。これには、探索的データ分析と次元削減に特化した Prince ライブラリを使用します。もしくは、SklearnのPCAを使用することもできます。実質的には同じです。

まず、3Dで主成分を計算し、次に表現を作成します。これらは前のステップで実行される2つの関数です:

import princeimport plotly.express as pxdef get_pca_2d(df, predict):    pca_2d_object = prince.PCA(    n_components=2,    n_iter=3,    rescale_with_mean=True,    rescale_with_std=True,    copy=True,    check_input=True,    engine='sklearn',    random_state=42    )    pca_2d_object.fit(df)    df_pca_2d = pca_2d_object.transform(df)    df_pca_2d.columns = ["comp1", "comp2"]    df_pca_2d["cluster"] = predict    return pca_2d_object, df_pca_2ddef get_pca_3d(df, predict):    pca_3d_object = prince.PCA(    n_components=3,    n_iter=3,    rescale_with_mean=True,    rescale_with_std=True,    copy=True,    check_input=True,    engine='sklearn',    random_state=42    )    pca_3d_object.fit(df)    df_pca_3d = pca_3d_object.transform(df)    df_pca_3d.columns = ["comp1", "comp2", "comp3"]    df_pca_3d["cluster"] = predict    return pca_3d_object, df_pca_3ddef plot_pca_3d(df, title = "PCA空間", opacity=0.8, width_line = 0.1):    df = df.astype({"cluster": "object"})    df = df.sort_values("cluster")    fig = px.scatter_3d(          df,           x='comp1',           y='comp2',           z='comp3',          color='cluster',          template="plotly",                    # symbol = "cluster",                    color_discrete_sequence=px.colors.qualitative.Vivid,          title=title).update_traces(              # mode = 'markers',              marker={                  "size": 4,                  "opacity": opacity,                  # "symbol" : "diamond",                  "line": {                      "width": width_line,                      "color": "black",                  }              }          ).update_layout(                  width = 800,                   height = 800,                   autosize = True,                   showlegend = True,                  legend=dict(title_font_family="Times New Roman",                              font=dict(size= 20)),                  scene = dict(xaxis=dict(title = 'comp1', titlefont_color = 'black'),                              yaxis=dict(title = 'comp2', titlefont_color = 'black'),                              zaxis=dict(title = 'comp3', titlefont_color = 'black')),                  font = dict(family = "Gilroy", color  = 'black', size = 15))                fig.show()

これらの関数について心配しないでください、次のように使用します:

pca_3d_object, df_pca_3d = pca_plot_3d(data_no_outliers, clusters_predict)plot_pca_3d(df_pca_3d, title = "PCA空間", opacity=1, width_line = 0.1)print("分散は次のとおりです:", pca_3d_object.eigenvalues_summary)

出力:

モデルによって作成されたPCA空間とクラスター(著者による画像)

クラスター間にほとんど分離がなく、明確な分割がないことがわかります。これはメトリックによって提供された情報と一致しています。

考慮すべきこと、そしてほとんどの人が考慮しないことは、PCAと固有ベクトルの分散です。

各フィールドは一定量の情報を含んでおり、それぞれが情報の一部を追加します。3つの主要な成分の累積合計が約80%の変動性を持っている場合、それは受け入れ可能であり、表現において良い結果を得ることができます。値が低い場合、他の固有ベクトルに含まれる多くの情報が欠落しているので、視覚化には注意が必要です。

次の質問は明らかです:実行されたPCAの変動性は何ですか?

以下が答えです:

上記のように、最初の3つの要素で48.37%の変動性があることがわかりますが、十分な結論を導くには不十分です。

PCA分析を実行すると、空間構造が保存されないことがわかります。幸いなことに、t-SNEと呼ばれるあまり知られていない方法があり、次元削減も空間構造も保持できます。これは視覚化に役立つことがありますが、前の方法ではあまり成功していませんでした。

コンピューターで試す場合、計算コストが高いことを念頭に置いてください。そのため、元のデータセットをサンプリングし、結果を得るのに約5分かかりました。コードは次のとおりです。

 from sklearn.manifold import TSNE
sampling_data = data_no_outliers.sample(frac = 0.5、replace=True、random_state=1)
sampling_clusters = pd.DataFrame(clusters_predict).sample(frac = 0.5、replace=True、random_state=1)[0].values
df_tsne_3d = TSNE( n_components = 3、learning_rate = 500、init='random'、perplexity = 200、n_iter = 5000).fit_transform(sampling_data)
df_tsne_3d = pd.DataFrame(df_tsne_3d、columns = ["comp1"、"comp2"、'comp3'])
df_tsne_3d ["cluster"] = sampling_clusters
plot_pca_3d(df_tsne_3d、title = "PCAスペース"、opacity = 1、width_line = 0.1) 

結果として、次の画像が表示されます。クラスター間の分離がより大きく、より明確な結論を導くことができます。

t-SNEスペースとモデルによって作成されたクラスター(Image by Author).

実際には、PCAとt-SNEの2次元での削減を比較することができます。2番目の方法を使用すると、改善が明らかです。

異なる次元削減方法とモデルによって定義されたクラスターの異なる結果(Image by Author).

最後に、モデルの動作方法、最も重要な特徴、およびクラスターの主な特徴について少し詳しく調査しましょう。

各変数の重要性を確認するために、このタイプの状況で一般的な「トリック」を使用します。 “X”がKmeansモデルの入力であり、「y」がKmeansモデルによって予測されたクラスターである分類モデルを作成します。

選択したモデルは LGBMClassifierです。このモデルはかなり強力で、カテゴリ変数と数値変数の両方にうまく適合します。新しいモデルをトレーニングし、SHAPライブラリを使用して、予測における各機能の重要性を取得することができます。コードは次のとおりです。

 import lightgbm as lgb
import shap
# LGBMClassifierモデルを作成してトレーニングする
clf_km = lgb.LGBMClassifier(colsample_by_tree = 0.8)
clf_km.fit(X = data_no_outliers、y = clusters_predict)
#SHAP values
explainer_km = shap.TreeExplainer(clf_km)
shap_values_km = explainer_km.shap_values(data_no_outliers)
shap.summary_plot(shap_values_km、data_no_outliers、plot_type = "bar"、plot_size = (15, 10))  

出力:

モデルの変数の重要性(Image by Author).

特徴量「住宅」が最も予測力があることがわかります。また、クラスタ番号4(緑)は主に「ローン」変数によって区別されます。

最後に、クラスタの特徴を分析する必要があります。これはビジネスにとって決定的な部分です。そのために、データセットの各特徴量ごとのクラスタごとの平均値(数値変数の場合)と最頻値(カテゴリ変数の場合)を取得します:

df_no_outliers = df[df.outliers == 0]df_no_outliers["cluster"] = clusters_predictdf_no_outliers.groupby('cluster').agg(    {        'job': lambda x: x.value_counts().index[0],        'marital': lambda x: x.value_counts().index[0],        'education': lambda x: x.value_counts().index[0],        'housing': lambda x: x.value_counts().index[0],        'loan': lambda x: x.value_counts().index[0],        'contact': lambda x: x.value_counts().index[0],        'age':'mean',        'balance': 'mean',        'default': lambda x: x.value_counts().index[0],            }).reset_index()

出力:

「job=blue-collar」というクラスタは、特徴の間には大きな差がないことがわかります。これは顧客を各クラスタごとに区別するのが難しいため、望ましくありません。「job=management」の場合、より良い差異化が得られます。

さまざまな方法で分析を行った後、同じ結論につながることがわかります:「結果を改善する必要がある」ということです。

メソッド2:K-プロトタイプ

元のデータセットを思い出すと、カテゴリ変数と数値変数があることがわかります。残念ながら、Skelearnが提供するKmeansアルゴリズムはカテゴリ変数を受け付けないため、元のデータセットを修正し、大幅に変更する必要があります。

幸いにも、私を読んでくれているあなたは私と私の投稿について、そして何よりもZHEXUE HUANGと彼の記事Extensions to the k-Means Algorithm for Clustering Large Data Sets with Categorical Valuesに感謝する必要があります。この記事では、カテゴリ変数をクラスタリングするためにカテゴリ変数を受け入れるアルゴリズムK-Prototypeがあります。提供しているブックストアはPrinceです。

手順は前の場合と同じです。この記事を永遠にすることなく、興味深い部分に進みましょう。ただし、こちらのJupyterノートブックにアクセスできることを覚えておいてください。

前処理済み

数値変数があるため、それらには一定の修正が必要です。すべての数値変数が同様のスケールになり、分布ができるだけガウス分布に近くなることが常に推奨されます。モデルを作成するために使用するデータセットは次のように作成されます:

pipe = Pipeline([('scaler', PowerTransformer())])df_aux = pd.DataFrame(pipe_fit.fit_transform(df_no_outliers[["age", "balance"]] ), columns = ["age", "balance"])df_no_outliers_norm = df_no_outliers.copy()# Replace age and balance columns by preprocessed valuesdf_no_outliers_norm = df_no_outliers_norm.drop(["age", "balance"], axis = 1)df_no_outliers_norm["age"] = df_aux["age"].valuesdf_no_outliers_norm["balance"] = df_aux["balance"].valuesdf_no_outliers_norm

外れ値

外れ値検出のために私が提案した方法(ECOD)は数値変数のみを受け入れるため、kmeansの方法と同じ変換を行う必要があります。外れ値検出モデルを適用し、削除する行を提供して最終的にK-Prototypeモデルの入力として使用するデータセットを残します:

モデリング

最適なkを取得するためにモデルを作成します。そのために、エルボーメソッドを使用し、以下のコードを使用します。

# エルボーメソッドを使用して最適なKを選択from kmodes.kprototypes import KPrototypesfrom plotnine import *import plotninecost = []range_ = range(2, 15)for cluster in range_:         kprototype = KPrototypes(n_jobs = -1, n_clusters = cluster, init = 'Huang', random_state = 0)        kprototype.fit_predict(df_no_outliers, categorical = categorical_columns_index)        cost.append(kprototype.cost_)        print('クラスタ開始: {}'.format(cluster)) # 結果をデータフレームに変換してプロットするdf_cost = pd.DataFrame({'クラスタ':range_, 'コスト':cost})# データビズplotnine.options.figure_size = (8, 4.8)(    ggplot(data = df_cost)+    geom_line(aes(x = 'クラスタ',                  y = 'コスト'))+    geom_point(aes(x = 'クラスタ',                   y = 'コスト'))+    geom_label(aes(x = 'クラスタ',                   y = 'コスト',                   label = 'クラスタ'),               size = 10,               nudge_y = 1000) +    labs(title = 'エルボーメソッドによる最適なクラスタ数')+    xlab('クラスタ数 k')+    ylab('コスト')+    theme_minimal())

出力:

Elbow score for different numbers of clusters (Image by Author).

最適な選択肢はK=5です。

注意してください。このアルゴリズムは通常使用されるものより少しだけ時間がかかります。前のグラフでは、86分が必要でしたので、心に留めておいてください。

さて、クラスタ数が明確になったので、モデルを作成するだけです。

# カテゴリカルな列のインデックスを取得するnumerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']categorical_columns = df_no_outliers_norm.select_dtypes(exclude=numerics).columnsprint(categorical_columns)categorical_columns_index = [df_no_outliers_norm.columns.get_loc(col) for col in categorical_columns]# モデルを作成するcluster_num = 5kprototype = KPrototypes(n_jobs = -1, n_clusters = cluster_num, init = 'Huang', random_state = 0)kprototype.fit(df_no_outliers_norm, categorical = categorical_columns_index)clusters = kprototype.predict(df_no_outliers , categorical = categorical_columns_index)print(clusters) " -> 配列([3, 1, 1, ..., 1, 1, 2], dtype=uint16)"

モデルと予測結果が得られましたので、評価するだけです。

評価

前に見たように、モデルの良さを直感的に把握するためにいくつかの可視化手法を適用することができます。残念ながらPCAメソッドとt-SNEはカテゴリカル変数を受け付けません。しかし、心配しないでください。 PrinceライブラリにはMCA(Multiple correspondence analysis)メソッドが含まれており、混合データセットを受け入れることができます。実際、このライブラリのGithubを訪れることをお勧めします。さまざまな状況について便利なメソッドがいくつかありますので、次の画像をご覧ください。

異なるケースの次元削減手法(画像:著者とPrinceドキュメンテーションによるもの)

さて、次元削減を行ってグラフィカルな表現ができるようにするために、MCAを適用する予定です。そのために、以下のコードを使用します:

from prince import MCAdef get_MCA_3d(df, predict):    mca = MCA(n_components=3, n_iter=100, random_state=101)    mca_3d_df = mca.fit_transform(df)    mca_3d_df.columns = ["comp1", "comp2", "comp3"]    mca_3d_df["cluster"] = predict    return mca, mca_3d_dfdef get_MCA_2d(df, predict):    mca = MCA(n_components=2, n_iter=100, random_state=101)    mca_2d_df = mca.fit_transform(df)    mca_2d_df.columns = ["comp1", "comp2"]    mca_2d_df["cluster"] = predict    return mca, mca_2d_df"-------------------------------------------------------------------"mca_3d、mca_3d_df = get_MCA_3d(df_no_outliers_norm, clusters)

すべてのステップを100%追いたい場合は、Jupyterノートブックを参照してください。

mca_3d_dfという名前のデータセットには、その情報が含まれています:

MCAメソッドによって提供された次元削減を使用してプロットを作成しましょう:

MCA空間とモデルによって作成されるクラスター(画像:著者によるもの)

わぁ、あまり見栄えがよくないですね… クラスターを区別することはできません。したがって、モデルは十分に優れていないと言えますね?

あなたが次のように言ったことを望みます:

「ねえ、ダミアン、そんなに早くやらないで!! MCAによって提供される3つの成分の変動性を見ましたか?」

実際、最初の3つの成分の変動性が十分であるかどうかを見る必要があります。MCAメソッドを使用すると、簡単にこれらの値を取得できます:

mca_3d.eigenvalues_summary

ああ、興味深いことがあります。データによると、基本的に変動性はゼロです。

つまり、MCAによって提供される次元削減の情報では明確な結論を引き出すことはできないということです。

これらの結果を示すことで、現実のデータプロジェクトで何が起こるかを示しようとしています。常に良い結果が得られるわけではありませんが、優れたデータサイエンティストは原因を認識することができます。

最後のオプションとして、K-Prototypeメソッドによって作成されたモデルが適切かどうかを視覚的に判断する方法があります。この手順は簡単です:

  1. カテゴリ変数を数値変数に変換するために前処理を行ったデータセットにPCAを適用する。
  2. PCAの成分を取得する。
  3. 軸や点の色など、PCAの成分を使用してK-Prototypeモデルを予測するプロットを作成する。

PCAによって提供されるコンポーネントは、メソッド1:Kmeansと同じです。なぜなら、それは同じデータフレームだからです。

さて、何が得られるか見てみましょう…

PCAの空間とモデルによって作成されたクラスタ(著者による画像)

これは悪くはないようです。実際には、Kmeansで得られた結果にある程度似ています。

最後に、クラスタの平均値と各変数の重要度を得ます:

モデル内の変数の重要度。テーブルは各クラスタの最も頻繁な値を表しています(著者による画像)

最も重要な重みを持つ変数は数値変数であり、特にこれら2つの特徴の制約が各クラスタを区別するのにほぼ十分であることがわかります。

要するに、Kmeansと同様の結果が得られたと言えます。

メソッド3:LLM + Kmeans

この組み合わせは非常に強力であり、得られる結果を改善することができます。肝心な点に入りましょう!

LLMsは直接テキストを理解することができませんので、このタイプのモデルには入力を変換する必要があります。そのためにはSentence Embeddingを行います。これはテキストを数値ベクトルに変換することを指します。以下の画像がアイデアを明確にします:

埋め込みと類似性の概念(著者による画像)

このコーディングは賢く行われます。つまり、意味が似ているフレーズは同様のベクトルを持つようになります。以下の画像を見てください:

埋め込みと類似性の概念(著者による画像)

文の埋め込みは、埋め込みに特化したアルゴリズムである「transforms」と呼ばれるものによって行われます。通常、このエンコーディングから得られる数値ベクトルのサイズを選択できます。そして、ここで重要なポイントの一つがあります:

埋め込みによって作成されるベクトルの大きな次元のおかげで、データのわずかな変動をより正確に見ることができます。

したがって、情報豊かなKmeansモデルに入力を提供すれば、より良い予測結果が得られます。これが追求しているアイデアであり、これがその手順です:

  1. 元のデータセットをSentence embeddingを通じて変換する
  2. Kmeansモデルを作成する
  3. 評価する

まず、情報をSentence embeddingを介してエンコードする必要があります。目的は、各クライアントの情報をまとめて、すべての特性を含むテキストに統一することです。このパートは計算時間がかかります。そのため、この作業を行うスクリプト「embedding_creation.py」を作成しました。このスクリプトは、トレーニングデータセットに含まれる値を収集し、埋め込みによって提供される新しいデータセットを作成します。以下はスクリプトのコードです:

import pandas as pd # データフレーム操作import numpy as np # 線形代数from sentence_transformers import SentenceTransformerdf = pd.read_csv("data/train.csv", sep = ";")# -------------------- 第1ステップ --------------------def compile_text(x):    text =  f"""Age: {x['age']},                  housing load: {x['housing']},                 Job: {x['job']},                 Marital: {x['marital']},                 Education: {x['education']},                 Default: {x['default']},                 Balance: {x['balance']},                 Personal loan: {x['loan']},                 contact: {x['contact']}            """    return textsentences = df.apply(lambda x: compile_text(x), axis=1).tolist()# -------------------- 第2ステップ --------------------model = SentenceTransformer(r"sentence-transformers/paraphrase-MiniLM-L6-v2")output = model.encode(sentences=sentences,         show_progress_bar=True,         normalize_embeddings=True)df_embedding = pd.DataFrame(output)df_embedding

このステップが理解されることは非常に重要です。ポイントごとに進んでいきましょう:

  • ステップ1:テキストは各行ごとに作成され、顧客/行の情報が含まれています。後で使用するために、Pythonのリストにも保存します。次の画像をご覧ください。
第1ステップのグラフィック説明(画像提供:著者)
  • ステップ2:これはトランスフォーマーの呼び出しが行われる時です。これにはHuggingFaceに保存されているモデルを使用します。このモデルは、文レベルでの埋め込みを行うために特別に訓練されており、Bertのモデルとは異なり、トークンと単語のエンコーディングに焦点を当てています。モデルを呼び出すためには、リポジトリのアドレスを指定するだけで十分です。この場合は「sentence-transformers/paraphrase-MiniLM-L6-v2」です。テキストごとに返される数値ベクトルは正規化されます。なぜなら、Kmeansモデルは入力のスケールに敏感だからです。作成されるベクトルは長さ384です。これらを使用して、同じ列数を持つデータフレームを作成します。次の画像をご覧ください:
第2ステップのグラフィック説明(画像提供:著者)

最後に、埋め込みからデータフレームを取得し、これがKmeansモデルへの入力になります。

このステップは最も興味深く重要なステップの1つです。なぜなら、Kmeansモデルの入力を作成したからです。

作成と評価の手順は上記のものと類似しています。投稿をあまり長くしないため、各ポイントの結果のみを表示します。心配しないでください、すべてのコードはembeddingというjupyterノートブックに含まれているため、自分自身で結果を再現することができます。

さらに、文埋め込みを適用した結果のデータセットはcsvファイルに保存されています。このcsvファイルの名前はembedding_train.csvです。Jupyterノートブックでは、このデータセットにアクセスし、それを基にモデルを作成します。

# 通常のデータセット
df = pd.read_csv("data/train.csv", sep = ";")
df = df.iloc[:, 0:8]
# 埋め込みデータセット
df_embedding = pd.read_csv("data/embedding_train.csv", sep = ",")

前処理済み

埋め込みは前処理と考えることができます。

外れ値

外れ値を検出するために既に紹介した方法ECODを適用します。このタイプのデータポイントを含まないデータセットを作成します。

df_embedding_no_out.shape  -> (40690, 384)
df_embedding_with_out.shape -> (45211, 384)

モデリング

まず、最適なクラスターの数を見つける必要があります。そのために、エルボー法を使用します。

異なるクラスター数に対するエルボースコア(画像提供:著者)

グラフを表示した後、クラスター数としてk=5を選択します。

n_clusters = 5clusters = KMeans(n_clusters=n_clusters, init = "k-means++").fit(df_embedding_no_out)print(clusters.inertia_)clusters_predict = clusters.predict(df_embedding_no_out)

評価

次に、k=5を使用してKmeansモデルを作成します。その後、以下のようないくつかのメトリックを取得できます:

Davies bouldin スコア: 1.8095386826791042Calinski スコア: 6419.447089002081シルエット スコア: 0.20360442824114108

前のケースで得られた値と非常に似ていることがわかります。PCA分析で得られた表現を調べましょう:

PCA空間とモデルによって作成されたクラスター(著者の画像)

従来の方法と比較して、クラスターがはるかに良く区別されていることがわかります。これは良いニュースです。PCA分析の最初の3つの成分に含まれる変動性を考慮することが重要であることを思い出しましょう。経験から、3D PCAで約50%の変動性があると、比較的明確な結論を得ることができると言えます。

PCA空間とモデルによって作成されたクラスター。最初の3つのPCA成分の変動性も表示されています(著者の画像)

したがって、3つの成分の累積変動性が40.44%であることがわかります。これは受け入れ可能ですが、理想的ではありません。

3Dの表現でクラスターがどれだけコンパクトかを視覚的に確認する方法の1つは、3D表現の点の不透明度を変更することです。つまり、点がある空間に凝集した場合、黒いスポットが観察されます。言っていることを理解するために、次のgifを示します:

plot_pca_3d(df_pca_3d, title = "PCA Space", opacity=0.2, width_line = 0.1)
PCA空間とモデルによって作成されたクラスター(著者の画像)

見ると、同じクラスターの点が空間のいくつかの点で集まっていることがわかります。これは、それらが他の点とはっきりと区別されており、モデルがそれらをかなりよく認識していることを示しています。

それにもかかわらず、いくつかのクラスターがうまく区別できないことがわかります(例:クラスター1と3)。このため、次元削減法であるt-SNE分析を行い、空間構造を維持する方法であることを思い出しましょう。

t-SNE空間とモデルによって作成されたクラスター(著者の画像)

顕著な改善が見られます。クラスターが重ならず、点との明確な区別があります。2つの次元削減方法を使用した改善は注目に値します。2Dの比較を見てみましょう:

異なる次元削減方法とモデルによって定義されたクラスターの異なる結果(著者の画像)

再び、t-SNEのクラスタはPCAよりもより分離され、より明確に区別されていることがわかります。さらに、品質の面での2つの方法の差は、従来のKmeansメソッドを使用した場合よりも小さくなっています。

Kmeansモデルがどの変数に依存しているかを理解するために、前と同様に同じ手順を実行します:分類モデル(LGBMClassifier)を作成し、特徴量の重要度を分析します。

モデル内の変数の重要度(著者撮影)

このモデルは、特に「marital」と「job」変数に基づいていることがわかります。一方で、情報をほとんど提供しない変数も存在することがわかります。実際のケースでは、これらの情報が少ない変数を除外した新バージョンのモデルを作成する必要があります。

Kmeans + Embeddingモデルは、良い予測を行うためにより少ない変数が必要なため、より最適です。うれしいニュースです!

最も示唆に富み、重要な部分で終わります。

マネージャーやビジネスは、PCA、t-SNE、または埋め込みに興味はありません。彼らが知りたいのは、この場合、顧客の主要な特徴が何であるかを知ることができることです。

そのため、各クラスタで見つけることができる優勢なプロファイルに関する情報を含むテーブルを作成します。

非常に興味深いことが起こります:最も頻繁なポジションが「management」であるクラスタは3つあります。それらでは、単一のマネージャーは若く、既婚者は年配で、離婚した人々はますます年配です。一方、バランスは異なる振る舞いをします。単身者の方が平均バランスが高く、離婚した人々よりも既婚者の方が平均バランスが高いです。これは次の画像で要約されます:

モデルによって定義される異なる顧客プロファイル(著者撮影)

この発見は現実と社会的な側面と一致しています。さらに、非常に具体的な顧客プロファイルを明らかにします。 これがデータサイエンスの魔法です。

結論

結論は明確です:

(著者撮影)

付加価値を提供するために、実際のプロジェクトではすべての戦略が機能するわけではないため、異なるツールを持つ必要があります。LLMの助けを借りて作成されたモデルが際立っていることがはっきりとわかります。

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more