ISODATA算法 python实现

本文介绍ISODATA算法,一种能够动态调整聚类数量的方法。它通过分裂与合并操作,不断优化聚类效果,适合处理复杂数据分布场景。文章提供Python实现代码及详细迭代过程。


前言

ISODATA经常被用来与Kmeans算法进行对比,其本质也是按照欧式距离来对样本进行分类,不同的是ISODATA可以根据一个大概的指定类别数去确定最终的聚类数(两者可能不同),而Kmeans指定聚类数是多少后,最终的聚类就一定是多少。


一、ISODATA的流程

本质上只有分裂和合并两个步骤加更新中心三个步骤。了解这个算法,核心需要解决下面的三个问题:

Question 1. 什么时候分裂?

现有的聚类数太少就进行分裂。你一开始指定100个聚类,现在只有2个,那就进行分裂。(大的分裂方向,还有细节见下面流程图)

Question 2. 什么时候合并?

现有的聚类数太多就进行分裂。你一开始指定100个聚类,现在上一次刚好裂成200个,那就进行合并。(大的合并方向,还有细节见下面流程图)

Question 3. 现在有的中心数不上不下怎么办?

如果是奇数次迭代,那就尝试去分裂吧(虽然最后不一定分裂了)
如果是偶数次迭代,那就尝试去合并吧(虽然最后不一定合并了)

1.流程图(这里按迭代的奇偶来判断分裂或者合并)

在这里插入图片描述

注意:

在流程图中,“合并”步骤并不一定执行了合并,只有满足在所有的中心中,存在一些中心的距离太近(这个距离低于了设定的阈值)才会真正的执行合并的操作,其余不执行合并的操作。而在分裂中,只有现有的中心数太少或者满足”类内的距离太大而且样本数太少“进行分裂的操作。其中类内的距离太大则表示了这个聚类太过于松散,再加上类的数量太少的话,才进行分裂。

分裂的细节:如何分裂?

计算需要分裂的这个簇在各个维度上的方差,如果最大的方差超过了特定的阈值,就在这个最大方差的维度上分裂成两个,其他维度的值保持不变。

比如现在有一个中心 (1, 3) , 对于属于这个中心的所有样本,我们计算其在第一个维度 (数值1的维度) 的方差,再计算其在第二个维度 (数值3的维度) 的方差。假设维度1计算的方差结果为 0.3,维度2计算的方差为1.5,预先设定的阈值为0.5;所以我们要在第二个维度上把中心分成2个:(1, 3 + 1.5 * k ), (1, 3 - 1.5 *k) ,其中k又是控制分裂远近的一个超参数,在代码中取0.5。由此,我们得到了新分裂的两个中心,并把原来的中心去掉。

合并的细节: 如何合并?

合并使用加权平均的方法,两个权重是两个中心控制的两簇样本的数量百分比,加权求和即可。

二、使用步骤

1.代码实现

Tips: 注意需要用到sklearn的库来产生数据集:

# -*- encoding:utf-8 -*-
"""
@author:zsiming
@fileName:ISODATA.py
@Time:2022/1/9  12:33
"""
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.metrics import euclidean_distances


class ISODATA():
    def __init__(self, designCenterNum, LeastSampleNum, StdThred, LeastCenterDist, iterationNum):
        #  指定预期的聚类数、每类的最小样本数、标准差阈值、最小中心距离、迭代次数
        self.K = designCenterNum
        self.thetaN = LeastSampleNum
        self.thetaS = StdThred
        self.thetaC = LeastCenterDist
        self.iteration = iterationNum

        # 初始化
        self.n_samples = 1500
        # 选一
        self.random_state1 = 200
        self.random_state2 = 160
        self.random_state3 = 170
        self.data, self.label = make_blobs(n_samples=self.n_samples, random_state=self.random_state3)

        self.center = self.data[0, :].reshape((1, -1))
        self.centerNum = 1
        self.centerMeanDist = 0

        # seaborn风格
        sns.set()

    def updateLabel(self):
        """
            更新中心
        """
        for i in range(self.centerNum):
            # 计算样本到中心的距离
            distance = euclidean_distances(self.data, self.center.reshape((self.centerN
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值