[機械学習のはじめ方] Part25: k-meansクラスタリングと初期値の影響

1. k-meansクラスタリングとは?

k-meansクラスタリング(k平均法)は、最も広く使われている教師なし学習アルゴリズムの一つです。 その目的は、与えられたデータセットを、事前に指定した数(k)個の互いに素なグループ(クラスタ)に分割することです。 各クラスタは、そのクラスタに属するデータ点の「中心」(セントロイド)によって特徴づけられます。

教師なし学習なので、正解ラベルは使いません。データの特徴だけを頼りに、似ているもの同士を自動的にグループ分けします。 例えば、顧客データから似たような購買傾向を持つグループを見つけ出す(顧客セグメンテーション)や、画像の色を減らして圧縮する、といった用途で利用されます。

2. k-meansのアルゴリズムの仕組み

k-meansアルゴリズムは、以下のステップを繰り返すことでクラスタリングを行います。

  1. 初期中心点の選択: データの中からランダムに、あるいは特定の方法でk個の点を「初期」クラスタ中心(セントロイド)として選びます。
  2. クラスタ割り当て: すべてのデータ点について、最も距離が近い中心点を見つけ、そのデータ点をその中心点が代表するクラスタに割り当てます。
  3. 中心点の更新: 各クラスタについて、そのクラスタに属するすべてのデータ点の平均ベクトル(重心)を計算し、それを新しいクラスタ中心点とします。
  4. 繰り返し: ステップ2とステップ3を、クラスタ中心点がほとんど変化しなくなるか、あるいは予め定めた最大反復回数に達するまで繰り返します。

このプロセスにより、各データ点は最終的にいずれか一つのクラスタに属し、各クラスタの中心はそのクラスタ内のデータ点の重心となります。

ポイント: k-meansは、クラスタ内のデータ点とその中心点との距離(通常はユークリッド距離の二乗和)の合計(イナーシャ, Inertia と呼ばれます)を最小化することを目指します。

3. Python (scikit-learn) で試してみよう

Pythonの機械学習ライブラリ scikit-learn を使えば、k-meansクラスタリングを簡単に実装できます。 ここでは、簡単なサンプルデータで試してみましょう。

# 必要なライブラリをインポート
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt # 可視化用(今回は使いませんが、通常は結果確認に使います)
# サンプルデータの生成 (2次元データ、3つの塊、150サンプル)
# centers でクラスタ中心の大まかな位置を指定できます
X, _ = make_blobs(n_samples=150, centers=3, cluster_std=0.9, random_state=42)
# KMeansクラスのインスタンス化 (クラスタ数 k=3 を指定)
kmeans = KMeans(n_clusters=3, random_state=42) # random_stateで初期値のランダム性を固定
# モデルの学習とクラスタリングの実行
kmeans.fit(X)
# 各データ点がどのクラスタに属するかを取得
labels = kmeans.labels_
# 各クラスタの中心点を取得
centers = kmeans.cluster_centers_
# 結果の確認(最初の10個のデータ点のラベルを表示)
print("各データ点のクラスタラベル (最初の10個):", labels[:10])
# 出力例: 各データ点のクラスタラベル (最初の10個): [1 0 0 0 2 1 1 2 0 2]
print("各クラスタの中心座標:")
print(centers)
# 出力例: 各クラスタの中心座標:
# [[-1.63123681 8.00134966]
# [ 1.91113368 1.0592691 ]
# [-1.54675937 3.1416793 ]]
# クラスタ内の誤差平方和(イナーシャ)
print("イナーシャ:", kmeans.inertia_)
# 出力例: イナーシャ: 114.15192606019781 

make_blobs で生成したデータに対し、KMeans を使って3つのクラスタに分類しました。fit メソッドで学習とクラスタリングが実行され、labels_ 属性で各データ点の所属クラスタ番号、cluster_centers_ 属性で最終的なクラスタ中心の座標、inertia_ 属性でクラスタ内の誤差平方和を取得できます。

4. なぜ初期値が重要なのか?

k-meansアルゴリズムの説明で「初期中心点の選択」というステップがありましたが、実はこの初期中心点の選び方が最終的なクラスタリング結果に大きく影響を与えることがあります。これはk-meansの重要な特性(そして弱点)の一つです。

なぜ影響があるのでしょうか? k-meansは、クラスタ内誤差平方和(イナーシャ)を最小化しようとしますが、アルゴリズムの性質上、必ずしも全体で最も良い解(大域的最適解)を見つけられるとは限りません。選んだ初期値によっては、そこそこ良いけれどもベストではない解(局所的最適解)にたどり着いて、そこで更新が止まってしまうことがあるのです。

