K均值算法
上一期介紹了機器學習中的監督式學習,並用了離散回歸與神經網絡模型算法來解決手寫數字的識別問題。今天我們介紹一種機器學習中的非監督式學習算法——K均值算法。
所謂非監督式學習,是一種與監督式學習相對的算法歸類,是指樣本並沒有一個與之對應的“標簽”。例如上一期中的識別手寫數字照片的例子,樣本是照片的像素數據,而標簽則是照片代表的數字。非監督式學習因為沒有這個標簽,因此就沒有對樣本的一個准確的“答案”。非監督式學習主要是用來解決樣本的聚類問題。
K均值算法是一種迭代求解的聚類分析算法,其步驟為:
- 隨機選取K個對象作為初始的聚類中心
- 計算每個對象與各個聚類中心之間的距離
- 把每個對象分配給距離它最近的聚類中心
- 將同一聚類中的對象的平均值作為新的聚類中心
- 從第2步開始循環,直到聚類中心不再變化
代碼實現
隨機初始化
% 樣本隨機排列
randidx = randperm(size(X, 1));
% 取前K個隨機樣本作為聚類中心
centroids = X(randidx(1:K), :);
其中X為樣本矩陣,每一行為一個樣本,每一列為樣本的維度,centroids就是隨機出的K個作為聚類中心的樣本矩陣。
分配聚類中心
% 遍歷所有樣本
for i = 1:size(X, 1)
distance = 9999999999;
for j = 1:K
% 距離公式:A(a1, a2)與B(b1, b2)之間的距離為根號下(a1-b1)^2 + (a2 - b2)^2
dist = sum((X(i, :) - centroids(j, :)) .^ 2);
if dist < distance
idx(i) = j;
distance = dist;
end
endfor
endfor
通過兩層循環,第一層為遍歷所有樣本,找到每個樣本所屬的聚類中心。第二層循環為遍歷所有聚類中心,計算樣本與聚類中心的距離,找出與類聚中心最小的距離,將該類聚中心作為樣本的類聚中心。
更新聚類中心
- 方式一:循環樣本
count = zeros(K, 1);
for i = 1 : m
id = idx(i);
% 累加該聚類中心下的所有樣本
centroids(id, :) = centroids(id, :) + X(i, :);
count(id) = count(id) + 1;
endfor
% 均值作為新的聚類中心
centroids = centroids ./ count;
- 方式二:循環類聚中心(效率更高)
for i = 1:K
% 直接通過mean函數計算均值
centroids(i,:) = mean(X(idx==i,:),1);
end
可以看到,通過matlab內置的mean函數,可以更加便捷的計算出矩陣對應行的均值,並且計算過程也會比累加的方式執行更加高效。
步驟演示
如上圖所示,有一些無標簽的二維坐標點。通過一些基本的判斷,我們大致可以將上述的數據分為左上、中右部、左下三個聚類,但是有些中間位置的點可能不太好划分,如下圖所示。
但是如何才能讓機器為我們做出分類呢?機器又如何決策這些處於中間位置的點呢?於是我們來運行上述已經寫好的K均值算法。
如圖所示,是運行了K均值算法的第一步:隨機初始化后的聚類。可以看到圖中的綠色、藍色、紅色分布,因為聚類中心點是隨機的,初始的聚類中心並不正確,於是我們繼續運行K均值算法的后面步驟。
以綠色的聚類來舉例,綠色的聚類通過一次均值計算之后,新的聚類中心點上升了許多。根據新的聚類中心點,再計算樣本歸屬時,則會判斷出下方的點已經不再屬於綠色,而屬於藍色,因此就對之前的聚類進行了修正。
再看之前比較有疑問的綠色聚類與紅色聚類之間的中間點(灰色框中所示),這幾個點在感官上距離兩個聚類都差不多,但根據與聚類中心點的計算對比,從數值上計算得出這幾個點實際上屬於紅色聚類。
進行多次循環之后,聚類中心點不再變化,則說明K均值算法應用成功,分出了3個聚類並用不同顏色表示。
圖片壓縮
我們知道,圖片是由許多像素點組成的,而每個像素點需要有RGB值。所謂RGB值是一種顏色標准,是通過對紅(R)、綠(G)、藍(B)三個顏色通道的變化以及它們相互之間的疊加來得到各式各樣的顏色的。每個顏色的強度值是0-255,剛好是1個字節的大小。而3個顏色就是3個字節的大小,所以每個像素點所占大小為3字節,即24比特(故又稱24位真彩色圖,有1677萬多色)。
以一張700*700像素的圖為例,一共有490,000個像素點,每個像素點占24比特,合計11,760,000比特。如果能夠應用K均值算法來實現圖片的壓縮呢?
壓縮的前提數據有冗余,或者近似冗余。一張圖片中數十萬的像素點,肯定有許多像素點的RGB值是比較相似的,即圖片中可能有某些位置都是同一個顏色。那么就可以應用K均值算法,將圖片分成許多個聚類,用一張聚類表來存儲每個聚類的顏色值,再用一張像素表來存儲每個像素所在的聚類,就可以達到壓縮的目的。
我選擇了K=16,即有16個聚類,則原圖片從1677萬色變成了16色。聚類表占用的大小為16 * 24bit,即384比特。16個聚類是2的4次方只需要4個比特就可以存儲聚類信息,則像素表的大小為490,000 * 4,即1,960,000。總大小為1,960,000+384=1,960,384比特,通過計算可得(壓縮后大小/壓縮前大小),壓縮率為16.67%。即如果是100KB的圖片,壓縮后的大小只有16.67KB。
圖片數據處理
% 調用imread讀取圖片
A = double(imread('test.png'));
% 將像素值處理到0-1
A = A / 255;
% 將數據從3維降到2維
img_size = size(A);
X = reshape(A, img_size(1) * img_size(2), 3);
獲取聚類中心
% 隨機初始化
initial_centroids = kMeansInitCentroids(X, K);
% 調用之前寫好的K均值算法
[centroids, idx] = runkMeans(X, initial_centroids, max_iters);
壓縮
% 調用之前寫好的“分配聚類中心”
idx = findClosestCentroids(X, centroids);
解壓縮
% 通過聚類表獲取原始像素值
X_recovered = centroids(idx,:);
% 將數據升至3維
X_recovered = reshape(X_recovered, img_size(1), img_size(2), 3);
壓縮效果展示
可以看到,壓縮后的圖片仍然具有較清晰的輪廓,只是色彩沒有那么豐富(只有16種顏色),某些細節有一些失真,但是不影響圖片的整體表達。
總結
- K均值算法是最常用也是最簡單的非監督式學習聚類算法
- 第一步的隨機聚類中心對聚類結果有一定影響,因此可以運行多次以達到比較好的聚類結果
- 將聚類算法的思想作用在圖片上,可以達到圖片壓縮的目的。
- 如果想要有更好的圖片質量,可以提高聚類數量K,但這樣會得到較差的壓縮率,需要在實踐中以平衡。