当前位置:   article > 正文

【机器学习】Fuzzy C-Means(模糊C均值聚类)原理概述和python代码实现完整版

fuzzy c-means

准备说明Python代码运行,需要有数据集,文章最后有csv格式的数据集,请自行下载。

理论知识讲解:

模糊理论

模糊控制是自动化控制领域的一项经典方法。其原理则是模糊数学、模糊逻辑。1965,L. A. Zadeh发表模糊集合“Fuzzy Sets”的论文, 首次引入隶属度函数的概念,打破了经典数学“非0即 1”的局限性,用[0,1]之间的实数来描述中间状态。

很多经典的集合(即:论域U内的某个元素是否属于集合A,可以用一个数值来表示。在经典集合中,要么0,要么1)不能描述很多事物的属性,需要用模糊性词语来判断。比如天气冷热程度、人的胖瘦程度等等。模糊数学和模糊逻辑把只取1或0二值(属于/不属于)的普通集合概念推广0~1区间内的多个取值,即隶属度。用“隶属度”来描述元素和集合之间的关系。

如图所示,对于冷热程度,我们采取三个模糊子集:冷、暖、热。对于某一个温度,可能同时属于两个子集。要进一步具体判断,我们就需要提供一个描述“程度”的函数,即隶属度。

例如,身高可以分为“高”、“中等”、“矮”三个子集。取论域U(即人的身高范围)为[1.0,3.0],单位m。在U上定义三个隶属度函数来确定身高与三个模糊子集的关系:

模糊规则的设定:

(1)专家的经验和知识

– 藉由询问经验丰富的专家,在获得系统的知 识后,将知识改为IF....THEN ....的型式。

(2)操作员的操作模式

– 记录熟练的操作员的操作模式,并将其整理为IF....THEN ....的型式。

(3)自学习

– 设定的模糊规则可能存在偏差,模糊控制器能依设定的目标,增加或修改模糊控制规则

Fuzzy C-Means算法原理

模糊c均值聚类融合了模糊理论的精髓。相较于k-means的硬聚类,模糊c提供了更加灵活的聚类结果。因为大部分情况下,数据集中的对象不能划分成为明显分离的簇,指派一个对象到一个特定的簇有些生硬,也可能会出错。故,对每个对象和每个簇赋予一个权值,指明对象属于该簇的程度。当然,基于概率的方法也可以给出这样的权值,但是有时候我们很难确定一个合适的统计模型,因此使用具有自然地、非概率特性的模糊c均值就是一个比较好的选择。

简单地说,就是要最小化目标函数Jm:(在一些资料中也定义为SSE即误差的平方和)

其中m是聚类的簇数;i,j是类标号;u_i_j表示样本x_i属于j类的隶属度。i表示第i个样本,x是具有d维特征的一个样本。c_j是j簇的中心,也具有d维度。||*||可以是任意表示距离的度量。

模糊c是一个不断迭代计算隶属度u_i_j和簇中心c_j的过程,直到他们达到最优。

注:对于单个样本x_i,它对于每个簇的隶属度之和为1。

迭代的终止条件为:

其中k是迭代步数,\varepsilon是误差阈值。上式含义是,继续迭代下去,隶属程度也不会发生较大的变化。即认为隶属度不变了,已经达到比较优(局部最优或全局最优)状态了。该过程收敛于目标Jm的局部最小值或鞍点。

抛开复杂的算式,这个算法的意思就是:给每个样本赋予属于每个簇的隶属度函数。通过隶属度值大小来将样本归类。

算法步骤

1、初始化

通常采用随机初始化。即权值随机地选取。簇数需要人为选定。

2、计算质心

FCM中的质心有别于传统质心的地方在于,它是以隶属度为权重做一个加权平均。

3、更新模糊伪划分

