机器学习之K-means原理详解、公式推导、简单实例(python实现,sklearn调包)

2023-11-11

在这里插入图片描述

1. 聚类原理

1.1. 无监督与聚类

在这部分我今天主要介绍K均值聚类算法,在这之前我想提一下“无监督学习”和“聚类”。

无监督学习是指训练样本的标记信息是未知的,目标是通过对无标记训练样本的学习来揭示数据的内在性质和规律的学习。

在 sklearn 官网首页中,非常贴心的将任务分为了分类,回归,聚类三类,以此我们可以看出聚类的重要性。实际上聚类就是把训练集中的样本划分为通常不相交的几个子集,每个子集称为一个簇,每个簇对应的名字聚类事先是不知道的,需要靠使用者来把握命名。

优点

简单有效

缺点

对于K均值算法来说,最明显的缺点有很多:一开始定中心点数量需要人为定;中心点选取初始样本是随机选的。很多K均值优化算法都是从这几个方面入手改进的。

1.2. K均值算法

对于K均值算法,西瓜书的伪代码其实已经说的很明白了:
在这里插入图片描述
简单的说就是三步:

  1. 选择初始中心点,最基本的方法是从数据集中选择样本。初始化后,K-means由其他两个步骤之间的循环组成。
  2. 将每个样本指定给其最近的中心。
  3. 通过获取分配给每个先前中心的所有样本的平均值来创建新的中心。计算新旧质心之间的差值,算法重复最后两个步骤,直到该值小于阈值。

一般来说,中心点不会是已经存在的点,一般都是算出来的虚构的点。

提一下 对于高维数据,我们知道存在维度诅咒这一说法,所以在很多时候聚类往往会跟PCA之类的降维算法搭配。在降维算法中 TSNE 因为能将数据降维至2-3维,非常适合可视化,而且降维效率也高,所以也很常用。

2. 公式推导

2.1. 距离

简单的说,对于每个簇来说,簇内相似度高,簇外相似度低(高内距,低耦合)。那么衡量距离的方法有哪些?

  • 曼哈顿距离
  • 欧式距离
  • 闵可夫斯基距离 d i s t m k ( x i , x j ) = ( ∑ u = 1 n ∣ x i u − x j u ∣ p ) 1 p dist_mk(x_i,x_j)=(\sum_{u=1}^n|{x_{iu}-x_{ju}|^p})^{\frac{1}{p}} distmk(xi,xj)=(u=1nxiuxjup)p1
  • 余弦相似度 c o s ( θ ) = ∑ i = 1 n ( x i × y i ) ∑ i = 1 n ( x i ) 2 × ∑ i = 1 n ( y i ) 2 cos(\theta)=\frac{\sum_{i=1}^n(x_i\times y_i)}{\sqrt{\sum^n_{i=1}{(x_i)^2}} \times \sqrt{\sum^n_{i=1}{(y_i)^2}}} cos(θ)=i=1n(xi)2 ×i=1n(yi)2 i=1n(xi×yi)
  • 等等

注:p=1,闵可夫斯基距离为曼哈顿距离;p=2,闵可夫斯基距离为欧氏距离。

2.2. 最小平方误差

说白了就是每个簇内每个点到中心点的距离的和最小。

E = ∑ i = 1 k ∑ x ∈ C i ∣ ∣ x − μ i ∣ ∣ 2 2 E = \sum^k_{i=1}{\sum_{x\in C_i}{||x-\mu_i||_2^2}} E=i=1kxCi∣∣xμi22
μ i \mu_i μi就是中心点,数学公式表示就是
μ i = 1 ∣ C i ∣ ∑ x ∈ C i x \mu_i=\frac{1}{|C_i|}\sum_{x\in C_i}{x} μi=Ci1xCix

3. 实例

西瓜数据集4.0为例

密度,含糖率
0.697,0.46
0.774,0.376
0.634,0.264
0.608,0.318
0.556,0.215
0.403,0.237
0.481,0.149
0.437,0.211
0.666,0.091
0.243,0.267
0.245,0.057
0.343,0.099
0.639,0.161
0.657,0.198
0.36,0.37
0.593,0.042
0.719,0.103
0.359,0.188
0.339,0.241
0.282,0.257
0.748,0.232
0.714,0.346
0.483,0.312
0.478,0.437
0.525,0.369
0.751,0.489
0.532,0.472
0.473,0.376
0.725,0.445
0.446,0.459

