Fuzzy Clustering详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40793975/article/details/82348358

Fuzzy Clustering详解

第十九次写博客,本人数学基础不是太好,如果有幸能得到读者指正,感激不尽,希望能借此机会向大家学习。这一篇文章是原型聚类中介绍的第三个算法,主要是谈一谈“模糊聚类”(Fuzzy   Clustering)。其他有关于原型聚类算法的讨论可以移步到该类算法的导航页《原型聚类算法综述(原型聚类算法开篇)》

模糊集合

  模糊集合论(Fuzzy   Set   Theory):允许对象以0到1之间的某个隶属度属于一个集合;
  模糊逻辑(Fuzzy   Logic):允许一个陈述以0到1之间的确定度为真。
下面以天气为例,假设有两个集合分别为“雨天”和“晴天”,那么某一天的降水概率为15%,则他在“雨天”集合中具有0.15的隶属度,而在“晴天”集合中具有0.85的隶属度。

模糊簇

  假设数据集 D = { x i | i = 1 , 2 , . . . , m } 中的每个样本点 x i = ( x i 1 , x i 2 , . . . , x i m ) 为一个 n 维向量,那么为了确保簇形成“模糊为划分”(Fuzzy   Psuedo-partion),需要满足以下条件:
1) 对于每个样本点 x i 与每个簇 C j ( j = 1 , 2 , . . . , k ) ,隶属度(权值) w i j 要满足:

2) 每个样本点 x i 的所有权值之和为1:

3) 每个簇 C j 以非零的权值至少包含一个样本点,且不以权值为1包含所有样本点:

模糊c均值(Fuzzy C-Means)

  模糊c均值(Fuzzy   C-Means)简称FCM,是K-Means的一种模糊版本,算法伪代码如下图所示,

由上图可以看出,模糊c均值与K-Means通过相似的步骤进行质心与簇划分的交替迭代更新,直到满足迭代停止条件为止。虽然两种算法均可以以最小化总误差平方和SSE为目标,但是在K-Means中,每个样本以0或1的隶属度被划分到对应的簇中,因此总SSE被定义为:

而在模糊聚类中,每个样本都可以以0到1的隶属度被划分到每个簇中,因此在模糊c均值中,总SSE定义为:

可以将上式看做是SSE的加权版本,其中, p [ 1 , ) 是确定权值 w i j 影响的指数,他的取值分为以下几种情况:
1)  p = 2 时,可以简化权值 w i j 更新公式;
2)  p 1 时,模糊c均值近乎与K-Means相同;
3)  p 越大,划分结果越模糊。

  算法第1行:模糊c均值与K-Means的初始化方法类似,通常对权值 w i j 进行随机初始化,但是要保证每个样本点 x i 的所有权值之和为1,因此也会面临局部最优的问题,具体的解决方法与在K-Means中讨论过的相同。
  算法第3行:簇质心(原型)的计算与K-Means中的更新方法类似,只不过要考虑到数据集中的所有样本点和他们对应于该簇的隶属度,具体的计算方法如下所示:

同样,如果将隶属度规定为0或1,那么上式会退化为K-Means中的质心更新公式。
  算法第4行:在更新模糊伪划分 w i j 时,需要保证每个样本点 x i 的所有权值之和为1,更新公式如下所示:

p = 2 时,上式可以简化为

直观上看,可以将隶属度(权值) w i j 理解为样本点 x i 与簇 C j 的临近度, x i 越靠近 C j w i j 越大,反之越小。
  下面是使用模糊c均值进行聚类的一个例子,从这个例子中我们可以看出,该算法产生的最终聚类结果显示出了簇中每个样本点的隶属度,与K-Means相比,他的计算复杂度明显增加,除此之外,他与K-Means有着相同的特点。


代码实现

下面是我自己实现的代码,可以自己手动输入 p 值,这里默认的是2,下面分别对不同 p 值进行了聚类实验并可视化出来。

代码细节

"""

@author: Ἥλιος
@CSDN:https://blog.csdn.net/qq_40793975/article/details/82348358

"""
print(__doc__)

import time
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from itertools import cycle, islice