即更新权重(隶属度)。简单地说,如果x越靠近质心c,则隶属度越高,反之越低。

 Python代码实现:

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. @author: panggezhenbucuo
  5. """
  6. import copy
  7. import math
  8. import random
  9. import time
  10. global MAX # 用于初始化隶属度矩阵U
  11. MAX = 10000.0
  12. global Epsilon # 结束条件
  13. Epsilon = 0.0000001
  14. def import_data_format_iris(file):
  15. """
  16. file这里是输入文件的路径,如iris.txt.
  17. 格式化数据,前四列为data,最后一列为类标号(有0,1,2三类)
  18. 如果是你自己的data,就不需要执行此段函数了。
  19. """
  20. data = []
  21. cluster_location = []
  22. with open(str(file), 'r') as f:
  23. for line in f:
  24. current = line.strip().split(",") # 对每一行以逗号为分割,返回一个list
  25. print(current)
  26. current_dummy = []
  27. for j in range(0, len(current) - 1):
  28. #print(current[j])
  29. current_dummy.append(float(current[j])) # current_dummy存放data
  30. # 下面注这段话提供了一个范例:若类标号不是0,1,2之类数字时该怎么给数据集
  31. j += 1
  32. if current[j] == "Iris-setosa\n":
  33. cluster_location.append(0)
  34. elif current[j] == "Iris-versicolor\n":
  35. cluster_location.append(1)
  36. else:
  37. cluster_location.append(2)
  38. data.append(current_dummy)
  39. print("加载数据完毕")
  40. return data
  41. # return data , cluster_location
  42. def randomize_data(data):
  43. """
  44. 该功能将数据随机化,并保持随机化顺序的记录
  45. """
  46. order = list(range(0, len(data)))
  47. random.shuffle(order)
  48. new_data = [[] for i in range(0, len(data))]
  49. for index in range(0, len(order)):
  50. new_data[index] = data[order[index]]
  51. return new_data, order
  52. def de_randomise_data(data, order):
  53. """
  54. 此函数将返回数据的原始顺序,将randomise_data()返回的order列表作为参数
  55. """
  56. new_data = [[] for i in range(0, len(data))]
  57. for index in range(len(order)):
  58. new_data[order[index]] = data[index]
  59. return new_data
  60. def print_matrix(list):
  61. """
  62. 以可重复的方式打印矩阵
  63. """
  64. for i in range(0, len(list)):
  65. print(list[i])
  66. def initialize_U(data, cluster_number):
  67. """
  68. 这个函数是隶属度矩阵U的每行加起来都为1. 此处需要一个全局变量MAX.
  69. """
  70. global MAX
  71. U = []
  72. for i in range(0, len(data)):
  73. current = []
  74. rand_sum = 0.0
  75. for j in range(0, cluster_number):
  76. dummy = random.randint(1, int(MAX))
  77. current.append(dummy)
  78. rand_sum += dummy
  79. for j in range(0, cluster_number):
  80. current[j] = current[j] / rand_sum
  81. U.append(current)
  82. return U
  83. def distance(point, center):
  84. """
  85. 该函数计算2点之间的距离(作为列表)。我们指欧几里德距离。闵可夫斯基距离
  86. """
  87. if len(point) != len(center):
  88. return -1
  89. dummy = 0.0
  90. for i in range(0, len(point)):
  91. dummy += abs(point[i] - center[i]) ** 2
  92. return math.sqrt(dummy)
  93. def end_conditon(U, U_old):
  94. """
  95. 结束条件。当U矩阵随着连续迭代停止变化时,触发结束
  96. """
  97. global Epsilon
  98. for i in range(0, len(U)):
  99. for j in range(0, len(U[0])):
  100. if abs(U[i][j] - U_old[i][j]) > Epsilon:
  101. return False
  102. return True
  103. def normalise_U(U):
  104. """
  105. 在聚类结束时使U模糊化。每个样本的隶属度最大的为1,其余为0
  106. """
  107. for i in range(0, len(U)):
  108. maximum = max(U[i])
  109. for j in range(0, len(U[0])):
  110. if U[i][j] != maximum:
  111. U[i][j] = 0
  112. else:
  113. U[i][j] = 1
  114. return U
  115. # m的最佳取值范围为[1.5,2.5]
  116. def fuzzy(data, cluster_number, m):
  117. """
  118. 这是主函数,它将计算所需的聚类中心,并返回最终的归一化隶属矩阵U.
  119. 参数是:簇数(cluster_number)和隶属度的因子(m)
  120. """
  121. # 初始化隶属度矩阵U
  122. U = initialize_U(data, cluster_number)
  123. # print_matrix(U)
  124. # 循环更新U
  125. while (True):
  126. # 创建它的副本,以检查结束条件
  127. U_old = copy.deepcopy(U)
  128. # 计算聚类中心
  129. C = []
  130. for j in range(0, cluster_number):
  131. current_cluster_center = []
  132. for i in range(0, len(data[0])):
  133. dummy_sum_num = 0.0
  134. dummy_sum_dum = 0.0
  135. for k in range(0, len(data)):
  136. # 分子
  137. dummy_sum_num += (U[k][j] ** m) * data[k][i]
  138. # 分母
  139. dummy_sum_dum += (U[k][j] ** m)
  140. # 第i列的聚类中心
  141. current_cluster_center.append(dummy_sum_num / dummy_sum_dum)
  142. # 第j簇的所有聚类中心
  143. C.append(current_cluster_center)
  144. # 创建一个距离向量, 用于计算U矩阵。
  145. distance_matrix = []
  146. for i in range(0, len(data)):
  147. current = []
  148. for j in range(0, cluster_number):
  149. current.append(distance(data[i], C[j]))
  150. distance_matrix.append(current)
  151. # 更新U
  152. for j in range(0, cluster_number):
  153. for i in range(0, len(data)):
  154. dummy = 0.0
  155. for k in range(0, cluster_number):
  156. # 分母
  157. dummy += (distance_matrix[i][j] / distance_matrix[i][k]) ** (2 / (m - 1))
  158. U[i][j] = 1 / dummy
  159. if end_conditon(U, U_old):
  160. print("结束聚类")
  161. break
  162. print("标准化 U")
  163. U = normalise_U(U)
  164. return U
  165. def checker_iris(final_location):
  166. """
  167. 和真实的聚类结果进行校验比对
  168. """
  169. right = 0.0
  170. for k in range(0, 3):
  171. checker = [0, 0, 0]
  172. for i in range(0, 50):
  173. for j in range(0, len(final_location[0])):
  174. if final_location[i + (50 * k)][j] == 1: # i+(50*k)表示 j表示第j类
  175. checker[j] += 1 # checker分别统计每一类分类正确的个数
  176. right += max(checker) # 累加分类正确的个数
  177. print('分类正确的个数是:', right)
  178. answer = right / 150 * 100
  179. return "准确率:" + str(answer) + "%"
  180. if __name__ == '__main__':
  181. # 加载数据
  182. data = import_data_format_iris("Iris.csv")
  183. # print_matrix(data)
  184. # 随机化数据
  185. data, order = randomize_data(data)
  186. # print_matrix(data)
  187. start = time.time()
  188. # 现在我们有一个名为data的列表,它只是数字
  189. # 我们还有另一个名为cluster_location的列表,它给出了正确的聚类结果位置
  190. # 调用模糊C均值函数
  191. final_location = fuzzy(data, 3, 2)
  192. # 还原数据
  193. final_location = de_randomise_data(final_location, order)
  194. # print_matrix(final_location)
  195. # 准确度分析
  196. print(checker_iris(final_location))
  197. print("用时:{0}".format(time.time() - start))

运行结果展示:

最后,需要数据集和代码的朋友们,请关注如下微信公众号,回复"模糊聚类",即可获取完整内容:

微信公众号:胖哥真不错。

祝您学习愉快。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/588383
推荐阅读
相关标签
  

闽ICP备14008679号