【机器学习-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);这时候可视化可能会看到某个质心在数据荒漠中孤独流浪,对应的类簇空无一人。这提醒我们:初始值的选择就像爱情,开始的位置错了,结局可能南辕北辙。
当数据开始随时间流动,传统的静态聚类就力不从心了。这时需要演化聚类的绝活——比如用滑动窗口跟踪数据漂移。假设我们有个随时间变化的数据流:
% 模拟动态数据变化 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值往往是最佳选择。不过现实中的数据可能像抽象画,根本找不到明显拐点,这时候就需要领域知识和业务理解来助攻了。
数据聚类的魅力在于,它让机器拥有了发现模式的火眼金睛。从静态画像到动态追踪,从规整空间到扭曲时空,每一次算法的调整都像是在调整显微镜的焦距,直到混沌中浮现出隐藏的秩序。下次当你在数据海洋中迷航时,不妨试试这些聚类罗盘,说不定就能找到新大陆的坐标。