K-Means聚類演算法(二):演算法實現及其優化
忘了說了,我把圖像壓縮的代碼傳上去了,但是圖片沒辦法傳送,因為本人的GitHub抽風了(準確的說是網路抽風)
DataScienceNote/KMeans_Img.m at master · TsingJyujing/DataScienceNote · GitHub
請準備好loli.jpg文件,並且配合k_means.m一起食用~
k_means.m代碼放在了GitHub上,大家自己下載著玩:P
DataScienceNote/k_means.m at master · TsingJyujing/DataScienceNote · GitHub其次修正一下上次的說法:並不只是歐氏空間可以用這個方法,只要是對於提供了以下幾種運算元且滿足以下性質的空間都能用:
首先有個計算任意兩點距離(或者叫不相似度)的算符記作且有交換性:其次有求任意一堆點的平均值的演算法:求出以後使得:就可以了。
如果這一段有錯誤,求指出哈~這一篇依舊不會涉及數學原理,我在糾結中,因為要從頭說清楚EM演算法是一件很費工夫的事情:首先是含有隱變數(Latent Variables)的模型去估計實際的數據,然後從概率論出發,到極大似然估計確定參數和E(Expectation)步和M(Maximization)步。以上是我清楚的,還有我到現在有雲里霧裡的收斂性證明。並不是說費工夫就不寫了,逃了,而是用最少的公式和步驟說清楚和K-Means有關的部分。下一章會比較高能,說是高能,其實也就是求個偏導數,極大似然估計啥的而已……而已……Part Ⅰ 編程實戰的時候那些坑:實戰的時候是會遇到很多坑的,首先說第一個:一、如何初始化中心點事實上,初始化的不好,是會震蕩的(當然,有了後面的解決震蕩的辦法要好得多)!初始化的時候,不妨先估計一下數據的中心在哪裡,數據的尺度(叫Scale合適么)有多大,然後在生成數據的時候以中心點為中心,以尺度為因子乘以服從標準正態分布的數據。大致就是這樣的一段代碼:
group_center = mean(data_set);group_range = range(data_set);centers = (randn(K,dim).*repmat(group_range,K,1)./3+repmat(group_center,K,1));
至於為什麼要除以3,是因為這裡,一般數據不會越過。
這裡根據每一個維度分別做了Scale,使得其能充填在數據中心點附近,然而又不太遠。二、如何計算沒有「爭取」到任意一點的中心點?有的時候某些中心點太強勢,直接把點搶沒了:主要出現在點少類多的情況下。這個時候,就需要忽略這些中心點。但是中心點不調整也不好,有可能就在那個地方註定孤獨一生了。這個時候我們把這個點初始化到中心點附近的地方:centers(i,:) = (randn(1,dim).*group_range./3+group_center);
三、我怎麼知道有沒有收斂呢?
這個在書中已經說了,使用代價函數:其中:
的解釋:如果第j個數據點屬於第i類,那就記作1,否則記作0的一個 大小的矩陣。代價函數的差分值小於一定數值的時候(N次越不過最小值點)即可認為收斂了。(以下一段算是題外廢話)這裡需要補充另外兩種評價聚類演算法的方法:F-Measure和信息熵但是使用這兩個代價函數以後就不能用原來提出的迭代演算法了,因為原來的EM演算法是針對上面的那個代價函數的。關於這兩個代價函數和如何推導會在下一篇講清楚。其中:
是最後中心點的取值,是當前集合的中心點,是原來的中心點坐標。代碼中的體現就是:dumper = 0.1;...centers = (1-dumper).*centers + dumper.*lst_cnt;
centers(i,:) = sum(data_set(cls_idx,:).*repmat(weight(cls_idx),1,dim))... ./sum(weight(cls_idx));
[N,~]=size(data_set )
dist_list = sqrt(sum((data_set - repmat(pnt,N,1)).^2,2))
for i = 1:K %求到每一個中心的距離向量,且不用張量求更加節約空間 dist_vec(:,i) = sqrt(sum((data_set - repmat(centers(i,:),N,1)).^2,2)); end
[~,cls_vec] = min(dist_vec,[],2);
rnk = zeros(k,N);rnk = rnk((0N-1)).*k+cls_vec)";
not_empty_cls = unique(cls_vec);k_means_result = cell(length(not_empty_cls),1);for i = 1:length(not_empty_cls) cls = not_empty_cls(i); sub_res = cell(3,1); cls_idx = find(cls_vec==cls); sub_res{1} = centers(cls,:); sub_res{3} = cls_idx; sub_res{2} = data_set(cls_idx,:); k_means_result{i} = sub_res;end
首先看一下有那幾類不空的,好、假設有N類。
然後生成一個的Cell向量,每一個Cell裡面放3個Cell:1,中心點,2,屬於這一類的原始數據,3,剛才那些數據在輸入數據里的Index。這樣的好處是:信息比較全面,調用方便。缺點是不夠直觀么,層次複雜。你只要開心可以兩種都返回,沒人攔著你(就好像我代碼里示範的那樣)推薦閱讀:
※對比傳統K-Means等聚類演算法,LDA主題模型在文本聚類上有何優缺點?
※海量數據的聚類通常如何做?
※哪種聚類演算法可以不需要指定聚類的個數,而且可以生成聚類的規則?
※如何根據每個策略的 daily return 對不同策略進行最為有效的分類?