3.1. python实现

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

def mykmeans(data, k):
    # 计算距离
    def get_dis(data, center, k):
        ret = []
        for point in data:
            # np.tile(a, (2, 1))就是把a先沿x轴复制1倍,即没有复制,仍然是[0, 1, 2]。 再把结果沿y方向复制2倍得到array([[0, 1, 2], [0, 1, 2]])
            # k个中心点,所以有k行
            diff = np.tile(point, (k, 1)) - center
            squaredDiff = diff ** 2  # 平方
            squaredDist = np.sum(squaredDiff, axis=1)  # 和  (axis=1表示行)
            distance = squaredDist ** 0.5  # 开根号
            ret.append(distance)
        return np.array(ret)

    def draw(data, cluster):
        D_data = pd.DataFrame(data)
        plt.rcParams["font.size"] = 14
        colors = np.array(["red", "gray", "orange", "pink", "blue", "green"])
        D_data["cluster"] = cluster
        D_data = D_data.sort_values(by="cluster")
        xx = np.array(D_data[D_data.columns[0]])  # 取前两个维度可视化
        yy = np.array(D_data[D_data.columns[1]])
        cc = np.array(D_data["cluster"])
        plt.scatter(xx, yy, c=colors[cc])  # c=colors[cc]
        plt.show()
        plt.close()

    # 计算质心
    def classify(data, center, k):
        # 计算样本到质心的距离
        clalist = get_dis(data, center, k)
        # 分组并计算新的质心
        minDistIndices = np.argmin(clalist, axis=1)  # axis=1 表示求出每行的最小值的下标
        newCenter = pd.DataFrame(data).groupby(
            minDistIndices).mean()  # DataFramte(dataSet)对DataSet分组,groupby(min)按照min进行统计分类,mean()对分类结果求均值
        newCenter = newCenter.values
        # 计算变化量
        changed = newCenter - center
        return changed, newCenter

    data = data.tolist()
    # 随机取质心
    centers = random.sample(data, k)
    # 更新质心 直到变化量全为0
    changed, newCenters = classify(data, centers, k)
    while np.any(changed != 0):
        changed, newCenters = classify(data, newCenters, k)
    centers = sorted(newCenters.tolist())  # tolist()将矩阵转换成列表 sorted()排序
    # 根据质心计算每个集群
    cluster = []
    clalist = get_dis(data, centers, k)  # 调用欧拉距离
    minDistIndices = np.argmin(clalist, axis=1)
    print(minDistIndices)
    for i in range(k):
        cluster.append([])
    for i, j in enumerate(minDistIndices):  # enymerate()可同时遍历索引和遍历元素
        cluster[j].append(data[i])
    draw(data, minDistIndices)

3.2. sklearn实现

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

def sk(data, k):
    # # 肘部法取k值
    # data = np.array(data)
    # SSE = []
    # right = min(7, data.shape[0])
    # for k in range(2, right):
    #     km = KMeans(n_clusters=k)
    #     km.fit(data)
    #     SSE.append(km.inertia_)
    # xx = range(2, right)
    # plt.xlabel("k")
    # plt.ylabel("SSE")
    # plt.plot(xx, SSE, "o-")
    # plt.show()

    D_data = pd.DataFrame(data)
    km = KMeans(n_clusters=k).fit(data)
    # print(km.labels_)
    print("质心")
    center = km.cluster_centers_
    print(center)
    D_data["cluster"] = km.labels_

    plt.rcParams["font.size"] = 14
    colors = np.array(["red", "gray", "orange", "pink", "blue", "green"])
    D_data["cluster"] = km.labels_
    D_data = D_data.sort_values(by="cluster")
    xx = np.array(D_data[D_data.columns[0]])  # 取前两个维度可视化
    yy = np.array(D_data[D_data.columns[1]])
    cc = np.array(D_data["cluster"])
    plt.scatter(xx, yy, c=colors[cc])  # c=colors[cc]
    if D_data.shape[0] >= 1:
        plt.scatter(center[:, 0], center[:, 1], marker="o", s=15, c="black")  # 画中心点
    plt.show()
    plt.close()

