第十五课.K均值算法

K均值算法原理

K均值(K-means)算法属于无监督学习中的聚类算法;聚类是根据样本特征向量之间的相似度或距离,将样本数据划分为若干个样本子集,每个子集定义为一个类;相似的样本聚集在相同的类,不相似的样本分散在不同的类。由上面的定义可知,聚类算法只使用了样本的特征向量 x x x,并没有使用样本的标签 y y y,故聚类算法属于无监督学习

样本距离
样本距离越小,样本的相似性越大。K均值聚类使用欧式距离的平方作为样本距离,计算公式如下:
d ( x i , x j ) = ∑ k = 1 m ( x k i − x k j ) 2 = ∣ ∣ x i − x j ∣ ∣ 2 2 d(x_{i},x_{j})=\sum_{k=1}^{m}(x_{ki}-x_{kj})^{2}=||x_{i}-x_{j}||_{2}^{2} d(xi,xj)=k=1m(xkixkj)2=xixj22
x i = [ x 1 i , x 2 i , . . . , x m i ] T , x j = [ x 1 j , x 2 j , . . . , x m j ] T x_{i}=[x_{1i},x_{2i},...,x_{mi}]^{T},x_{j}=[x_{1j},x_{2j},...,x_{mj}]^{T} xi=[x1i,x2i,...,xmi]T,xj=[x1j,x2j,...,xmj]T
如上所述,先计算向量对应元素的差值,然后取平方,最后求和;这个计算过程还可以表示为:先对两个样本的特征向量作差,然后求二范数的平方。

K均值聚类使用欧氏距离的平方表示样本之间的距离或相似度。除此之外,欧氏距离、相关系数、夹角余弦等也可以用来表示两个样本特征向量之间的距离或相似度。

聚类中心
聚类得到的结果是若干样本子集,对样本子集中的特征向量求均值可以得到类中心:
x ‾ G = 1 ∣ G ∣ ∑ i = 1 ∣ G ∣ x i , x i ∈ G \overline{x}_{G}=\frac{1}{|G|}\sum_{i=1}^{|G|}x_{i},x_{i}\in G xG=G1i=1Gxi,xiG
其中, x i x_{i} xi为样本特征向量, ∣ G ∣ |G| G为类别 G G G的样本个数;

算法流程

  • 1.随机选取 K K K 个样本点作为初始聚类中心;
  • 2.计算样本特征向量 x i x_{i} xi 和每个聚类中心 c j c_{j} cj 的距离,计算公式如下:
    d ( x i , c j ) = ∑ k = 1 m ( x k i − c k j ) 2 d(x_{i},c_{j})=\sum_{k=1}^{m}(x_{ki}-c_{kj})^{2} d(xi,cj)=k=1m(xkickj)2
    x i = [ x 1 i , x 2 i , . . . , x m i ] T , c j = [ c 1 j , c 2 j , . . . , c m j ] T x_{i}=[x_{1i},x_{2i},...,x_{mi}]^{T},c_{j}=[c_{1j},c_{2j},...,c_{mj}]^{T} xi=[x1i,x2i,...,xmi]T,cj=[c1j,c2j,...,cmj]T
    i ∈ { 1 , 2 , . . . , n } , j ∈ { 1 , 2 , . . . , K } i\in \left \{1,2,...,n \right \},j\in \left \{1,2,...,K \right \} i{1,2,...,n},j{1,2,...,K} n n n为数据集的样本数, K K K为聚类的类别数;
  • 3.将样本 x i x_{i} xi 划分到离他最近的聚类中心所对应的类中;
  • 4.使用样本特征向量的均值,更新各个聚类中心,均值的计算公式如下:
    x ‾ G = 1 ∣ G ∣ ∑ i = 1 ∣ G ∣ x i , x i ∈ G \overline{x}_{G}=\frac{1}{|G|}\sum_{i=1}^{|G|}x_{i},x_{i}\in G xG=G1i=1Gxi,xiG
    其中, x i x_{i} xi为样本特征向量, ∣ G ∣ |G| G为类别 G G G的样本个数;
  • 5.重复步骤 2~4,直到聚类中心不再变化

K均值算法的改进:K-means++

K 均值聚类的算法流程,由于原理简单、实现方便、模型的可解释性较强,所以比较常用。K均值聚类是通过最小化样本与其所属类中心的距离总和来进行算法迭代,具体做法是:先随机初始化类中心,将样本划分到离他最近的类中心,然后更新类中心,重复上述步骤,直到聚类中心不再变化。

但是K均值算法并不能保证收敛到全局最优,初始类中心的选择会直接影响到聚类的结果。一般来说:相同类中的样本距离较近,不同类中的样本距离较远。因此,初始的各个聚类中心应该越远越好。

