目录
可访问 实现机器学习的循序渐进指南系列汇总,获取本系列完成文章列表。
介绍
基于密度的噪声应用空间聚类(DBSCAN)是一种基于密度的聚类算法。与其他聚类算法不同,DBSCAN将最大密度可到达样本集视为聚类。KMeans缺乏对非球形数据进行聚类的能力,而DBSCAN可以对任何形状的数据进行聚类。
DBSCAN模型
开始
- Ε邻域:来自对象的半径为ε的对象。
- 核心要点:如果一个样本在ε-邻居内具有超过指定数量的点(m),则该样本是核心点。
- 直接密度可达:对于样本集合D,如果样本点q在p的Ε邻域内,并且p为核心对象,那么对象q从对象p直接密度可达。
- 密度可达:如果存在对象链p1,...,pn,对象q可以从p密度到达,其中p1 = p,pn = q使得pi + 1可以直接密度可达pi,对于所有1 <= i <= n(直接密度可达和密度可达之间的差值是当直接密度可达时p在q的邻居内,而当密度可达时p不在q的邻居内)
- 密度连通性:对象p是与对象q连接的密度,如果有一个对象o,使得p和q都是从对象o可达的密度
- 噪声:至少一个核心对象无法达到密度可达的对象
让我们举一个例子来解释上面的术语,如下所示,其中m = 4。因为在所有红色样本的ε-邻居中有超过m个样本,所以它们都是核心点且密度可达。B和C不是核心要点。青色圆圈中的样品直接密度可达。棕色圆圈中的样本是密度连通性的。N对任何其他样本都不可达,因此它是一个噪声点。
聚类算法
首先,我们通过计算所有样本ε-邻居中的样本数来找到所有核心点。剩余样本暂时标记为噪声点。我们提供几种类型的距离,即:
- 欧几里德距离:
- 余弦距离:
- 曼哈顿距离:
def calculateDistance(self, x1, x2):
if self.distance_type == "Euclidean":
d = np.sqrt(np.sum(np.power(x1 - x2, 2), axis=1))
#d = np.sqrt(np.sum(np.power(x1 - x2, 2)))
elif self.distance_type == "Cosine":
d = np.dot(x1, x2)/(np.linalg.norm(x1)*np.linalg.norm(x2))
elif self.distance_type == "Manhattan":
d = np.sum(x1 - x2)
else:
print("Error Type!")
sys.exit()
return d
对于给定样本,如果此样本与另一个样本之间的距离小于ε,则它们位于同一邻居中。
def getCenters(self, train_data):
neighbor = {}
for i in range(len(train_data)):
distance = self.calculateDistance(train_data[i], train_data)
index = np.where(distance <= self.eps)[0]
if len(index) > self.m:
neighbor[i] = index
return neighbor
然后,我们随机选择一个未访问的核心点P来合并与其密度连通的样本。接下来,我们访问P的ε-邻居内的样本Q。如果Q是核心点,Q在P的同一簇中并在Q上进行相同的处理(如深度优先搜索); 否则,请访问下一个示例。具体来说,P的ε-邻居的搜索过程如下所示:
- 选择核心点A并找到其ε-邻居中的所有样本。A的ε-邻居内的样本属于A的簇。然后连续访问A的ε-邻域内的样本。
- 访问A的ε-邻居中的样本B。 B是核心样本,访问B的ε-邻居内的样本。
- 访问B的ε-邻居中的样本C。C属于A而C不是核心点。访问B的ε-邻居内的其他样本。
- 访问B的ε-邻居中的另一个样本D。 D是核心点。访问D的ε-邻居内的样本。
- 访问样本E。 E不是核心点。现在,没有任何点密度可达到A。停止A的聚类。
k = 0
unvisited = list(range(sample_num)) # samples which are not visited
while len(centers) > 0:
visited = []
visited.extend(unvisited)
cores = list(centers.keys())
# choose a random cluster center
randNum = np.random.randint(0, len(cores))
core = cores[randNum]
core_neighbor = [] # samples in core's neighbor
core_neighbor.append(core)
unvisited.remove(core)
# merege the samples density-connectivity
while len(core_neighbor) > 0:
Q = core_neighbor[0]
del core_neighbor[0]
if Q in initial_centers.keys():
diff = [sample for sample in initial_centers[Q] if sample in unvisited]
core_neighbor.extend(diff)
unvisited = [sample for sample in unvisited if sample not in diff]
k += 1
label[k] = [val for val in visited if val not in unvisited]
for index in label[k]:
if index in centers.keys():
del centers[index]
参数估计
DBSCAN算法需要2个参数ε,它们指定了彼此之间的接近点应该如何被视为集群的一部分;而参数m,它指定一个点应该包含在一个簇中的邻居数量。以wiki为例,我们计算在同一分区内从每个点到最近邻居的距离,如下所示,我们可以很容易地确定ε= 22。
对于参数m,我们计算核心点的ε邻域内有多少样本,如下所示。我们选择m = 129,因为它是第一个谷底。
结论与分析
DBSCAN能够聚类非球形数据,但不能反映高维数据。当密度分布不均匀时,聚类性能不太好。KMeans和DBSCAN之间的聚类性能如下所示。很容易发现DBSCAN具有比KMeans更好的聚类性能。
可以在MachineLearning中找到本文中的相关代码和数据集。
有兴趣的小伙伴可以查看上一篇。
原文地址:https://www.codeproject.com/Articles/5129186/Step-by-Step-Guide-to-Implement-Machine-Learning-8