DBSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪聲的基于密度的聚類方法)是一種很典型的密度聚類算法,和只適用于凸樣本集的K-Means聚類相比,DBSCAN既可以適用于凸樣本集,也可以適用于非凸樣本集。
DBSCAN一般假定類別可以通過樣本分布的緊密程度決定。同一類別的樣本,他們之間的緊密相連的,也就是說,在該類別任意樣本周圍不遠(yuǎn)處一定有同類別的樣本存在。通過將緊密相連的樣本劃為一類,這樣就得到了一個(gè)聚類類別。通過將所有各組緊密相連的樣本劃為各個(gè)不同的類別,則我們就得到了最終的所有聚類類別結(jié)果。

紅色的點(diǎn)都是核心對(duì)象,黑色的樣本是非核心對(duì)象。所有核心對(duì)象密度直達(dá)的樣本在以紅色核心對(duì)象為中心的超球體內(nèi),如果不在超球體內(nèi),則不能密度直達(dá)。圖中用綠色箭頭連起來的核心對(duì)象組成了密度可達(dá)的樣本序列。
1、特點(diǎn)
和傳統(tǒng)的K-Means算法相比,DBSCAN最大的不同就是不需要輸入類別數(shù)k,當(dāng)然它最大的優(yōu)勢(shì)是可以發(fā)現(xiàn)任意形狀的聚類簇,而不是像K-Means,一般僅僅使用于凸的樣本集聚類。同時(shí)它在聚類的同時(shí)還可以找出異常點(diǎn),這點(diǎn)和BIRCH算法類似。
那么我們什么時(shí)候需要用DBSCAN來聚類呢?一般來說,如果數(shù)據(jù)集是稠密的,并且數(shù)據(jù)集不是凸的,那么用DBSCAN會(huì)比K-Means聚類效果好很多。如果數(shù)據(jù)集不是稠密的,則不推薦用DBSCAN來聚類。
2、Python實(shí)現(xiàn)
2.1 引入相關(guān)的模塊
import numpy as np
from sklearn.cluster import DBSCAN
from sklearn import metrics
import seaborn as sns
import pandas as pd
from sklearn.datasets.samples_generator import make_blobs
from sklearn.preprocessing import StandardScaler
2.2查看樣本的分布
我們對(duì)data的分布用seaborn進(jìn)行查看。
data=[
[-2.68420713,1.469732895],[-2.71539062,-0.763005825],[-2.88981954,-0.618055245],[-2.7464372,-1.40005944],[-2.72859298,1.50266052],
[-2.27989736,3.365022195],[-2.82089068,-0.369470295],[-2.62648199,0.766824075],[-2.88795857,-2.568591135],[-2.67384469,-0.48011265],
[-2.50652679,2.933707545],[-2.61314272,0.096842835],[-2.78743398,-1.024830855],[-3.22520045,-2.264759595],[-2.64354322,5.33787705],
[-2.38386932,6.05139453],[-2.6225262,3.681403515],[-2.64832273,1.436115015],[-2.19907796,3.956598405],[-2.58734619,2.34213138],
[1.28479459,3.084476355],[0.93241075,1.436391405],[1.46406132,2.268854235],[0.18096721,-3.71521773],[1.08713449,0.339256755],
[0.64043675,-1.87795566],[1.09522371,1.277510445],[-0.75146714,-4.504983795],[1.04329778,1.030306095],[-0.01019007,-3.242586915],
[-0.5110862,-5.681213775],[0.51109806,-0.460278495],[0.26233576,-2.46551985],[0.98404455,-0.55962189],[-0.174864,-1.133170065],
[0.92757294,2.107062945],[0.65959279,-1.583893305],[0.23454059,-1.493648235],[0.94236171,-2.43820017],[0.0432464,-2.616702525],
[4.53172698,-0.05329008],[3.41407223,-2.58716277],[4.61648461,1.538708805],[3.97081495,-0.815065605],[4.34975798,-0.188471475],
[5.39687992,2.462256225],[2.51938325,-5.361082605],[4.9320051,1.585696545],[4.31967279,-1.104966765],[4.91813423,3.511712835],
[3.66193495,1.0891728],[3.80234045,-0.972695745],[4.16537886,0.96876126],[3.34459422,-3.493869435],[3.5852673,-2.426881725],
[3.90474358,0.534685455],[3.94924878,0.18328617],[5.48876538,5.27195043],[5.79468686,1.139695065],[3.29832982,-3.42456273]
]
data = pd.DataFrame(data)
data.columns=['x','y']
sns.relplot(x="x",y="y",data=data)
可以看出來,樣本數(shù)據(jù)可以分為三類。

接下來我通過DBSCAN算法如何把這三個(gè)分類找出來。
2.3 建立一個(gè)簡單的模型
db = DBSCAN(eps=1, min_samples=5).fit(data) #DBSCAN聚類方法 還有參數(shù),matric = ""距離計(jì)算方法
data['labels'] = db.labels_ #和X同一個(gè)維度,labels對(duì)應(yīng)索引序號(hào)的值 為她所在簇的序號(hào)。若簇編號(hào)為-1,表示為噪聲,我們把標(biāo)簽放回到data數(shù)據(jù)集中方便畫圖
labels = db.labels_
raito = data.loc[data['labels']==-1].x.count()/data.x.count() #labels=-1的個(gè)數(shù)除以總數(shù),計(jì)算噪聲點(diǎn)個(gè)數(shù)占總數(shù)的比例
print('噪聲比:', format(raito, '.2%'))
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) # 獲取分簇的數(shù)目
print('分簇的數(shù)目: %d' % n_clusters_)
print("輪廓系數(shù): %0.3f" % metrics.silhouette_score(data, labels)) #輪廓系數(shù)評(píng)價(jià)聚類的好壞
sns.relplot(x="x",y="y", hue="labels",data=data)
噪聲比: 28.33%
分簇的數(shù)目: 4
輪廓系數(shù): 0.332

