本文记录周志华《机器学习》聚类内容:***(文中公式内容均来自周志华《机器学习》)***
聚类是无监督学习中应用最广的方法,是根据将样本划分为k个不相交的簇,其性能度量通常使用内部指标(直接考察聚类结果而不利用参考模型)。
内部指标:通常为DB指数(DBI)和Dunn指数(DI),下式中
a
v
g
(
C
)
avg(C)
avg(C)表示簇C内样本间平均距离,
d
i
a
m
(
C
)
diam(C)
diam(C)表示簇C内样本间的最远距离,
d
min
(
C
i
,
C
j
)
{d_{\min }}({C_i},{C_j})
dmin(Ci,Cj)表示两簇最近样本间的距离,KaTeX parse error: Undefined control sequence: \cen at position 5: {d_{\̲c̲e̲n̲}}({C_i},{C_j})表示两簇中心点间的距离。DBI值越小越好,DI越大越好。
1.
D
B
I
=
1
k
∑
i
=
1
k
max
j
≠
i
(
a
v
g
(
C
i
)
+
a
v
g
(
C
j
)
d
c
e
n
(
C
i
,
C
j
)
)
DBI = {1 \over k}\sum\limits_{i = 1}^k {\mathop {\max }\limits_{j \ne i} ({{avg({C_i}) + avg({C_j})} \over {{d_{cen}}({C_i},{C_j})}})}
DBI=k1i=1∑kj=imax(dcen(Ci,Cj)avg(Ci)+avg(Cj))
2.
D
I
=
max
1
≤
i
≤
k
{
min
j
≠
i
(
d
min
(
C
i
,
C
j
)
max
1
≤
l
≤
k
d
i
a
m
(
C
l
)
)
}
DI = \mathop {\max }\limits_{1 \le i \le k} \{ \mathop {\min }\limits_{j \ne i} ({{{d_{\min }}({C_i},{C_j})} \over {{{\max }_{1 \le l \le k}}diam({C_l})}})\}
DI=1≤i≤kmax{j=imin(max1≤l≤kdiam(Cl)dmin(Ci,Cj))}
距离计算:主要分为连续属性(可数字化的特征值)和离散属性(只存在有限个取值),其中连续属性距离计算是通过欧式距离,离散属性距离计算通过VDM(令
m
u
,
a
{m_{u,a}}
mu,a表示在属性u上取值为a的样本数,
m
u
,
a
,
i
{m_{u,a,i}}
mu,a,i表示在第i个样本簇中在属性u上取值为a的样本数,k为样本族数),其距离为两种属性距离之和
V
D
M
p
(
a
,
b
)
=
∑
i
=
1
k
∣
m
u
,
a
,
i
m
u
,
a
−
m
u
,
b
,
i
m
u
,
b
∣
2
VD{M_p}(a,b) = {\sum\limits_{i = 1}^k {|{{{m_{u,a,i}}} \over {{m_{u,a}}}} - {{{m_{u,b,i}}} \over {{m_{u,b}}}}|} ^2}
VDMp(a,b)=i=1∑k∣mu,amu,a,i−mu,bmu,b,i∣2
聚类算法:
1.k-means算法:
优点:简单快速,适合常规数据集
缺点:K值(分类数)难确定,复杂度与样本呈线性关系,很难发现任意形状的簇
注意点:(1)由于初始化位置的不同,可导致最后分类结果的不同,故可进行多次程序,选取得分最高的一次。(2)样本集数据要进行标准化。
选取K值时,可对多个K值的评估分数进行画图展示,选择拐点出的K值。该评估标准可选择样本对其簇中心点的距离和
代码:
其中数据集文件用excal创建如下文件,并改后缀名为csv即可
class Kmean(object):
def __init__(self, data_path):
self.data_path = data_path
self.clusters = []
self.center = np.zeros([2, 2])
def get_data(self):
data = pd.read_csv(self.data_path, encoding="gbk")
self.inputs = data.loc[:, ['密度', '含糖率']].values
self.labels = data.loc[:, '好瓜'].values.reshape((self.inputs.shape[0], -1))
def train(self, k=2, max_epioch=200):
for _ in range(k):
cluster = []
self.clusters.append(cluster)
num_data = self.inputs.shape[0]
center = self.inputs[random.sample(range(num_data), k)]
for _ in range(max_epioch):
for idx in range(num_data):
data = self.inputs[idx]
length = np.sqrt(np.sum(np.square(data - center), axis=1))
self.clusters[np.argmin(length)].append(data)
for i in range(k):
center[i] = np.mean(self.clusters[i], axis=0)
self.center = center
def draw(self):
fig = plt.figure()
ax = fig.add_subplot(1, 2, 1)
ax.plot(self.inputs[:, 0], self.inputs[:, 1], 'bo')
bx = fig.add_subplot(1, 2, 2)
x = np.array(self.clusters[0])
bx.plot(x[:, 0], x[:, 1], 'bo', color='red')
bx.plot(self.center[0, 0], self.center[0, 1], 'bo')
x = np.array(self.clusters[1])
bx.plot(x[:, 0], x[:, 1], 'bo', color='yellow')
bx.plot(self.center[1, 0], self.center[1, 1], 'bo')
plt.show()
a = Kmean('data.csv')
a.get_data()
a.train()
#a.draw()
2.学习向量量化(LVQ):
特点:LVQ假设数据样本带有类别标记,学习过程利用样本的监督信息来辅助聚类。
算法第一行对原型向量初始化(例如对第q个簇可从类别标记为tq的样本中随机选取一个作为原型向量),第2~12行对原型向量进行迭代优化,随机选取一个训练样本,找出与其距离最近的原型向量,并根据两者的类别标记是否一致来对原型向量进行相应更新。
主要思想:让原型向量在对不同样本训练时,靠近类别一样的样本,远离类别相异的。
3.高斯混合聚类
特点:高斯混合聚类采用概率形式来表达聚类,适用于样本由多个符合高斯分布的组合数据集。
由于高斯分布仅由均值向量μ和协方差矩阵Σ决定,再加上混合系数α(占比权重),通过迭代更新这三个组参数,可实现模拟出K个高斯公式,从而判断样本在不同高斯分布情况下的概率。
图中(9.30)式为
γ
j
i
=
P
M
(
z
j
=
i
∣
x
j
)
=
α
i
∗
P
(
x
j
∣
μ
i
,
Σ
i
)
∑
l
=
1
k
α
l
∗
P
(
x
j
∣
μ
l
,
Σ
l
)
{\gamma _{ji}} = {P_M}({z_j} = i|{x_j}) = {{{\alpha _i}*P({x_j}|{\mu _i},{\Sigma _i})} \over {\sum\limits_{l = 1}^k {{\alpha _l}*P({x_j}|{\mu _l},{\Sigma _l})} }}
γji=PM(zj=i∣xj)=l=1∑kαl∗P(xj∣μl,Σl)αi∗P(xj∣μi,Σi)
P
(
x
∣
μ
,
Σ
)
=
1
(
2
π
)
n
2
∣
Σ
∣
1
2
e
−
1
2
(
x
−
μ
)
T
Σ
−
1
(
x
−
μ
)
P(x|\mu ,\Sigma ) = {1 \over {{{(2\pi )}^{{n \over 2}}}|\Sigma {|^{{1 \over 2}}}}}{e^{ - {1 \over 2}{{(x - \mu )}^T}{\Sigma ^{ - 1}}(x - \mu )}}
P(x∣μ,Σ)=(2π)2n∣Σ∣211e−21(x−μ)TΣ−1(x−μ)该式子给出了样本xj由第i个高斯混合成分生成的后验概率
(9.31)式为
λ
j
=
arg
max
i
∈
{
1
,
2
,
.
.
.
,
k
}
γ
j
i
{\lambda _j} = \mathop {\arg \max }\limits_{i \in \{ 1,2,...,k\} } {\gamma _{ji}}
λj=i∈{1,2,...,k}argmaxγji
4.密度聚类(DBSCAN)
特点:此类算法从样本密度的角度来考察样本之间的可连接性,并基于可连接样本不断扩展聚类簇以获得最终聚类结果。
优点:不需要指定簇个数K,可以发现任意形状的簇,擅长找到离群点(检测任务),参数少(半径e和范围内样本点MinPts)
缺点:高维数据有些困难(可做降维),参数难以选择(参数对结果影响极大)
基本概念:
算法思想:首先找出所有核心对象点,然后由任一核心对象,根据密度MinPts和半径e找出密度可达的样本点作为同一簇,循环此过程,直到核心对象均被访问,其余未被访问的样本点为噪声点。
代码:(该代码是这位大神原创DBSCAN聚类算法(附代码)
)
# -*- coding: utf-8 -*-
import math
import random
import matplotlib.pyplot as plt
class DBSCAN(object):
STATUS_UNVISITED = 'unvisited'
STATUS_VISITED = 'visited'
STATUS_GROUP = 1
STATUS_NOGROUP = 0
data = dict()
def __init__(self, e, minPts):
"""
e 最小距离
minPts 最少样本数量
"""
self.e = e
self.minPts = minPts
def nearby(self, id):
nearby_points = list()
for link_id in self.scores[id]:
if self.scores[id][link_id] <= self.e:
nearby_points.append(link_id)
return nearby_points
def visit_nearby_points(self, points, group):
for id in points:
if self.data[id]['is_visited'] == self.STATUS_VISITED \
and self.data[id]['is_group'] == self.STATUS_GROUP:
continue
self.data[id]['is_visited'] = self.STATUS_VISITED
if self.data[id]['is_group'] == self.STATUS_NOGROUP:
group.append(id)
self.data[id]['is_group'] = self.STATUS_GROUP
nearby_points = self.nearby(id)
if len(nearby_points) >= self.minPts:
self.visit_nearby_points(nearby_points, group)
def fit(self, data_set, scores):
self.scores = scores
groups = list()
for index, item in enumerate(data_set):
self.data[index] = {'id': index,
'is_visited': self.STATUS_UNVISITED,
'is_group': self.STATUS_NOGROUP
}
for id in self.data:
if self.data[id]['is_visited'] == self.STATUS_VISITED:
continue
self.data[id]['is_visited'] = self.STATUS_VISITED
nearby_points = self.nearby(id)
if len(nearby_points) >= self.minPts:
group = list()
group.append(id)
self.data[id]['is_group'] = self.STATUS_GROUP
self.visit_nearby_points(nearby_points, group)
groups.append(group)
for id in self.data:
if self.data[id]['is_group'] == self.STATUS_NOGROUP:
groups.append([id])
return groups
def init_data(num, min, max):
data = []
for i in range(num):
data.append([random.randint(min, max), random.randint(min, max)])
return data
def mat_score(data_set):
score = dict()
for i in range(len(data_set)):
score[i] = dict()
for i in range(len(data_set) - 1):
j = i + 1
while j < len(data_set):
score[i][j] = math.sqrt(abs(data_set[i][0] - data_set[j][0]) ** 2 + abs(data_set[i][1] - data_set[j][1]) ** 2)
score[j][i] = score[i][j]
j += 1
return score
def show_cluster(data_set, groups):
plt.title(u'DBSCAN')
mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']
for index, group in enumerate(groups):
for i in group:
plt.plot(data_set[i][0], data_set[i][1], mark[index])
plt.xlim(0.0, 100)
plt.ylim(0.0, 100)
plt.show()
if __name__ == '__main__':
data_set1 = init_data(20, 0, 30)
data_set2 = init_data(20, 40, 60)
data_set3 = init_data(20, 70, 100)
data_set = data_set1 + data_set2 + data_set3
score_mat = mat_score(data_set)
groups = DBSCAN(20, 3).fit(data_set, score_mat)
show_cluster(data_set, groups)