運悪く初期値が偏っていたりすると、人間が見れば明らかにおかしいようなクラスタリング結果になってしまうこともあります。したがって、良いクラスタリング結果を得るためには、初期値の選び方が非常に重要になります。

注意: 同じデータ、同じkの値でも、実行するたびに初期値が変わると(ランダムに選ぶ場合)、結果が変わる可能性があります。再現性が必要な場合は、初期値を固定する(例: `random_state` を指定)か、後述する対策を取る必要があります。

5. 初期値問題への対策

幸いなことに、この初期値問題に対するいくつかの効果的な対策が存在します。

  • k-means++ アルゴリズム: これは、初期中心点をより賢く選ぶためのアルゴリズムです。最初の1点はランダムに選びますが、2点目以降は、既に選ばれている中心点からなるべく遠いデータ点を確率的に選びやすくします。これにより、初期中心点が互いに離れて配置されやすくなり、より良い(または最適な)クラスタリング結果を得られる可能性が高まります。 scikit-learnのKMeansでは、これがデフォルトの初期値選択方法 (`init=’k-means++’`) になっています。
  • 複数回実行 (`n_init` パラメータ): 異なる初期中心点の組み合わせでk-meansアルゴリズムを複数回実行し、その中で最もイナーシャ(クラスタ内誤差平方和)が小さかった結果を採用するという方法です。単純ですが効果的な方法です。 scikit-learnの KMeans クラスでは、n_init というパラメータで実行回数を指定できます。デフォルトでは n_init=10 (バージョンによって異なる可能性あり、最新版を確認) となっており、10回の異なる初期値で実行し、最良の結果を返します。

これらの対策により、初期値に由来する不安定さを大幅に軽減し、より安定して良いクラスタリング結果を得ることができます。

6. scikit-learnでのパラメータ設定

scikit-learnの KMeans クラスで初期値に関連する主要なパラメータは以下の通りです。

パラメータ説明デフォルト値 (scikit-learn 1.4時点)主な選択肢
init初期中心点の選択方法を指定します。'k-means++'
  • 'k-means++': k-means++アルゴリズムを使用。推奨。
  • 'random': データセットからランダムにk個の点を選択。
  • NumPy配列: 形状が (n_clusters, n_features) の配列を直接指定。
n_init異なる初期中心でk-meansアルゴリズムを実行する回数を指定します。最終的に最もイナーシャが低い結果が採用されます。10
  • 整数: 実行回数を指定。
  • 'auto': init='k-means++' の場合は1回、init='random' の場合は10回実行(将来変更される可能性あり)。
random_state初期中心点選択やk-means++の際の乱数生成器の状態を固定します。結果の再現性を確保したい場合に整数値を指定します。None整数値、または None

例えば、ランダムな初期値で5回実行したい場合は、以下のように指定します。

# ランダムな初期値で5回実行する例
kmeans_random_init = KMeans( n_clusters=3, init='random', # 初期値選択をランダムに n_init=5, # 5回実行 random_state=42 # 実行の再現性を確保
)
kmeans_random_init.fit(X)
print("ランダム初期値でのイナーシャ:", kmeans_random_init.inertia_)
# k-means++ (デフォルト) で実行する場合 (n_init=10 がデフォルト)
kmeans_plus_plus = KMeans( n_clusters=3, init='k-means++', # デフォルトなので省略可 n_init=10, # デフォルトだが明示的に記載 random_state=42
)
kmeans_plus_plus.fit(X)
print("k-means++でのイナーシャ:", kmeans_plus_plus.inertia_)
# 通常、k-means++の方が小さい(良い)イナーシャになりやすい 
ヒント: 多くの場合、デフォルトの init='k-means++'n_init=10 で十分良い結果が得られます。特別な理由がない限り、これらの設定から始めるのが良いでしょう。

7. まとめ

今回は、教師なし学習の代表的なアルゴリズムであるk-meansクラスタリングについて学びました。

  • k-meansはデータをk個のクラスタに分割するシンプルなアルゴリズムです。
  • アルゴリズムは、中心点の選択、データ点の割り当て、中心点の更新を繰り返します。
  • 初期中心点の選び方によって結果が変わる「初期値依存性」があります。
  • k-means++複数回実行 (n_init) によって、初期値問題の影響を軽減できます。
  • scikit-learnを使えば簡単に実装でき、パラメータで初期値戦略を制御できます。

k-meansは理解しやすく強力な手法ですが、クラスタの形状が球状であることを仮定していたり、事前にクラスタ数kを決める必要があるなどの特徴もあります。 次のステップでは、他のクラスタリング手法(階層クラスタリング、DBSCANなど)や、クラスタリング結果の評価方法についても学んでいきましょう!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です