K-means++ 算法原理
基于上述观点,K-means++算法针对初始聚类中心的选择做了优化和改进,具体步骤如下:

  • 步骤1:从数据集 X X X 中随机选择一个样本点作为第一个初始类中心 c 1 c_{1} c1
  • 步骤2:计算 X X X 中的每个样本点 x i x_{i} xi 和已有聚类中心的最近样本距离 d ( x i ) d(x_{i}) d(xi)
    假如已经初始化了 3 个聚类中心,则分别计算该样本点和这 3 个聚类中心的样本距离,取其中的最短距离作为计算结果,用数学公式表示如下:
    d ( x i ) = m i n j ∈ { 1 , 2 , 3 } ∣ ∣ x i − c j ∣ ∣ 2 2 d(x_{i})=min_{j\in \left \{1,2,3 \right \}}||x_{i}-c_{j}||_{2}^{2} d(xi)=minj{1,2,3}xicj22
    样本距离是欧氏距离的平方,和 K-means 算法保持一致;
  • 步骤3:计算每个样本点作为聚类中心的概率 p ( x i ) p(x_{i}) p(xi),公式如下:
    p ( x i ) = d ( x i ) ∑ j = 1 n d ( x j ) p(x_{i})=\frac{d(x_{i})}{\sum_{j=1}^{n}d(x_{j})} p(xi)=j=1nd(xj)d(xi)
  • 步骤4:根据 p ( x i ) p(x_{i}) p(xi) 从样本数据中随机选择聚类中心,距离已有聚类中心越远的样本点越有可能称为下一个初始聚类中心;
  • 步骤5:重复步骤 2~4,直到初始聚类中心的数量达到 K K K

除了初始聚类中心的选择,K-means++ 其余部分与 K-means 算法的流程一致;

K-means++ 算法关于初始聚类中心的选择实现如下:

import numpy as np 

# 欧氏距离的平方
def distance(u,v):
    '''
    u,v: 一维 numpy 数组表示的特征向量
    '''
    return sum((u-v)**2)

def center_init(data,k):
    '''
    data: numpy array,样本特征向量集合
    k: int,聚类中心的个数
    '''
    # 用来保存聚类中心
    center = []
    # 随机选择一个样本特征向量的索引
    index = np.random.choice(range(len(data)))
    # 根据索引获得第一个聚类中心
    center.append(data[index])
    # 获取剩余 k-1 个聚类中心
    for _ in range(k-1):
        # 样本被选为聚类中心的概率
        prob = []
        # 遍历每个样本数据
        for x in data:
            # 根据步骤 2 的公式计算样本与最近类中心的距离
            d = min([distance(x,c) for c in center])
            prob.append(d)
        # 计算每个样本的被选概率
        prob = np.array(prob)/sum(prob)
        # 根据被选中的概率随机选择一个样本特征向量的索引
        i = np.random.choice(a=range(len(data)),p=prob)
        # 根据索引添加一个聚类中心
        center.append(data[i])
    # 返回 k 个初始化的聚类中心
    return np.array(center)

除了初始聚类中心,K值的选择以及样本距离计算方法的选择也会影响到聚类的结果。其中,K值属于超参数,需要根据实际聚类结果来调节

numpy实现K-means

定义函数distance计算样本距离:

def distance(u,v):
    '''
    u,v: 一维 numpy 数组表示的特征向量
    '''
    # 返回欧氏距离的平方
    return sum((u-v)**2)

初始化聚类中心:

import numpy as np

# 从 data 中随机选择 k 个样本特征向量,作为初始聚类中心
def center_init(data,k):
    '''
    data: 存放样本特征向量的 numpy array
    k: 聚类中心的个数
    '''
    # 设置随机种子
    np.random.seed(2021)
    # 随机选择 k 个样本特征向量的索引
    index = np.random.choice(a=range(len(data)),size=k)
    # 根据索引获得 k 个聚类中心
    return data[index]

样本聚类:

def predict(x,center):
    '''
    x: 一维数组表示的样本特征向量
    center: 存放 k 个聚类中心的数组
    '''
    # 输入的样本 x 必须为一维数组,否则触发异常
    assert np.array(x).ndim==1
    # 返回距离样本 x 最近的聚类中心索引
    return np.argmin([distance(x,c) for c in center])

获取聚类中心对应的样本子集:

def cluster(data,center):
    '''
    data: 存放样本特征向量的 numpy array
    center: 存放 k 个聚类中心的数组
    '''
    # 存放各类样本子集的字典:key = 聚类中心索引; value = data 子集
    center_data = dict()
    # 遍历样本特征向量
    for x in data:
        # 获取样本 x 的聚类索引
        i =  predict(x,center)
        # 将 x 添加到聚类索引对应的样本子集中
        """
        D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
        D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
        """
        center_data.setdefault(i,[])
        center_data[i].append(x)
    # 返回各聚类中心对应的样本子集
    return center_data

