訂閱
糾錯
加入自媒體

簡單一文助你理解DBSCAN是什么

一般說到聚類算法,大多數(shù)人會想到k-means算法,但k-means算法一般只適用于凸樣本集,且需要預(yù)先設(shè)定k值,而DBSCAN聚類既可以用于凸樣本集,也可以用于非凸樣本集,也不需要提前設(shè)定簇族數(shù)。關(guān)于凸樣本集的解釋如下圖所示。

關(guān)于DBSCAN聚類,它是基于密度的聚類,一般通過樣本間的緊密程度來進行聚類,將緊密相連的一類樣本化為一類,直至遍歷所有樣本點。

而DBSCAN聚類有下面幾個定義。

1.ε-鄰域:有一個樣本點x1,以x1為圓心,半徑為ε的一個范圍

2.min_sample(最小樣本點數(shù)):在樣本點x1的ε-鄰域內(nèi)的所有樣本點總數(shù)n;如果n>=min_sample,樣本點成為核心點,否則為非核心點。而非核心又分為邊界點和噪聲點。他們的區(qū)別在于其ε-鄰域內(nèi)是否存在核心點,如果存在則為邊界點,否則為噪聲點。

3.密度直達(dá):有樣本點x1位于x2的ε-鄰域內(nèi),且x2為核心點,則稱x1由x2密度直達(dá)。

4.密度可達(dá):有樣本點x1位于x2的ε-鄰域內(nèi),且x1和x2均為核心點,則稱x1和x2密度可達(dá)。

5.密度相連:有非核心點x1和x2均在核心點x3的ε-鄰域內(nèi),則稱x1和x2密度相連。所有密度相連的樣本點組成一個集合。

上圖中的紅色點為核心點,黑色點為非核心點(包括邊界點和噪音點)。一共有兩組密度可達(dá),第一組(左邊)有七個核心點,其集合包括七個核心點以及各個ε-鄰域內(nèi)的所有邊界點。第二組(右邊)有五個核心點,其集合包括五個核心點以及各個ε-鄰域內(nèi)的所有邊界點。當(dāng)所有非噪聲點均在不同集合內(nèi)時,聚類結(jié)束。

因此,可以將DBSCAN聚類的流程定義如下:

有數(shù)據(jù)集X={x1,x2,...,xn},設(shè)置好min_sample和鄰域半徑值。

1.遍歷數(shù)據(jù)集,將各個樣本點間的距離保存到一個矩陣中;

2.遍歷數(shù)據(jù)集,將所有的核心點,以及各個核心點鄰域內(nèi)的樣本點找出;

3.如果核心點間的距離小于半徑值,則將兩個核心點連接到一起;最終會形成若干簇族;

4.將所有邊界點分配到離他最近的核心點;

5.直至所有非噪音點完成分配,算法結(jié)束。

python實現(xiàn)

用的是sklearn庫自帶的數(shù)據(jù)集---make_circles。散點圖如下。

根據(jù)上面定義的流程,開始寫代碼啦。

首先要得到各個樣本點間的距離:

def dis(self,va,vb):    s=(va-vb)    f=sqrt(s*s.T)    return f[0,0]
def get_distance(self,dataset):    m,n=shape(dataset)[0],shape(dataset)[1]    dataset=mat(dataset)    dis=mat(zeros((m,m)))    for i in range(m):        for j in range(i,m):            dis[i,j]=self.dis(dataset[i,],dataset[j,])            dis[j,i]=dis[i,j]    return dis

然后找到所有的核心點,以及各個核心點鄰域內(nèi)的所有樣本點集合。

def find_core_point(self,dismatrix):    core_point=[]    core_point_dict={}    m=shape(dismatrix)[0]    for i in range(m):        ind=[]        for j in range(m):            if dismatrix[i,j]<self.eps:                ind.a(chǎn)ppend(j)        if len(ind)>=self.min_sample:            core_point.a(chǎn)ppend(i)            core_point_dict[str(i)]=ind    core_point_core={}    for key,value in core_point_dict.items():        o=[]        for i in value:            if i in core_point:                o.a(chǎn)ppend(i)        core_point_core[key]=o    return core_point,core_point_dict,core_point_core其中core_point是一個列表,存儲所有的核心點core_point_dict是一個字典,key為核心點,value為該核心點鄰域內(nèi)的所有樣本點集合core_point_core是一個字典,key為核心點,value為該核心點鄰域內(nèi)所有核心點集合

接下來就是找出密度直達(dá)點集合,也就是在鄰域內(nèi)的核心點集合

def join_core_point(self,core_point,core_point_dict,core_point_core):    labels=array(zeros((1,len(core_point))))    num=1    result={}    result[str(num)]=core_point_core[str(core_point[0])]    for i in range(1,len(core_point)):        q=[]        for key,value in result.items():            r=self.get_same(core_point_core[str(core_point[i])],value)            if r:                q.a(chǎn)ppend(key)        if q:            n=result[q[0]].copy()            n.extend(core_point_core[str(core_point[i])])            for i in range(1,len(q)):                n.extend(result[q[i]])                del result[q[i]]            result[q[0]]=list(set(n))        else:            num=num+1            result[str(num)]=core_point_core[str(core_point[i])]    return result

再將所有邊界點劃分到其最近的核心點一簇并畫出。

