1. 程式人生 > >關於聚類演算法Kmeans/K-mediods/層次聚類/OPTICS較為詳細的介紹

關於聚類演算法Kmeans/K-mediods/層次聚類/OPTICS較為詳細的介紹

 基於約束的聚類:現實應用中可能需要在各種條件下進行聚類。因為同一個聚類演算法,在不同的應用場景中所帶來的聚類結果也是各異的,因此找到滿足特定約束的具有良好聚類特性的資料分組是十分有挑戰性的。

9)  可解釋性和可用性:我們希望得到的聚類結果都能用特定的語義、知識進行解釋,和實際的應用場景相聯絡。

一般情況,聚類演算法可以劃分為以下幾類:劃分方法(partitioning method)、層次方法(hierarchical methods)、基於密度的方法(density-based methods)、基於網格的方法(grid-based methods)、基於模型的方法(model-based methods

.k-means演算法屬於劃分方法中的一種。

K-means演算法的整個流程:首先從聚類物件中隨機選出K個物件作為類簇的質心(當然了,初始引數的K代表聚類結果的類簇數),對剩餘的每個物件,根據它們分別到這個K個質心的距離,將它們指定到最相似的簇(因為K-means是利用距離來量化相似度的,所以我們這裡可以理解為是“將它們指定到離最近最近距離的質心所屬類簇”)。然後重新計算質心位置。以上過程不斷反覆,直到準則函式收斂為止。通常採用平方誤差準則,定義如下:

其中,E代表的意思是所有類簇中各物件到其所屬類簇質點平方誤差和.

K:聚類結果類簇個數

Ci:i個類簇

P:類簇中聚類物件

mi:

i個類簇的質心

K-means的優點和不足:能處理大型資料集,結果簇相當緊湊,並且簇和簇之間明顯分離。計算複雜性O(tkn) t:迭代次數、K :聚類數 n:樣本數;但是

1)該演算法必須事先給定類簇數和質點,簇數和質點的初始值設定往往會對聚類的演算法影響較大。

2 ) 通常會在獲得一個區域性最優值時停止,

3 ) 並且只適合對數值型資料聚類,

4) 只適用於聚類結果為凸形的資料集,K-means方法不適合發現非凸面形狀的類簇,或者大小差別很大的簇。

5) 噪音和孤立點資料敏感,少量的該類資料對質點的計算會產生極大的影響。

關於K-means的程式碼實現網上有很多。

K

中心點演算法(K-medoids

前面介紹了k-means演算法,並列舉了該演算法的缺點。而K中心點演算法(K-medoids)正好能解決k-means演算法中的 “噪聲”敏感這個問題。

如何解決的呢?

首先,我們得介紹下k-means演算法為什麼會對“噪聲”敏感。還記得K-means尋找質點的過程嗎?對某類簇中所有的樣本點維度求平均值,即獲得該類簇質點的維度。當聚類的樣本點中有“噪聲”(離群點)時,在計算類簇質點的過程中會受到噪聲異常維度的干擾,造成所得質點和實際質點位置偏差過大,從而使類簇發生“畸變”。

Eg: 類簇C1中已經包含點A(1,1)B(2,2) C(1,2) D(2,1) 假設N(100,100)為異常點,當它納入類簇C1時,計算質點Centroid((1+2+1+2+100)/5,(1+2+2+1+100)/5)=centroid(21,21),此時可能造成了類簇C1質點的偏移,在下一輪迭代重新劃分樣本點的時候,將大量不屬於類簇C1的樣本點納入,因此得到不準確的聚類結果。

為了解決該問題,K中心點演算法(K-medoids)提出了新的質點選取方式,而不是簡單像k-means演算法採用均值計演算法。在K中心點演算法中,每次迭代後的質點都是從聚類的樣本點中選取,而選取的標準就是當該樣本點成為新的質點後能提高類簇的聚類質量,使得類簇更緊湊。該演算法使用絕對誤差標準來定義一個類簇的緊湊程度。

聚類分析(三) <wbr>K中心點演算法(k-mediods)  (p是空間中的樣本點,Oj是類簇Cj的質點)

如果某樣本點成為質點後,絕對誤差能小於原質點所造成的絕對誤差,那麼K中心點演算法認為該樣本點是可以取代原質點的,在一次迭代重計算類簇質點的時候,我們選擇絕對誤差最小的那個樣本點成為新的質點。

Eg:樣本點A –>E1=10

樣本點B –>E2=11

樣本點C –>E3=12

原質點O–>E4=13,那我們選舉A作為類簇的新質點。

K-means演算法一樣,K-medoids也是採用歐幾里得距離來衡量某個樣本點到底是屬於哪個類簇。終止條件是,當所有的類簇的質點都不在發生變化時,即認為聚類結束。

該演算法除了改善K-means的“噪聲”敏感以後,其他缺點和K-means一致,並且由於採用新的質點計算規則,也使得演算法的時間複雜度上升:Ok(n-k)2

Java實現程式碼如下:

package com.kmedoids;
import java.util.ArrayList;
public class Cluster {
    private String clusterName; // 類簇名
    private Medoid medoid; // 類簇的質點
    private ArrayList<DataPoint> dataPoints; // 類簇中各樣本點

    public Cluster(String clusterName) {
        this.clusterName = clusterName;
        this.medoid = null; // will be set by calling setCentroid()
        dataPoints = new ArrayList<DataPoint>();
    }

    public void setMedoid(Medoid c) {
        medoid = c;
    }

    public Medoid getMedoid() {
        return medoid;
    }

   
    public void addDataPoint(DataPoint dp) { // called from CAInstance
        dp.setCluster(this);// 標註該類簇屬於某點,計算歐式距離
        this.dataPoints.add(dp);
    }

    public void removeDataPoint(DataPoint dp) {
        this.dataPoints.remove(dp);
    }

    public int getNumDataPoints() {
        return this.dataPoints.size();
    }

    public DataPoint getDataPoint(int pos) {
        return (DataPoint) this.dataPoints.get(pos);
    }


    public String getName() {
        return this.clusterName;
    }

    public ArrayList<DataPoint> getDataPoints() {
        return this.dataPoints;
    }
}

------------------------------------

package com.kmedoids;
import java.util.ArrayList;

public class DataPoint {
    private double dimension[]; //樣本點的維度
    private String pointName; //樣本點名字
    private Cluster cluster; //類簇
    private double euDt;//樣本點到質點的距離

    public DataPoint(double dimension[], String pointName) {
        this.dimension = dimension;
        this.pointName = pointName;
        this.cluster = null;
    }

    public void setCluster(Cluster cluster) {
        this.cluster = cluster;
    }


   
    public double calEuclideanDistanceSum() {
        double sum=0.0;
        Cluster cluster=this.getCluster();
        ArrayList<DataPoint> dataPoints=cluster.getDataPoints();

        for(int i=0;i<dataPoints.size();i++){
            double[] dims=dataPoints.get(i).getDimensioin();
            for(int j=0;j<dims.length;j++){
                 double temp=Math.pow((dims[j]-this.dimension[j]),2);
                 sum=sum+temp;
            }
        }

        return Math.sqrt(sum);
    }

   
    public double testEuclideanDistance(Medoid c) {
        double sum=0.0;
        double[] cDim=c.getDimensioin();

        for(int i=0;i<dimension.length;i++){
           double temp=Math.pow((dimension[i]-cDim[i]),2);
           sum=sum+temp;
        }

        return Math.sqrt(sum);