聚类算法K-means

聚类算法****K-means

Kmeans演示:https://www.naftaliharris.com/blog/visualizing-k-means-clustering/

介绍

K-Means算法是无监督的聚类算法,算法简单,聚类效果好,即使是在巨大的数据集上也非常容

易部署实施。正因为如此,它在很多领域都得到的成功的应用,如市场划分、机器视觉、 地质统

计学、天文学和农业等。K-Means算法有大量的变体,包括初始化优化K-Means++以及大数据

应用背景下的k-means||和Mini Batch K-Means

K-Means算法的思想很简单,对于给定的样本集,按照样本之间的距离大小,将样本

集划分为K个簇。让簇内的点尽量紧密的连在一起,而让簇间的距离尽量的大。

如果用数学表达式表示,假设簇划分为(C1 ,C2 ,…Ck ),则我们的目标是最小化平方误差E:
E = ∑ i = 1 k ∑ x ∈ C i ∣ ∣ x − μ i ∣ ∣ 2 2 E=\sum_{i=1}^k\sum_{x \in C_i}||x - \mu_i||_2^2 E=i=1kxCi∣∣xμi22
其中 μi 是簇 Ci 的均值向量,有时也称为质心,表达式为
u i = 1 ∣ C i ∣ ∑ x ∈ C i x u_i = \frac{1}{|C_i|}\sum_{x\in C_i}x ui=Ci1xCix

K-means算法流程

输入是样本集D={x1 ,x2 ,…xN},聚类的簇数k,最大迭代次数T

输出是簇划分C={C1 ,C2 ,…Ck }

1)从数据集D中随机选择k个样本作为初始的k个质心向量: {μ1 ,μ2 ,…,μk },将每个簇初始化为空集

• 2)对于t=1,2,…,T

a) 对于i=1,2…N,计算样本 xi 和各个质心向量 μj,j=1,2,…k的欧式距离,将 xi 划分到最近的簇中,即更新Cj=Cj∪{xi}

b) 对于j=1,2,…,k,对Cj中所有的样本点重新计算新的质心

c)如果所有的k个质心向量都没有发生变化,则转到步骤3)

3) 输出簇划分C={C1 ,C2 ,…Ck }

Kmeans算法的缺点

缺点一:聚类中心的个数K需要事先给定,但在实际中K值的选定是非常困难的,很多时候我们并不知道给定的数据集应该聚成多少个类别才最合适

缺点二:k-means算法需要随机地确定初始聚类中心,不同的初始聚类中心可能导致完全不同的聚类结果,有可能导致算法收敛很慢甚至出现聚类出错的情况

针对第一个缺点:很难在k-means算法以及其改进算法中解决,一般来说,我们会根据对数据的先验经验选择一个合适的k值,如果没有什么先验知识,则可以通过“肘方法”选择一个合适的k值

针对第二个缺点:可以通过k-means++算法来解决

肘方法

手肘法的核心指标是SSE(sum of the squared errors,误差平方和),
S S E = ∑ i = 1 k ∑ x ∈ C i ∣ ∣ x − μ i ∣ ∣ 2 2 S S E = ∑ i = 1 k ∑ x ∈ C i ∣ ∣ x − μ i ∣ ∣ 2 2 SSE=\sum_{i=1}^k\sum_{x \in C_i}||x - \mu_i||_2^2SSE=\sum_{i=1}^k\sum_{x \in C_i}||x - \mu_i||_2^2 SSE=i=1kxCi∣∣xμi22SSE=i=1kxCi∣∣xμi22
• 其中,Ci是第i个簇,x是Ci中的样本点,μi是Ci的质心(Ci中所有样本的均值),SSE是所有样本的聚类误差,代表了聚类效果的好坏。

手肘法的核心思想是:随着聚类数k的增大,样本划分会更加精细,每个簇的聚合程度会逐渐提高,那么误差平方和SSE自然会逐渐变小。并且,当k小于真实聚类数时,由于k的增大会大幅增加每个簇的聚合程度,故SSE的下降幅度会很大,而当k到达真实聚类数时,再增加k所得到的聚合程度回报会迅速变小,所以SSE的下降幅度会骤减,然后随着k值的继续增大而趋于平缓,也就是说SSE和k的关系图是一个手肘的形状,而这个肘部对应的k值就是数据的真实聚类数。当然,这也是该方法被称为手肘法的原因。

