Andrew Ng の「機械学習」 - KMeans クラスタリング アルゴリズム

データセットとソースファイルは、Github プロジェクト
リンクから入手できます: https://github.com/Raymond-Yang-2001/AndrewNg-Machine-Learing-宿題

1.教師なし学習とKMeansアルゴリズム

以前に紹介したアルゴリズムとは異なり、この記事で紹介する 2 つのアルゴリズムは教師なし学習アルゴリズムです。線形回帰、ロジスティック回帰、またはニューラル ネットワークのいずれであっても、アルゴリズムをトレーニングするとき、入力はサンプル自体とサンプルのラベルの 2 つの部分で構成されます。パラメータの学習は、実際にはサンプル ラベルの「指導」の下で行われます。教師なし学習では、そのような指導は存在しません。教師なし学習アルゴリズムは、データ内の分布パターンを見つけて、それを使用して特定のタスクを完了しようとします。

前の分類例では、データ特徴の分布、つまり 2 つの座標の分布には明らかな規則があります。同じクラスに属するサンプルは互いに近くにあり、同じクラスのサンプルは一緒に分布する傾向があります。ここでの「距離」は、実際には類似性の尺度です。KMeans アルゴリズムは、サンプル間の類似性を使用してサンプルの教師なし分類を実行するアルゴリズムです。

2. アルゴリズム原理

KMeans クラスタリングはセンターベースのパーティショニング テクノロジであり、具体的な反復計算手順は次のとおりです。

  1. 属性ベクトル空間でkk をランダムに生成するkクラスターの中心座標。
  2. データセットDDを別途計算D内のD i ( 1 ≤ i ≤ n ) D_{i} (1≤i≤n)D私は( 1n )全てのkkk中心の距離測定 D ist ( i , j ) ( 1 ≤ i ≤ n , 1 ≤ j ≤ k )Dis t ( i , _ _j ) ( 1n 1jk ) を実行し、データ オブジェクト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
  3. 中心の定義に従って各クラスターの中心座標を計算し、次世代kkを形成しますk中心座標。
  4. 終了条件が満たされていない場合は 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... メートル=メートル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... メートル)
最初のループは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]))

ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/d33332/article/details/128568547