def ddbscan(self,data, label):        m=shape(data)[0]        dismatrix=self.get_distance(data)        types=array(zeros((1,m)))        number=1        core_point, core_point_dict,core_point_core=self.find_core_point(dismatrix)        if len(core_point):            core_result=self.join_core_point(core_point,core_point_dict,core_point_core)            for key,value in core_result.items():                k=int(key)                for i in value:                    types[0,i]=k                    for j in core_point_dict[str(i)]:                        types[0, j] = k            print(types)        newlabel=types.tolist()[0]        data=array(data)        q=list(set(newlabel))        print(q)        colors = ['r', 'b', 'g', 'y', 'c', 'm', 'orange']        for ii in q:            i=int(ii)            xy=data[types[0,:]==i,:]            plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=colors[q.index(ii)], markeredgecolor='w', markersize=5)        plt.title('DBSCAN' )        plt.show()

最后的結(jié)果圖如下:

雖然效果不錯,但自己寫的就是比較辣雞,一共用了10.445904秒;如果真的要用這個算法的話,不推薦大家用自己寫的,事實上sklearn庫就有DBSCAN這個函數(shù),只需要0.0284941秒。

效果如上所示。而且代碼也只有幾行。代碼復(fù)制于(http://itindex.net/detail/58485-%E8%81%9A%E7%B1%BB-%E7%AE%97%E6%B3%95-dbscan)

def skdbscan(self,data,label):        data = array(data)        db = DBSCAN(eps=self.eps, min_samples=self.min_sample, metric='euclidean').fit(data)        core_samples_mask = zeros_like(db.labels_, dtype=bool)        core_samples_mask[db.core_sample_indices_] = True        labels = db.labels_        n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)        unique_labels = set(labels)        colors = ['r', 'b', 'g', 'y', 'c', 'm', 'orange']        for k, col in zip(unique_labels, colors):            if k == -1:                col = 'k'            class_member_mask = (labels == k)            xy = data[class_member_mask & core_samples_mask]            plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=col, markeredgecolor='w', markersize=10)        plt.title('Estimated number of clusters: %d' % n_clusters_)        plt.show()

關(guān)于DBSCAN這個函數(shù)有幾個要注意的地方:

DBSCAN(eps=0.1, min_samples=5, metric='euclidean',

algorithm='auto', leaf_size=30, p=None, n_jobs=1)

核心參數(shù):

eps: float-鄰域的距離閾值

min_samples :int,樣本點要成為核心對象所需要的 ?-鄰域的樣本數(shù)閾值

其他參數(shù):

metric :度量方式,默認(rèn)為歐式距離,可以使用的距離度量參數(shù)有:

歐式距離 “euclidean”

曼哈頓距離 “manhattan”

切比雪夫距離“chebyshev”

閔可夫斯基距離 “minkowski”

帶權(quán)重閔可夫斯基距離 “wminkowski”

標(biāo)準(zhǔn)化歐式距離 “seuclidean”

馬氏距離“mahalanobis”

自己定義距離函數(shù)

algorithm:近鄰算法求解方式,有四種:

“brute”蠻力實現(xiàn)

“kd_tree” KD樹實現(xiàn)

“ball_tree”球樹實現(xiàn)

“auto”上面三種算法中做權(quán)衡,選擇一個擬合最好的最優(yōu)算法。

leaf_size:使用“ball_tree”或“kd_tree”時,停止建子樹的葉子節(jié)點數(shù)量的閾值

p:只用于閔可夫斯基距離和帶權(quán)重閔可夫斯基距離中p值的選擇,p=1為曼哈頓距離, p=2為歐式距離。如果使用默認(rèn)的歐式距離不需要管這個參數(shù)。

n_jobs :CPU并行數(shù),若值為 -1,則用所有的CPU進行運算

DBSCAN聚類的優(yōu)缺點

優(yōu)點:

可以很好的發(fā)現(xiàn)噪聲點,但是對其不敏感;

可以對任意形狀的稠密數(shù)據(jù)進行聚類;

缺點:

1.需要設(shè)定min_sample和eps;不同的組合差別非常大;

2.?dāng)?shù)據(jù)量很大時,效率會特別低,收斂時間很長;

3.對于密度不均勻,聚類間差距很大的數(shù)據(jù)集效果很差。

最后,送一個基于DBSCAN聚類的笑臉給大家?梢匀ミ@個網(wǎng)站自行嘗試。

文章到這里就暫時告一段落啦,小伙伴們有沒有收獲滿滿咧?

------------------- End -------------------


聲明: 本文由入駐維科號的作者撰寫,觀點僅代表作者本人,不代表OFweek立場。如有侵權(quán)或其他問題,請聯(lián)系舉報。

發(fā)表評論

0條評論,0人參與

請輸入評論內(nèi)容...

請輸入評論/評論長度6~500個字

您提交的評論過于頻繁,請輸入驗證碼繼續(xù)

暫無評論

暫無評論

    掃碼關(guān)注公眾號
    OFweek人工智能網(wǎng)
    獲取更多精彩內(nèi)容
    文章糾錯
    x
    *文字標(biāo)題:
    *糾錯內(nèi)容:
    聯(lián)系郵箱:
    *驗 證 碼:

    粵公網(wǎng)安備 44030502002758號