4. 运行(可直接食用)

import random
from collections import Counter
import numpy as np
import pandas as pd
import warnings
from matplotlib import pyplot as plt
from sklearn.cluster import KMeans
from sklearn.manifold import TSNE

warnings.filterwarnings("ignore")




def sk(data, k):
    # # 肘部法取k值
    # data = np.array(data)
    # SSE = []
    # right = min(7, data.shape[0])
    # for k in range(2, right):
    #     km = KMeans(n_clusters=k)
    #     km.fit(data)
    #     SSE.append(km.inertia_)
    # xx = range(2, right)
    # plt.xlabel("k")
    # plt.ylabel("SSE")
    # plt.plot(xx, SSE, "o-")
    # plt.show()

    D_data = pd.DataFrame(data)
    km = KMeans(n_clusters=k).fit(data)
    # print(km.labels_)
    print("质心")
    center = km.cluster_centers_
    print(center)
    D_data["cluster"] = km.labels_

    plt.rcParams["font.size"] = 14
    colors = np.array(["red", "gray", "orange", "pink", "blue", "green"])
    D_data["cluster"] = km.labels_
    D_data = D_data.sort_values(by="cluster")
    xx = np.array(D_data[D_data.columns[0]])  # 取前两个维度可视化
    yy = np.array(D_data[D_data.columns[1]])
    cc = np.array(D_data["cluster"])
    plt.scatter(xx, yy, c=colors[cc])  # c=colors[cc]
    if D_data.shape[0] >= 1:
        plt.scatter(center[:, 0], center[:, 1], marker="o", s=15, c="black")  # 画中心点
    plt.show()
    plt.close()


def mykmeans(data, k):
    # 计算距离
    def get_dis(data, center, k):
        ret = []
        for point in data:
            # np.tile(a, (2, 1))就是把a先沿x轴复制1倍,即没有复制,仍然是[0, 1, 2]。 再把结果沿y方向复制2倍得到array([[0, 1, 2], [0, 1, 2]])
            # k个中心点,所以有k行
            diff = np.tile(point, (k, 1)) - center
            squaredDiff = diff ** 2  # 平方
            squaredDist = np.sum(squaredDiff, axis=1)  # 和  (axis=1表示行)
            distance = squaredDist ** 0.5  # 开根号
            ret.append(distance)
        return np.array(ret)

    def draw(data, cluster):
        D_data = pd.DataFrame(data)
        plt.rcParams["font.size"] = 14
        colors = np.array(["red", "gray", "orange", "pink", "blue", "green"])
        D_data["cluster"] = cluster
        D_data = D_data.sort_values(by="cluster")
        xx = np.array(D_data[D_data.columns[0]])  # 取前两个维度可视化
        yy = np.array(D_data[D_data.columns[1]])
        cc = np.array(D_data["cluster"])
        plt.scatter(xx, yy, c=colors[cc])  # c=colors[cc]
        plt.show()
        plt.close()

    # 计算质心
    def classify(data, center, k):
        # 计算样本到质心的距离
        clalist = get_dis(data, center, k)
        # 分组并计算新的质心
        minDistIndices = np.argmin(clalist, axis=1)  # axis=1 表示求出每行的最小值的下标
        newCenter = pd.DataFrame(data).groupby(
            minDistIndices).mean()  # DataFramte(dataSet)对DataSet分组,groupby(min)按照min进行统计分类,mean()对分类结果求均值
        newCenter = newCenter.values
        # 计算变化量
        changed = newCenter - center
        return changed, newCenter

    data = data.tolist()
    # 随机取质心
    centers = random.sample(data, k)
    # 更新质心 直到变化量全为0
    changed, newCenters = classify(data, centers, k)
    while np.any(changed != 0):
        changed, newCenters = classify(data, newCenters, k)
    centers = sorted(newCenters.tolist())  # tolist()将矩阵转换成列表 sorted()排序
    # 根据质心计算每个集群
    cluster = []
    clalist = get_dis(data, centers, k)  # 调用欧拉距离
    minDistIndices = np.argmin(clalist, axis=1)
    # print(minDistIndices)
    for i in range(k):
        cluster.append([])
    for i, j in enumerate(minDistIndices):  # enymerate()可同时遍历索引和遍历元素
        cluster[j].append(data[i])
    draw(data, minDistIndices)





