K-Means 算法

认识

K-Means 是属于聚类算法中的一种, 聚类算法呢, 是属于 无监督学习. 不需要数据的标签(label). 主要用途是为了发现数据中的规律(模式), 就咱平时说的数据挖掘. 使用的场景, 从营销领域来看, 可用来做用户细分, 行为聚类, 精准营销, 推荐系统等, 当然在其他领域,诸如图像分割, 生物研究也可以的.

无监督: 就是只计算特征之间的关系不关注label. 场景的,如 聚类, PCA...

一言蔽之, 物以类聚, 人以群分. 核心是把相似的物体聚在一起, 处理上则是计算物体间的相似度.

K-Means 算法

是一种循环迭代式的算法.

初始化

  • 随机选择 K 个点, 作为初始点的中心, 每个点作为一个 group.

交替更新

  • 计算每个点到中心点的距离, 把最近的距离记录并将group赋给当前的点
  • 针对每一个group的点, 计算其平均并作为这个group的新中心点

循环上两步即可, 从代码的角度讲, 停止循环有两个注意点

  • 外层循环,设置一个最大循环次数, 比如100次.
  • 计算中心点, 前后两次位置不变 (完全分开了呀)

伪代码

为了说明意思, 搬的伪代码哈, 有空再好好写一波.

def dist_eclud(arr1, arr2):
    """计算两个点之间的欧式距离"""
    return np.sqrt(np.sum((arr1 - arr2)**2))

def random_center(X, k): 
    """随机初始化k个中心点,X 是(n, p)维的样本"""
    # 获取样本矩阵的行列数
    n, p = np.shape(X)
    #创建 k 行 p 列的全为0 的矩阵
    center = np.matrix(np.zeros([k, p]))  
    for j in range(p):
        min_j = np.min(X[:,j])
         # 计算极值
        range_j = float(np.max(X[:,j]) - min_j)    
        # 得到 k 个随机中心点, 每个点是p维度
        center[:,j] = np.mat(min_j + range_j * np.random.rand(k, 1))   
    return center

def fit(X, k):
    # 获取样本数(行数)
    n,p = np.shape(X)
    # 创建一个n行p列的矩阵
    cluster = np.mat(np.zeros([n,p]))
    # 随机初始化k个中心点,每个点是p维
    center = random_center(X, k)
    
    while True:
        for i in range(n):
            # 初始化最小距离 (默认非常大)
            min_dist = np.inf
            # 中心点下标
            min_index = 0
            # 计算k个中心点到该样本的距离
            for j in range(k):
                dist_j = dist_eclud(center[j,:], X[i,:])
                # 更新最小距离
                if dist_j < min_dist:
                    min_dist = dist_j
                    min_index = j 
            # 判断: 如果中心点前后没有变化,说明不能再继续分, 则终止循环
            if cluster[i, 0] != min_index:
                break
            # 将 index, k中心点, 最小距离存到数组
            cluster[i, :] = min_index, min_dist**2
        # 打印中心点
        print(center)
        
        # 更新中心点的位置
        for i in range(k):
            # 平铺cluster 为一个一维数组, 分别找到属于k类的数据
            points = dataSet[np.nonzero(cluster[:,0].A == i)[0]] 
            #得到更新后的中心点
            center[i,:] = np.mean(points, axis = 0)  
    return center, cluster 
            

K-Means 算法特性

当然是考量每一次迭代的复杂度了.

第一步的选取 k 个点, 这里不考虑了, 关键是计算和迭代的部分.

首先,计算每个点到中心点的距离, 把最近的距离记录下来并把group赋给当前点. 假设有 n个样本点.则此过程的时间复杂度为:

\(O(kn)\)

因为有 k 个中心点. 每次计要把 n 个点 计算到 k 个点的距离

然后, 针对每一个 group 里的点, 计算平均作为这个 group 的新中心点. 则此过程的时间复杂度为:

\(O(n)\)

K-means 的几点思考

目标函数

已知观测集 \((x_1, x_2, ... x_n)\) 其中每个观测 xi 都是 d 维的实向量. k-平均聚类, 就是要把 这n个观测划分到 k 个集合中 ( k <= n) , 使得簇内平方和最小. 即找到使得下式满足的聚类 \(S_i\)

\(arg \ min _S \ \sum \limits _{i=1}^k \sum \limits _{x \in S_i} ||X-\mu_i||^2\)

\(\mu_i\)\(S_i\) 类中的所有点的均值

\(\sum \limits _{x \in S_i} ||X-\mu_i||^2\) 表示类别为 i 的样本点, 到中心的距离尽可能小

外层的求和, 是对每个类到其自身中心的距离, 总体上达到最小

关键: 是如何找到这些 xi

是否一定会收敛

是的

首先, 将N个数据分为 k 个聚类, 最多有 \(k^N\) 种可能, 这是一个有限的数值. 对于算法迭代, 我们仅基于旧的聚类产生一个新的聚类.

  • 如果旧的聚类和新的聚类相同, 则下一次迭代的结果也再次相同
  • 新,旧不同, 则更新的群集的目标函数值较低

其次, 算法的不同迭代, 最终会进入一个循环, 即k均值在有限的迭代次数中收敛

不同初始化对结果的影响

不同初始点,会带来不同的结果

K 如何选择

inertia: 群集中,所有点离其所属cluster中心的距离的总和.

不论数据集如何, 随着k的增加, inertia 呈递减趋势, 开始速度很快, 慢慢变小, 会存在一个拐点.

极限情况是, 每一个点都各自一类, 则总距离为0了, 也就没有了所谓 "聚类''的存在

KMeans ++

  • 从数据集中随机选择一个中心
  • 对于每个 xi, 计算与**已经选择的最接近中心之间的距离 **D(xi)
  • 使用加权概率分布随机选择一个新的数据点作为新中心, 该点的概率与 \(D(x_i)^2\) 成正比
  • 重复步骤 2,3 直到选择了 k 个中心
  • 根据选择好了的初始中心, 继续使用k-means 聚类

这种算法, 其实就是在选择初始中心的时候, 尽可能 "分布在数据四周" , 后面不变.

KMeans 优缺点

优点

  • 容易理解和实现
  • 结果的解释性强
  • 计算复杂度低

缺点

  • 异常值 和 初始化 非常敏感

  • 对数据的 分布不均匀 条件下, 效果不太理想 (样本不均衡就有点麻烦)

  • 默认前提假设是特征之间的联合分布是椭圆形的, S形, 环形就凉凉了 ( svm 线性不可分)

  • 即便 k 给定, 聚类的结果也不唯一. 最好要多调试几次, 选择 inertia 最小时的参数

posted @ 2019-12-14 22:40  致于数据科学家的小陈  阅读(475)  评论(0编辑  收藏  举报