KMeans クラスタリング アルゴリズム
データセットとソースファイルは、Github プロジェクト
リンクから入手できます: https://github.com/Raymond-Yang-2001/AndrewNg-Machine-Learing-宿題
1.教師なし学習とKMeansアルゴリズム
以前に紹介したアルゴリズムとは異なり、この記事で紹介する 2 つのアルゴリズムは教師なし学習アルゴリズムです。線形回帰、ロジスティック回帰、またはニューラル ネットワークのいずれであっても、アルゴリズムをトレーニングするとき、入力はサンプル自体とサンプルのラベルの 2 つの部分で構成されます。パラメータの学習は、実際にはサンプル ラベルの「指導」の下で行われます。教師なし学習では、そのような指導は存在しません。教師なし学習アルゴリズムは、データ内の分布パターンを見つけて、それを使用して特定のタスクを完了しようとします。
前の分類例では、データ特徴の分布、つまり 2 つの座標の分布には明らかな規則があります。同じクラスに属するサンプルは互いに近くにあり、同じクラスのサンプルは一緒に分布する傾向があります。ここでの「距離」は、実際には類似性の尺度です。KMeans アルゴリズムは、サンプル間の類似性を使用してサンプルの教師なし分類を実行するアルゴリズムです。
2. アルゴリズム原理
KMeans クラスタリングはセンターベースのパーティショニング テクノロジであり、具体的な反復計算手順は次のとおりです。
- 属性ベクトル空間でkk をランダムに生成するkクラスターの中心座標。
- データセットDDを別途計算D内のD i ( 1 ≤ i ≤ n ) D_{i} (1≤i≤n)D私は( 1≤私≤n )全てのkkへk中心の距離測定 D ist ( i , j ) ( 1 ≤ i ≤ n , 1 ≤ j ≤ k )Dis t ( i , _ _j ) ( 1≤私≤n 、1≤j≤k ) を実行し、データ オブジェクトD i D_{i}D私は最小距離メトリックを使用してクラスターに集まります。つまり、T i ∈ CJ T_{i}∈C_{J}T私は∈CJ、データ オブジェクトD i D_{i}を表します。D私はJJに集まったクラスターJでは、ここで、J = arg min ( D ist ( i , j ) ) J=\argmin(Dist(i,j))J=引数min ( D i s t ( i ,j ))、JJJ は次のようなものですD ist (i, j) Dist(i,j)Dis t ( i , _ _j )は最小値jjj。
- 中心の定義に従って各クラスターの中心座標を計算し、次世代kkを形成しますk中心座標。
- 終了条件が満たされていない場合は 2. 反復を継続し、満たされていない場合は終了します。
その中で、クラスターの重心はさまざまな定義を持つことができます。たとえば、クラスター内のデータ オブジェクトの属性ベクトルの平均値(つまり、重心)にすることも、中心点にすることもできます。など; 距離尺度にはさまざまな定義もあり、一般的に使用されるものは、ユークリッド距離、マンハッタン (または都市街区、街区) 距離、ミンコフスキー距離などです。
終了条件は次のいずれかになります。
- 別のクラスターに再割り当てされるオブジェクトはありません (または最小限のオブジェクト)。
- クラスター センターの変更はもうありません (または最小限の数)。
- 二乗誤差の合計は極小値です。
μ ( 1 ) 、μ ( 2 ) 、⋯ \mu^{(1)},\mu^{(2)},\cdots を使用しますメートル( 1 )、メートル( 2 )、⋯ c (1), c (2), ⋯ c^{(1)},c^{(2)},\cdots を使用してクラスターの中心を表しますc( 1 )、c( 2 )、⋯ i 番目のインスタンス データに最も近いクラスター中心のインデックスを格納する
K-means アルゴリズムの疑似コードは次のとおりです。
Repeat {
for i = 1 to m
ci := index (form 1 to K) of cluster centroid closest to xi
for k = 1 to K
μk := average (mean) of points assigned to cluster k
}
KMeans の目的関数は次のとおりです:
J ( c ( 1 ) , . . . , c ( m ) , μ 1 , . . . , μ k ) = 1 m ∑ i = 1 m ∣ ∣ x ( i ) − μ c ( i ) ∣ ∣ 2 J(c^{(1)},...,c^{(m)},\mu_{1},...,\mu_{k})=\frac{ 1 }{m}\sum_{i=1}^{m}{||x^{(i)}-\mu_{c^{(i)}}||^{2}}J ( c( 1 )、... 、c(メートル)、メートル1、... 、メートルk)=メートル1i = 1∑メートル∣∣ x(私)−メートルc(私)∣ ∣2
最適化の目的は次のとおりです:
min c ( 1 ) , . . . , c ( m ) J ( c ( 1 ) , . . . , c ( m ) , μ 1 , . . . , μ k ) \min_{c ^{(1)},...,c^{(m)}}{J(c^{(1)},...,c^{(m)},\mu_{1},.. .,\mu_{k})}c( 1 )、...、c(メートル)分J ( c( 1 )、... 、c(メートル)、メートル1、... 、メートルk)
最初のループはc (i) c^{(i)}c( i )はコストの原因となり、2 番目のループはμ i \mu_{i}メートル私は
発生した費用。反復プロセスでは、反復ごとにコスト関数を削減する必要があります。そうしないと、エラーが発生します。
アルゴリズムのブロック図は次のとおりです。
2.1 中心点の初期化の解決策
中心点の最も単純な初期化ソリューションは、データ範囲内でランダムにサンプリングすることです。ほとんどの場合、このサンプリング方法で分類タスクを適切に実行できます。ただし、いくつかの問題もあります。
したがって、ここではより複雑な初期化方法が使用されます。つまり、
サンプルの各次元の特徴をソートし、ソートされたシーケンスに従って等間隔でサンプリングして中心点を取得します。これは、すべてのカテゴリのサンプルが特徴空間内に均一に分布しているという前提に基づいています。
3. Pythonの実装
import numpy as np
import matplotlib.pyplot as plt
def visual_result(label_pred, x, centroids, fig_size, fig_dpi):
"""
Visualization of clustering result (2-dimension sample)
:param label_pred: (n, )
:param x:
:param centroids:
:param fig_size:
:param fig_dpi:
:return:
"""
k = centroids.shape[0]
plt.figure(figsize=fig_size, dpi=fig_dpi)
for i in range(k):
indices = np.where(label_pred == i)[0]
cls_i = x[indices, :]
plt.scatter(cls_i[:, 0], cls_i[:, 1], color=plt.cm.Set1(i % 8), s=10, label='class ' + str(i))
plt.scatter(x=centroids[:, 0], y=centroids[:, 1], color=plt.cm.Set1(9), marker='*', label='Cluster center')
plt.legend(loc=2)
plt.title("Clustering Result")
plt.show()
def euclid(p1, p2):
"""
Compute Euclidian distance between p1 and p2
:param p1: ndarray with shape of (n_samples, n_clusters, n_features)
:param p2: ndarray with shape of (n_samples, n_clusters, n_features)
:return: Euclidian distance. ndarray with shape (n_samples, n_clusters)
"""
# (n,k,d)
distance = (p1 - p2) ** 2
# return (n,k)
return np.sqrt(np.sum(distance, axis=2))
class KMeans:
"""
KMeans Class
---------------------------------------------------------------
Parameters:
k:
Number of clusters to classify.
max_iter:
Max iterations of k-means algorithm.
---------------------------------------------------------------
Attributes:
distance: ndarray with shape of (n_samples, n_clusters)
The distance between each sample and each centroid.
centroids: ndarray with shape od (n_clusters, n_features)
"""
def __init__(self, k, max_iter=1000):
self.k = k
self.max_iter = max_iter
self.distance = None
self.centroids = None
self.cls = None
self.__stop = False
def __init_centroids(self, x):
"""
Uniform sampling on each dimension to init centroids
:param x: (n,d)
:return:
"""
idx = np.sort(x, axis=0)
_k = np.linspace(1, self.k, self.k)
step = x.shape[0] / (self.k + 1)
sample_idx = step * _k
sample_idx = np.floor(sample_idx).astype(int)
self.centroids = idx[sample_idx, :]
def __get_distance(self, x):
"""
x: (n,d) -> (n,k,d)
centroids (k,d) -> (n,k,d)
distance (n,k)
:param x: (n,d)
:return: (n,k)
"""
_x = np.expand_dims(x, axis=1).repeat(repeats=self.k, axis=1)
_c = np.expand_dims(self.centroids, axis=0).repeat(repeats=x.shape[0], axis=0)
self.distance = euclid(_x, _c)
def __update_centroids(self, x):
"""
Compute the average value on each dimension of each sample to for each class to get new centroid
If the bias of new centroid and current centroids is the same,
new centroids can be seen as the result
:param x: ndarray (n,d), samples
:return:
"""
new_centroids = self.centroids.copy()
for i in range(self.k):
indices = np.where(self.cls == i)[0]
new_centroids[i, :] = np.average(x[indices, :], axis=0)
if not np.equal(self.centroids, new_centroids).all():
self.centroids = new_centroids
else:
self.__stop = True
def __get_closest_centroid(self):
"""
distance (n,k)
:return: cls (n,)
"""
self.cls = np.argmin(self.distance, axis=1)
def fit(self, x):
"""
Fit the data
:param x: ndarray (n,d)
:return:
"""
self.__init_centroids(x)
for i in range(self.max_iter):
self.__get_distance(x)
self.__get_closest_centroid()
if not self.__stop:
self.__update_centroids(x)
else:
break
if not self.__stop:
print("KMeans fails to converge, please try larger max_inter.")
return self.cls, self.centroids
データセットの視覚化
from KMeans import KMeans, visual_result
clf = KMeans(k=3,max_iter=1000)
cls, centroids = clf.fit(x)
visual_result(cls,x,centroids,fig_size,fig_dpi)
分類結果を視覚化する
3.1 KMeans は画像圧縮を実装します
標準の 24 ビット画像は、H × W × 3 H\times W\times3で構成されます。H×W×3 つのピクセルで構成され、各ピクセルの値の範囲は 0 ~ 255、つまり 8 ビットです。128*128 の画像が占めるスペースは次のとおりです:
128 × 128 × 3 × 8 = 393216 bit 128\times128\times3\times8=393216\ \rm{bit}128×128×3×8=393216ビット
元の画像を 16 色で置き換える場合、16 色には16 × 24 = 384 ビット 16\times24=384\ \rm{bit}16×24=384ビット さらに、各ピクセルの置換ピクセルのインデックス、これには128 × 128 × 4 = 65536 ビット 128\times128\times4=65536\ \rm{bit} が128×128×4=65536ビット 、合計384 + 65536 = 65920 ビット 384+65536=65920\ \rm{ビット}384+65536=65920ビット 。
元画像表示は
元画像を正規化します
image = image / 255
クラスタリングを実行し、表示を復元します
clf_image = KMeans(k=16, max_iter=1000)
cls, centroids = clf_image.fit(image_x)
x_recovered = centroids[cls.astype(int),:]
x_recovered = np.reshape(x_recovered, (image.shape[0], image.shape[1], image.shape[2]))