0
点赞
收藏
分享

微信扫一扫

ML 自实现/k-means聚类算法

一世独秀 2022-02-09 阅读 33

简介

  • 非监督学习
  • 聚类算法,样本自动聚集成类,使得成本函数最小,使得样本点到聚类中心的欧式距离最小

原理

过程

  1. 初始化簇中心:随机选择k个数据对象作为初始簇中心
  2. 聚类剩余数据对象:对剩余的每个对象,根据其与各簇中心的相似度(距离),将它赋给与其最相似(距离最近)的簇中心对应的簇
  3. 判断成本函数(样本点到聚类中心的欧式距离)是否达到最优(样本点的聚类中心不再发生变化)或者达到最大迭代次数:达到最优或者最大迭代次数则退出,否则进入第4步
  4. 重新计算新簇中心:计算每个簇中所有对象的平均值,作为新的簇中心

相似度

similarity

  • 相似度通过距离衡量

距离
设特征空间 X X X是m维实数向量空间 R n R^n Rn x i , x j ∈ X x_i,x_j\in X xi,xjX x i = ( x i ( 1 ) , x i ( 2 ) , . . . , x i ( m ) ) T x_i=(x_i^{(1)},x_i^{(2)},...,x_i^{(m)})^T xi=(xi(1),xi(2),...,xi(m))T x j = ( x j ( 1 ) , x j ( 2 ) , . . . , x j ( m ) ) T x_j=(x_j^{(1)},x_j^{(2)},...,x_j^{(m)})^T xj=(xj(1),xj(2),...,xj(m))T

  • x i , x j x_i,x_j xi,xj L p L_p Lp距离: L p ( x i , x j ) = ( ∑ l = 1 m ∣ x i ( l ) − x j ( l ) ∣ p ) 1 p L_p(x_i,x_j)=\Big(\sum\limits_{l=1}^m|x_i^{(l)}-x_j^{(l)}|^p\Big)^{\frac{1}{p}} Lp(xi,xj)=(l=1mxi(l)xj(l)p)p1
  • p = 2 p=2 p=2欧式距离: L 2 ( x i , x j ) = ( ∑ l = 1 m ∣ x i ( l ) − x j ( l ) ∣ 2 ) 1 2 L_2(x_i,x_j)=\Big(\sum\limits_{l=1}^m|x_i^{(l)}-x_j^{(l)}|^2\Big)^{\frac{1}{2}} L2(xi,xj)=(l=1mxi(l)xj(l)2)21

剩余样本与各簇中心的相似度(距离)

  • L 2 ( x i , x j ) = ( ∑ l = 1 m ∑ j = 1 k ∣ x i ( l ) − c j ( l ) ∣ 2 ) 1 2 L_2(x_i,x_j)=\Big(\sum\limits_{l=1}^m\sum\limits_{j=1}^k|x_i^{(l)}-c_j^{(l)}|^2\Big)^{\frac{1}{2}} L2(xi,xj)=(l=1mj=1kxi(l)cj(l)2)21
  • c i c_i ci是第i个簇中心

成本函数

成本函数: m i n ∑ i = 1 k ∑ x ∈ c i d i s t ( c i , x ) 2 min\sum\limits_{i=1}^k\sum\limits_{x\in c_i}dist(c_i,x)^2 mini=1kxcidist(ci,x)2

  • 最小化每个数据对象到所属中心点的距离的方差,共有k个聚类
  • k是聚类个数,簇的个数
  • c i c_i ci是第i个簇的中心点
  • c i c_i ci x x x的所属的聚类中心
  • d i s t ( c i , x ) dist(c_i,x) dist(ci,x)为x到 c i c_i ci的距离

代码

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import random
from sklearn.datasets import make_blobs

matplotlib.rcParams['font.family'] = 'STSong'
matplotlib.rcParams['font.size'] = 20
np.random.seed(1)