if __name__ == '__main__':
    random.seed(1129)
    data = pd.read_csv("watermelonData.csv").sample(frac=1, random_state=1129)
    # 因为西瓜数据集每列都是0到1的,所以这里就不进行标准化了

    data_shuffled = np.array(data)
    # 划分训练集测试集,感觉不太需要测试集,直接把比例拉到1
    data_train = data_shuffled[:int(data_shuffled.shape[0]*1), :]
    data_test = data_shuffled[data_train.shape[0]:, :]
    # 维度上去了就降维
    if data_train.shape[1]>10:
        tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000, method='exact', random_state=0)
        data_train = tsne.fit_transform(data_train)

    choice = 0
    while choice != 3:
        print("1. 手写\n2. sklearn\n3. 退出")
        try:
            choice = int(input())
        except:
            break
        if choice == 1:
            print("请输入k值")
            try:
                k = int(input())
            except:
                break
            print("手写求解中...")
            # 参考:https://blog.csdn.net/qq_43741312/article/details/97128745
            # 主要是发现了很多np和pd在计算的时候的用法
            mykmeans(data_train, k)
        elif choice == 2:
            print("请输入k值")
            try:
                k = int(input())
            except:
                break
            print('sklearn yyds')
            sk(data_train, k)
        else:
            print("退出成功")
            choice = 3
            break

参考
吴恩达《机器学习》
sklearn官网
《百面机器学习》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

机器学习之K-means原理详解、公式推导、简单实例(python实现,sklearn调包) 的相关文章

