《统计学习方法》 第十四章 聚类方法

news/2024/5/20 9:22:36 标签: 聚类, 学习方法

聚类方法

1.聚类是针对给定的样本,依据它们属性的相似度或距离,将其归并到若干个“类”或“簇”的数据分析问题。一个类是样本的一个子集。直观上,相似的样本聚集在同类,不相似的样本分散在不同类。

2.距离或相似度度量在聚类中起着重要作用。

常用的距离度量有闵可夫斯基距离,包括欧氏距离曼哈顿距离、切比雪夫距离、、以及马哈拉诺比斯距离。常用的相似度度量有相关系数、夹角余弦。
用距离度量相似度时,距离越小表示样本越相似;用相关系数时,相关系数越大表示样本越相似。

3.类是样本的子集,比如有如下基本定义:
G G G表示类或簇,用 x i x_i xi, x j x_j xj;等表示类中的样本,用 d i j d_{ij} dij表示样本 x i x_i xi与样本 x j x_j xj之间的距离。如果对任意的 x i , x j ∈ G x _ { i } , x _ { j } \in G xi,xjG,有 d i j ≤ T d _ { i j } \leq T dijT
则称 G G G为一个类或簇。

描述类的特征的指标有中心、直径、散布矩阵、协方差矩阵。

4.聚类过程中用到类与类之间的距离也称为连接类与类之间的距离包括最短距离、最长距离、中心距离、平均距离。

5.层次聚类假设类别之间存在层次结构,将样本聚到层次化的类中层次聚类又有聚合或自下而上、分裂或自上而下两种方法。

聚合聚类开始将每个样本各自分到一个类;之后将相距最近的两类合并,建立一个新的类,重复此操作直到满足停止条件;得到层次化的类别。分裂聚类开始将所有样本分到一个类;之后将已有类中相距最远的样本分到两个新的类,重复此操作直到满足停止条件;得到层次化的类别。

聚合聚类需要预先确定下面三个要素:

(1)距离或相似度;
(2)合并规则;
(3)停止条件。

根据这些概念的不同组合,就可以得到不同的聚类方法。

6. k k k均值聚类是常用的聚类算法,有以下特点。基于划分的聚类方法;类别数k事先指定;以欧氏距离平方表示样本之间的距离或相似度,以中心或样本的均值表示类别;以样本和其所属类的中心之间的距离的总和为优化的目标函数;得到的类别是平坦的、非层次化的;算法是迭代算法,不能保证得到全局最优。

k k k均值聚类算法,首先选择k个类的中心,将样本分到与中心最近的类中,得到一个聚类结果;然后计算每个类的样本的均值,作为类的新的中心;重复以上步骤,直到收敛为止。


层次聚类

  1. 聚合(自下而上):聚合法开始将每个样本各自分裂到一个类,之后将相距最近的两类合并,建立一个新的类,重复次操作知道满足停止条件,得到层次化的类别。

  2. 分裂(自上而下): 分裂法开始将所有样本分到一个类,之后将已有类中相距最远的样本分到两个新的类,重复此操作直到满足停止条件,得到层次化的类别。


k均值聚类

k均值聚类是基于中心的聚类方法,通过迭代,将样本分到k个类中,使得每个样本与其所属类的中心或均值最近,得到k个平坦的,非层次化的类别,构成对空间的划分


代码实现

import math
import random
import numpy as np
from sklearn import datasets,cluster
import matplotlib.pyplot as plt

# 定义聚类数的节点

class ClusterNode:
    def __init__(self, vec, left=None, right=None, distance=-1, id=None, count=1):
        """
        :param vec: 保存两个数据聚类后形成新的中心
        :param left: 左节点
        :param right:  右节点
        :param distance: 两个节点的距离
        :param id: 用来标记哪些节点是计算过的
        :param count: 这个节点的叶子节点个数
        """
        self.vec = vec
        self.left = left
        self.right = right
        self.distance = distance
        self.id = id
        self.count = count
        
def euler_distance(point1: np.ndarray, point2: list) -> float:
    """
    计算两点之间的欧拉距离,支持多维
    """
    distance = 0.0
    for a, b in zip(point1, point2):
        distance += math.pow(a - b, 2)
    return math.sqrt(distance)
# 层次聚类(聚合法)

class Hierarchical:
    def __init__(self, k):
        self.k = k
        self.labels = None
        
    def fit(self, x):
        nodes = [ClusterNode(vec=v, id=i) for i, v in enumerate(x)]
        distances = {}
        point_num, feature_num = x.shape
        self.labels = [-1] * point_num
        currentclustid = -1
        while(len(nodes)) > self.k:
            min_dist = math.inf
            nodes_len = len(nodes)
            closest_part = None
            for i in range(nodes_len - 1):
                for j in range(i+1, nodes_len):
                    d_key = (nodes[i].id, nodes[j].id)
                    if d_key not in distances:
                        distances[d_key] = euler_distance(nodes[i].vec, nodes[j].vec)
                    d = distances[d_key]
                    if d < min_dist:
                        min_dist = d
                        closest_part = (i, j)
                        
            part1, part2 = closest_part
            node1, node2 = nodes[part1], nodes[part2]
            new_vec = [ (node1.vec[i] * node1.count + node2.vec[i] * node2.count ) / (node1.count + node2.count)
                        for i in range(feature_num)]
            new_node = ClusterNode(vec=new_vec,
                                   left=node1,
                                   right=node2,
                                   distance=min_dist,
                                   id=currentclustid,
                                   count=node1.count + node2.count)
            currentclustid -= 1
            del nodes[part2], nodes[part1]
            nodes.append(new_node)
            
        self.nodes = nodes
        self.calc_label()
        
    def calc_label(self):
        """
        调取聚类的结果
        """
        for i, node in enumerate(self.nodes):
            # 将节点的所有叶子节点都分类
            self.leaf_traversal(node, i)

    def leaf_traversal(self, node: ClusterNode, label):
        """
        递归遍历叶子节点
        """
        if node.left == None and node.right == None:
            self.labels[node.id] = label
        if node.left:
            self.leaf_traversal(node.left, label)
        if node.right:
            self.leaf_traversal(node.right, label)

