K均值聚类的理解和实现
目錄
1. 距離的測度
1.1 歐式距離
1.2 馬氏距離
1.2.1 利用馬氏距離對數據進行歸一化
1.2.2 利用馬氏距離進行分類
2. K均值的基本理論
2.1 K均值的原理和實現
2.2 K均值的缺點
2.3 K均值改進
3. 算法實現
3.1 獲取樣本
3.2 協方差逆陣方根的計算方法
3.3 聚類實驗
3.3.1 一般的K均值聚類
3.3.2 基于馬氏距離K-means++聚類
3.3.3 基于肘部法則的K-means++聚類
4.參考資料
1. 距離測度
1.1 歐式距離
在數學中,歐氏距離或歐幾里德度量是歐幾里得空間中兩點之間的“普通” 直線距離。通過這個距離,歐幾里德空間成為度量空間。相關的規范稱為歐幾里得范數。較早的文獻將度量指為畢達哥拉斯度量。廣義的歐幾里得范數項是L2范數或L2距離。
通常,對于n維空間來說,歐幾里得距離可以表示為:
中的歐式距離如圖1.1-1所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? 圖1.1-1?中歐幾里得距離的表達
標準的歐幾里德距離可以是平方的,以便逐漸將更大的重量放置在更遠的物體上。在這種情況下,等式變為:
? ? ? ? ? ? ? ? ? ? ? ? ? ?
平方歐幾里德距離不是一個度量,因為它不滿足三角不等式 ;?然而,它經常用于僅需要比較距離的優化問題。
它在理性三角學領域也被稱為quadrance。
1.2 馬氏距離
馬氏距離是對點P和分布D的距離度量。馬氏距離對多維數據進行了歸一化,并測量了P點相對于D的平均值的標準差。如果P在D分布中心,那么馬氏距離為0。如果對數據進行主成分分析,如圖1.2-1所示,那么,當P相對于主軸越遠,馬氏距離的數值也就隨之增長。當我們對主軸進行進行歸一化后,馬氏距離也就等同于在歐式空間的仿射變換。因此,馬氏距離具有“無單位”和“尺度不變性”的特性,并且考慮了數據集的相關性。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? 圖1.2-1 數據的主成分分析
馬哈拉諾比斯觀察距離 :
?從一組帶有均值的觀察中得出:
那么,觀察值與集合的距離使用協方差矩陣S表示為:
集合中兩個隨機變量的距離為:
如果協方差矩陣是單位矩陣,則馬哈拉諾比斯距離減小到歐幾里得距離。如果協方差矩陣是對角矩陣,那么得到的距離度量稱為標準歐式距離:
其中,表示變量的標準差。
1.2.1 利用馬氏距離進行數據歸一化
如圖1.2.1-1所示,當數據在空間中以非常不對稱的形式進行分布時,k-means算法總是試圖挖掘出一些與聚類相關的信息,因為k-means聚類的核心觀點在于數據是以不均勻的方式進行聚類的。然而,“不對稱”和“不均勻”之間卻有著重要的區別。例如,當數據在某個維度上分布很遠,而在其他維度上距離相對較小時,k-means必然不會收斂到好的結果。
? ? ? ?? 圖1.2.1-1 (a)原始數據的垂直距離比水平距離小 (b)對空間進行方差歸一化,數據之間的水平距離小于垂直距離
這種情況往往只是因為數據向量在不同的維度有不同的底層單位。例如,如果使用身高、年齡和學齡代表一個人,那么由于單位的不同,數據在不同的維度上必然產生不同的分布。同樣,年齡和學齡雖有著相同的單位,但是,在自然人口中也存在差異。
當我們將整個數據集作為一個整體來看待時,我們可以通過協方差矩陣來重新調整整個數據集,這種技術也稱之為數據歸一化。
傳統意義上,馬氏距離是以該點在特定的方向上的方差來度量數據點與分布之間的距離。我們使用分布的協方差的倒數來計算馬氏距離:
其中, 表示數學期望,馬氏距離可表示為:
對此有以下兩種方式來解決:在K-means算法中使用馬氏距離而不是歐式距離,或對數據進行尺度變化,然后在放縮的空間中使用歐幾里得距離。第一種方法更為直觀,但第二種方法更容易計算,因為數據轉化是線性的。
數據集的轉化:
在這里,是即將使用的一組新的數據向量,D是原始數據。的因子是逆協方差的平方根。
1.2.2 利用馬氏距離分類
利用K-means聚類或者任何其他的方法給定一組聚類標簽,利用這些標簽可以預測一些新的數據點最可能屬于哪個聚類。假設聚類本身服從高斯分布,將馬氏距離的概念引進該分類問題也是非常有意義的。
分類方法:對每個聚類計算平均協方差,這樣我們可以基于協方差來計算新的數據點到每個聚類中心的馬氏距離,從而確定分類。
但是,是不是說具有最小馬氏距離的類是最優的解呢?
事實并非如此,當且僅當所有的聚類都擁有相同數量的數據點時(每個集群內的數據點的先驗概率大小相等),此結論才成立。
貝葉斯規則簡明扼要的說了這一區別:
對于給定的命題A和命題B,給定B的情況下,A發生的概率一般來說并不等于給定A的情況下,B發生的概率:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
相反,給定A的情況下B發生的概率,乘以A的概率必然等于給定B的情況下A發生的概率誠意B發生的概率:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
同樣的道理,馬氏距離表示的是一個特定樣本來自特定聚類的概率,事實上,我們想知道的是:給定變量x,它出現在某一特定類中的概率。顯然,馬氏距離告訴我們的結論恰恰是相反的。例如,對于給定的變量x出現在C類的概率:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
其中,表示聚類的數據量和整體數據里。
也就是說,為了比較不同聚類的馬氏聚類,我們必須考慮聚類的大小。考慮到每個聚類的概率服從高斯分布,聚類之間的似然性可以表示如下:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
這就好比說,發現一只看似恐龍的蜥蜴屬于恐龍的概率遠遠小于它屬于蜥蜴的概率,正是因為兩個集群的先驗概率是不同的。
2. K均值的基本理論
2.1 K均值的基本原理和實現
K-means嘗試尋找數據的自然類別,用戶設置類別的個數,從而尋找到好的類別中心。
算法的流程如下:
1.輸入數據集合和類別數K;
2.隨機分配類別中心點的位置;
3.將每個點放入離它最近的類別中心點所在的集合;
4.移動類別中心點到它所在的集合;
5.轉到第3步,直至收斂。
K均值的迭代示意圖如圖2.1-1所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖2.1-1 K均值的迭代示意圖
2.2 K均值的缺點
K-means是一個極其高效的聚類算法,但是它存在以下三個問題:
1.它不能保證定位到聚類中心的最佳方案,但能保證收斂到某個解決方案;
2.K-means無法指出應該使用多少個類別。在同一數據集中,選擇不同的類別得到的結果是不一樣的,甚至是不合理的;
3.K-means假定空間的協方差矩陣不會影響到結果。
2.3 K-means的改進
1.多進行幾次聚類,每次初始化的聚類中心不一樣,最后選擇方差最小的那個結果;
2.首先將類別設置為1,然后提高類別數(到達某個上限),一般情況下,總方差會快速下降直到達到某個拐點,這意味著再加一個新的聚類中心也不會顯著減少總體方差。在拐點處停止,保存此時的類別數;
3.將數據乘以逆協方差矩陣進行數據的歸一化。
3.算法實現
3.1獲取樣本
基本思路:利用隨機數RNG函數算子對矩陣進行填充,這里采用了正態分布生成點集。
實現代碼:
//定義實驗允許的最大類別數const int MAX_CLUSTERS = 5;//定義數據點的顏色Scalar colorTab[] = { Scalar(0,0,255),Scalar(0,255,0),Scalar(255,100,100),Scalar(255,0,255),Scalar(0,255,255)};//基于均勻分布生成隨機的K值和樣本數量RNG rng(12345);int k=rng.uniform(2, MAX_CLUSTERS);int sampleCount = rng.uniform(100,1000);Mat points(sampleCount, 1, CV_32FC2);Mat lables;Mat centers(k,1,CV_32FC1);Mat img(500, 500,CV_8UC3);//基于初始化的K值和sampleCount生成點集for (int i = 0; i < k; i++){//指定ROIMat pointChunk = points.rowRange(i*sampleCount/k,i==k-1?sampleCount:(i+1)*sampleCount/k);//基于正態分布填充ROIrng.fill(pointChunk,RNG::NORMAL,Scalar(rng.uniform(0,img.cols),rng.uniform(0,img.rows)),Scalar(img.cols*0.05, img.rows*0.05));}//打亂點的生成順序randShuffle(points,1,&rng);3.2 協方差逆陣平方根的計算方法
傳統意義上,馬氏距離是以該點在特定的方向上的方差來度量數據點與分布之間的距離。我們使用分布的協方差的倒數來計算馬氏距離:
其中, 表示數學期望,馬氏距離可表示為:
對此有以下兩種方式來解決:在K-means算法中使用馬氏距離而不是歐式距離,或對數據進行尺度變化,然后在放縮的空間中使用歐幾里得距離。第一種方法更為直觀,但第二種方法更容易計算,因為數據轉化是線性的。
數據集的轉化:
在這里,是即將使用的一組新的數據向量,D是原始數據。的因子是逆協方差的平方根。
那么如何求解呢?
由于協方差矩陣是對稱矩陣,基于特殊矩陣而言采用特征值分解的方法可以更準確的解出逆矩陣,現在我們假設已經求出,且有:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
由線性代數的基本理論知道,計算矩陣的冪的基本思想是對矩陣進行對角化,即:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
當n=2時,只需要對特征向量計算平方,隨后反乘特征向量S即可,計算代碼如下:
//矩陣對角化計算A的平方Mat A = (Mat_<float>(4,4)<<0,1,1,-1,1,0,-1,1,1,-1,0,1,-1,1,1,0);Mat value;Mat vector;bool M=eigen(A,value,vector);Mat eigenValueMat= Mat::zeros(Size(value.rows,value.rows),CV_32FC1);value.convertTo(value,CV_32FC1);vector.convertTo(vector,CV_32FC1);for (int i = 0; i < value.rows; i++){eigenValueMat.at<float>(i, i) = pow(value.at<float>(i),2);}Mat eigenVectorMat;transpose(vector, eigenVectorMat);//對角化方法計算的解Mat A_1 = eigenVectorMat*eigenValueMat* eigenVectorMat.inv();//理論解Mat A_2 = A*A;//誤差Mat error = A_1 - A_2;最終的誤差矩陣error如圖3.2-1所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? 圖3.2-1 誤差矩陣
顯然,這個解是完全可以接受的。
3.3 聚類實驗
3.3.1 一般的K均值聚類方法
在該條件下,對數據的樣本點不加任何歸一化處理,并基于已知的K值來對3.1中的樣本進行聚類分析,實現的代碼如下:
//1.一般的均值聚類//1.1 直接調用聚類函數kmeans(points,k,lables,TermCriteria(TermCriteria::EPS | TermCriteria::EPS,10,1.0),3,KMEANS_RANDOM_CENTERS,centers);//1.2 繪制聚類結果for (int i = 0; i < sampleCount; i++){int colorIndex = lables.at<int>(i);Point pt = points.at<Point2f>(i);circle(img,pt,2,colorTab[colorIndex],-1);}聚類的結果如下圖3.3.1-1所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? 圖3.3.1-1 一般的均值聚類
3.3.2 基于馬氏距離k-means++的聚類方法的實現
首先,為了創造馬氏距離聚類的基本條件,在這里為了更好的可視化,數據采用二維矢量,但是在x和y兩個維度上的服從不同的分布,為了使數據更具可信度,我參考了《機器學習》(周志華著)原型聚類章節里面的樣本數據,如表3.3.2-1:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 表3.3.2-1 西瓜數據集
其次,在原型聚類的實例中,書中給出的最終的迭代解,如圖3.3.2-1所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖3.3.2-1 西瓜數據集均值聚類的結果
因此,為了方便進行數據實驗,不妨設置聚類數目為k=3,實驗的代碼如下:
//2.基于馬氏距離的k-means++聚類//定義實驗允許的最大類別數const int MAX_CLUSTERS = 5;//定義數據點的顏色Scalar colorTab[] = { Scalar(0,0,255),Scalar(0,255,0),Scalar(255,100,100),Scalar(255,0,255),Scalar(0,255,255) };//設置基本參數,k=3RNG rng(12345);int k = 3;Mat lables;Mat centers(k, 1, CV_32FC1);Mat img(500, 500, CV_8UC3, Scalar(0));Mat re_points = (Mat_<float>(30, 2) << 0.697,0.460,0.774,0.376,0.634,0.264,0.608,0.318,0.556,0.215,0.403,0.237,0.481,0.149,0.437,0.211,0.666,0.091,0.243,0.267,0.245,0.057,0.343,0.099,0.639,0.161,0.657,0.198,0.360,0.370,0.593,0.042,0.719,0.103,0.359,0.188,0.339,0.241,0.282,0.257,0.748,0.232,0.714,0.346,0.483,0.312,0.478,0.437,0.525,0.369,0.751,0.489,0.532,0.472,0.473,0.376,0.725,0.445,0.446,0.459);//求解協方差矩陣Mat covar;Mat mean;calcCovarMatrix(re_points, covar,mean,CV_COVAR_NORMAL | CV_COVAR_ROWS);//求解協方差的逆陣的方根Mat covar_inv;double inv=invert(covar,covar_inv,DECOMP_EIG);Mat value;Mat vector;bool M = eigen(covar_inv, value, vector);Mat eigenValueMat = Mat::zeros(Size(value.rows, value.rows), CV_32FC1);value.convertTo(value,CV_32FC1);vector.convertTo(vector, CV_32FC1);for (int i = 0; i < value.rows; i++){float tem = sqrt(value.at<float>(i, 0));eigenValueMat.at<float>(i, i) = tem;}Mat eigenVectorMat;transpose(vector, eigenVectorMat);Mat result = eigenVectorMat*eigenValueMat* eigenVectorMat.inv();//基于馬氏距離進行歸一化,重新生成點集Mat New_points = re_points*result;//聚類kmeans(New_points, k, lables, TermCriteria(TermCriteria::EPS | TermCriteria::EPS, 10, 1.0), 3, KMEANS_PP_CENTERS, centers);for (int i = 0; i < 30; i++){int colorIndex = lables.at<int>(i);int x = re_points.at<float>(i, 0) * 500;int y= 500-re_points.at<float>(i, 1) * 500;Point pt = Point(x,y);circle(img, pt, 2, colorTab[colorIndex], -1);}最后,我想說的是k-means++與k-means方法最大的不同點在于中心的初始化方法,因為對于不同的初始點,聚類的結果必然會有差異,如果初始點選擇的不好,那么極有可能陷入局部最小值下,從而得不到好的分類結果,而cv::KMEANS_PP_CENTERS選項會使cv::kmeans使用文獻"Arthur, David, and S. Vassilvitskii. "k-means++: the advantages of careful seeding"中所謂的kmeans++的方法來重新分配聚類中心。這種方法謹慎的選擇了聚類的中心起點,通常能以少于默認方法的迭代得出更好的結果。
上述代碼的結果如圖3.3.2-2所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖3.3.2-2 基于馬氏距離的kmeans++聚類結果
3.3.3 基于肘部法則的K-means++聚類
? ? ? ? 如果問題中沒有指定的值,可以通過肘部法則這一技術來估計聚類數量。肘部法則會把不同值的成本函數值畫出來。隨著值的增大,平均畸變程度會減小;每個類包含的樣本數會減少,于是樣本離其重心會更近。但是,隨著值繼續增大,平均畸變程度的改善效果會不斷減低。值增大過程中,畸變程度的改善效果下降幅度最大的位置對應的值就是肘部。
在這里,我將畸變程度定義為所有類的樣本點距該類中心的方差和。
考慮k從k=2到k=5逐步進行迭代,求解產生的畸變系數的程序如下:
//肘部法則的聚類//定義實驗允許的最大類別數const int MAX_CLUSTERS = 5;//定義數據點的顏色Scalar colorTab[] = { Scalar(0,0,255),Scalar(0,255,0),Scalar(255,100,100),Scalar(255,0,255),Scalar(0,255,255) };//設置基本參數RNG rng(12345);int k;Mat lables;Mat img(500, 500, CV_8UC3, Scalar(0));Mat points = (Mat_<float>(30, 2) << 0.697, 0.460, 0.774, 0.376,0.634, 0.264, 0.608, 0.318, 0.556,0.215, 0.403, 0.237, 0.481, 0.149, 0.437, 0.211, 0.666, 0.091, 0.243, 0.267, 0.245, 0.057, 0.343, 0.099,0.639, 0.161, 0.657, 0.198, 0.360, 0.370, 0.593, 0.042,0.719, 0.103, 0.359, 0.188, 0.339, 0.241, 0.282,0.257, 0.748, 0.232, 0.714, 0.346, 0.483, 0.312, 0.478,0.437, 0.525, 0.369, 0.751, 0.489, 0.532, 0.472,0.473, 0.376, 0.725, 0.445, 0.446, 0.459);//定義方差Mat varMat(5, 1, CV_32FC1, Scalar(0));for (int k = 2; k <= MAX_CLUSTERS; k++){Mat centers(k, 1, CV_32FC1);kmeans(points, k, lables, TermCriteria(TermCriteria::EPS | TermCriteria::EPS, 10, 1.0), 3, KMEANS_PP_CENTERS, centers);vector<float> sumVar(k, 0);//統計每類點的數量Mat lableCount(k, 1, lables.type(), Scalar(0));for (int i = 0; i < 30; i++){lableCount.at<int>(lables.at<int>(i)) += 1;}//計算方差for (int i = 0; i < 30; i++){int index = lables.at<int>(i);float x = points.at<float>(i, 0);float y = points.at<float>(i, 1);Point2f center = Point2f(centers.at<float>(index, 0), centers.at<float>(index, 1));sumVar[index] += ((x - center.x)*(x - center.x) + (y - center.y)*(y - center.y)) / lableCount.at<int>(index);}for (auto m : sumVar){varMat.at<float>(k - 2, 0) += m;}}//數據歸一化[0,500]normalize(varMat,varMat,0,400,NORM_MINMAX);//繪圖vector<Point> pts;for (int i = 0; i < varMat.rows; i++){Point tem;tem.x = i*400/4+50;tem.y = 450 - varMat.at<float>(i);pts.push_back(tem);}for (int i = 0; i < pts.size()-1;i++){circle(img,pts[i],3,colorTab[i],-1);}line(img,pts[0],pts[1],Scalar(255,255,255));line(img, pts[1], pts[2], Scalar(255, 255, 255));line(img, pts[2], pts[3], Scalar(255, 255, 255));最終的結果如圖3.3.3-1所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖3.3.3-1 基于肘部法則確定拐點
很顯然,拐點發生在k=3的地方,因此最佳的聚類結果為k=3;
4.相關資料
1.OpenCV隨機數發生器RNG:
《學習OpenCV3》:Page 157-161
2.對小矩陣使用逗號分隔式初始化函數:
《OpenCV編程入門》:Page 91
3.矩陣對角化理論、矩陣對角化Matlab實現:
https://blog.csdn.net/qq_18343569/article/details/49823441
https://blog.csdn.net/compression/article/details/49180775
4.聚類算法的一些思考:
https://blog.csdn.net/u013719780/article/details/51755124?utm_source=blogxgwz2
https://www.jianshu.com/p/95a4bcff2198
https://www.cnblogs.com/data-miner/p/6288227.html
5.原型聚類的基本理論:
《機器學習》-周志華:Page 202-211
6.維基百科:歐式距離與馬氏距離
https://en.wikipedia.org/wiki/Euclidean_distance
https://en.wikipedia.org/wiki/Mahalanobis_distance
?
?
總結
以上是生活随笔為你收集整理的K均值聚类的理解和实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【项目】区块链+人工智能 ---PAI白
- 下一篇: 漫画 | 揭密微信诞生记之民间传说