【可視化調參之DBSCAN】- 集成學習DBSCAN密度聚類算法詳解和可視化調參

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

紅色的點都是核心對象,黑色的樣本是非核心對象。所有核心對象密度直達的樣本在以紅色核心對象爲中心的超球體內,如果不在超球體內,則不能密度直達。圖中用綠色箭頭連起來的核心對象組成了密度可達的樣本序列。

1、特點

和傳統的K-Means算法相比,DBSCAN最大的不同就是不需要輸入類別數k,當然它最大的優勢是可以發現任意形狀的聚類簇,而不是像K-Means,一般僅僅使用於凸的樣本集聚類。同時它在聚類的同時還可以找出異常點,這點和BIRCH算法類似。
那麼我們什麼時候需要用DBSCAN來聚類呢?一般來說,如果數據集是稠密的,並且數據集不是凸的,那麼用DBSCAN會比K-Means聚類效果好很多。如果數據集不是稠密的,則不推薦用DBSCAN來聚類。

2、Python實現

2.1 引入相關的模塊

 

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查看樣本的分佈

我們對data的分佈用seaborn進行查看。

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)

可以看出來,樣本數據可以分爲三類。

接下來我通過DBSCAN算法如何把這三個分類找出來。

2.3 建立一個簡單的模型

db = DBSCAN(eps=1, min_samples=5).fit(data) #DBSCAN聚類方法 還有參數,matric = ""距離計算方法
data['labels'] = db.labels_ #和X同一個維度,labels對應索引序號的值 爲她所在簇的序號。若簇編號爲-1,表示爲噪聲,我們把標籤放回到data數據集中方便畫圖
labels = db.labels_
raito = data.loc[data['labels']==-1].x.count()/data.x.count() #labels=-1的個數除以總數,計算噪聲點個數佔總數的比例
print('噪聲比:', format(raito, '.2%'))
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) # 獲取分簇的數目
print('分簇的數目: %d' % n_clusters_)
print("輪廓係數: %0.3f" % metrics.silhouette_score(data, labels)) #輪廓係數評價聚類的好壞
sns.relplot(x="x",y="y", hue="labels",data=data)

噪聲比: 28.33%
分簇的數目: 4
輪廓係數: 0.332

其中-1的點表示異常點,即噪聲。可以看出來有17個點是噪聲,所以噪聲比就是17/60=28.33%,自動分爲4個類別。
這個分類和我們的實際還是有一點的差距的。接下來我們調整參數,看看效果會不會好一點。

2.4 調參

我們嘗試對eps和min_samples的各自參數組合進行擬合計算。

rs= []#存放各個參數的組合計算出來的模型評估得分和噪聲比
eps = np.arange(0.2,4,0.2) #eps參數從0.2開始到4,每隔0.2進行一次
min_samples=np.arange(2,20,1)#min_samples參數從2開始到20

best_score=0
best_score_eps=0
best_score_min_samples=0

for i in eps:
 for j in min_samples:
 try:#因爲不同的參數組合,有可能導致計算得分出錯,所以用try
            db = DBSCAN(eps=i, min_samples=j).fit(data)
            labels= db.labels_#得到DBSCAN預測的分類便籤
            k=metrics.silhouette_score(data,labels) #輪廓係數評價聚類的好壞,值越大越好
            raito = len(labels[labels[:] == -1]) / len(labels) #計算噪聲點個數佔總數的比例
            n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) # 獲取分簇的數目
            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 計算輪廓係數會出錯的,出錯的就跳過
 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)

不同的參數組合的得分情況,得分用的是輪廓係數,此係數評價聚類的好壞,值越大越好,值越大,圖中的點就越大。

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

通過上圖可以看出來,同時參考得分越大越好,噪聲比越小越好,eps取值在1-2.5之間,min_samples取值在3-15之間,並且min_samples的影響不大。有很多的參數組合的結果的差不多,我們在合適的組合中隨便選擇一組。
修改參數eps=1.3 min_samples=3,運行下列代碼:

db = DBSCAN(eps=1.5, min_samples=3).fit(data) #DBSCAN聚類方法 還有參數,matric = ""距離計算方法
data['labels'] = db.labels_ #和X同一個維度,labels對應索引序號的值 爲她所在簇的序號。若簇編號爲-1,表示爲噪聲,我們把標籤放回到data數據集中方便畫圖
labels = db.labels_
raito = data.loc[data['labels']==-1].x.count()/data.x.count() #labels=-1的個數除以總數,計算噪聲點個數佔總數的比例
print('噪聲比:', format(raito, '.2%'))
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) # 獲取分簇的數目
print('分簇的數目: %d' % n_clusters_)
print("輪廓係數: %0.3f" % metrics.silhouette_score(data, labels)) #輪廓係數評價聚類的好壞
sns.relplot(x="x",y="y", hue="labels",data=data)