聚类可视化,二维空间可视化:

import matplotlib.pyplot as plt

def visualization_2D(center,center_data):
    '''
    center: 存放 k 个聚类中心的数组
    center_data: 聚类中心对应的样本子集的集合
    '''
    # 设置画布大小
    plt.figure(figsize=(8,6))
    # 设置可选颜色:红、绿、蓝
    color = ['r','g','b']
    # 遍历每个聚类中心的索引
    for i in center_data:
        # 根据索引,取该聚类中心对应的样本第一列特征
        x = np.array(center_data[i])[:,0]
        # 根据索引,取该聚类中心对应的样本第二列特征
        y = np.array(center_data[i])[:,1]
        # 绘制当前聚类的样本散点图,s 控制大小,c 控制颜色
        plt.scatter(x,y,s=30,c=color[i])
    # 遍历每个聚类中心 c 和对应的索引 i
    for i,c in enumerate(center):
        # 在二维平面上绘制聚类中心,alpha 控制透明度
        plt.scatter(x=c[0],y=c[1],s=200,c=color[i],alpha=0.2)
    # 显示图片
    plt.show()

三维空间可视化:

from mpl_toolkits import mplot3d
    
def visualization_3D(center,center_data):
    '''
    center: 存放 k 个聚类中心的数组
    center_data: 聚类中心对应的样本子集
    '''
    # 取第一个聚类的样本子集,若样本的特征维数小于 3
    if np.array(center_data[0]).shape[1] < 3:
        # 则返回空值,放弃三维可视化
        return None
    # 设置画布大小
    plt.figure(figsize=(10,8))
    # 设置可选颜色:红、绿、蓝
    color = ['r','g','b']
    # 创建三维空间直角坐标系
    ax = plt.axes(projection='3d')
    # 遍历每个聚类中心的索引
    for i in center_data:
        # 根据索引,取该聚类中心对应的样本第一列特征
        x = np.array(center_data[i])[:,0]
        # 根据索引,取该聚类中心对应的样本第二列特征
        y = np.array(center_data[i])[:,1]
        # 根据索引,取该聚类中心对应的样本第三列特征
        z = np.array(center_data[i])[:,2]
        # 绘制当前聚类的样本散点图,s 控制大小,c 控制颜色
        ax.scatter3D(x,y,z,s=30,c=color[i])
    # 遍历每个聚类中心 c 和对应的索引 i
    for i,c in enumerate(center):
        # 在三维空间中绘制当前聚类中心,alpha 控制透明度
        ax.scatter3D(xs=c[0],ys=c[1],zs=c[2],s=200,c=color[i],alpha=0.2)
    # 显示图片
    plt.show()

算法迭代:

def fit(data,k,visualization=None):
    '''
    data: 存放样本特征向量的 numpy array
    visualization: 可视化选项,默认为 None,不对聚类过程做可视化
    '''
    # 初始化聚类中心
    center = center_init(data,k)
    # 循环迭代
    while True:
        # 根据已有的聚类中心将样本划分到不同的聚类集合中
        center_data = cluster(data,center)
        # 当前聚类中心和样本特征向量在二维空间的可视化 
        if visualization == '2d':
            visualization_2D(center,center_data)
        # 当前聚类中心和样本特征向量在三维空间的可视化 
        if visualization == '3d':
            visualization_3D(center,center_data)
        # 保存上一次迭代的聚类中心
        old_center = center.copy()
        # 遍历各个聚类中心的索引
        for i in center_data:
            # 更新每一个聚类中心:样本子集特征向量的均值
            center[i] = np.mean(center_data[i],axis=0)
        # 循环迭代的停止条件:最近两次迭代,聚类中心的位置不再变化
        if sum([distance(center[i],old_center[i]) for i in range(k)]) == 0:
            break
    return center,center_data

算法实例:

x1 = [1,2,4,5,7,8,9,3,6]
x2 = [2,2,5,7,1,3,5,2,6]
data = np.array([i for i in zip(x1,x2)])
"""
array([[1, 2],
       [2, 2],
       [4, 5],
       [5, 7],
       [7, 1],
       [8, 3],
       [9, 5],
       [3, 2],
       [6, 6]])
"""

# 聚类簇数
k = 3
# 获取最终的聚类中心和对应的样本子集,并对聚类过程可视化
center,center_data = fit(data,k,visualization='2d')
# 聚类中心和样本特征向量在二维空间的最终可视化 
visualization_2D(center,center_data)

fig1

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值