DBSCAN算法是一種很典型的密度聚類法,它與K-means等只能對凸樣本集進行聚類的算法不同,它也可以處理非凸集。
關於DBSCAN算法的原理,筆者覺得下面這篇寫的甚是清楚練達,推薦大家閱讀:
https://www.cnblogs.com/pinard/p/6208966.html
DBSCAN的主要優點有:
1) 可以對任意形狀的稠密數據集進行聚類,相對的,K-Means之類的聚類算法一般只適用於凸數據集。
2) 可以在聚類的同時發現異常點,對數據集中的異常點不敏感。
3) 聚類結果沒有偏倚,相對的,K-Means之類的聚類算法初始值對聚類結果有很大影響。
DBSCAN的主要缺點有:
1)如果樣本集的密度不均勻、聚類間距差相差很大時,聚類質量較差,這時用DBSCAN聚類一般不適合。
2) 如果樣本集較大時,聚類收斂時間較長,此時可以對搜索最近鄰時建立的KD樹或者球樹進行規模限制來改進。
3) 調參相對於傳統的K-Means之類的聚類算法稍復雜,主要需要對距離閾值ϵ,鄰域樣本數閾值MinPts聯合調參,不同的參數組合對最后的聚類效果有較大影響。
R中的fpc包中封裝了dbscan(data,eps,MinPts),其中data為待聚類的數據集,eps為距離閾值ϵ,MinPts為樣本數閾值,這三個是必須設置的參數,無缺省項。
一、三種聚類算法在非凸樣本集上的性能表現
下面我們以正弦函數為材料構造非凸樣本集,分別使用DBSCAN、K-means、K-medoids算法進行聚類,並繪制最終的聚類效果圖:
library(fpc) library(cluster) #構造非凸樣本集 x1 <- seq(0,pi,0.01) y1 <- sin(x1)+0.06*rnorm(length(x1)) y2 <- sin(x1)+0.06*rnorm(length(x1))+0.6 plot(x1,y1,ylim=c(0,2.0)) points(x1,y2) c1 <- c(x1,x1) c2 <- c(y1,y2) data1 <- as.matrix(cbind(c1,c2))
構造的樣本集如下:
接着我們依次使用上述三種聚類算法:
#分別繪制三種聚類算法的聚類效果圖 par(mfrow=c(1,3)) #DBSCAN聚類法 db <- dbscan(data1,eps=0.2,MinPts = 5) db$cluster plot(data1,col=db$cluster) title('DBSCAN Cluster') #K-means聚類法 km <- kmeans(data1,centers=2) km$cluster plot(data1,col=km$cluster) title('K-means Cluster') #K-medoids聚類法 pm <- pam(data1,k=2) pm$clustering plot(data1,col=pm$clustering) title('K-medoids Cluster')
具體的聚類效果如下:
可以看出,在對非凸樣本集的聚類上,DBSCAN效果非常好,而另外兩種專門處理凸集的聚類算法就遇到了麻煩。
二、DBSCAN算法在常規凸樣本集上的表現
上面我們研究了DBSCAN算法在非凸樣本集上的表現,比K-means和K-medoids明顯優秀很多,下面我們構造一個10維的凸樣本集,具體的代碼和聚類結果如下:
> library(fpc) > library(Rtsne) > > #創建待聚類數據集 > data1 <- matrix(rnorm(10000,0,0.6),nrow=1000) > data2 <- matrix(rnorm(10000,1,0.6),nrow=1000) > > data <- rbind(data1,data2) > > #對原高維數據集進行降維 > tsne <- Rtsne(data) > > par(mfrow=c(4,4)) > for(i in 1:16){ + #進行DBSCAN聚類 + db <- dbscan(data,eps=1.1+i*0.025,MinPts = 25) + #繪制聚類效果圖 + plot(tsne$Y[,1],tsne$Y[,2],col=db$cluster) + title(paste('eps=',as.character(1.1+i*0.025),sep='')) + print(paste('eps=',as.character(1.1+i*0.025))) + print(table(db$cluster)) + } [1] "eps= 1.125" 0 2000 [1] "eps= 1.15" 0 1 2 1950 26 24 [1] "eps= 1.175" 0 1 2 1920 59 21 [1] "eps= 1.2" 0 1 2 1834 120 46 [1] "eps= 1.225" 0 1 2 1682 177 141 [1] "eps= 1.25" 0 1 2 1515 250 235 [1] "eps= 1.275" 0 1 2 1305 344 351 [1] "eps= 1.3" 0 1 2 1163 425 412 [1] "eps= 1.325" 0 1 2 989 521 490 [1] "eps= 1.35" 0 1 2 854 596 550 [1] "eps= 1.375" 0 1 2 707 670 623 [1] "eps= 1.4" 0 1 2 572 732 696 [1] "eps= 1.425" 0 1 2 500 766 734 [1] "eps= 1.45" 0 1 420 1580 [1] "eps= 1.475" 0 1 355 1645 [1] "eps= 1.5" 0 1 285 1715
可以看出,DBSCAN雖然性能優越,但是涉及到有些麻煩的調參數的過程,需要進行很多次的試探,沒有K-means和K-medoids來的方便快捷。
Python
在Python中,DBSCAN算法集成在sklearn.cluster中,我們利用datasets構造兩個非凸集和一個凸集,效果如下:
from sklearn import datasets import numpy as np import matplotlib.pyplot as plt from matplotlib.pyplot import style from sklearn.cluster import KMeans,DBSCAN style.use('ggplot') '''構造樣本集''' X1, y1=datasets.make_circles(n_samples=5000, factor=.6,noise=.05) X2, y2 = datasets.make_blobs(n_samples=1000, n_features=2, centers=[[1.2,1.2]], cluster_std=[[.1]],random_state=9) X = np.concatenate((X1, X2)) plt.scatter(X[:, 0], X[:, 1], marker='*') plt.title('Samples')
分別使用K-means和DBSCAN對上述樣本集進行聚類,效果如下:
'''利用K-means''' km = KMeans(n_clusters=3).fit_predict(X) col = [(['red','green','blue','yellow','grey','purple'])[i] for i in km] plt.figure(figsize=(16,8)) plt.subplot(121) plt.scatter(X[:, 0], X[:, 1], marker='*',c=col) plt.title('K-means') '''利用DBSCAN''' db = DBSCAN(eps = 0.12, min_samples = 19).fit_predict(X) col = [(['red','green','blue','yellow'])[i] for i in db] plt.subplot(122) plt.scatter(X[:, 0], X[:, 1], marker='*',c=col) plt.title('DBSCAN')
對DBSCAN中的參數eps(超球體半徑)進行試探:
'''對eps進行試探性調整''' plt.figure(figsize=(15,15)) for i in range(9): db = DBSCAN(eps = 0.05+i*0.04, min_samples = 19).fit_predict(X) col = [(['red','green','blue','yellow','purple','aliceblue','antiquewhite','black','blueviolet','cyan','darkgray'])[i] for i in db] plt.subplot(331+i) plt.scatter(X[:, 0], X[:, 1], marker='*',c=col) plt.title('eps={}'.format(str(round(0.05+i*0.04,2))))
對DBSCAN中的參數MinPts(核心點內最少樣本個數)進行試探:
'''對MinPts進行試探性調整''' plt.figure(figsize=(15,15)) for i in range(9): db = DBSCAN(eps = 0.12, min_samples = 10+i*4).fit_predict(X) col = [(['red','green','blue','yellow','purple','aliceblue','antiquewhite','black','blueviolet','cyan','darkgray'])[i] for i in db] plt.subplot(331+i) plt.scatter(X[:, 0], X[:, 1], marker='*',c=col) plt.title('MinPts={}'.format(str(round(10+i*4))))
可見參數的設置對聚類效果的影響非常顯著。
以上就是DBSCAN的簡單介紹,若發現錯誤望指出。