class DataSet(object):
    """
    数据集
    """

    def __init__(self, X, y):
        self.X = X
        self.y = y


def get_data_set():
    """
    获取数据集
    """
    # X.shape=1000x2,y.shape=1000x1;X共有1000个样本,每个样本2个特征值;y是这个样本所属的中心
    X, y = make_blobs(centers=4, n_samples=1000)
    print("X.shape=", X.shape)

    plt.figure(figsize=(10, 8))
    # X的第1列做横轴,X的第2列做纵轴;c=color 与y的值大小有关,值相同颜色相同
    plt.scatter(X[:, 0], X[:, 1], c=y)
    plt.title("4个簇")
    plt.xlabel("X特征1")
    plt.ylabel("X特征2")
    plt.show()

    data_set = DataSet(X, y)
    return data_set


class KMeans():
    """
    k-means算法
    """

    def __init__(self, k=4):
        """
        构造函数
        :param k: 簇个数,聚类中心个数
        """
        self.k = k
        self.centers = None
        self.initial_centers = None

    def clustering(self, data, num_iter=100):
        """
        聚类函数
        达到最优(样本点的聚类中心不再发生变化)或者最大迭代次数则退出
        :param data: X
        """
        print("data.shape=", data.shape)
        # 初始化簇中心:随机选择k个数据对象作为初始簇中心
        self.centers = np.array(random.sample(list(data), self.k))
        self.initial_centers = np.copy(self.centers)

        # 记录样本点所属中心点的下标
        old_belongs = None
        real_iter = 0

        for i in range(num_iter):
            # new_belongs是长度为1000的array,存储样本点所属中心点的下标
            new_belongs = [self.classify(point) for point in data]

            # 达到最优(样本点的聚类中心不再发生变化);样本点的聚类中心(下标)是否发生了变化
            if new_belongs == old_belongs:
                print("要求迭代次数:", num_iter, ",实际迭代次数:", real_iter)
                return

            old_belongs = new_belongs

            # 重新计算新簇中心:计算每个簇中所有对象的平均值,作为新的簇中心
            for ki in range(self.k):
                # 获取中心点为ki的所有样本的下标;points.shape=(point_idxs.size,2)
                point_idxs = np.where(np.array(new_belongs) == ki)
                points = data[point_idxs]
                # 计算每个簇中所有对象的平均值,作为新的簇中心;axis=0按列求平均值
                self.centers[ki] = points.mean(axis=0)

            real_iter += 1

    def classify(self, point):
        """
        计算给定样本点与k个中心点的距离,返回最近距离的中心点下标
        """
        # 样本点到聚类中心的欧式距离;centers.shape=(4,2) point.shape=(2,)是行向量(1,2);axis=1 按行求和
        dists = np.sqrt(np.sum((point - self.centers) ** 2, axis=1))
        # 返回最近距离的中心点下标
        return np.argmin(dists)

    def show_clusters(self, data_set):
        plt.figure(figsize=(10, 8))
        plt.title("初始聚类中心点是黑色,最终聚类中心点是红色")
        # X的第1列做横轴,X的第2列做纵轴;c=color 与y的值大小有关,值相同颜色相同
        plt.scatter(data_set.X[:, 0], data_set.X[:, 1], c=data_set.y)
        # 聚类的初始中心点
        plt.scatter(self.initial_centers[:, 0], self.initial_centers[:, 1], marker="*", c='k')
        # 聚类的最终中心点
        plt.scatter(self.centers[:, 0], self.centers[:, 1], marker="*", c='r')
        plt.show()


def main():
    data_set = get_data_set()
    # k是簇数量,聚类中心数量
    kmeans = KMeans(k=4)
    # 开始聚类
    kmeans.clustering(data_set.X, num_iter=100)
    # 展示初始聚类中心,最终聚类中心
    kmeans.show_clusters(data_set)


if __name__ == "__main__":
    main()

举报

相关推荐

0 条评论