numpy实现周志华机器学习 9.4.1 k均值聚类

本文主要参考周志华《机器学习》的9.4.1章节,对以k均值聚类为代表的原型聚类做简单介绍,并使用numpy实现kmeans聚类。

在讲kmeans聚类之前,先说一说原型聚类。

原型聚类亦称“ 基于原型的聚类” ,此类算法假设聚类结构能通过一组原型刻画,在现实聚类任务中极为常用.通常情形下,算法先对原型进行初始化,然后对原型进行迭代更新求解.采用不同的原型表示、不同的求解方式,将产生不同的算法。 《机器学习》

包括更为常用的GMM(高斯混合聚类),模糊C均值聚类,都是属于原型聚类的范畴。那么如何理解原型聚类呢?我是这样理解的,所谓原型,先定义一个模型,对这个模型迭代求最优解,最后获取聚类结果。
说到这里,对第一次接触聚类的朋友可能不太好理解。但是不用着急,随着逐渐熟悉,会慢慢理解。

给定样本集D= {X1,X2,...Xn}, “K 均值” (k-means)算法针对聚类所得簇划分c = {C1,C2,. . .,Ck}最小化平方误差,即求下式极小值:
E = i = 1 k x C i x μ i 2 2 E=\sum_{i=1}^{k} \sum_{\boldsymbol{x} \in C_{i}}\left\|\boldsymbol{x}-\boldsymbol{\mu}_{i}\right\|_{2}^{2}
其中上μi表示第i类的中心,这是一个需要估计的参数。双竖线中的内容就是i类中的元素到第i类的中心的距离(欧式距离)。根据上述公式,我们可以这样理解,kmeans算法要做的就是把每一类到其聚类中心的距离和最小化。

但是要如何求解上述问题呢?首先可以想到的是暴力求解,即把每一种聚类组合按照上述公式计算,选取最小的。但是这是一个NP问题,一共有k的n次方中划分方法(包涵有些类为空的情况)。庞大的计算复杂度,不允许这种方式。于是就有了kmeans算法。该方法的核心思想是,先初试化k个聚类中心,然后根据聚类中心划分所有元素(划入离其最近的聚类中心)。接着又根据这一类的元素计算新的聚类中心,反复迭代至聚类中心几乎不变。kmean的本质是EM算法。

算法伪代码如下:
引自机器学习一书
numpy实现如下:

import numpy as np
from sklearn.datasets import load_iris
import seaborn as sns
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt


def distance( a, b, p):
    a = np.array(a)
    b = np.array(b)
    return np.sum((a - b) ** p) ** (1 / p)


class Mykmean(object):
    def __init__(self, n_clusters, max_iter, init=""):
        self.max_iter = max_iter
        self.n_clusters = n_clusters
        self.center = 0
        self.labels = 0

    def fit(self, X):
        # 初始化聚类中心 采用平均的方法
        center = np.arange(0, self.n_clusters) / (self.n_clusters) + 1 / (2 * self.n_clusters)
        temp = []
        for i in range(X.shape[1]):
            temp.append(center)
        self.center = np.max(X, axis=0) * np.array(temp).T

        # 初始化类别标记
        self.labels = np.zeros((X.shape[0])).astype("int8")
        while self.max_iter > 0:
            # 根据初试化得聚类中心划分类
            for m in range(0, X.shape[0]):
                dists = []
                for k in range(0, self.n_clusters):
                    o_dist = distance(X[m, :], self.center[k, :], 2)
                    dists.append(o_dist)
                max_index = dists.index(min(dists))
                self.labels[m] = max_index

            # 计算新的聚类中心
            for k in range(0, self.n_clusters):
                temp = X.copy()
                temp = np.delete(temp, np.where(self.labels == k), axis=0)
                self.center[k, :] = np.mean(temp, axis=0)
            self.max_iter -= 1


def pairing(data, truth, label):
    datatemp = data.copy()
    centerTruth = []
    center = []
    new_label = np.zeros(label.shape) - 1
    for i in range(0, np.max(truth) + 1):
        centerTruth.append(list(np.mean(datatemp[np.argwhere(truth == i)], axis=0)))
        center.append(list(np.mean(datatemp[np.argwhere(label == i)], axis=0)))
    for i in range(0, np.max(truth) + 1):
        temp = []
        for j in range(0, np.max(truth) + 1):
            temp.append(distance(centerTruth[i], center[j], 2))
        number = temp.index(min(temp))
        print(number)
        new_label[label == number] = i
    return new_label


def calcu_acc(truth, label):
    temp = np.zeros(label.shape)
    temp[np.argwhere(label == truth)] = 1
    return np.sum(temp) / label.shape[0]

if __name__ == '__main__':
    iris = load_iris()
    estimator = KMeans(n_clusters=3,)  
    estimator.fit(iris.data)  
    label = pairing(iris.data, iris.target, estimator.labels_)
    acc = calcu_acc(iris.target, label)
    print("kmeans acc is :", acc)

    kmeanmodel = Mykmean(3, 100)
    kmeanmodel.fit(iris.data)
    label = pairing(iris.data, iris.target, kmeanmodel.labels)
    acc = calcu_acc(iris.target, label)
    print("mykmeans acc is :", acc)

鸢尾花数据测试结果如下:
测试结果
这里我实现的kmean得到的结果相比label的准确率低于scipy库实现的,主要是因为初始化聚类中心的方式,scipy中默认采用了kmean++的初始化方法,而本文中采用的是平均划分的方法。
这里引出了一个局部最优解的问题,对于非凸问题来说我们的EM算法只能求得其局部的最优解。

tips:ML新手,如果哪里写得不好,大家多多包涵。如果哪里有疑问,欢迎留言讨论。此外,我将继续有间断用numpy实现周志华《机器学习》的算法,欢迎大家关注。

发布了3 篇原创文章 · 获赞 7 · 访问量 133

猜你喜欢

转载自blog.csdn.net/weixin_42217488/article/details/104602344