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

news2024/11/28 14:41:51

在这里插入图片描述

目录

  • 1. 聚类原理
    • 1.1. 无监督与聚类
    • 1.2. K均值算法
  • 2. 公式推导
    • 2.1. 距离
    • 2.2. 最小平方误差
  • 3. 实例
    • 3.1. python实现
    • 3.2. sklearn实现
  • 4. 运行(可直接食用)

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官网
《百面机器学习》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/335664.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

01-幂等性解释,问题及常用解决方案

目录 1. 幂等性简介 2. 后端如何解决幂等性问题 2.1 数据库层面 -> 2.1.1 防重表 -> 2.1.2 数据库悲观锁(不建议,容易出现死锁情况) -> 2.1.3 数据库乐观锁 -> 2.1.4 乐观锁CAS算法原理 2.2 锁层面 2.3 幂等性token层面 -> 2.3.1 简介文字描述: …

Java开发 - 问君能有几多愁,Spring Boot瞅一瞅。

前言 首先在这里恭祝大家新年快乐,兔年大吉。本来是想在年前发布这篇博文的,奈何过年期间走街串巷,实在无心学术,所以不得不放在近日写下这篇Spring Boot的博文。在还没开始写之前,我已经预见到,这恐怕将是…

中国社科院与美国杜兰大学金融管理硕士,让我们相遇在春暖花开时

在芸芸众生中,能拥有志同道合的朋友是一件多么幸运的事。人们常说:你是谁,就会遇见谁。走过半生才知道,看似命中注定的遇见谁、发生的事,其实都取决于自己。只有自己足够优秀,才能遇到更优秀的别人。在这个…

IT人的晋升之路——关于人际交往能力的培养

对于咱们的程序员来说,工作往往不是最难的,更难的是人际交往和关系的维护处理。很多时候我们都宁愿加班,也不愿意是社交,认识新的朋友,拓展自己的圈子。对外的感觉就好像我们丧失了人际交往能力,是个呆子&a…

【chatGPT】持续火热一路狂飙,简单了解下TA的功能和示例代码吧

🎉🎉 最近chatGPT持续火爆,一路狂飙,对应如何注册和使用的优质文章非常多。 所以,此篇文章除了整理chatGPT文章外,主要是讲解如何获取API Key进行接口的调用🎉🎉 目录1、chatGPT解读…

蓝牙单点技术实现路径介绍

本文主要介绍蓝牙设备与手机一对一相连的 蓝牙单点 技术。 准备工作 系统要求&#xff1a;蓝牙使用需要安卓 4.3 以及以上版本&#xff0c;智能生活 App SDK 从安卓 4.4 开始支持。Manifest 权限&#xff1a; <uses-permission android:name"android.permission.ACCE…

Fluent Python 笔记 第 3 章 字典和集合

3.1 泛映射类型 只有可散列 的数据类型才能用作这些映射里的键 字典构造方法&#xff1a; >>> a dict(one1, two2, three3) >>> b {one: 1, two: 2, three: 3} >>> c dict(zip([one, two, three], [1, 2, 3])) >>> d dict([(two, 2…

5. Spring 事务

文章目录1. Spring 事务简介2. Spring 事务角色3. Spring 事务属性3.1 事务配置3.2 案例&#xff1a;转账业务追加日志3.3 事务传播行为1. Spring 事务简介 Spring 事务作用&#xff1a;在数据层或业务层保障一系列的数据库操作同成功、同失败。 数据层有事务我们可以理解&am…

多传感器融合定位十三-基于图优化的建图方法其二

多传感器融合定位十二-基于图优化的建图方法其二3.4 预积分方差计算3.4.1 核心思路3.4.2 连续时间下的微分方程3.4.3 离散时间下的传递方程3.5 预积分更新4. 典型方案介绍4.1 LIO-SAM介绍5. 融合编码器的优化方案5.1 整体思路介绍5.2 预积分模型设计Reference: 深蓝学院-多传感…

