一:算法思想
- 硬聚类:传统K-均值算法中,每个对象只能从属于所有类别中的一类,称之为硬聚类。这种聚类方法会带来一个问题:所有的对象对于计算聚类中心的贡献都是相同的,也就是说,对于从属于一个类的所有元素,算法是无法将其区分开来的。这种分配方式在处理一些复杂数据集合(例如数据有重叠)会造成类别指派不合理
- 软聚类:一个对象可以从属于多个类别的聚类方法
模糊K-均值算法(FCM算法):对于每个类别,对象都对应一个取值范围在[0,1]的数值,它表示该对象从属于某一类别的可能性。一个对象对于所有类别的对应取值和应该为1。在更新簇中心点的过程中,该数值就反映了该对象对于这一类的贡献程度,根据贡献程度的不同,反映出对象更倾向于分配到哪个类别中
FCM算法与K-均值算法的目标函数类似
J
m
=
∑
k
=
1
N
∑
j
=
1
u
i
j
m
∣
∣
x
i
?
c
j
∣
∣
2
,
1
≤
m
<
∞
J_{m}=\sum_{k=1}^{N}\sum_{j=1}u_{ij}^{m}||x_{i}-c_{j}||^{2},1\leq m <\infty
Jm?=k=1∑N?j=1∑?uijm?∣∣xi??cj?∣∣2,1≤m<∞
其中
-
m
(
>
1
)
m(>1)
m(>1):模糊系数
-
N
N
N:样本数
-
C
C
C:聚类中心数
-
c
j
c_{j}
cj?:第
j
j
j个聚类中心
-
x
i
x_{i}
xi?:第
i
i
i个样本
-
u
i
j
u_{ij}
uij?:样本
x
i
x_{i}
xi?对聚类中心
c
j
c_{j}
cj?的隶属度,也即
x
i
x_{i}
xi?属于
c
j
c_{j}
cj?的概率。显然有
∑
j
=
1
C
u
i
j
\sum\limits_{j=1}^{C}u_{ij}
j=1∑C?uij?=1
FCM算法通过更新
u
i
j
u_{ij}
uij?和
c
j
c_{j}
cj?来迭代优化目标函数
u
i
j
=
1
∑
k
=
1
C
(
∣
∣
x
i
?
c
j
∣
∣
∣
∣
x
i
?
c
k
∣
∣
)
2
m
?
1
u_{ij}=\frac{1}{\sum\limits_{k=1}^{C}(\frac{||x_{i}-c_{j}||}{||x_{i}-c_{k}||})^{\frac{2}{m-1}}}
uij?=k=1∑C?(∣∣xi??ck?∣∣∣∣xi??cj?∣∣?)m?12?1?
c
j
=
∑
i
=
1
N
u
i
j
m
x
i
∑
i
=
1
N
u
i
j
m
c_{j}=\frac{\sum\limits_{i=1}^{N}u_{ij}^{m}x_{i}}{\sum\limits_{_{i=1}}^{N}u_{ij}^{m}}
cj?=i=1?∑N?uijm?i=1∑N?uijm?xi??
FCM算法的收敛条件一般设置为两次迭代过程中计算的
S
S
E
SSE
SSE差值,其中
ξ
\xi
ξ是预先设定好的容忍误差,当两次迭代过程中计算的
S
S
E
SSE
SSE差值小于该预设值时,判定算法收敛
E
(
t
)
=
∣
∣
S
S
E
t
?
S
S
E
t
?
1
∣
∣
<
ξ
E(t)=||SSE^{t}-SSE^{t-1}||<\xi
E(t)=∣∣SSEt?SSEt?1∣∣<ξ
二:算法流程
- 初始化隶属度矩阵
U
0
U^{0}
U0:若有
N
N
N个样本,指定类别个数为
C
C
C,则隶属度矩阵应为
N
N
N×
C
C
C
- 根据公式更新聚类中心
c
j
c_{j}
cj?
- 根据公式更新隶属度矩阵(注意保存更新前的隶属度矩阵)
- 判断是否收敛,若收敛则停止迭代,反之返回步骤2
三:Python实现
import numpy as np
import copy
def distance(data, centroid):
return np.sqrt(np.sum(np.power(data-centroid, 2)))
def fcm(data_set, m, k, eps):
example_nums = np.shape(data_set)[0]
cluster = np.zeros(example_nums)
random_mat = np.random.rand(example_nums, k)
random_mat_sum = 1 / np.sum(random_mat, axis=1)
membership_mat = np.multiply(random_mat.T, random_mat_sum)
membership_mat = membership_mat.T
membership_mat_old = np.zeros((example_nums, k))
while True:
centorids = np.empty((k, np.shape(data_set)[1]))
for j in range(k):
centorids[j] = np.dot(membership_mat[:, j]**m, data_set) / (np.sum(membership_mat[:, j]**m))
membership_mat_old = membership_mat.copy()
for i in range(example_nums):
for j in range(k):
for z in range(k):
membership_mat[i, j] += ((distance(data_set[i], centorids[j])) / distance(data_set[i], centorids[z])) ** (2 / (m-1))
membership_mat = 1 / membership_mat
if np.max(np.abs(membership_mat - membership_mat_old)) < eps:
cluster = np.argmax(membership_mat, axis=1)
return centorids, cluste
四:效果展示
import pandas as pd
import matplotlib.pyplot as plt
import FCM
import numpy as np
Iris_types = ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica']
Iris_data = pd.read_csv('dataSet/Iris.csv')
x_axis = 'SepalLengthCm'
y_axis = 'SepalWidthCm'
examples_num = Iris_data.shape[0]
train_data = Iris_data[[x_axis, y_axis]].values.reshape(examples_num, 2)
min_vals = train_data.min(0)
max_vals = train_data.max(0)
ranges = max_vals - min_vals
normal_data = np.zeros(np.shape(train_data))
nums = train_data.shape[0]
normal_data = train_data - np.tile(min_vals, (nums, 1))
normal_data = normal_data / np.tile(ranges, (nums, 1))
k = 3
max_iterations = 50
centroids, cluster = FCM.fcm(normal_data, 2, k, 1e-6)
plt.figure(figsize=(12, 5), dpi=80)
plt.subplot(1, 2, 1)
for Iris_type in Iris_types:
plt.scatter(Iris_data[x_axis], Iris_data[y_axis], c='black')
plt.title('raw')
plt.subplot(1, 2, 2)
for centroid_id, centroid in enumerate(centroids):
current_examples_index = (cluster == centroid_id).flatten()
plt.scatter(normal_data[current_examples_index, 0], normal_data[current_examples_index, 1])
for centroid_id, centroid in enumerate(centroids):
plt.scatter(centroid[0], centroid[1], c='red', marker='x')
plt.title('label kemans')
plt.show()
五:算法缺陷
- 对离群点十分敏感
- 算法很可能是一个局部最优解
- 算法运算量十分大
- 算法伸缩性差,不适合处理大规模数据集合
- 模糊系数
m
m
m对聚类结果影响非常大
|