【机器学习-Matlab】【演化数据聚类】 《聚类是一项无监督的机器学习任务,许多现实世界的问题都可以被描述为此类问题并转换为此类问题 聚类分析是对一组数据对象进行分组,使组(或聚类)成员的相似性最大化,另一方面,将两个不同组中成员的相似性最小化 这个问题出现在机器学习、数据挖掘和统计数据分析等多个领域,在模式识别、图像处理、信号处理、生物信息学和信息检索方面有着巨大的应用 》

在南京某个闷热的下午,我对着电脑屏幕上的散点图发呆。这些数据点就像夜空中无序的繁星,直到用聚类算法给它们画上星座连线——突然就看得懂星图了。今天我们就用Matlab来玩这个数据魔术,顺便聊聊那些隐藏在算法背后的"星象学"。

先来点手工数据热身,用Matlab生成三坨假装很乖的数据点:

mu1 = [2 3];
sigma1 = [0.9 0; 0 0.9];
data1 = mvnrnd(mu1,sigma1,100);

mu2 = [6 8];
sigma2 = [1.2 0.5; 0.5 1.2];
data2 = mvnrnd(mu2,sigma2,80);

mu3 = [10 4];
sigma3 = [0.7 -0.3; -0.3 0.7];
data3 = mvnrnd(mu3,sigma3,120);

dataset = [data1; data2; data3];

这三坨数据各自扭成不同的椭圆形状,σ矩阵里的非对角项就像调皮的手指,把原本规矩的圆形面团捏成各种倾斜造型。现在问题来了:如果只给你这300个二维点,怎么让机器自己发现这三个隐藏的部落?

上菜谱时间!Matlab自带的kmeans函数就像智能炒菜机:

[cluster_idx, centers] = kmeans(dataset,3,'Replicates',5,'Display','iter');

'Replicates'参数就像让大厨反复试菜5次,避免手抖放错盐。跑完这段代码,工作区会多出两个变量:cluster_idx记录着每个数据点的户籍,centers则是三个部落的首都坐标。

验证成果最直观的方式是可视化:

figure;
gscatter(dataset(:,1),dataset(:,2),cluster_idx,'rgb','osd');
hold on;
plot(centers(:,1),centers(:,2),'kx','MarkerSize',15,'LineWidth',3);
legend('Cluster 1','Cluster 2','Cluster 3','Centroids');

当散点图弹出时,如果看到三个颜色分明的区域被黑色X精准定位,说明算法成功破译了数据密码。不过现实往往更骨感——试过把初始质心设在奇怪的位置吗?比如:

initial_centers = [0 0; 1 1; 2 2];  % 随便乱写的初始点
[cluster_idx, centers] = kmeans(dataset,3,'Start',initial_centers);

这时候可视化可能会看到某个质心在数据荒漠中孤独流浪,对应的类簇空无一人。这提醒我们:初始值的选择就像爱情,开始的位置错了,结局可能南辕北辙。

【机器学习-Matlab】【演化数据聚类】 《聚类是一项无监督的机器学习任务,许多现实世界的问题都可以被描述为此类问题并转换为此类问题 聚类分析是对一组数据对象进行分组,使组(或聚类)成员的相似性最大化,另一方面,将两个不同组中成员的相似性最小化 这个问题出现在机器学习、数据挖掘和统计数据分析等多个领域,在模式识别、图像处理、信号处理、生物信息学和信息检索方面有着巨大的应用 》

当数据开始随时间流动,传统的静态聚类就力不从心了。这时需要演化聚类的绝活——比如用滑动窗口跟踪数据漂移。假设我们有个随时间变化的数据流:

% 模拟动态数据变化
time_steps = 10;
stream_data = cell(time_steps,1);
for t = 1:time_steps
    drift = 0.5*t;  % 漂移量随时间增加
    stream_data{t} = [mvnrnd([2+drift,3],sigma1,30);
                     mvnrnd([6,8-drift],sigma2,30);
                     mvnrnd([10-drift,4+drift],sigma3,30)];
end

处理这种场景,增量式k-means可以派上用场。核心思想是让质心跟着数据动:

window_size = 3;  % 滑动窗口长度
centroid_traj = zeros(time_steps, 3, 2);  % 存储质心轨迹

% 初始化前三个时间步
init_data = vertcat(stream_data{1:window_size});
[~, initial_centers] = kmeans(init_data,3);
centroid_traj(1:window_size,:,:) = repmat(initial_centers,[1,1,window_size]);

% 滑动更新
for t = window_size+1:time_steps
    new_data = stream_data{t};
    old_centers = squeeze(centroid_traj(t-1,:,:));
    
    % 混合新旧数据重新聚类
    combined_data = [new_data; old_centers];
    [~, updated_centers] = kmeans(combined_data,3,'Start',old_centers);
    
    centroid_traj(t,:,:) = updated_centers;
end

这个策略就像给质心装上滑轮,让它们能随着数据流的冲刷慢慢漂移。通过观察centroid_traj的三维轨迹,我们能清晰看到各类簇中心如何随时间翩翩起舞。

当处理基因序列或文本数据时,常规的欧氏距离就捉襟见肘了。这时需要自定义距离函数,比如用动态时间规整(DTW)处理时间序列:

% 自定义DTW距离的k-medoids聚类
load('time_series_data.mat');  % 假设载入24条时间序列
dist_fun = @(x,Z) dtw(x,Z);  % 定义距离函数

opts = statset('UseParallel',true);
[medoid_indices, medoid_coords] = kmedoids(series_data,3,'Distance',dist_fun,'Options',opts);

这里用k-medoids代替k-means,因为时间序列的"中心点"必须是真实存在的数据点(medoid)。开启并行计算能让漫长的DTW计算加速,毕竟等待聚类结果就像等待泡面,三分钟都嫌久。

最后给个实用小技巧:当不确定聚类数目时,可以画个手肘图辅助决策:

% 肘部法则确定最佳k值
wcss = zeros(1,6);  # 假设测试k=1到6
for k = 1:6
    [~, ~, sumd] = kmeans(dataset,k);
    wcss(k) = sum(sumd);
end

plot(1:6, wcss, 'b-o');
xlabel('Number of clusters');
ylabel('Within-cluster sum of squares');

当折线图的斜率突然变缓(像手肘弯曲),对应的k值往往是最佳选择。不过现实中的数据可能像抽象画,根本找不到明显拐点,这时候就需要领域知识和业务理解来助攻了。

数据聚类的魅力在于,它让机器拥有了发现模式的火眼金睛。从静态画像到动态追踪,从规整空间到扭曲时空,每一次算法的调整都像是在调整显微镜的焦距,直到混沌中浮现出隐藏的秩序。下次当你在数据海洋中迷航时,不妨试试这些聚类罗盘,说不定就能找到新大陆的坐标。

更多推荐