Vue3 - 自定义指令封装

Vue3 - 自定义指令封装一. 自定义指令封装1.1 全局/局部注册自定义聚焦指令1.2 自定义指令相关参数1.3 自定义指令参数传递二. 总结一. 自定义指令封装 vue中有很多内置的指令&#xff0c;我们一般在开发中也经常用到&#xff0c;比如v-if&#xff0c;v-for等等。那么本篇文章…

Vue极简使用

Vue安装Vue模板语法安装Vue 安装nodejs 这里我安装的是14.5.4版本 https://nodejs.org/download/release/v14.15.4/解压后配置一下环境变量就行 安装cnpm镜像 (这个安装的版本可能过高&#xff0c;后面安装Vue可能出问题) npm install -g cnpm --registryhttps://registry…

二十二、Gtk4-ListView

GTK 4添加了新的列表对象GtkListView、GtkGridView和GtkColumnView。这个新特性在Gtk API参考—列表小构件概述中有描述。 GTK 4还有其他实现列表的方法。它们是GtkListBox和GtkTreeView&#xff0c;它们是从GTK 3接管的。在Gtk开发博客中有一篇关于Matthias Clasen所写的列表…

vscode执行Python输出exited with code=9009 in 0.655 seconds

vscode执行Python输出exited with code9009 in 0.655 seconds 想用vscode写个脚本&#xff0c;用自己电脑配置了下vscode的python环境&#xff0c;结果点击右上角三角图标运行时却只会输出exited with code9009 in 0.655 seconds 这就不太理解了&#xff0c;我在公司时是能正…

linux性能分析 性能之巅学习笔记和内容摘录

本文只是在阅读《性能之巅》的过程中&#xff0c;对一些觉得有用的地方进行的总结和摘录&#xff0c;并附加一些方便理解的材料&#xff0c;完整内容还请阅读Gregg的大作 概念和方法 性能分析领域一词的全栈代表了整个操作系统的软硬件在内的所有事物 软件生命周期和性能规划…

LabWindows CVI 2017开发笔记--串口API

参考资料&#xff1a;https://download.csdn.net/download/Stark_/87424565?spm1001.2014.3001.5501 转载请注明出处&#xff1a;https://blog.csdn.net/Stark_/article/details/128966962?spm1001.2014.3001.5501 打开串口OpenComConfig OpenComConfig 打开一个串行并进行…

HTML-CSS-js教程

HTML 双标签<html> </html> 单标签<img> html5的DOCTYPE声明 <!DOCTYPE html>html的基本骨架 <!DOCTYPE html> <html> </html>head标签 用于定义文档的头部。文档的头部包含了各种属性和信息&#xff0c;包括文档的标题&#…

【成为架构师课程系列】架构设计中的核心思维方法

架构设计中的核心思维方法 目录 前言 #一、抽象思维 #二、分层思维 #三、分治思维 #四、演化思维 #五、如何培养架构设计思维

leaflet 加载WKT数据(示例代码050)

第050个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中加载WKT文件,将图形显示在地图上。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果; 注意如果OpenStreetMap无法加载,请加载其他来练习 文章目录 示例效果配置方式示例源代码(共67行…

中国特色地流程管理系统,天翎让流程审批更简单

编者按&#xff1a;本文分析了国内企业在采购流程管理系统常遇到的一些难点&#xff0c;并从适应中国式流程管理模式的特点出发&#xff0c;介绍了符合中国特色的流程审批管理系统——天翎流程管理系统。关键词&#xff1a;可视化开发&#xff0c;拖拽建模&#xff0c;审批控制…

威联通ContainerStation部署Oracle11g

文章目录前言部署过程详解使用docker-compose文件创建容器临时开启NAS的SSH远程访问通过SSH客户端远程连接NAS进入容器创建用户拷贝容器中的数据库相关文件至宿主机在ContainerStation中修改docker-compose文件总结前言 ContainerStation本质上是对Docker可视化的一款软件&…