K-means++

K-Means++的对于初始化质心的优化策略,如下:

a) 从输入的数据点集合中随机选择一个点作为第一个聚类中心 μ1

b) 对于数据集中的每一个点xi,计算它与已选择的聚类中心中最近聚类中心的距离D

c) 选择一个新的数据点作为新的聚类中心,选择的原则是:D较大的点,被选取作为聚类中心的概率较大。

d) 重复b和c直到选择出k个聚类质心

e) 利用这k个质心来作为初始化质心去运行标准的K-Means算法

K-means++的缺点

它内在的有序性特性:下一个中心点的选择依赖于已经选择的中心点

K-means||

k-means||是k-means++的变体,k-means||在初始化中心点时对kmeans++的缺点做了规避,主要体现在不需要根据k的个数严格地寻找k个点,突破了算法在大规模数据集上的应用瓶颈,同时初始化的中心点更加健壮

解决K-Means++算法缺点而产生的一种算法;主要思路是改变每次遍历时候的取样规则,并非按照K-Means++算法每次遍历只获取一个样本,而是每次获取K个样本,重复该取样操作O(logn)次,然后再将这些抽样出来的样本聚类出K个点,最后使用这K个点作为K-Means算法的初始聚簇中心点。实践证明:一般5次重复采用就可以保证一个比较好的聚簇中心点。

Mini Batch K-Means*

• 在传统的K-Means算法中,要计算所有的样本点到所有的质心的距离。如果样本量非常大,比如达到10万以上,特征有100以上,此时用传统的K-Means算法非常的耗时,就算加上elkanK-Means优化也依旧。在大数据时代,这样的场景越来越多。此时Mini Batch K-Means应运而生。

Mini Batch,也就是用样本集中的一部分的样本来做传统的K-Means,这样可以避免样本量太大时的计算难题,算法收敛速度大大加快。当然此时的代价就是我们的聚类的精确度也会有一些降低。一般来说这个降低的幅度在可以接受的范围之内

一般是通过无放回的随机采样得到的。

为了增加算法的准确性,我们一般会多跑几次Mini Batch K-Means算法,用得到不同的随机采样集来得到聚类簇,选择其中最优的聚类簇。

评估方法——轮廓系数法

簇内不相似度:计算样本i到同簇其它样本的平均距离为ai,ai越小,表示样本i越应该被聚类到该簇,簇C中的所有样本的ai的均值被称为簇C的凝聚度。

簇间不相似度:计算样本i到其它簇Cj的所有样本的平均距离bij,bi=min{bi1 ,bi2 ,…,bik };bi越大,表示样本i越不属于其它簇。

