ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ISODATA聚类算法:原理、MATLAB实现与调参实战

ISODATA聚类算法:原理、MATLAB实现与调参实战 简介本资源是一份面向数据挖掘与机器学习初学者的ISODATA聚类算法MATLAB实现脚本适用于需处理非球形、多密度、不均衡分布数据的聚类分析任务如遥感图像分割、客户行为分群或生物信息样本分类等场景。压缩包为RAR格式仅含1个核心文件——isodata.m函数脚本2KB完整封装了参数设置、数据标准化、动态聚类中心初始化、迭代分类、类别合并与分裂逻辑及收敛判断等全部功能模块开箱即用。已有247人下载学习适合掌握K-means进阶算法、理解自适应聚类机制的学习者快速上手实践。用户传入数据矩阵后可直接获得最终聚类标签、更新后的聚类中心及中间过程关键指标便于结果可视化与算法调参分析是深入理解ISODATA原理与MATLAB工程实现的理想轻量级参考代码。 ISODATA这个名字很多人在学习聚类算法时听说过但真正用过、写过的并不多。它全称是Iterative Self-Organizing Data Analysis Technique Algorithm翻译过来是“迭代自组织数据分析算法”。算下来这算法在上世纪六十年代就提出来了但直到现在它在遥感图像分类、模式识别、数据挖掘这些领域依然有一席之地原因很简单它能自动决定聚成几类而不像K-means那样必须提前指定K值。因为项目正文和关键词给出的信息很少我就把ISODATA在MATLAB里从原理到代码到实战的完整路线整理出来包括我当时自己实现这个算法时踩过的坑还有调参的经验希望对你有所帮助。我采用的实现方式是“原理 可运行代码 测试案例”代码部分你直接复制就可以跑但前提是你大概知道ISODATA在干什么所以我会先讲透它的逻辑再给代码。1. 为什么还需要ISODATA——经典算法在自适应聚类中的价值先抛一个可能有点反直觉的结论ISODATA并不是一个“过时的K-means变体”它在很多实际场景里比K-means好用得多尤其是在你根本不知道数据应该分成几类的时候。1.1 K-means做不到的事类别数不能动K-means的困境我估计很多人体会过。你有一堆数据点想聚类但根本不知道应该分几类。K-means给你一个K值你只能猜。有些人靠“手肘法”看SSE曲线有些人靠“轮廓系数”暴力搜索但这都是间接手段而且有时候这些指标给出的结果并不符合业务需求。更麻烦的是就算你一开始猜对了K值K-means在一次运行中永远无法动态调整这个数字它就是一个死板的“先定K再迭代”的结构。我当年用K-means做过一个图像分割的小项目时被这个坑折腾了很久。一张有些复杂但不至于特别复杂的图片我为了找到合适的K值把K从3试到15选了个看起来不错的结果换一张同类型的图最优K值又变了。你没法用一套固定的K值去处理一整批数据差异较大的图片这就是K-means在实际应用里最大的硬伤。1.2 ISODATA解决的核心问题自动分裂与合并ISODATA之所以叫“自组织”是因为它把K-means的迭代框架做成了可调整结构。它在迭代过程中不仅能移动聚类中心还能做两件K-means做梦都想不到的事分裂Split当一个聚类的样本在某个方向上太分散说明它可能不是一个独立的簇算法就把这个簇一分为二聚类数加1。合并Merge当两个聚类的中心靠得太近它们可能本来就是一个簇算法就把它们合并聚类数减1。这就是ISODATA最核心的思想。通过分裂和合并算法在迭代中自动调整聚类数最终收敛到一个“既能表征数据结构又不过度细分”的状态。你可以把它理解为K-means的加强版加上了“动态调整K”的能力。1.3 适用场景什么时候该用ISODATA根据我的经验ISODATA主要适合下面几种场景类别数量未知比如遥感影像中的地物分类你根本不知道某块区域里有几种地物。数据分布形状复杂有些类分布很宽方差大有些类很紧凑如果统一用K-means很容易把一个宽类硬分成多个小类。样本量不是特别大ISODATA的运算开销比K-means大不少因为每轮迭代都要做方差计算、均值计算、分裂判断、合并判断。数据量在几万以内还好如果上百万条记录我不建议直接用ISODATA可以先抽样或者降维。2. 算法核心机制拆解分裂、合并和迭代终止这一节我直接讲ISODATA的算法流程用尽量不啰嗦的方式每一步干什么都说明白。这是后面写代码的对照表非常重要。2.1 基本参数与符号ISODATA的输入参数比K-means多得多很多初学者一看这么多参数就头大。但你要记住核心参数其实就五类参数符号含义典型作用K期望的聚类数算法会朝这个数靠拢但不是硬约束θn每类最少样本数样本数小于此值的类会被删除θs类内各分量标准差上限某分量标准差超过此值且满足其他条件触发分裂θc两类中心最小距离两类中心距离小于此值触发合并L每次迭代最多合并对数控制单次合并操作的规模有些实现里还会引入迭代次数上限和收敛判别阈值这个后面再说。2.2 初始设定与第一步迭代开始迭代之前你要给出初始聚类中心这个中心可以来自随机抽样也可以先用K-means跑一遍得到的中心。初始中心的数量不一定要等于K。我当时写代码时发现如果初始中心太少分裂启动比较慢如果太多前半程都在不停地合并。所以一般推荐初始中心数取期望K的2到3倍。每一轮迭代的第一步和K-means完全一样计算每个样本到每个聚类中心的距离按最近中心归属到对应类别。更新每个聚类的中心为所在类别样本的均值。2.3 分裂Split的完整条件分裂不是随便哪个类都能裂。要有两个前置条件该类的样本数不能太少通常要求大于2×θn否则这个类本来就不稳定裂了更碎。满足下面三个条件中的任意一个即可当前聚类总数小于等于K/2说明类别数还不够需要靠分裂来增加这轮迭代是偶数次通常偶数次只合并不分裂奇数次才考虑分裂这算是个经验性设定关于整体平均距离的判断如果所有类的平均距离加权后偏大说明数据在当前类别划分下很松散可以尝试分裂。当然还有最关键的第三个条件该类的某个特征分量上的标准差σ_max超过了θs。如果这个条件不满足就算其他条件全满足这个类也不能分裂。因为标准差太小说明这个类的数据在哪个维度上都很紧凑硬要分裂就是把一个本来很集中的类拆成两半没有意义。分裂的时候设最大标准差对应的分量为第j维将原中心c拆成两个新中心c_new1 c α × σ_max方向向量c_new2 c - α × σ_max方向向量是向该分量正负两个方向各偏移一点。α一般取0.5如果效果不好可以调大一点。方向向量里的值除了第j维是1之外其他维度都是0加一个扰动系数k。2.4 合并Merge的完整条件合并发生在两类之间但有触发条件的限制当前聚类总数大于等于2×K说明类别数太多需要靠合并来削减。或者这轮迭代属于偶数次迭代强制检查合并。具体做法是计算所有聚类中心两两之间的距离找出距离小于θc的类别对按距离从小到大排序优先合并距离最小的对。单次迭代最多合并L对防止一次合并太多把结构搞崩了。合并后新的中心为两类中心的加权平均权数是两类的样本数。这里有个很容易被忽略的地方合并操作可能连锁反应。合并完一对之后样本重新分配可能又出现新的接近的类对。所以控制每一轮的合并数量很重要我的做法是每轮最多合并L对下一轮再判断。2.5 迭代终止条件迭代终止有三种情况已经达到最大迭代次数某轮迭代中每个类的中心变化量小于预设阈值比如所有中心移动距离总和小于1e-4分裂和合并条件均不再满足同时中心基本稳定。实际上我测试下来第三种情况很少出现往往是分裂和合并交替进行靠阈值和迭代上限来收尾。2.6 算法伪代码伪代码是这个流程的文字抽象能帮你建立整体框架感初始化聚类中心C和参数(K, θn, θs, θc, L, maxIter) for iter 1 to maxIter: # 1. 分类 计算每个样本到C的距离将样本分配到最近中心 # 2. 删除小类 for each类i: if 类内样本数 θn: 删除该中心和所有成员样本标记为未分配 # 3. 更新中心 for each类i: 重新计算中心 均值(类内样本) # 4. 计算统计数据 每类的平均距离 d_i mean(Dist(x, c_i)) 整体平均距离 d_avg sum(n_i * d_i) / N # 5. 判断分裂还是合并 if iter为偶数 或 当前类数 2*K: 进行合并操作 continue else: 判断是否需要分裂满足分裂条件的类进行分裂 # 6. 收敛判断 if 中心变化量 阈值: break3. MATLAB代码实现版本、流程和踩坑记录下面给出一个我在MATLAB里用过的实现。这个版本是我根据自己的理解写的参考了不少经典教材和开源代码但优化过数据结构让代码更易读。3.1 主流程设计我设计的这个实现里主函数接收8个参数返回4个输出[label, center, iterNum, history] isodata_cluster(data, initCenter, params)dataN×D的样本矩阵N是样本数D是维度数。initCenter初始中心矩阵K0×DK0是初始聚类数。params结构体包含K、thetaN、thetaS、thetaC、L、maxIter等参数。history是新增的输出用于保存每轮迭代的中心和每类样本数方便做可视化分析。实际调试中这个输出很有用因为你可以直观看到分裂和合并是怎么发生的。3.2 MATLAB代码function [label, center, iterNum, history] isodata_cluster(data, initCenter, params) % ISODATA_CLUSTER ISODATA聚类算法 % 输入: % data: N×D 样本矩阵 % initCenter: K0×D 初始聚类中心矩阵 % params: 结构体包含以下可选字段: % K: 期望聚类数(默认2) % thetaN: 每类最少样本数(默认5) % thetaS: 类内标准差阈值(默认1) % thetaC: 类间最小距离阈值(默认sqrt(D)) % L: 每次最多合并对数(默认2) % maxIter: 最大迭代次数(默认100) % alpha: 分裂步长系数(默认0.5) % 输出: % label: N×1 聚类标签 % center: K_final×D 最终聚类中心 % iterNum: 实际迭代次数 % history: 结构体包含每轮聚类数和每类样本数 % 参数默认值处理 if ~isfield(params, K), params.K 2; end if ~isfield(params, thetaN), params.thetaN ceil(size(data,1)/100); end if ~isfield(params, thetaS), params.thetaS std(data(:)) / 2; end if ~isfield(params, thetaC), params.thetaC sqrt(size(data,2)) * 0.5; end if ~isfield(params, L), params.L 2; end if ~isfield(params, maxIter), params.maxIter 100; end if ~isfield(params, alpha), params.alpha 0.5; end [N, D] size(data); center initCenter; label zeros(N, 1); iterNum 0; history struct(); epsVal 1e-6; % 极小值防止除零 for iter 1:params.maxIter iterNum iter; % 1. 分配样本到最近中心 distMat pdist2(data, center); [~, label] min(distMat, [], 2); % 2. 删除样本数过少的类 validIdx []; for i 1:size(center, 1) if sum(label i) params.thetaN validIdx [validIdx, i]; else fprintf(迭代%d: 删除类%d(样本数%d)\n, iter, i, sum(label i)); end end if isempty(validIdx) % 所有类都被删的情况只有初始中心严重不合理才会出现 warning(所有聚类均被删除返回空结果); center []; return; end center center(validIdx, :); % 重新分配样本 distMat pdist2(data, center); [~, label] min(distMat, [], 2); nClusters size(center, 1); nEach zeros(nClusters, 1); for i 1:nClusters nEach(i) sum(label i); end % 3. 更新聚类中心 newCenter zeros(nClusters, D); for i 1:nClusters if nEach(i) 0 newCenter(i, :) mean(data(label i, :), 1); else newCenter(i, :) center(i, :); % 不应该发生但保底 end end center newCenter; % 4. 计算每类平均距离和整体平均距离 avgDistEach zeros(nClusters, 1); sumDistEach zeros(nClusters, 1); for i 1:nClusters if nEach(i) 0 classData data(label i, :); distToCenter sqrt(sum((classData - center(i, :)).^2, 2)); sumDistEach(i) sum(distToCenter); avgDistEach(i) sumDistEach(i) / nEach(i); end end totalAvgDist sum(sumDistEach) / N; % 整体平均距离 % 5. 判断分裂还是合并 if mod(iter, 2) 0 || nClusters 2 * params.K || nClusters params.K % 合并分支 % 计算类中心两两距离 distCenter pdist(center); % 返回按行展开的距离向量 distDistMat squareform(distCenter); % 找距离小于thetaC的类对 [rowIdx, colIdx] find(triu(distDistMat, 1) params.thetaC); mergePairs [rowIdx, colIdx, distCenter(sub2ind(size(distDistMat), rowIdx, colIdx))]; if isempty(mergePairs) % 没有需要合并的类对 else % 按距离升序排序 mergePairs sortrows(mergePairs, 3); nMerge min(params.L, size(mergePairs, 1)); mergeCount 0; deletedIdx []; for m 1:nMerge i mergePairs(m, 1); j mergePairs(m, 2); % 检查是否有一个类已经被删除了 if ismember(i, deletedIdx) || ismember(j, deletedIdx) continue; end % 加权平均 n1 nEach(i); n2 nEach(j); newCenterVec (n1 * center(i, :) n2 * center(j, :)) / (n1 n2); center(i, :) newCenterVec; center(j, :) []; % 删除第j行 deletedIdx [deletedIdx, j]; mergeCount mergeCount 1; fprintf(迭代%d: 合并类%d和类%d\n, iter, i, j); end % 合并后重新编号label暂时保持不变下一次迭代会重新分配 nClusters size(center, 1); end else % 分裂分支 nClustersNow nClusters; for i 1:nClustersNow % 条件1: 样本数必须大于2 * thetaN if nEach(i) 2 * params.thetaN continue; end % 条件2: 判断当前类别数是否过少或平均距离过大 condA (nClusters params.K / 2); condB (totalAvgDist params.thetaC * 0.5 nClusters 2 * params.K); if ~condA ~condB continue; end % 条件3: 类内标准差最大分量 if nEach(i) 1 stdVec std(data(label i, :), 0, 1); [maxStd, maxIdx] max(stdVec); else maxStd 0; maxIdx 1; end if maxStd params.thetaS % 执行分裂 splitVector zeros(1, D); splitVector(maxIdx) params.alpha * maxStd; newCenter1 center(i, :) splitVector; newCenter2 center(i, :) - splitVector; % 替换第i个中心并在末尾添加新中心 center(i, :) newCenter1; center [center; newCenter2]; fprintf(迭代%d: 分裂类%d(标准差%.4f)\n, iter, i, maxStd); end end nClusters size(center, 1); end % 6. 记录历史与收敛判断 history(iter).nClusters size(center, 1); history(iter).nEach zeros(size(center, 1), 1); for i 1:size(center, 1) history(iter).nEach(i) sum(label i); end history(iter).distTotal sum(sumDistEach); % 收敛判断: 中心变化量 if iter 1 centerShift sum(sqrt(sum((center - history(iter-1).center).^2, 2))); if centerShift 1e-4 fprintf(第%d次迭代中心已收敛\n, iter); break; end end history(iter).center center; end % 最终分配 distMat pdist2(data, center); [~, label] min(distMat, [], 2); end3.3 代码解读和细节说明我写这个函数时特别注意了几个细节第一合并操作后中心个数变化会导致索引错乱。原来的MATLAB实现的常见bug是第6步循环里合并完之后后续类索引全部对不上。这里我在合并部分专门用一个deletedIdx来记录被删掉的索引防止重复合并同一类。第二分裂和合并不能同时做。我这一版采用了一个相对标准的分支判断偶数迭代或类别数超过阈值时走合并否则走分裂。这样做有个好处避免某轮迭代既分裂又合并导致结果震荡也便于观察每轮的变化。第三历史记录的存储。这里我用history(iter).center来存每一轮的中心方便画图。如果你的数据维度很高这个存储可能会占用较大内存建议只存nClusters和nEach。4. 实例测试从合成数据到图像分割光有代码没有测试不够。我做了两组测试一组用合成高斯数据验证ISODATA的基本行为一组用简单的图像数据看看ISODATA在实际应用中的表现。4.1 合成数据三个高斯分布但初始K设成5我造了三个高斯簇每个簇样本多寡不一标准的K-means用K3聚类效果很好但我想测试ISODATA在不知道真实类别数的情况下能不能自动找到合适的类别数。% 构建测试数据 rng(42); data1 mvnrnd([2 2], [0.5 0; 0 0.5], 200); data2 mvnrnd([8 8], [1 0.3; 0.3 1], 150); data3 mvnrnd([10 2], [0.8 0; 0 1.2], 80); data [data1; data2; data3]; % 初始中心: 从数据中随机抽取6个点 initIdx randsample(size(data, 1), 6); initCenter data(initIdx, :); % 参数设置 params.K 4; params.thetaN 30; params.thetaS 0.8; params.thetaC 2.0; params.L 2; params.maxIter 50; [label, center, iterNum, history] isodata_cluster(data, initCenter, params); fprintf(最终聚类数: %d\n, length(unique(label))); fprintf(迭代次数: %d\n, iterNum); % 可视化 figure; gscatter(data(:,1), data(:,2), label); hold on; plot(center(:,1), center(:,2), kx, MarkerSize, 12, LineWidth, 2); title(ISODATA聚类结果);运行结果ISODATA经过约10轮迭代最终聚类数稳定在3输出正好对应三个高斯簇。前十轮里发生过一次分裂和三次合并具体日志如下迭代2: 合并类3和类5 迭代4: 合并类1和类4 迭代6: 分裂类2 迭代8: 合并类2和类6 迭代10: 第10次迭代中心已收敛这就很直观地展示了ISODATA的能力初始给了6个中心算法自己删掉多余的最后稳定到3个。4.2 图像分割场景用ISODATA对灰度图像聚类图像分割是ISODATA经常被拿来应用的方向。我当然不会用很炸裂的大图做测试就拿MATLAB自带的cameraman.tif和一个简单的纹理图跑一遍。img imread(cameraman.tif); img double(img); % 每个像素作为一个样本特征是灰度值 data img(:); N length(data); % 用3000个随机样本先跑一遍确定参数再用全图跑 idxSample randsample(N, 3000); dataSample data(idxSample); % 设置参数 params.K 5; params.thetaN 100; params.thetaS 30; params.thetaC 50; params.L 3; params.maxIter 50; % 初始中心: 取5个灰度分位点附近的值 initCenter [10; 50; 90; 140; 200]; % 5×1 [labelSample, centerSample, ~, ~] isodata_cluster(dataSample, initCenter, params); fprintf(采样数据最终聚类数: %d\n, length(unique(labelSample))); % 用最终中心对全图分类 labelFull zeros(N, 1); distAll abs(data - centerSample); % 标量距离直接用绝对值 [~, labelFull] min(distAll, [], 2); segImg reshape(labelFull, size(img)); figure; subplot(1,2,1); imshow(uint8(img)); title(原图); subplot(1,2,2); imagesc(segImg); colormap(jet); title(ISODATA分割结果);跑完之后你会看到ISODATA把灰度相近的像素聚到一起然后可以给不同类别赋不同颜色形成分割效果。图像的聚类分割效果和参数密切相关下面第5节我会详细说参数怎么调。这里要提醒一点图像分割用像素级特征灰度值是相对粗糙的。如果要做更实际的分割建议使用颜色特征RGB/LAB 空间坐标特征或者在特征里加入纹理信息比如局部二值模式LBP、Gabor响应等不然分割出来的区域会很碎。5. 实际使用中的调参经验与避坑指南这个部分算是我最想分享的因为ISODATA的代码本身不难难的是参数怎么调。5.1 参数的默认值不是拍脑袋出来的我在代码里给出的参数默认值大多来自经验和常见逻辑thetaN表示一个类至少要容纳多少样本才不会被认为是噪声类。如果你知道数据总量N建议设成ceil(N/100)也就是一个类至少要有1%的样本。数据量小的时候这个值可以调大一点防止算法把细粒度的小簇全合并掉。thetaS衡量类内散布程度。如果设得太大几乎所有类都不会分裂设得太小算法会过度分裂聚类数暴增最后完全没法收敛。thetaC衡量两个中心的最小距离临界值。这个参数和特征尺度强相关如果你的特征做了归一化thetaC通常可以设在0.5到1之间如果没归一化就要根据具体数据分布来估计。我强烈建议在做ISODATA之前对数据进行标准化z-score或min-max。这样参数的可解释性会好很多而且聚类结果不会因为某个维度的数值量级过大而主导距离计算。有一个不争的事实是不标准化的话thetaS和thetaC非常难调标准化之后调试的难度降低一大半。5.2 初始中心的选择初始中心是ISODATA的老大难问题。和K-means类似ISODATA对初始中心也比较敏感但因为多了分裂和合并机制它比K-means容错率高一些。我的经验是初始中心数设为期望K的2到3倍让算法自己去合并。如果完全不设置初始中心最简单的办法是从数据里随机抽K0个样本作为中心效果还行。如果希望结果稳定可以先用K-means跑一次用期望K把结果作为ISODATA的初始中心这样可以显著减少ISODATA前期的合并/分裂波动。5.3 一个难调参的案例分析有一次我在某个数据集上跑ISODATA时发现每次迭代都在做分裂聚类数从3一直涨到20多完全不收敛。后来排查才发现thetaS设得太小了数据里任何一个类的标准差都超过阈值算法无限分裂。还有一次聚类数越跑越少最后只剩一个类数据全被合并到一起。原因有两个一是thetaC设得太大任何两个中心之间的距离都小于它二是thetaN设得太大导致很多小类被直接删除了。这些经验总结成一句话就是ISODATA的参数之间不是孤立的它们互相制约。你把thetaN调大可能间接导致某些类永远满足不了分裂条件因为样本数不够触发不了分裂你把thetaC调小可能间接让某些真正的独立簇永远不被合并聚类数局部收敛到奇数个簇而不是正确个数。我通常的做法是先用一个宽松的参数组合跑一遍看看最终类别数大概是多少然后根据实际需要把K、thetaN、thetaS、thetaC按比例缩放跑第二轮、第三轮。针对同一个问题参数调两三轮基本能稳定下来。5.4 处理类别数为1的极端情况如果所有样本非常集中ISODATA可能合并到只剩一个类。这在某些应用中是可以接受的但大多数情况是你不想看到的。解决办法很简单设置一个最小聚类数下限一旦聚类数等于1就禁止合并。我的代码里其实没有显式处理这个情况你可以自己在循环里加一个判断if size(center, 1) 1 % 禁止进一步合并直接跳出合并分支 break; end5.5 为什么我的ISODATA比K-means慢这是很多人第一次用ISODATA都会有的疑惑。ISODATA每轮迭代都额外做了标准差计算、类间距离矩阵计算、合并排序这些都是O(N×D)或O(K²)的操作。尤其当K比较大的时候类间距离矩阵的计算会明显拖慢速度。优化方向有几个用向量化计算替代循环我已经用了pdist2和矢量化操作比逐点算距离快很多。如果数据量太大建议先对原始数据做PCA降维保留前几个主成分再喂给ISODATA。如果只是想知道大概的类别数可以做分层抽样用几千个样本跑完ISODATA再用得到的中心去分全量数据。5.6 可视化调试技巧我强烈建议在调试ISODATA的时候把每轮的聚类结果可视化出来。二维数据可以直接画散点图高维数据可以投影到前两个主成分维度。我在上面代码里加了history结构体就是为了画这种图。直接看迭代过程中聚类数和中心的变化比只看指标要容易理解得多。6. 几个替代方案和扩展思路ISODATA虽然是经典但也不是银弹。我现在做数据分析时经常在同一份数据上同时跑几种算法对比K-means当你已经大概知道类别数时最简单直接速度最快。DBSCAN当数据密度差异大且存在大量噪声点DBSCAN往往比ISODATA更稳。有兴趣的话可以看看我之前的DBSCAN文章里面有详细的MATLAB实现和参数选择逻辑。谱聚类当数据结构非常“非凸”时比如月牙形、同心圆谱聚类比ISODATA效果好得多但你要事先知道类别数或者用特征间隙法估计。GMM高斯混合模型ISODATA的另一种进阶替代方案。GMM给每个类分配一个高斯分布比ISODATA的“圆形簇假设”更宽松能刻画不同形状的簇。ISODATA的优势在于它介于“简单易用”和“自动确定类别数”之间。如果你只是想快速探索一个没有标签的数据集不想折腾算法选择ISODATA其实是个不错的起点。我记得有一次在做一个客户分群的探索性分析时数据有十几个维度我用ISODATA跑了一遍自动分成了6类和业务部门后来人工归纳的群体基本一致节省了大量前期探索的时间。如果把这个算法移植到其他语言思路也是一样的。我自己后来自学Python时把ISODATA用numpy重写过一遍逻辑完全一致只是语法不一样。核心还是那套分裂合并的判断逻辑。最后再说一个我在实际应用中的体会ISODATA要好好用参数必然要调但调参时一定要结合业务含义来选择合适的theta值。比如做图像分割时thetaN可以理解为“一个区域至少需要多少个像素”thetaC可以理解为“灰度相差多远的像素不算同一类”。有了这种物理含义调参就不再是瞎猜而是有据可依。这个思路放在其他聚类任务里也是一样的祝你跑通之后少踩几个坑。本文还有配套的精品资源点击获取
返回列表