噪聲比: 8.33%
分簇的數目: 3
輪廓係數: 0.376

 

比較好的自動分爲3類了。

 

2.5 軌跡清洗 - DBSCAN實例

import seaborn as sns
# earth's radius in km
kms_per_radian = 6371.0088
# define epsilon as 0.5 kilometers, converted to radians for use by haversine
epsilon = 0.8 / kms_per_radian
min_samples = [100]

# eps is the max distance that points can be from each other to be considered in a cluster
# min_samples is the minimum cluster size (everything else is classified as noise)
data_X = np.radians(coords)
db = DBSCAN(eps=epsilon, min_samples=120, algorithm='ball_tree', metric='haversine').fit(data_X)
# 大致看一下示意圖
sns.relplot(x="lon",y="lat",data=data1[['lat', 'lon']])

# 和X同一個維度,labels對應索引序號的值,爲其所在簇的序號。
# 若簇編號爲-1,表示爲噪聲,我們把標籤放回到data數據集中方便畫圖
y_hat = cluster_labels = db.labels_  

# coords = np.column_stack((cluster_labels, coords))
data1['labels'] = cluster_labels
print(data1)

y_unique = np.unique(y_hat)
# get the number of clusters (ignore noisy samples which are given the label -1)
num_clusters = y_unique.size - (1 if -1 in y_hat else 0)  # 把噪聲點過濾掉,因爲噪聲點無法聚類,它們獨自一類
# num_clusters = len(set(cluster_labels) - set([-1]))  # 法2

raito = data1.loc[data1['labels']==-1].lon.count()/data1.lon.count() #labels=-1的個數除以總數,計算噪聲點個數佔總數的比例
print('噪聲比:', format(raito, '.2%'))
print('分簇的數目: %d' % num_clusters)
print("輪廓係數: %0.3f" % metrics.silhouette_score(data_X, y_hat)) #輪廓係數評價聚類的好壞
print ('Clustered ' + str(len(data1)) + ' points to ' + str(num_clusters) + ' clusters')

sns.relplot(x="lon",y="lat", hue="labels",data=data1)


#%% 【可視化調參】

rs= []#存放各個參數的組合計算出來的模型評估得分和噪聲比
eps = np.arange(0.8,1.0,0.1) / kms_per_radian #eps參數從0.2開始到1,每隔0.2進行一次
min_samples=np.arange(118,124,2)#min_samples參數從2開始到20
print(eps.shape)
print(min_samples.shape)


best_score=0
best_score_eps=0
best_score_min_samples=0

for i in eps:
    for j in min_samples:
        try:#因爲不同的參數組合,有可能導致計算得分出錯,所以用try
            db = DBSCAN(eps=i, min_samples=j, algorithm='ball_tree', metric='haversine').fit(data_X)
            labels = db.labels_#得到DBSCAN預測的分類便籤
            k = metrics.silhouette_score(data_X,labels) #輪廓係數評價聚類的好壞,值越大越好
            raito = len(labels[labels[:] == -1]) / len(labels) #計算噪聲點個數佔總數的比例
            n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0) # 獲取分簇的數目
            rs.append([i*kms_per_radian,j,k,raito,n_clusters_])
            print('OK: ', i*kms_per_radian,', ', j,', ', k,', ', raito,', ', n_clusters_)
            if k > best_score:
                best_score = k
                best_score_eps = i * kms_per_radian
                best_score_min_samples = j
        except:
            db='' #這裏用try就是遍歷i,j 計算輪廓係數會出錯的,出錯的就跳過
        else:
            db=''
            
rs= pd.DataFrame(rs)
rs.columns=['eps','min_samples','score','raito','n_clusters']
print(rs)

3、DBSCAN算法的優缺點

3.1 DBSCAN的主要優點有:

  1. 可以對任意形狀的稠密數據集進行聚類,相對的,K-Means之類的聚類算法一般只適用於凸數據集。
  2. 可以在聚類的同時發現異常點,對數據集中的異常點不敏感。
  3. 聚類結果沒有偏倚,相對的,K-Means之類的聚類算法初始值對聚類結果有很大影響。

3.2 DBSCAN的主要缺點有:

  1. 如果樣本集的密度不均勻、聚類間距差相差很大時,聚類質量較差,這時用DBSCAN聚類一般不適合。
  2. 如果樣本集較大時,聚類收斂時間較長,此時可以對搜索最近鄰時建立的KD樹或者球樹進行規模限制來改進。
  3. 調參相對於傳統的K-Means之類的聚類算法稍複雜,主要需要對距離閾值ϵ,鄰域樣本數閾值MinPts聯合調參,不同的參數組合對最後的聚類效果有較大影響。
發佈了101 篇原創文章 · 獲贊 317 · 訪問量 31萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章