赞
踩
k-means聚类是一种无监督学习的聚类算法,它的目的是将数据集中的样本划分成若干个类别,使得同一类别内的样本相似度高,而不同类别之间的样本相似度低。其目标是将数据点划分为k个类簇,找到每个簇的中心并使其度量最小化。
此外,K-means算法有一些优点,如简单易懂、计算速度快,适合处理大数据集。但它也有一些缺点,比如需要预先指定簇的数量K,对初始聚类中心的选择敏感,可能收敛到局部最优而非全局最优解,对于非凸数据集和噪声数据敏感。
在实际应用中,K-means算法广泛应用于市场细分、图像分割、社交网络分析等领域。本文主要将其应用于图像分割领域。
通过计算相似度(默认欧氏距离),将相似度大的样本聚集到同一个类别,k表示聚成k个类别,means表示每个类别的聚类中心点是通过簇中所有样本点的均值得到。
聚类的效果可采用误差平方和SSE进行评价,SSE值越小,表示数据点越接近它们的中心点,聚类效果越好。
下面是K-Means聚类算法的分析流程,步骤如下:
第一步,确定K值,即将数据集聚集成K个类簇或小组。
第二步,从数据集中随机选择K个数据点作为质心(Centroid)或数据中心。
第三步,分别计算每个点到每个质心之间的距离,并将每个点划分到离最近质心的小组,跟定了那个质心。
第四步,当每个质心都聚集了一些点后,重新定义算法选出新的质心。
第五步,比较新的质心和老的质心,如果新质心和老质心之间的距离小于某一个阈值,则表示重新计算的质心位置变化不大,收敛稳定,则认为聚类已经达到了期望的结果,算法终止。
第六步,如果新的质心和老的质心变化很大,即距离大于阈值,则继续迭代执行第三步到第五步,直到算法终止。
对于图像分割任务,需要在第一步之前将图像reshape为m*n行、p列的矩阵,这样每一行就是一个p维数据点。通过以上聚类算法,得到最终的聚类结果,最后再将结果reshape为原始图像的大小即可。
MATLAB自带函数
kmeans函数的说明如下:
idx = kmeans(X,k) 执行 k 均值聚类,以将 n×p 数据矩阵 X 的观测值划分为 k个聚类,并返回包含每个观测值的簇索引的 n×1 向量 (idx)。X 的行对应于点,列对应于变量。
根据上面的kmeans函数的说明,我们需要将图像矩阵reshape一下,然后再调用该函数,最后再reshape回来,下面是测试的主要代码:
[m,n,p]=size(img) %m,n为所求,p=3为通道数
% 将图像进行RGB——3通道分解
% org(:, :, 1)......分别代表rgb通道
A = reshape(img(:, :, 1), m*n, 1);
B = reshape(img(:, :, 2), m*n, 1);
C = reshape(img(:, :, 3), m*n, 1);
data = [A B C]; % r g b分量组成样本的特征,每个样本有三个属性值,共width*height个样本
% kmeans第一个参数: N*P的数据矩阵,N为数据个数,P为单个数据维度
res = kmeans(double(data), k);
result = reshape(res, m, n); %转化为图片形式
MATLAB实现
根据上述算法流程,使用matlab编写如下代码,其中设置强制截断的迭代次数,并添加了迭代过程展示。
my_kmeans函数:
function [C, label, J] = my_kmeans(data, k, is_gpu, is_show) %% 语法 : % [C, label, J] = my_kmeans(data, k, is_gpu, is_show) %% 说明: % data 为输入图像 % k 为聚类的k个数据中心 % is_gpu 是否启用gpu加速 % is_show 参数决定是否显示聚类过程 % ---------------------------------------- % return label 为返回的聚类结果 % return C 聚类中心 % return J 每次迭代的误差 %% if nargin >= 3 is_gpu = 1; else is_gpu = 0; end if nargin == 4 is_show = 1; else is_show = 0; end %% % data可以为一维数据 m = size(data, 1); n = size(data, 2); p = size(data, 3); % [m, n, p] = size(I); %图片的大小m*n,p代表RGB三层 X = reshape(double(data), m*n, p); if is_gpu X = gpuArray(X); end rng('default'); C = X(randperm(m*n, k), :);%随机选k个聚类中心 J = []; J_prev = inf; tol = 1e-1; %容忍度tol,inf为无穷大 iter = 0; rand_c = rand(k,3)*255; rand_c = uint8(rand_c); while true iter = iter + 1; dist = sum(X.^2, 2)*ones(1, k) + (sum(C.^2, 2)*ones(1, m*n))' - 2*X*C'; %计算各个点到K个聚类中心的距离 [~,label] = min(dist, [], 2) ; %label记录最小值的行数 for i = 1:k C(i, :) = mean(X(label == i , :)); %取新的k个聚类中心 end J_cur = sum(sum((X - C(label, :)).^2, 2)); %距离之和 J = [J, J_cur]; fprintf('#iteration: %03d, objective norm diff: %f\n', iter, norm(J_cur-J_prev, 'fro')); if norm(J_cur-J_prev, 'fro') < tol % A和A‘的积的对角线和的平方根,即sqrt(sum(diag(A'*A))),本次与上次距离之差 break; end if (iter==100) %设置强制截断的迭代次数 break; end J_prev = J_cur; if is_show % 录制gif rand_c = uint8(C); temp = uint8(label); temp = rand_c(temp,:); sg=reshape(temp,m,n, []); imshow(mat2gray(sg)); F=getframe(gcf); data=frame2im(F); [data,map]=rgb2ind(data,256); if iter == 1 imwrite(data,map,'test.gif','gif','Loopcount',inf,'DelayTime',0.2); else imwrite(data,map,'test.gif','gif','WriteMode','append','DelayTime',0.2); end end end % C = rand_c; end
测试代码如下,如果是单纯是为了处理图像,可以将“聚类”这小段的代码重新封装成一个function。
clear; close all; clc; tic %% 读取数据 img=imread('city.png'); % img = rgb2gray(img); %% 聚类 m = size(img, 1); n = size(img, 2); p = size(img, 3); k = 4; [C, label, J2] = my_kmeans(img, k, 1, 1); % 使用gather函数将gpuarray转换为普通数组 C = gather(C); C = uint8(C); % randn('seed', sum(100*clock)) % sprintf('seed=%d', sum(100*clock)) % rand_c = rand(k,3)*255; % C = uint8(rand_c); label = uint8(label); temp = C(label,:); dst = reshape(temp, m, n, []);%转换成图片矩阵的格式 toc %% 显示结果 figure subplot(221), imshow(img,[]); subplot(222), imshow(dst,[]); subplot(212), plot(J2), xlabel('iters'), ylabel('norm diff')
测试的原始图片如下:
聚类过程如下,是不是有种延时摄影的感觉。
测试结果及相应的迭代过程如下所示:
MATLAB自带函数不同K值下的效果:
自己实现的不同K值下的效果:
MATLAB自带函数的效果和我实现的效果有所区别,主要是由于MATLAB自带函数采用了kmeans++进行了优化,在此就不赘述了,感兴趣的朋友可以去深入了解。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。