随机推荐

  • 为什么文学也可以解决许多问题

    文学是一种艺术形式 通过语言和叙事手法创造出各种文学作品 如小说 诗歌 戏剧等 它具有独特的力量和功能 能够解决许多问题的原因如下 情感共鸣与同理心 文学作品可以引起人们的情感共鸣和同理心 通过创造生动的角色 复杂的情节和丰富的描绘 文学作
  • 浅谈VVC(H.266)的变换模块

    转自 https zhuanlan zhihu com p 108792210 本文将分为四个部分对下一代视频编码标准Versatile Video Coding VVC 的变化模块进行介绍 第一部分简单介绍一下视频编码的发展历程以及VVC
  • surface pro 4专业版没有64位虚拟机选项的解决办法

    前言 因为surface没办法开bios的虚拟化支持 所以博主也是打电话亲自询问了微软的客服然后得出的结论 这个因为可能微软对自己产品的封锁吧 你装vmware也好 Virualbox也好 都是只有32位系统的 然后呢 也是一样的 选择代数
  • 【QT】——信号和槽

    1 信号和槽的概念 信号和槽是 Qt 特有的信息传输机制 是 Qt 设计程序的重要基础 它可以让互不干扰的 对象建立一种联系 当信号发出时 被连接的槽函数会自动被回调 这就类似观察者模式 当发生了感兴趣的事件 某一个操作就会被自动触发 1
  • 《C++ Primer》学习笔记(十四):重载运算与类型转换

    C Primer 学习笔记 十四 重载运算与类型转换 输入和输出运算符 算术和关系运算符 赋值运算符 下标运算符 递增和递减运算符 成员访问运算符 函数调用运算符 标准库定义的函数对象 可调用对象与function 当一个重载的运算是成员函
  • 做视频剪辑必须学会的几个剪辑软件,你知道哪些?

    现在短视频非常火热 身边70 以上的人或多或少都会使用手机APP快速剪辑视频 但是如果大家想要通过视频剪辑变现 或者想要自己的视频出彩 那么掌握系统的剪辑方法 剪辑软件的使用是必不可少的 今天小编就给大家分享几款我在剪辑视频中会常常用到的软
  • android 验证码短信验证码,Android​短信验证码倒计时验证的2种常用方式

    前言 本文主要介绍的是短信验证码功能 这里总结了两种常用的方式 可以直接拿来使用 看图 计时器 说明 这里的及时从10开始 是为了演示的时间不要等太长而修改的 方法如下 1 第一种方式 Timer Description 自定义Timer
  • thinkcmf5 pc切换手机

    1 在simplewind cmf common php 里找到 获取当前主题名 添加 if cmf is mobile theme config cmf mobile default theme else theme config cmf
  • java 返回function_Java8通过Function获取字段名

    摘要 Java8通过Function获取字段名 不用再硬编码 效果类似于mybatis plus的LambdaQueryWrapper 本文总共三个步骤 1 使Function获取序列化能力 2 通过SFunction获取字段名 3 建一些
  • 【Spring配置文件】Spring定时器的使用及配置

    如何在spring中配置定时任务 spring的定时任务配置分为三个步骤 1 定义任务 2 任务执行策略配置 3 启动任务 1 定义任务
  • OpenCV部署CRNN文字识别

    一 参考链接 1 模型获取及训练 2 github解答 二 模型转化 pytorch 转 ONNX import torch import models crnn as crnn model crnn CRNN 32 1 37 256 mo
  • dotfuscator使用方法

    转载自 http hi baidu com free3people item 0fba87d34091df15d80e4400 dotfuscator如何对 net程序进行混淆保护对于程序代码的保护 网上有很多资料 有的说混淆 有的说加密
  • jsp中使用response.sendRedirect重定向页面传递中文参数

    1 要跳转的jsp页面的书写 2 跳转的jsp页面对参数的获取 使用jsp内置对象
  • Linux read的核心函数generic_file_buffered_read

    内核 5 9 0 流程图 generic file buffered read一种调用路径 cat某个文件触发 0 ondemand readahead mapping 0xffff888005c61340 ra 0xffff8880059
  • 启动Fiddler导致浏览器显示“您的连接不是私密连接”

    浏览器出现如下问题 解决 在浏览器受信任的根证书颁发机构列表中添加fiddler证书 fiddler导出根证书 谷歌浏览器证书管理 受信任的根证书颁发机构列表中添加fiddler证书 注意 如果以上操作后还是无效 就在fiddler先重置根
  • Tracy JS 小笔记 - 运算符,条件语句,循环

    运算符 数学运算和字符串连接 任何数据加上字符串都等于字符串 var a 1 1 a 1 2 a 2a12 var a 1 1 a 1 2 a 2a3 var a aa true a aatrue var a 1 0 a NaN NaN N
  • 基于CentOS系统的网站搭建(入门级)

    准备 使用到的网站 非广告 阿里云 https www aliyun com utm content se 1008364713 宝塔面板 宝塔面板下载 免费全能的服务器运维软件 以阿里云为例 入门选择 轻量级应用服务器 选择服务器配置 选
  • 7-3 两个有序链表序列的合并 (20 分)

    已知两个非降序链表序列S1与S2 设计函数构造出S1与S2合并后的新的非降序链表S3 输入格式 输入分两行 分别在每行给出由若干个正整数构成的非降序序列 用 1表示序列的结尾 1不属于这个序列 数字用空格间隔 输出格式 在一行中输出合并后新
  • Redis之十大类型(三)(下)

    3 6 Redis位图 bitmap 由 0 和 1 表示的二进制位的 bit 数组 介绍 用String类型作为底层数据结构实现的一种统计二值状态的数据类型 位图本质是数组 它是基于String数据类型的按位的操作 该数组由多个二进制位组
  • 机器学习之K-means原理详解、公式推导、简单实例(python实现,sklearn调包)

    目录 1 聚类原理 1 1 无监督与聚类 1 2 K均值算法 2 公式推导 2 1 距离 2 2 最小平方误差 3 实例 3 1 python实现 3 2 sklearn实现 4 运行 可直接食用 1 聚类原理 1 1 无监督与聚类 在这部