目錄
本文采用數據集為iris,將iris.txt放在程序的同一文件夾下。請先自行下載好。
模糊理論
模糊控制是自動化控制領域的一項經典方法。其原理則是模糊數學、模糊邏輯。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是類標號;表示樣本
屬於j類的隸屬度。i表示第i個樣本,x是具有d維特征的一個樣本。
是j簇的中心,也具有d維度。||*||可以是任意表示距離的度量。》。
模糊c是一個不斷迭代計算隸屬度和簇中心
的過程,直到他們達到最優。
,
注:對於單個樣本,它對於每個簇的隸屬度之和為1。
迭代的終止條件為:
其中k是迭代步數,是誤差閾值。上式含義是,繼續迭代下去,隸屬程度也不會發生較大的變化。即認為隸屬度不變了,已經達到比較優(局部最優或全局最優)狀態了。該過程收斂於目標Jm的局部最小值或鞍點。
拋開復雜的算式,這個算法的意思就是:給每個樣本賦予屬於每個簇的隸屬度函數。通過隸屬度值大小來將樣本歸類。
算法步驟
1、初始化
通常采用隨機初始化。即權值隨機地選取。簇數需要人為選定。
2、計算質心
FCM中的質心有別於傳統質心的地方在於,它是以隸屬度為權重做一個加權平均。
3、更新模糊偽划分
即更新權重(隸屬度)。簡單地說,如果x越靠近質心c,則隸屬度越高,反之越低。
python實現
這段代碼是以iris數據集為例的,雛形源於網絡,在錯誤的地方做了一些修正。是專門針對iris寫的:
如果要使用你自己的數據集,請看第二段代碼。
-
#!/usr/bin/env python3
-
# -*- coding: utf-8 -*-
-
"""
-
Created on Wed Mar 27 10:51:45 2019
-
@author: youxinlin
-
"""
-
import copy
-
import math
-
import random
-
import time
-
-
global MAX
# 用於初始化隸屬度矩陣U
-
MAX =
10000.0
-
-
global Epsilon
# 結束條件
-
Epsilon =
0.0000001
-
-
def import_data_format_iris(file):
-
"""
-
file這里是輸入文件的路徑,如iris.txt.
-
格式化數據,前四列為data,最后一列為類標號(有0,1,2三類)
-
如果是你自己的data,就不需要執行此段函數了。
-
"""
-
data = []
-
cluster_location =[]
-
with open(str(file),
'r')
as f:
-
for line
in f:
-
current = line.strip().split(
",")
#對每一行以逗號為分割,返回一個list
-
current_dummy = []
-
for j
in range(
0, len(current)
-1):
-
current_dummy.append(float(current[j]))
#current_dummy存放data
-
-
#下面注這段話提供了一個范例:若類標號不是0,1,2之類數字時該怎么給數據集
-
j +=
1
-
if current[j] ==
"Iris-setosa\n":
-
cluster_location.append(
0)
-
elif current[j] ==
"Iris-versicolor\n":
-
cluster_location.append(
1)
-
else:
-
cluster_location.append(
2)
-
data.append(current_dummy)
-
print(
"加載數據完畢")
-
return data
-
# return data , cluster_location
-
-
def randomize_data(data):
-
"""
-
該功能將數據隨機化,並保持隨機化順序的記錄
-
"""
-
order = list(range(
0, len(data)))
-
random.shuffle(order)
-
new_data = [[]
for i
in range(
0, len(data))]
-
for index
in range(
0, len(order)):
-
new_data[index] = data[order[index]]
-
return new_data, order
-
-
def de_randomise_data(data, order):
-
"""
-
此函數將返回數據的原始順序,將randomise_data()返回的order列表作為參數
-
"""
-
new_data = [[]
for i
in range(
0, len(data))]
-
for index
in range(len(order)):
-
new_data[order[index]] = data[index]
-
return new_data
-
-
def print_matrix(list):
-
"""
-
以可重復的方式打印矩陣
-
"""
-
for i
in range(
0, len(list)):
-
print (list[i])
-
-
def initialize_U(data, cluster_number):
-
"""
-
這個函數是隸屬度矩陣U的每行加起來都為1. 此處需要一個全局變量MAX.
-
"""
-
global MAX
-
U = []
-
for i
in range(
0, len(data)):
-
current = []
-
rand_sum =
0.0
-
for j
in range(
0, cluster_number):
-
dummy = random.randint(
1,int(MAX))
-
current.append(dummy)
-
rand_sum += dummy
-
for j
in range(
0, cluster_number):
-
current[j] = current[j] / rand_sum
-
U.append(current)
-
return U
-
-
def distance(point, center):
-
"""
-
該函數計算2點之間的距離(作為列表)。我們指歐幾里德距離。閔可夫斯基距離
-
"""
-
if len(point) != len(center):
-
return
-1
-
dummy =
0.0
-
for i
in range(
0, len(point)):
-
dummy += abs(point[i] - center[i]) **
2
-
return math.sqrt(dummy)
-
-
def end_conditon(U, U_old):
-
"""
-
結束條件。當U矩陣隨着連續迭代停止變化時,觸發結束
-
"""
-
global Epsilon
-
for i
in range(
0, len(U)):
-
for j
in range(
0, len(U[
0])):
-
if abs(U[i][j] - U_old[i][j]) > Epsilon :
-
return
False
-
return
True
-
-
def normalise_U(U):
-
"""
-
在聚類結束時使U模糊化。每個樣本的隸屬度最大的為1,其余為0
-
"""
-
for i
in range(
0, len(U)):
-
maximum = max(U[i])
-
for j
in range(
0, len(U[
0])):
-
if U[i][j] != maximum:
-
U[i][j] =
0
-
else:
-
U[i][j] =
1
-
return U
-
-
# m的最佳取值范圍為[1.5,2.5]
-
def fuzzy(data, cluster_number, m):
-
"""
-
這是主函數,它將計算所需的聚類中心,並返回最終的歸一化隸屬矩陣U.
-
參數是:簇數(cluster_number)和隸屬度的因子(m)
-
"""
-
# 初始化隸屬度矩陣U
-
U = initialize_U(data, cluster_number)
-
# print_matrix(U)
-
# 循環更新U
-
while (
True):
-
# 創建它的副本,以檢查結束條件
-
U_old = copy.deepcopy(U)
-
# 計算聚類中心
-
C = []
-
for j
in range(
0, cluster_number):
-
current_cluster_center = []
-
for i
in range(
0, len(data[
0])):
-
dummy_sum_num =
0.0
-
dummy_sum_dum =
0.0
-
for k
in range(
0, len(data)):
-
# 分子
-
dummy_sum_num += (U[k][j] ** m) * data[k][i]
-
# 分母
-
dummy_sum_dum += (U[k][j] ** m)
-
# 第i列的聚類中心
-
current_cluster_center.append(dummy_sum_num/dummy_sum_dum)
-
# 第j簇的所有聚類中心
-
C.append(current_cluster_center)
-
-
# 創建一個距離向量, 用於計算U矩陣。
-
distance_matrix =[]
-
for i
in range(
0, len(data)):
-
current = []
-
for j
in range(
0, cluster_number):
-
current.append(distance(data[i], C[j]))
-
distance_matrix.append(current)
-
-
# 更新U
-
for j
in range(
0, cluster_number):
-
for i
in range(
0, len(data)):
-
dummy =
0.0
-
for k
in range(
0, cluster_number):
-
# 分母
-
dummy += (distance_matrix[i][j ] / distance_matrix[i][k]) ** (
2/(m
-1))
-
U[i][j] =
1 / dummy
-
-
if end_conditon(U, U_old):
-
print (
"結束聚類")
-
break
-
print (
"標准化 U")
-
U = normalise_U(U)
-
return U
-
-
def checker_iris(final_location):
-
"""
-
和真實的聚類結果進行校驗比對
-
"""
-
right =
0.0
-
for k
in range(
0,
3):
-
checker =[
0,
0,
0]
-
for i
in range(
0,
50):
-
for j
in range(
0, len(final_location[
0])):
-
if final_location[i + (
50*k)][j] ==
1:
#i+(50*k)表示 j表示第j類
-
checker[j] +=
1
#checker分別統計每一類分類正確的個數
-
right += max(checker)
#累加分類正確的個數
-
print (
'分類正確的個數是:',right)
-
answer = right /
150 *
100
-
return
"准確率:" + str(answer) +
"%"
-
-
if __name__ ==
'__main__':
-
-
# 加載數據
-
data = import_data_format_iris(
"iris.txt")
-
# print_matrix(data)
-
-
# 隨機化數據
-
data , order = randomize_data(data)
-
# print_matrix(data)
-
-
start = time.time()
-
# 現在我們有一個名為data的列表,它只是數字
-
# 我們還有另一個名為cluster_location的列表,它給出了正確的聚類結果位置
-
# 調用模糊C均值函數
-
final_location = fuzzy(data ,
3 ,
2)
-
-
# 還原數據
-
final_location = de_randomise_data(final_location, order)
-
# print_matrix(final_location)
-
-
# 准確度分析
-
print (checker_iris(final_location))
-
print (
"用時:{0}".format(time.time() - start))
如果要用你自己的數據集做聚類:替換下面代碼的data為你自己的數據集;自己寫一個准確率的判斷方法。
-
#!/usr/bin/env python3
-
# -*- coding: utf-8 -*-
-
"""
-
Created on Wed Mar 27 10:51:45 2019
-
模糊c聚類:https://blog.csdn.net/lyxleft/article/details/88964494
-
@author: youxinlin
-
"""
-
import copy
-
import math
-
import random
-
import time
-
-
global MAX
# 用於初始化隸屬度矩陣U
-
MAX =
10000.0
-
-
global Epsilon
# 結束條件
-
Epsilon =
0.0000001
-
-
def print_matrix(list):
-
"""
-
以可重復的方式打印矩陣
-
"""
-
for i
in range(
0, len(list)):
-
print (list[i])
-
-
def initialize_U(data, cluster_number):
-
"""
-
這個函數是隸屬度矩陣U的每行加起來都為1. 此處需要一個全局變量MAX.
-
"""
-
global MAX
-
U = []
-
for i
in range(
0, len(data)):
-
current = []
-
rand_sum =
0.0
-
for j
in range(
0, cluster_number):
-
dummy = random.randint(
1,int(MAX))
-
current.append(dummy)
-
rand_sum += dummy
-
for j
in range(
0, cluster_number):
-
current[j] = current[j] / rand_sum
-
U.append(current)
-
return U
-
-
def distance(point, center):
-
"""
-
該函數計算2點之間的距離(作為列表)。我們指歐幾里德距離。閔可夫斯基距離
-
"""
-
if len(point) != len(center):
-
return
-1
-
dummy =
0.0
-
for i
in range(
0, len(point)):
-
dummy += abs(point[i] - center[i]) **
2
-
return math.sqrt(dummy)
-
-
def end_conditon(U, U_old):
-
"""
-
結束條件。當U矩陣隨着連續迭代停止變化時,觸發結束
-
"""
-
global Epsilon
-
for i
in range(
0, len(U)):
-
for j
in range(
0, len(U[
0])):
-
if abs(U[i][j] - U_old[i][j]) > Epsilon :
-
return
False
-
return
True
-
-
def normalise_U(U):
-
"""
-
在聚類結束時使U模糊化。每個樣本的隸屬度最大的為1,其余為0
-
"""
-
for i
in range(
0, len(U)):
-
maximum = max(U[i])
-
for j
in range(
0, len(U[
0])):
-
if U[i][j] != maximum:
-
U[i][j] =
0
-
else:
-
U[i][j] =
1
-
return U
-
-
-
def fuzzy(data, cluster_number, m):
-
"""
-
這是主函數,它將計算所需的聚類中心,並返回最終的歸一化隸屬矩陣U.
-
輸入參數:簇數(cluster_number)、隸屬度的因子(m)的最佳取值范圍為[1.5,2.5]
-
"""
-
# 初始化隸屬度矩陣U
-
U = initialize_U(data, cluster_number)
-
# print_matrix(U)
-
# 循環更新U
-
while (
True):
-
# 創建它的副本,以檢查結束條件
-
U_old = copy.deepcopy(U)
-
# 計算聚類中心
-
C = []
-
for j
in range(
0, cluster_number):
-
current_cluster_center = []
-
for i
in range(
0, len(data[
0])):
-
dummy_sum_num =
0.0
-
dummy_sum_dum =
0.0
-
for k
in range(
0, len(data)):
-
# 分子
-
dummy_sum_num += (U[k][j] ** m) * data[k][i]
-
# 分母
-
dummy_sum_dum += (U[k][j] ** m)
-
# 第i列的聚類中心
-
current_cluster_center.append(dummy_sum_num/dummy_sum_dum)
-
# 第j簇的所有聚類中心
-
C.append(current_cluster_center)
-
-
# 創建一個距離向量, 用於計算U矩陣。
-
distance_matrix =[]
-
for i
in range(
0, len(data)):
-
current = []
-
for j
in range(
0, cluster_number):
-
current.append(distance(data[i], C[j]))
-
distance_matrix.append(current)
-
-
# 更新U
-
for j
in range(
0, cluster_number):
-
for i
in range(
0, len(data)):
-
dummy =
0.0
-
for k
in range(
0, cluster_number):
-
# 分母
-
dummy += (distance_matrix[i][j ] / distance_matrix[i][k]) ** (
2/(m
-1))
-
U[i][j] =
1 / dummy
-
-
if end_conditon(U, U_old):
-
print (
"已完成聚類")
-
break
-
-
U = normalise_U(U)
-
return U
-
-
-
if __name__ ==
'__main__':
-
data= [[
6.1,
2.8,
4.7,
1.2], [
5.1,
3.4,
1.5,
0.2], [
6.0,
3.4,
4.5,
1.6], [
4.6,
3.1,
1.5,
0.2], [
6.7,
3.3,
5.7,
2.1], [
7.2,
3.0,
5.8,
1.6], [
6.7,
3.1,
4.4,
1.4], [
6.4,
2.7,
5.3,
1.9], [
4.8,
3.0,
1.4,
0.3], [
7.9,
3.8,
6.4,
2.0], [
5.2,
3.5,
1.5,
0.2], [
5.9,
3.0,
5.1,
1.8], [
5.7,
2.8,
4.1,
1.3], [
6.8,
3.2,
5.9,
2.3], [
5.4,
3.4,
1.5,
0.4], [
5.4,
3.7,
1.5,
0.2], [
6.6,
3.0,
4.4,
1.4], [
5.1,
3.5,
1.4,
0.2], [
6.0,
2.2,
4.0,
1.0], [
7.7,
2.8,
6.7,
2.0], [
6.3,
2.8,
5.1,
1.5], [
7.4,
2.8,
6.1,
1.9], [
5.5,
4.2,
1.4,
0.2], [
5.7,
3.0,
4.2,
1.2], [
5.5,
2.6,
4.4,
1.2], [
5.2,
3.4,
1.4,
0.2], [
4.9,
3.1,
1.5,
0.1], [
4.6,
3.6,
1.0,
0.2], [
4.6,
3.2,
1.4,
0.2], [
5.8,
2.7,
3.9,
1.2], [
5.0,
3.4,
1.5,
0.2], [
6.1,
3.0,
4.6,
1.4], [
4.7,
3.2,
1.6,
0.2], [
6.7,
3.3,
5.7,
2.5], [
6.5,
3.0,
5.8,
2.2], [
5.4,
3.4,
1.7,
0.2], [
5.8,
2.7,
5.1,
1.9], [
5.4,
3.9,
1.3,
0.4], [
5.3,
3.7,
1.5,
0.2], [
6.1,
3.0,
4.9,
1.8], [
7.2,
3.2,
6.0,
1.8], [
5.5,
2.3,
4.0,
1.3], [
5.7,
2.8,
4.5,
1.3], [
4.9,
2.4,
3.3,
1.0], [
5.4,
3.0,
4.5,
1.5], [
5.0,
3.5,
1.6,
0.6], [
5.2,
4.1,
1.5,
0.1], [
5.8,
4.0,
1.2,
0.2], [
5.4,
3.9,
1.7,
0.4], [
6.5,
3.2,
5.1,
2.0], [
5.5,
2.4,
3.7,
1.0], [
5.0,
3.5,
1.3,
0.3], [
6.3,
2.5,
5.0,
1.9], [
6.9,
3.1,
4.9,
1.5], [
6.2,
2.2,
4.5,
1.5], [
6.3,
3.3,
4.7,
1.6], [
6.4,
3.2,
4.5,
1.5], [
4.7,
3.2,
1.3,
0.2], [
5.5,
2.4,
3.8,
1.1], [
5.0,
2.0,
3.5,
1.0], [
4.4,
2.9,
1.4,
0.2], [
4.8,
3.4,
1.9,
0.2], [
6.3,
3.4,
5.6,
2.4], [
5.5,
2.5,
4.0,
1.3], [
5.7,
2.5,
5.0,
2.0], [
6.5,
3.0,
5.2,
2.0], [
6.7,
3.0,
5.0,
1.7], [
5.2,
2.7,
3.9,
1.4], [
6.9,
3.1,
5.1,
2.3], [
7.2,
3.6,
6.1,
2.5], [
4.8,
3.0,
1.4,
0.1], [
6.3,
2.9,
5.6,
1.8], [
5.1,
3.5,
1.4,
0.3], [
6.9,
3.1,
5.4,
2.1], [
5.6,
3.0,
4.1,
1.3], [
7.7,
2.6,
6.9,
2.3], [
6.4,
2.9,
4.3,
1.3], [
5.8,
2.7,
4.1,
1.0], [
6.1,
2.9,
4.7,
1.4], [
5.7,
2.9,
4.2,
1.3], [
6.2,
2.8,
4.8,
1.8], [
4.8,
3.4,
1.6,
0.2], [
5.6,
2.9,
3.6,
1.3], [
6.7,
2.5,
5.8,
1.8], [
5.0,
3.4,
1.6,
0.4], [
6.3,
3.3,
6.0,
2.5], [
5.1,
3.8,
1.9,
0.4], [
6.6,
2.9,
4.6,
1.3], [
5.1,
3.3,
1.7,
0.5], [
6.3,
2.5,
4.9,
1.5], [
6.4,
3.1,
5.5,
1.8], [
6.2,
3.4,
5.4,
2.3], [
6.7,
3.1,
5.6,
2.4], [
4.6,
3.4,
1.4,
0.3], [
5.5,
3.5,
1.3,
0.2], [
5.6,
2.7,
4.2,
1.3], [
5.6,
2.8,
4.9,
2.0], [
6.2,
2.9,
4.3,
1.3], [
7.0,
3.2,
4.7,
1.4], [
5.0,
3.2,
1.2,
0.2], [
4.3,
3.0,
1.1,
0.1], [
7.7,
3.8,
6.7,
2.2], [
5.6,
3.0,
4.5,
1.5], [
5.8,
2.7,
5.1,
1.9], [
5.8,
2.8,
5.1,
2.4], [
4.9,
3.1,
1.5,
0.1], [
5.7,
3.8,
1.7,
0.3], [
7.1,
3.0,
5.9,
2.1], [
5.1,
3.7,
1.5,
0.4], [
6.3,
2.7,
4.9,
1.8], [
6.7,
3.0,
5.2,
2.3], [
5.1,
2.5,
3.0,
1.1], [
7.6,
3.0,
6.6,
2.1], [
4.5,
2.3,
1.3,
0.3], [
4.9,
3.0,
1.4,
0.2], [
6.5,
2.8,
4.6,
1.5], [
5.7,
4.4,
1.5,
0.4], [
6.8,
3.0,
5.5,
2.1], [
4.9,
2.5,
4.5,
1.7], [
5.1,
3.8,
1.5,
0.3], [
6.5,
3.0,
5.5,
1.8], [
5.7,
2.6,
3.5,
1.0], [
5.1,
3.8,
1.6,
0.2], [
5.9,
3.0,
4.2,
1.5], [
6.4,
3.2,
5.3,
2.3], [
4.4,
3.0,
1.3,
0.2], [
6.1,
2.8,
4.0,
1.3], [
6.3,
2.3,
4.4,
1.3], [
5.0,
2.3,
3.3,
1.0], [
5.0,
3.6,
1.4,
0.2], [
5.9,
3.2,
4.8,
1.8], [
6.4,
2.8,
5.6,
2.2], [
6.1,
2.6,
5.6,
1.4], [
5.6,
2.5,
3.9,
1.1], [
6.0,
2.7,
5.1,
1.6], [
6.0,
3.0,
4.8,
1.8], [
6.4,
2.8,
5.6,
2.1], [
6.0,
2.9,
4.5,
1.5], [
5.8,
2.6,
4.0,
1.2], [
7.7,
3.0,
6.1,
2.3], [
5.0,
3.3,
1.4,
0.2], [
6.9,
3.2,
5.7,
2.3], [
6.8,
2.8,
4.8,
1.4], [
4.8,
3.1,
1.6,
0.2], [
6.7,
3.1,
4.7,
1.5], [
4.9,
3.1,
1.5,
0.1], [
7.3,
2.9,
6.3,
1.8], [
4.4,
3.2,
1.3,
0.2], [
6.0,
2.2,
5.0,
1.5], [
5.0,
3.0,
1.6,
0.2]]
-
start = time.time()
-
-
# 調用模糊C均值函數
-
res_U = fuzzy(data ,
3 ,
2)
-
# 計算准確率
-
print (
"用時:{0}".format(time.time() - start))
https://blog.csdn.net/zwqhehe/article/details/75174918