【可视化调参之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联合调参,不同的参数组合对最后的聚类效果有较大影响。
发布了104 篇原创文章 · 获赞 319 · 访问量 32万+

猜你喜欢

转载自blog.csdn.net/ljyljyok/article/details/103555408