聚类算法之——K-Means算法

聚类算法之——K-Means算法

聚类算法属于无监督学习,它将相似的对象归到同一个簇中。K-Means算法是聚类算法中最常用到算法;

1. 预备知识点

距离计算

闵可夫斯基距离

x = ( x 1 , x 2 , … , x n ) 和 y = ( y 1 , y 2 , … , y n ) x=(x_1,x_2,\dots,x_n)和y=(y_1,y_2,\dots,y_n) x=(x1,x2,,xn)y=(y1,y2,,yn)之间的闵可夫斯基距离为
d ( x , y ) = ( ∑ i = 1 n ∣ x i − y i ∣ p ) 1 p 其 中 p ≥ 1 d(x,y)=(\sum_{i=1}^{n}{|x_i-y_i|^p})^\frac{1}{p} \quad 其中p\geq1 d(x,y)=(i=1nxiyip)p1p1

欧式距离

x = ( x 1 , x 2 , … , x n ) 和 y = ( y 1 , y 2 , … , y n ) x=(x_1,x_2,\dots,x_n)和y=(y_1,y_2,\dots,y_n) x=(x1,x2,,xn)y=(y1,y2,,yn)之间的欧氏距离为:
d ( x , y ) = ( x 1 − y 1 ) 2 + ( x 2 − y 2 ) 2 + ⋯ + ( x n − y n ) 2 = ∑ i = 1 n ( x i − y i ) 2 \begin{aligned}d(x,y)&=\sqrt{(x_1-y_1)^2+(x_2-y_2)^2+\dots+(x_n-y_n)^2}\\&=\sqrt{\sum_{i=1}^{n}{(x_i-y_i)^2}} \end{aligned} d(x,y)=(x1y1)2+(x2y2)2++(xnyn)2 =i=1n(xiyi)2

曼哈顿距离(Manhattan Distance )

在这里插入图片描述
图中红线代表曼哈顿距离,绿线代表欧式距离,也就是直线距离,而蓝色和黄色代表等价的曼哈顿距离。

曼哈顿距离:两点在南北方向上的距离加上在东西方向上的距离。

x = ( x 1 , x 2 , … , x n ) 和 y = ( y 1 , y 2 , … , y n ) x=(x_1,x_2,\dots,x_n)和y=(y_1,y_2,\dots,y_n) x=(x1,x2,,xn)y=(y1,y2,,yn)之间的曼哈顿距离为:
d ( x , y ) = ∑ i = 1 n ∣ x i − y i ∣ d(x,y)=\sum_{i=1}^{n}{|x_i-y_i|} d(x,y)=i=1nxiyi

余弦距离

两个向量 A 和 B,其余弦距离(即两向量夹角的余弦)由点积和向量长度给出,计算公式如下:
c o s θ = A ⋅ B ∣ ∣ A ∣ ∣ ⋅ ∣ ∣ B ∣ ∣ = ∑ i = 1 n A i B i ∑ i = 1 n ( A i ) 2 ∑ i = 1 n ( B i ) 2 cos\theta=\frac{A \cdot B}{||A|| \cdot ||B||}=\frac{\sum_{i=1}^{n}{A_iB_i}}{\sqrt{\sum_{i=1}^{n}{(A_i)^2}}\sqrt{\sum_{i=1}^{n}{(B_i)^2}}} cosθ=ABAB=i=1n(Ai)2 i=1n(Bi)2 i=1nAiBi
其中, A i 和 B i A_i和B_i AiBi分别代表向量 A A A B B B的各分量 。

2. K-Means算法步骤

输入:样本集{ x 1 , x 2 , … , x m x_1,x_2,\dots,x_m x1,x2,,xm}

​ 聚类簇数k

输出:簇划分 C = C= C={ C 1 , C 2 , … , C k C_1,C_2,\dots,C_k C1,C2,,Ck}

  1. 随机初始化k个点作为簇质心;

  2. 将样本集中的每个点分配到一个簇中;

    计算每个点与质心之间的距离(常用欧式距离和余弦距离),并将其分配给距离最近的质心所对应的簇中;

  3. 更新簇的质心;

    每个簇的质心更新为该簇所有点的平均值;

  4. 反复迭代2 - 3 步骤,直到达到某个终止条件;

    常用的终止条件有:1)达到指定的迭代次数;2)簇心不再发生明显的变化,即收敛;3)最小误差平方和SSE;
    在这里插入图片描述

3. 聚类效果的评价指标SSE

SSE(Sum of Square Error, 误差平方和),SSE值越小表示数据点越接近于它们的质心,聚类效果也越好。

S S E = ∑ i = 1 k ∑ x ϵ C i ( x − μ i ) 2 μ i = 1 ∣ C i ∣ ∑ x ϵ C i x \begin{aligned}SSE&=\sum_{i=1}^{k}{\sum_{x\epsilon C_i}{(x-\mu_i)^2}}\\\mu_i&=\frac{1}{|C_i|}\sum_{x\epsilon C_i}{x}\end{aligned} SSEμi=i=1kxϵCi(xμi)2=Ci1xϵCix

4. python脚本

调用sklearn包里的K-Means算法来实现

完整版脚本,直接运行即可