轮廓系数:si值取值范围为[-1,1],越接近1表示样本i聚类越合理,越接近-1,表示样本i应该分类到另外的簇中,近似为0,表示样本i应该在边界上;所有样本的si的均值被称为聚类结果的轮廓系数。
s ( i ) = b ( i ) − a ( i ) m a x { a ( i ) , b ( i ) } s ( i ) = { 1 − a ( i ) b ( i ) , a ( i ) < b ( i ) 0 , a ( i ) > b ( i ) b ( i ) a ( i ) − 1 , a ( i ) > b ( i ) s(i)=\frac{b(i)-a(i)}{max\{a(i),b(i)\}}\\ s(i)=\begin{cases} 1-\frac{a(i)}{b(i)},a(i)<b(i)\\ 0,a(i)>b(i)\\ \frac{b(i)}{a(i)}-1,a(i)>b(i) \end{cases} s(i)=max{a(i),b(i)}b(i)a(i)s(i)= 1b(i)a(i),a(i)<b(i)0,a(i)>b(i)a(i)b(i)1,a(i)>b(i)


import numpy as np


X = np.array([[1.0, 2.0], [2.0, 2.0], [6.0, 8.0], [7.0, 8.0]])
C = np.array([[1.0, 2.0], [2.0, 2.0]])

iters = 5
while iters > 0:
    ###计算每个样本到每个聚类中心的距离
    A = []
    for c in C:
        a = np.sqrt(np.sum((X - c) ** 2, axis=1))
        A.append(a)
    A = np.array(A)
    print(A)


    ###将样本分配的所属的聚类中心(获取最小值的下标)
    minidx = np.argmin(A, axis=0)
    print(minidx)

    for i in range(len(C)):
        a = X[minidx == i]
        print('a\n', a) #找到样本
        C[i] = np.mean(a, axis=0) #更新聚类中心
        print(C)
    print('C\n', C)
    # todo: 就是C不再更新停止
    print('---------------------------------')
    iters -= 1
import numpy as np
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')

X = np.array([[1, 2], [2, 2], [6, 8], [7, 8]])

keans = KMeans(n_clusters=2)
keans.fit(X)
center = keans.cluster_centers_
print(center)##簇中心
x_center = center[:, 0]
y_center = center[:, 1]
print(keans.labels_)###簇的标签
print(keans.score(X))
#
x = X[:, 0]
y = X[:, 1]
print(x)
print(y)

plt.scatter(x, y, color='blue', marker='o')
plt.plot(x_center, y_center, 'r-')
plt.show()
import pandas as pd
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')

df = pd.read_csv('D:/save/datas/iris.data', header=None)
# print(df.head())
X = df.iloc[:, :-1]
print(X)


clf = KMeans(n_clusters=3)
clf.fit(X)
print(clf.cluster_centers_)
print(clf.labels_)
print(clf.predict(X.iloc[[-1], :]))

x = X.iloc[:, 0]
y = X.iloc[:, 1]

cx = clf.cluster_centers_[:, 0]
cy = clf.cluster_centers_[:, 1]

plt.scatter(x, y, color='gray')
plt.scatter(cx, cy, color='red')
plt.show()
import pandas as pd
from sklearn.metrics import silhouette_score
from  sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')

df = pd.read_csv('D:/save/datas/iris.data', header=None)
X = df.iloc[:, :-1]

sses = [] # 簇内误差平方和
S = [] # 轮廓系数

for k in range(2, 10):
    kmeans = KMeans(n_clusters=k, init='k-means++' , n_init=10)
    kmeans.fit(X)

    inertia = kmeans.inertia_  # 簇内误差平方和
    sses.append(inertia)

    lables = kmeans.labels_
    s = silhouette_score(X, lables)
    S.append(s)

plt.figure(num=1)
plt.plot(range(2, 10), sses)
plt.title('SSE vs. Number of Clusters')
plt.xlabel('Number of Clusters (K)')
plt.ylabel('SSE')

plt.figure(num=2)
plt.plot(range(2, 10), S)
plt.title('Silhouette Score vs. Number of Clusters')
plt.xlabel('Number of Clusters (K)')
plt.ylabel('Silhouette Score')

plt.show()
import random

import numpy as np


class Kmeans():
    
    cluster_center_ = []
    labels_ = []
    
    def __init__(self, k, iters=100):
        self.k = k
        self.iters = iters
    
    def fit(self, X):
        # 随机获取K个初始化质心
        C = []
        for i in range(self.k):
            n = random.choice(list(range(X.shape[0])))
            c = np.array(X.iloc[n, :])[0]
            C.append(c)
        C = np.array(C)
        iters = self.iters
        temp_labels = np.zeros_like(X.shape[0])
        while iters > 0:
            A = []
            for c in C:
                a = np.sqrt(np.sum((X - c) ** 2, axis=1))
                A.append(a)
            A = np.array(A)
            minidx = np.argmin(A, axis=0)
            
            for i in range(len(C)):
                a = X[minidx == i]
                C[i] = np.mean(a, axis=0)
            
            if sum(temp_labels==minidx)==len(minidx):
                print(f"提前终止循环,iters{iters}")
                break
            temp_labels = minidx
            iters -= 1    
        self.cluster_center_ = C
        self.labels_ = minidx
    
    def predict(self, x, y):
            pass
    
if __name__ == '__main__':
    pass
  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值