大家好!欢迎来到我的技术分享博客~ 在前期系列中,我们从K-means的随机初始化陷阱出发,逐步剖析了Canopy+K-means的粗筛优化、K-means++的概率采样和二分K-means的层次分裂。今天,迎来K-means家族的终极进化形态——ISODATA算法!它不仅解决初始点敏感和K值预设问题,更能动态分裂合并簇,彻底突破球形假设限制!
今天,我们将一起学习 ISODATA算法,看看它是如何通过动态调整聚类数量和合并/分裂聚类,来优化聚类效果的!
ISODATA(Iterative Self-Organizing Data Analysis Technique Algorithm) 是一种改进的聚类算法,它结合了K-means的思想,但增加了动态调整聚类数量的能力。ISODATA可以根据数据的分布情况,自动合并或分裂聚类,从而更灵活地适应不同的数据集。
ISODATA的核心思想是:在迭代过程中,根据聚类的内部特性和数据分布,动态地调整聚类数量。具体来说,ISODATA会监控每个聚类的样本数量、样本到聚类中心的平均距离等指标,并根据预设的阈值决定是否合并或分裂聚类。
初始化:设置初始聚类中心数量、最小聚类样本数、最大聚类样本数、聚类中心距离阈值、合并阈值、分裂阈值等参数,并随机选择初始聚类中心。
分配样本:将每个样本分配到最近的聚类中心。
更新聚类中心:根据分配的样本重新计算聚类中心。
计算聚类特性:计算每个聚类的样本数量、样本到聚类中心的平均距离等。
分裂操作:如果某个聚类的样本数量超过最大聚类样本数,且样本到聚类中心的平均距离大于分裂阈值,则对该聚类进行分裂。分裂时,选择距离聚类中心最远的样本作为新的聚类中心。
合并操作:计算所有聚类中心之间的距离,如果两个聚类中心之间的距离小于合并阈值,则合并这两个聚类。合并时,取两个聚类中心的均值作为新的聚类中心。
终止条件判断:如果满足终止条件(如达到最大迭代次数、聚类数量不再变化等),则算法终止;否则,返回步骤2继续迭代。
ISODATA适用于数据分布复杂、聚类数量不确定的场景。例如:
由于ISODATA算法较为复杂,直接实现可能较为繁琐。以下是一个简化示例代码:
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial.distance import cdist
class ISODATA:
def __init__(self, k=3, max_clusters=6, min_cluster_size=50,
max_std=0.5, merge_threshold=1.0, split_threshold=1.5):
self.k = k # 初始聚类数量
self.max_clusters = max_clusters # 最大允许聚类数量
self.min_cluster_size = min_cluster_size # 最小聚类样本数
self.max_std = max_std # 最大标准差阈值(用于分裂)
self.merge_threshold = merge_threshold # 合并距离阈值
self.split_threshold = split_threshold # 分裂标准差阈值
self.centers = None
self.labels = None
def fit(self, X, max_iter=100):
# 1. 初始化聚类中心
np.random.seed(42)
self.centers = X[np.random.choice(X.shape[0], self.k, replace=False)]
for _ in range(max_iter):
# 2. 分配样本到最近聚类中心
distances = cdist(X, self.centers, metric='euclidean')
self.labels = np.argmin(distances, axis=1)
# 3. 更新聚类中心
new_centers = np.array([X[self.labels == i].mean(axis=0)
if np.any(self.labels == i) else self.centers[i]
for i in range(self.k)])
# 4. 合并操作
merged = False
center_distances = cdist(new_centers, new_centers, metric='euclidean')
np.fill_diagonal(center_distances, np.inf)
while np.any(center_distances < self.merge_threshold):
# 找到距离最近的两个聚类
min_idx = np.unravel_index(np.argmin(center_distances),
center_distances.shape)
# 合并中心
merged_center = (new_centers[min_idx[0]] + new_centers[min_idx[1]]) / 2
# 更新中心列表
new_centers = np.delete(new_centers, min_idx[1], axis=0)
new_centers = np.vstack([new_centers, merged_center])
# 更新合并标志和距离矩阵
merged = True
center_distances = cdist(new_centers, new_centers, metric='euclidean')
np.fill_diagonal(center_distances, np.inf)
# 限制最大聚类数量
if len(new_centers) >= self.max_clusters:
break
# 5. 分裂操作
split_candidates = []
for i in range(len(new_centers)):
cluster_points = X[self.labels == i]
if len(cluster_points) < 2 * self.min_cluster_size:
continue # 样本数不足时不分裂
# 计算标准差
std = np.std(cluster_points, axis=0)
if np.max(std) > self.max_std:
split_candidates.append(i)
# 执行分裂操作
for i in reversed(split_candidates): # 反向遍历避免索引问题
# 找到距离中心最远的点
distances = cdist(new_centers[i:i+1], X[self.labels == i])
farthest_idx = np.argmax(distances)
# 创建新中心
new_center = X[self.labels == i][farthest_idx]
new_centers = np.vstack([new_centers[:i],
new_centers[i],
new_center,
new_centers[i+1:]])
# 更新聚类中心(处理可能的数量变化)
self.k = len(new_centers)
self.centers = new_centers
# 终止条件检查(可选)
if not merged and len(split_candidates) == 0:
break
return self
# 生成测试数据
np.random.seed(42)
X = np.vstack([
np.random.normal(loc=[0, 0], scale=0.5, size=(100, 2)),
np.random.normal(loc=[3, 3], scale=0.5, size=(100, 2)),
np.random.normal(loc=[-3, 3], scale=0.5, size=(100, 2)),
np.random.normal(loc=[0, 5], scale=0.3, size=(50, 2)) # 添加一个小簇
])
# 训练ISODATA模型
iso = ISODATA(k=3, max_clusters=6, min_cluster_size=40,
max_std=0.6, merge_threshold=1.2, split_threshold=1.0)
iso.fit(X)
# 可视化结果
plt.figure(figsize=(10, 6))
plt.scatter(X[:, 0], X[:, 1], c=iso.labels, cmap='viridis', s=50, alpha=0.7)
plt.scatter(iso.centers[:, 0], iso.centers[:, 1], c='red', s=200, marker='*',
edgecolor='white', linewidth=1.5)
plt.title('ISODATA Clustering Result')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.colorbar(label='Cluster')
plt.show()
注意:上面的代码是一个简化版的ISODATA实现,用于演示算法原理。在实际应用中,你可能需要寻找专门的库(如pyclustering库中的isodata实现)或自定义实现ISODATA算法。
能力 | K-means | K-means++ | 二分K-means | ISODATA |
---|---|---|---|---|
自动确定K值 | ❌ | ❌ | ❌ | ✅ |
动态分裂/合并簇 | ❌ | ❌ | ❌ | ✅ |
初始点鲁棒性 | 低 | 高 | 极高 | ✅ 极高 |
处理非凸数据 | ❌ | ❌ | ❌ | ✅ |
抗噪声能力 | 中 | 中 | 中 | ✅ 高 |
在下一篇博客中,我们将继续探索聚类算法的优化方案,介绍其他有趣的聚类算法或优化技巧。敬请期待哦!
感谢大家的阅读!如果你对ISODATA或任何其他技术话题有疑问或建议,欢迎在评论区留言!
希望这篇博客能帮助你更好地理解ISODATA算法!如果你觉得有用,别忘了点赞、分享和关注哦!
1、一文搞懂K-means聚类:原理、选K技巧、实战代码全解析
2、Canopy + K-means:聚类算法的“黄金搭档”优化方案(附代码)
3、K-means++:让K-means“聪明”地选择初始中心点
4、二分K-means:让聚类更高效、更精准!