import time
import matplotlib.pyplot as plt
import matplotlib
from  sklearn.cluster import KMeans
from sklearn.datasets import load_iris 
matplotlib.rcParams['font.sans-serif'] = [u'SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False

# 获取鸢尾花数据集,特征分别是sepal length、sepal width、petal length、petal width
iris = load_iris() 
X = iris.data[:,2:]  # 通过花瓣的两个特征来聚类
k=3  # 假设聚类为3类
# 构建模型
s=time.time()
km = KMeans(n_clusters=k) 
km.fit(X)
print("用sklearn内置的K-Means算法聚类耗时:",time.time()-s)

label_pred = km.labels_   # 获取聚类后的样本所属簇对应值
centroids = km.cluster_centers_  # 获取簇心

#绘制K-Means结果
# 未聚类前的数据分布
plt.subplot(121)
plt.scatter(X[:, 0], X[:, 1], s=50)
plt.xlabel('petal length')
plt.ylabel('petal width')
plt.title("未聚类前的数据分布")
plt.subplots_adjust(wspace=0.5)

plt.subplot(122)
plt.scatter(X[:, 0], X[:, 1], c=label_pred, s=50, cmap='viridis')
plt.scatter(centroids[:,0],centroids[:,1],c='red',marker='o',s=100)
plt.xlabel('petal length')
plt.ylabel('petal width')
plt.title("用sklearn内置的K-Means算法聚类结果")
plt.show()  

运行结果

在这里插入图片描述

按原理来实现K-Means算法

import time
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.sans-serif'] = [u'SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False
import numpy as np
def distEclud(vecA,vecB):
    """
    计算两个向量的欧式距离
    """
    return np.sqrt(np.sum(np.power(vecA-vecB,2)))

def randCent(dataSet,k):
    """
    随机生成k个点作为质心,其中质心均在整个数据数据的边界之内
    """
    n=dataSet.shape[1] # 获取数据的维度
    centroids = np.mat(np.zeros((k,n)))
    for j in range(n):
        minJ = np.min(dataSet[:,j])
        rangeJ = np.float(np.max(dataSet[:,j])-minJ)
        centroids[:,j] = minJ+rangeJ*np.random.rand(k,1)
    return centroids

def kMeans(dataSet,k,distMeas=distEclud, createCent=randCent):
    """
    k-Means聚类算法,返回最终的k各质心和点的分配结果
    """
    m = dataSet.shape[0]  #获取样本数量
    # 构建一个簇分配结果矩阵,共两列,第一列为样本所属的簇类值,第二列为样本到簇质心的误差
    clusterAssment = np.mat(np.zeros((m,2)))
    # 1. 初始化k个质心
    centroids = createCent(dataSet,k) 
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):
            minDist = np.inf
            minIndex = -1
            # 2. 找出最近的质心
            for j in range(k):
                distJI = distMeas(centroids[j,:],dataSet[i,:])
                if distJI < minDist:
                    minDist = distJI
                    minIndex = j
            # 3. 更新每一行样本所属的簇
            if clusterAssment[i,0] != minIndex:
                clusterChanged = True
            clusterAssment[i,:]=minIndex,minDist**2
        print(centroids) # 打印质心
        # 4. 更新质心
        for cent in range(k):
            ptsClust = dataSet[np.nonzero(clusterAssment[:,0].A==cent)[0]] # 获取给定簇的所有点
            centroids[cent,:] = np.mean(ptsClust,axis=0) # 沿矩阵列的方向求均值
    return centroids,clusterAssment

s=time.time()
myCentroids,clustAssing=kMeans(X,3)  # myCentroids为簇质心
print("用K-Means算法原理聚类耗时:",time.time()-s)
centroids=myCentroids.A # 将matrix转换为ndarray类型
# 获取聚类后的样本所属的簇值,将matrix转换为ndarray
y_kmeans=clustAssing[:,0].A[:,0]
# 未聚类前的数据分布
plt.subplot(121)
plt.scatter(X[:, 0], X[:, 1], s=50)
plt.xlabel('petal length')
plt.ylabel('petal width')
plt.title("未聚类前的数据分布")
plt.subplots_adjust(wspace=0.5)

plt.subplot(122)
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis')
plt.scatter(centroids[:, 0], centroids[:, 1], c='red', s=100, alpha=0.5)
plt.xlabel('petal length')
plt.ylabel('petal width')
plt.title("用K-Means算法原理聚类的效果")
plt.show()  

运行结果
在这里插入图片描述

算法优缺点和适用场景

优点:容易实现

缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢

适用数据类型:数值型数据

如何避免陷入局部最优

  1. 当k=2~10之间时,可运行多次,一般次数在50到1000之间,在得到的多个损失函数中选取最小的;

    例如:循环100次,选取损失函数最小的那个聚类结果作为最终的聚类结果;

   for i =1 to 100:
		随机初始化中心点;
		运行k-means算法步骤;
		计算损失函数J(即第3部分的误差平方和SSE);

   选取损失函数最小的那个聚类结果作为最终的聚类结果;
  1. 当k比较大时,不建议采取(1)的方法。

如何选择K的数量

  1. 用“Elbow Method”(肘部法则)来选择K的数量;

    需要画图(横轴聚类的数量,纵轴损失函数),选择曲线明显的拐点作为K

    但也会存在没有清晰的拐点,无法选出K的数;
    在这里插入图片描述

  2. 根据后续目标来选择聚类数目;

    比如哪种衬衫尺码会更好的满足我的顾客。

    衬衫的尺码数k=3(S M L)还是k=5(XS S M L XL),选哪个会让顾客更满意(这是目标);

补充

如果没有数据,也可以通过python现有库模拟产生数据,代码如下

from sklearn.datasets import make_blobs  # 导入产生模拟数据的方法
# 生成模拟数据
k = 5 # 给定聚类数量
X, Y = make_blobs(n_samples=1000, n_features=2, centers=k, random_state=1)

用模拟生成的数据替换上面代码中的x和k,则调算法包和用原理实现的运行效果分别如下

在这里插入图片描述在这里插入图片描述

总结

用封装好的算法和用原理实现的算法相比,从时间上来看,封装好的算法要比按原理实现的速度快,后续探索到其他方面再补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值