# 加载数据集(从文件中)
def load_Data1(filename):
    data_set = []
    with open(filename) as fi:
        for line in fi.readlines():
            cur_line = line.strip().split('\t')
            flt_line = []
            for i in cur_line:
                flt_line.append(float(i))
            data_set.append(flt_line)
    data_mat = np.mat(data_set)  # 转化为矩阵形式
    return data_mat


# 加载数据集(自建数据集)
def load_Data2(n_samples=1500):
    # 带噪声的圆形数据
    noisy_circles = datasets.make_circles(n_samples=n_samples, factor=.5, noise=.05)

    # 带噪声的月牙形数据
    noisy_moons = datasets.make_moons(n_samples=n_samples, noise=.05)

    # 随机分布数据
    no_structure = np.random.rand(n_samples, 2), np.ones((1, n_samples), dtype=np.int32).tolist()[0]

    # 各向异性分布数据(Anisotropicly distributed data)
    random_state = 170
    X, y = datasets.make_blobs(n_samples=n_samples, random_state=random_state)
    transformation = [[0.6, -0.6], [-0.4, 0.8]]
    X_aniso = np.dot(X, transformation)
    aniso = (X_aniso, y)

    # 不同方差的气泡形数据(blobs with varied variances)
    varied = datasets.make_blobs(n_samples=n_samples, cluster_std=[1.0, 2.5, 0.5], random_state=random_state)

    # 相同方差的气泡形数据
    blobs = datasets.make_blobs(n_samples=n_samples, random_state=8)

    # 合并数据集
    data_sets = [noisy_circles, noisy_moons, no_structure, aniso, varied, blobs]
    cluster_nums = [2, 2, 3, 3, 3, 3]
    data_mats = []
    for i in range(data_sets.__len__()):
        X, y = data_sets[i]
        X = StandardScaler().fit_transform(X)  # 对数据集进行标准化处理
        X_mat = np.mat(X)
        y_mat = np.mat(y)
        data_mats.append((X_mat, y_mat))

    # 展示数据集
    plt.figure(figsize=(2.5, 14))
    plt.subplots_adjust(left=.02, right=.98, bottom=.001, top=.96, wspace=.05, hspace=.01)
    for i in range(data_sets.__len__()):
        X, y = data_sets[i]
        X = StandardScaler().fit_transform(X)  # 对数据集进行标准化处理
        plt.subplot(len(data_sets), 1, i+1)
        if i == 0:
            plt.title("Self-built Data Set", size=18)
        color = np.array(y) + 1
        plt.scatter(X[:, 0], X[:, 1], c=color, s=10)
        plt.xlim(-2.5, 2.5)
        plt.ylim(-2.5, 2.5)

    plt.show()

    return data_mats, cluster_nums


# 计算样本点A与B间距离(欧氏距离)
def dist_Euclid(VecA, VecB):
    return np.sqrt(np.sum(np.power(VecA-VecB, 2)))


# 对k个聚类中心随机初始化(随机选取数据集中的k个样本点)
def rand_initial_center1(data_mat, k):
    (m, n) = np.shape(data_mat)
    centroids = np.mat(np.zeros((k, n), dtype=np.float32))
    for i in range(k):
        index = int(np.random.rand()*m)
        centroids[i, :] = data_mat[index, :]
    return centroids


# 对k个聚类中心随机初始化(在样本空间范围内随机选取)
def rand_initial_center2(data_mat, k):
    n = np.shape(data_mat)[1]
    centroids = np.mat(np.zeros((k, n), dtype=np.float32))
    for i in range(n):
        minJ = np.min(data_mat[:, i])
        maxJ = np.max(data_mat[:, i])
        centroids[:, i] = np.mat(np.random.rand(k, 1)*(maxJ - minJ)) + minJ
    return centroids


