1. 程式人生 > >聚類分析之dbscan

聚類分析之dbscan

演算法概念解析

DBSCAN:一種基於高密度連通區域的基於密度的聚類方法,該演算法將具有足夠高密度的區域劃分為簇,並在具有噪聲的空間資料庫中發現任意形狀的簇。它將簇定義為密度相連的點的最大集合;為了理解基於密度聚類的思想,首先要掌握以下幾個定義:

給定物件半徑r內的鄰域稱為該物件的r鄰域;

如果物件的r鄰域至少包含最小數目MinPts的物件,則稱該物件為核心物件;

給定一個物件集合D,如果p在q的r鄰域內,並且q是一個核心物件,則我們說物件p從物件q出發是直接密度可達的;

    如果存在一個物件鏈p1,p2,p3,...,pn,p1=q,pn=p,對於pi屬於D,i屬於1~n,p(i+1)是從pi關於r和MinPts直接密度可達的,則物件p是從物件q關於r和MinPts密度可達的;

    如果存在物件o屬於D,使物件p和q都是從o關於r和MinPts密度可達的,那麼對於物件p到q是關於r和MinPts密度相連的;密度可達是直接密度可達的傳遞閉包,這種關係非對稱,只有核心物件之間相互密度可達;密度相連是一種對稱的關係;

    有了以上的概念接下來就是演算法描述了:DBSCAN通過檢查資料庫中每點的r鄰域來搜尋簇。如果點p的r鄰域包含的點多餘MinPts個,則建立一個以p為核心物件的新簇。然後,DBSCAN迭代的聚集從這些核心物件直接密度可達的物件,這個過程可能涉及一些密度可達簇的合併。當沒有新的點可以新增到任何簇時,該過程結束;

具體的虛擬碼描述如下(摘自維基百科):

DBSCAN(D, eps, MinPts)
   C = 0                                          //類別標示
   for each unvisited point P in dataset D        //遍歷
      mark P as visited                           //已經訪問
      NeighborPts = regionQuery(P, eps)           //計算這個點的鄰域    
      if sizeof(NeighborPts) < MinPts             //不能作為核心點
         mark P as NOISE                          //標記為噪音資料
      else                                        //作為核心點,根據該點建立一個類別
         C = next cluster
         expandCluster(P, NeighborPts, C, eps, MinPts)    //根據該核心點擴充套件類別
          
expandCluster(P, NeighborPts, C, eps, MinPts)
   add P to cluster C                                     //擴充套件類別,核心點先加入
   for each point P' in NeighborPts                       //然後針對核心店鄰域內的點,如果該點沒有被訪問,
      if P' is not visited
         mark P' as visited                               //進行訪問
         NeighborPts' = regionQuery(P', eps)              //如果該點為核心點,則擴充該類別
         if sizeof(NeighborPts') >= MinPts
            NeighborPts = NeighborPts joined with NeighborPts'
      if P' is not yet member of any cluster              //如果鄰域內點不是核心點,並且無類別,比如噪音資料,則加入此類別
         add P' to cluster C
          
regionQuery(P, eps)                                       //計算鄰域
   return all points within P's eps-neighborhood

現實場景應用

場景描述:例如活動送一些禮品套件,惡意黑產來利用這個活動頻繁刷取這些套件,我們需要智慧識別出這些黑產使用者,將其和正常使用者區別開來。

問題分析:觀察這些惡意刷取套件的使用者id存在高密度聚合,那我們可以採取時間作為x軸,uid作為y軸建立一個二維空間資料集合,資料集合如下所示:

圖中所示的點都是惡意使用者的資料點集合,他們的特點從y座標軸可以看出上下相差幅度不到5000,所以在5000之內是一個高密度聚合的特點。我們採用dbscan來進行處理。

dbscan線上實時運算架構

整個實現可以分為三個關鍵模組:實時運算、非同步資料採集、資料聚類及模型輸出。

由於資料量較大的情況下線上實時聚類效率較低,1.5W資料實時聚類需要10s,考慮到實時聚類的時效性,可以採取非同步聚類,實時拖取模型線上運算的模式。

實現

流程圖

這裡實現和標準的地方有如下不一樣的點:

1、  系統關注每個簇的中心節點,會記錄下中心節點;

2、系統設定每個簇的大小不是依據擴充套件查詢得到的,而是直接使用本節點的鄰域值大小來設定,這點原因是由於擴充套件查詢會忽略掉其它簇依據被訪問新增節點,所以會出現一個鄰域查詢到節點為n,而實際擴充套件查詢之後只有m(其中m<n),由於後續的線上聚類是關注每個簇的實際大小,所以這裡直接設定鄰域查詢的大小。

3、線上實時運算只需要將一個線上點和所有簇的中心節點計算歐式距離,找出一個歐式距離最小的簇作為該節點所在的簇,進而計算本次節點所在聚類的簇群大小。

4、非同步聚類的時候由於資料集合我們關注的是賬戶id這項,所以採取id作為x軸,而二位平面的座標點的y軸我們採取隨機值實現(小於1)這樣聚類的演算法就完全關注x軸,最終結果也會依據x軸進行聚類,這樣處理對關注點的聚類效果比較好。

關鍵程式碼實現

Dbscan聚類
for (final Clusterable instance : instances) {
			if (visited.get(instance) != null) {
				continue;
			}
			final List<Clusterable> neighbors = (List<Clusterable>) getNeighbors(instance, instances);
			if (neighbors.size() - 1 >= minPts) {
				final Cluster<Clusterable> cluster = new Cluster<Clusterable>();
				expandCluster(cluster, instance, neighbors,
						instances, visited);
				final SimpleCluster<Clusterable> simpleCluster = new SimpleCluster<Clusterable>();
				simpleCluster.setCenterInstance(instance);
				simpleCluster.setClusterSize(neighbors.size());
				clusters.add(simpleCluster);
			} else {
				visited.put(instance, InstanceStatus.NOISE);
				noiseSize++;
			}
Dbscan線上分類
DBscanResult dBscanResult = new DBscanResult();
		double minDistans = 0;
		double pointCliusterSize = 0;
		for (SimpleCluster<Clusterable> cluster : clusters) {
			if(!cluster.isNoise()){
				double distans = getDistanceMeasure().compute(instances.getFeatures(), cluster.getCenterInstance().getFeatures());
				if(distans <= this.eps && (minDistans == 0 || distans < minDistans)){
					pointCliusterSize = cluster.getClusterSize();
					minDistans = distans;
				}
			}
		}
		
		double result = 1;
		result = 1 - (pointCliusterSize / instanceSize);
		dBscanResult.setResult(result);

結果

Y軸為(1-所在簇大小佔總聚類數的比例值),也就是說密度越高的資料Y值就越小,正常使用者的Y軸是接近1.0,而惡意使用者的Y軸都分佈在0.6以下。