其中-1的點(diǎn)表示異常點(diǎn),即噪聲??梢钥闯鰜碛?7個(gè)點(diǎn)是噪聲,所以噪聲比就是17/60=28.33%,自動(dòng)分為4個(gè)類別。
這個(gè)分類和我們的實(shí)際還是有一點(diǎn)的差距的。接下來我們調(diào)整參數(shù),看看效果會(huì)不會(huì)好一點(diǎn)。
2.4 調(diào)參
我們嘗試對(duì)eps和min_samples的各自參數(shù)組合進(jìn)行擬合計(jì)算。
rs= []#存放各個(gè)參數(shù)的組合計(jì)算出來的模型評(píng)估得分和噪聲比
eps = np.arange(0.2,4,0.2) #eps參數(shù)從0.2開始到4,每隔0.2進(jìn)行一次
min_samples=np.arange(2,20,1)#min_samples參數(shù)從2開始到20
best_score=0
best_score_eps=0
best_score_min_samples=0
for i in eps:
for j in min_samples:
try:#因?yàn)椴煌膮?shù)組合,有可能導(dǎo)致計(jì)算得分出錯(cuò),所以用try
db = DBSCAN(eps=i, min_samples=j).fit(data)
labels= db.labels_#得到DBSCAN預(yù)測(cè)的分類便簽
k=metrics.silhouette_score(data,labels) #輪廓系數(shù)評(píng)價(jià)聚類的好壞,值越大越好
raito = len(labels[labels[:] == -1]) / len(labels) #計(jì)算噪聲點(diǎn)個(gè)數(shù)占總數(shù)的比例
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) # 獲取分簇的數(shù)目
rs.append([i,j,k,raito,n_clusters_])
if k>best_score:
best_score=k
best_score_eps=i
best_score_min_samples=j
except:
db='' #這里用try就是遍歷i,j 計(jì)算輪廓系數(shù)會(huì)出錯(cuò)的,出錯(cuò)的就跳過
else:
db=''
rs= pd.DataFrame(rs)
rs.columns=['eps','min_samples','score','raito','n_clusters']
sns.relplot(x="eps",y="min_samples", size='score',data=rs)
sns.relplot(x="eps",y="min_samples", size='raito',data=rs)
不同的參數(shù)組合的得分情況,得分用的是輪廓系數(shù),此系數(shù)評(píng)價(jià)聚類的好壞,值越大越好,值越大,圖中的點(diǎn)就越大。

我們也可以看看噪聲比,噪聲比越小越好。

通過上圖可以看出來,同時(shí)參考得分越大越好,噪聲比越小越好,eps取值在1-2.5之間,min_samples取值在3-15之間,并且min_samples的影響不大。有很多的參數(shù)組合的結(jié)果的差不多,我們?cè)诤线m的組合中隨便選擇一組。
修改參數(shù)eps=1.3 min_samples=3,運(yùn)行下列代碼:
db = DBSCAN(eps=1.5, min_samples=3).fit(data) #DBSCAN聚類方法 還有參數(shù),matric = ""距離計(jì)算方法
data['labels'] = db.labels_ #和X同一個(gè)維度,labels對(duì)應(yīng)索引序號(hào)的值 為她所在簇的序號(hào)。若簇編號(hào)為-1,表示為噪聲,我們把標(biāo)簽放回到data數(shù)據(jù)集中方便畫圖
labels = db.labels_
raito = data.loc[data['labels']==-1].x.count()/data.x.count() #labels=-1的個(gè)數(shù)除以總數(shù),計(jì)算噪聲點(diǎn)個(gè)數(shù)占總數(shù)的比例
print('噪聲比:', format(raito, '.2%'))
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) # 獲取分簇的數(shù)目
print('分簇的數(shù)目: %d' % n_clusters_)
print("輪廓系數(shù): %0.3f" % metrics.silhouette_score(data, labels)) #輪廓系數(shù)評(píng)價(jià)聚類的好壞
sns.relplot(x="x",y="y", hue="labels",data=data)
噪聲比: 8.33%
分簇的數(shù)目: 3
輪廓系數(shù): 0.376

比較好的自動(dòng)分為3類了。
3、DBSCAN算法的優(yōu)缺點(diǎn)
3.1 DBSCAN的主要優(yōu)點(diǎn)有:
- 可以對(duì)任意形狀的稠密數(shù)據(jù)集進(jìn)行聚類,相對(duì)的,K-Means之類的聚類算法一般只適用于凸數(shù)據(jù)集。
- 可以在聚類的同時(shí)發(fā)現(xiàn)異常點(diǎn),對(duì)數(shù)據(jù)集中的異常點(diǎn)不敏感。
- 聚類結(jié)果沒有偏倚,相對(duì)的,K-Means之類的聚類算法初始值對(duì)聚類結(jié)果有很大影響。
3.2 DBSCAN的主要缺點(diǎn)有:
- 如果樣本集的密度不均勻、聚類間距差相差很大時(shí),聚類質(zhì)量較差,這時(shí)用DBSCAN聚類一般不適合。
- 如果樣本集較大時(shí),聚類收斂時(shí)間較長,此時(shí)可以對(duì)搜索最近鄰時(shí)建立的KD樹或者球樹進(jìn)行規(guī)模限制來改進(jìn)。
- 調(diào)參相對(duì)于傳統(tǒng)的K-Means之類的聚類算法稍復(fù)雜,主要需要對(duì)距離閾值?,鄰域樣本數(shù)閾值MinPts聯(lián)合調(diào)參,不同的參數(shù)組合對(duì)最后的聚類效果有較大影響。