# 模糊c均值
def fuzzy_cMeans(data_mat, k, dist_measure=dist_Euclid, p=2.0):  # p-决定权重的影响程度 p大于等于1
    m, n = np.shape(data_mat)
    weight_mat = np.mat(np.random.rand(m, k), dtype=np.float32)
    centroids = np.mat(np.zeros((k, 2)), dtype=np.float32)
    cluster_assment = np.mat(np.zeros((m, 3)), dtype=np.float32)  # 存储每个样本点的簇隶属、簇隶属度和距离所属簇质心的长度
    cluster_changed = True

    while cluster_changed:
        cluster_changed = False

        for i in range(k):  # E步:更新质心
            centroids[i, :] = (np.power(weight_mat[:, i], p).T*data_mat)/np.sum(np.power(weight_mat[:, i], p), axis=0)

        for i in range(m):  # M步:更新权重
            sum_dist_iq = 0  # 用来更新权重的分母
            for q in range(k):
                dist_iq = dist_measure(data_mat[i, :], centroids[q, :])
                sum_dist_iq += pow((1/pow(dist_iq, 2)), (1/(p-1)))
            for j in range(k):
                dist_ij = dist_measure(data_mat[i, :], centroids[j, :])
                weight_mat[i, j] = pow((1/pow(dist_ij, 2)), (1/(p-1))) / sum_dist_iq

        for i in range(m):  # 更新簇信息
            max_weight = 0
            max_weight_index = -1
            for j in range(k):
                cur_weight = weight_mat[i, j]
                if cur_weight > max_weight:
                    max_weight = cur_weight
                    max_weight_index = j
            if max_weight_index != cluster_assment[i, 0]:
                cluster_changed = True
            cluster_assment[i, 0] = max_weight_index
            cluster_assment[i, 1] = max_weight
            cluster_assment[i, 2] = dist_measure(data_mat[i, :], centroids[max_weight_index, :])

    # # 静态显示
    # plt.scatter(data_mat[:, 0].T.A[0], data_mat[:, 1].T.A[0], c=cluster_assment[:, 0].T.A[0])
    # plt.scatter(centroids[:, 0].T.tolist()[0], centroids[:, 1].T.tolist()[0], s=100, c='red', marker='x')
    # plt.show()

    return centroids, cluster_assment


# Fuzzy C-Means
data_mats, cluster_nums = load_Data2()
plt.figure(figsize=(2.5, 14))
plt.subplots_adjust(left=.02, right=.98, bottom=.001, top=.96, wspace=.05, hspace=.01)
for i in range(len(data_mats)):
    data_mat = data_mats[i][0]  # 获取自建数据集
    k = cluster_nums[i]  # 获取自建数据集的簇标记
    t0 = time.time()  # 计算运行时间
    centroids, cluster_assment = fuzzy_cMeans(data_mat, k, p=1.05)
    t1 = time.time()

    color = np.multiply(cluster_assment[:, 0].A + 1, cluster_assment[:, 1].A).T[0]  # 获得渐进色图
    plt.subplot(len(data_mats), 1, i + 1)
    if i == 0:
        plt.title("Fuzzy c-Means(Self-programming Implementation)", size=10)
    im = plt.scatter(data_mat[:, 0].T.A[0], data_mat[:, 1].T.A[0], c=color, s=10, alpha=0.5)
    plt.scatter(centroids[:, 0].T.tolist()[0], centroids[:, 1].T.tolist()[0], s=100, c='red', marker='x')
    plt.xlim(-2.5, 2.5)
    plt.ylim(-2.5, 2.5)
    plt.text(.99, .01, ('%.2fs' % (t1 - t0)).lstrip('0'), transform=plt.gca().transAxes, size=15,
             horizontalalignment='right')
    plt.colorbar(im)
plt.show()

算法效果

  上面两附图从左至右依次是 p = 1.05 p = 2 时的聚类效果,可以看出随着 p 值的增大,聚类簇越来越模糊,当 p 近似等于1时,该算法类似于K-Means,当 p 足够大时,聚类效果会变得非常不明显,大家可以去试一下,另外,这里为了方便可视化,将簇标记改为 1 , 2 , 3 , . . . ,而不是 0 , 1 , 2 , . . . ,下图也是如此。

  上图从左至右依次是:自建数据集和 p = 1.05 时的模糊c均值,可以以清楚的看出,两个簇边界上的点以某一概率隶属于某个簇,且该算法的时间复杂度高于K-Means,还有一点需要注意的是,当簇之间的距离比较远时,该算法很容易陷入局部最优。

猜你喜欢

转载自blog.csdn.net/qq_40793975/article/details/82348358