iris = datasets.load_iris()
my = Hierarchical(3)
my.fit(data)
labels = np.array(my.labels)
# visualize result

cat1 = data[np.where(labels==0)]
cat2 = data[np.where(labels==1)]
cat3 = data[np.where(labels==2)]

plt.scatter(cat1[:,0], cat1[:,1], color='green')
plt.scatter(cat2[:,0], cat2[:,1], color='red')
plt.scatter(cat3[:,0], cat3[:,1], color='blue')
plt.title('Hierarchical clustering with k=3')
plt.xlim(4, 8)
plt.ylim(1, 5)
plt.show()

在这里插入图片描述


# kmeans

class MyKmeans:
    def __init__(self, k, n=20):
        self.k = k
        self.n = n
        
    def fit(self, x, centers=None):
        # 第一步,随机选择 K 个点, 或者指定
        if centers is None:
            idx = np.random.randint(low=0, high=len(x), size=self.k)
            centers = x[idx]
        #print(centers)
        
        inters = 0
        while inters < self.n:
            #print(inters)
            #print(centers)
            points_set = {key: [] for key in range(self.k)}

            # 第二步,遍历所有点 P,将 P 放入最近的聚类中心的集合中
            for p in x:
                nearest_index = np.argmin(np.sum((centers - p) ** 2, axis=1) ** 0.5)
                points_set[nearest_index].append(p)

            # 第三步,遍历每一个点集,计算新的聚类中心
            for i_k in range(self.k):
                centers[i_k] = sum(points_set[i_k])/len(points_set[i_k])
                
            inters += 1

        
            
        return points_set, centers

from sklearn.cluster import KMeans

loss = []

for i in range(1, 10):
    kmeans = KMeans(n_clusters=i, max_iter=100).fit(data)
    loss.append(kmeans.inertia_ / len(data) / 3)

plt.title('K with loss')
plt.plot(range(1, 10), loss)
plt.show()

在这里插入图片描述


http://www.niftyadmin.cn/n/12149.html

相关文章

学习JavaScript基础

JavaScript基础 JavaScript简介 什么是javascript? JS是一种脚本, 动态, 弱类型语言. 基于在网页上实现复杂功能而产生的语言. 虽然JS做为开发web页面的脚本语言而出名. 但是如今已被用到了很多非浏览器环境当中, 比如NodeJS, Apache, Electron, Frida. 动态语言意味着JS不需…

《数据结构》八大排序(详细图文分析讲解)

目录 排序 排序的应用 排序简介 排序的分类 排序算法的好坏评判 冒泡排序法 思路分析 代码实现 选择排序法 思路分析 代码实现 插入排序 思路分析 代码实现 希尔排序 思路分析 代码演示 归并排序法 思路分析 代码演示 快速排序 思路分析 代…

【ML特征工程】第 7 章 :通过K-Means 模型堆叠进行非线性特征化

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

消息队列 - RabbitMQ

1. 名词解释 Producer&#xff1a;生产者 Broker&#xff1a;接收和分发消息的应用 Connection&#xff1a;生产者和消费者与 Broker 之间的 TCP 连接 Channel&#xff1a;信道&#xff1b;在 Connection 内部建立的逻辑连接&#xff0c;每个 Channel 之间是相互隔离的。相…

【网络安全必看】如何提升自身WEB渗透能力?

前言 web渗透这个东西学起来如果没有头绪和路线的话&#xff0c;是非常烧脑的。 理清web渗透学习思路&#xff0c;把自己的学习方案和需要学习的点全部整理&#xff0c;你会发现突然渗透思路就有点眉目了。 程序员之间流行一个词&#xff0c;叫35岁危机&#xff0c;&#xf…

鲲鹏devkit开发套件——编译调试工具介绍

鲲鹏devkit编译调试工具介绍 编译调试插件是其中的一个子工具。编译调试插件即插即用&#xff0c;支持一键安装服务器鲲鹏编译器&#xff0c;支持单机下Nvidia GPU应用调试能力&#xff0c;通过统一调试界面调试GPU应用&#xff0c;实现cuda-gdb调试能力&#xff0c;以及鲲鹏平…

集合框架----源码解读ArrayList篇

1.ArrayList<E> ArrayList是继承AbstractList<E> List 接口的可调整数组实现。实现所有可选的列表操作&#xff0c;并允许所有元素&#xff0c;包括null。除了实现List接口之外&#xff0c;该类还提供了一些方法来操作内部用于存储列表的数组的大小。(这个类大致相…

裸 VSCode 必备插件

VSCode 轻量、开源&#xff0c;新鲜下载的 VSCode 可谓是身无长物、一穷二白&#xff0c;连个项目管理的功能都没有。 身轻如燕的 VSCode 对于后端开发说可能有点幼稚&#xff0c;但对于前端来说刚刚好&#xff0c;毕竟不需要搞什么 Docker、数据库等等&#xff0c;装俩 VSCod…