聚類算法:
聚類算法屬于無監督學習,沒有給出分類,通過相似度得到種類。
主要會講四種:Kmeans均值,層次聚類,DBSCAN,譜聚類。
再講算法前先講一下幾種衡量相似度的方法:
1.歐氏距離:
p=2時就說平時計算的幾何距離,當p趨向于正無窮的時候,其實求的就不是x,y的距離了,而是求x y中最長的一個了。因為如果x大于y,在指數增長下x回遠大于y,所以y會被忽略的。這也是比較常用的了。
2.杰卡得相似度:
這個一般用于是推薦系統的應用,比如推薦的是A,用戶選的是B,那么就可以用這個來衡量了。
3.余弦相似度:
余弦相似度一般用于對詞向量的相似度判斷,他會省略刻度的變化,正好符合向量的判斷方法。
4.Pearson相似度:
pearson相似度和cos相似度其實很像,其實就是平移到原點求余弦
5.相對熵:
這個在決策樹會用到判斷熵增變化
一下的算法中我們只會用到歐氏距離,想用其他的改一下distance函數就OK了。
==================================================================================================================
Kmeans均值算法:
算是比較簡單而且實用的方法了。
實現步驟:
1.選定k個類別中心點,u1,u2......un
2.對于每個樣本點,選定離他最近的那個類別作為一個類別。
3.將類別中心更新為所有樣本點的均值
4.重復步驟,只到均值點不再更新為止。
簡單python實現Kmeans均值:
首先是數據的準備:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sea
import pandas as pd
import random
def read_file(filename):
df = pd.read_csv(filename)
df.drop('編號' , axis=1 , inplace=True)
return df
導包,文件讀取方法。
這是數據:
編號,密度,含糖率
1,0.697,0.46
2,0.774,0.376
3,0.634,0.264
4,0.608,0.318
5,0.556,0.215
6,0.403,0.237
7,0.481,0.149
8,0.437,0.211
9,0.666,0.091
10,0.243,0.267
11,0.245,0.057
12,0.343,0.099
13,0.639,0.161
14,0.657,0.198
15,0.36,0.37
16,0.593,0.042
17,0.719,0.103
18,0.359,0.188
19,0.339,0.241
20,0.282,0.257
21,0.784,0.232
22,0.714,0.346
23,0.483,0.312
24,0.478,0.437
25,0.525,0.369
26,0.751,0.489
27,0.532,0.472
28,0.473,0.376
29,0.725,0.445
30,0.446,0.459
需要的復制到Data.txt文件里面就可以了。
dataFile = read_file('Data.txt')
data = dataFile.values
mean_data = np.mean(data , axis=0)
data -= mean_data
data = list(data)
k = 3
mean_vectors = random.sample(data , k)
'''
計算距離
'''
def dist(p1 , p2):
return np.sqrt(sum(np.square(p1 - p2)))
這是讀取文件,去均值化的操作。mean_vectors = random.sample(data , k)選取k個點作為均值。下面的就是計算歐氏距離的了。
聚類過程:
while True:
clusters = list(map((lambda x : [x]) , mean_vectors))
for sample in data:
distances = list(map((lambda m : dist(sample , m)) , mean_vectors))
min_indexs = distances.index(min(distances))
clusters[min_indexs].append(sample)
new_mean_vectors = []
for c , v in zip(clusters , mean_vectors):
new_mean_vector = sum(c)/len(c)
if all(np.divide((new_mean_vector-v),v) < np.array([0.0001,0.0001]) ):
new_mean_vectors.append(v)
else:
new_mean_vectors.append(new_mean_vector)
if np.array_equal(mean_vectors,new_mean_vectors):
break
else:
mean_vectors = new_mean_vectors
print(mean_vectors)
print(new_mean_vectors)
pass
先把中心點變成一個有三個list的大list
clusters = list(map((lambda x : [x]) , mean_vectors))
對于每一個點,計算他和三個中心點的距離,然后取最小的作為分類,放進對應的list里面。
for sample in data:
distances = list(map((lambda m : dist(sample , m)) , mean_vectors))
min_indexs = distances.index(min(distances))
clusters[min_indexs].append(sample)
new_mean_vectors = []
計算新的中心點,如果差距不大的話就作為新的中心點
for c , v in zip(clusters , mean_vectors):
new_mean_vector = sum(c)/len(c)
if all(np.divide((new_mean_vector-v),v) < np.array([0.0001,0.0001]) ):
new_mean_vectors.append(v)
else:
new_mean_vectors.append(new_mean_vector)
if np.array_equal(mean_vectors,new_mean_vectors):
break
else:
mean_vectors = new_mean_vectors
print(mean_vectors)
接下來就是畫圖了。
total_colors = ['r','y','g','b','c','m','k']
shapes = ['p','o','v','^','s']
colors = random.sample(total_colors,k)
index = 0
x = list(map((lambda x : x[0]) , mean_vectors))
y = list(map((lambda x : x[1]) , mean_vectors))
for cluster,color in zip(clusters,colors):
density = list(map(lambda arr:arr[0],cluster))
sugar_content = list(map(lambda arr:arr[1],cluster))
plt.scatter(density,sugar_content,c = color,marker=shapes[index])
index += 1
plt.scatter(x , y , c = 'blue' , marker='x')
plt.show()
說實話效果還是不錯的。
這就是一個比較簡單python實現了。
Kmeans的公式化解釋:
改進:
1.Kmeans算法如果是遇到均值偏離嚴重的,可能會導致均值不準確,可以選擇中值點。
2.如果是初始的均值點選擇不好,可能達不到全局的收斂,只能達到局部收斂。而Kmeans就是一直改進方法:改進了選擇K初始值的方法,假設已經選取了n個初始聚類中心(0<n<K),則在選取第n+1個聚類中心時:距離當前n個聚類中心越遠的點會有更高的概率被選為第n+1個聚類中心。在選取第一個聚類中心(n=1)時同樣通過隨機的方法??梢哉f這也符合我們的直覺:聚類中心當然是互相離得越遠越好。這個改進雖然直觀簡單,但是卻非常得有效。
Kmeans選擇的時候注意不要選擇最大距離的點做為下一個初始值,因為可以最大的這個點是噪音,所以只是要求遠的點有很多概率會被選擇到。
Kmeans方法總結:
優點:
1.簡單,快速,計算很方便。
2.處理大數據的時候,算法可以保持伸縮性和高效性
3.當簇近似于高斯發布時,效果最好。事實上如果涉及到減去均值然后平方都是有關于高斯發布的。因為這個就是高斯分布推出來的。
缺點:
1.在簇的平均值可被定義的情況下才有用,所以不適用于某種情況。
2.必須給出初始值的K值,對初始很敏感。
3.不適合發現非凸形狀或者是大小差別很大的形狀。
4.對噪聲點和孤立點敏感。
==================================================================================================================
層次聚類方法:
層次聚類分為兩種,一種是凝聚層次聚類,一種是分裂層次聚類。這里主要講凝聚的。
算法很簡單:一開始每一個點都是一個類別,然后計算每一個所有點里面兩個距離最小的,合并一個類,直到合并到K個類別為止,不阻止他會合并到1的。
python實現:
import numpy as np
import sklearn.datasets as datasets
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import math
from sklearn.decomposition import PCA
def get_data():
df = datasets.load_iris()
data = df.data
label = df.target
data_matrix = np.matrix(data)
label_matrix = np.matrix(label)
return data , label
def ou_distance(vec1 , vec2):
distance = 0.0
for a, b in zip(vec1, vec2):
distance += math.pow(a - b, 2)
return math.sqrt(distance)
pass
導包,得到數據,計算歐氏距離,使用的數據集是sklearn的iris數據集。
class Node(object):
def __init__(self ,
vec=None ,
left = None ,
right = None ,
distance = -1 ,
id = None ,
count = 1):
self.vec = vec
self.left = left
self.right = right
self.distance = distance
self.id = id
self.count = count
pass
定義一個節點,這個節點包括了中心向量,做節點,右節點,id號(方便找到對應的點)
class Hierarchical(object):
def __init__(self , k = 1):
assert k > 0
self.k = k
self.labels = None
self.nodesList = []
pass
def fit(self , x):
#一開始一個點就是一個類別
nodes = [Node(vec=v , id=i) for i ,v in enumerate(x)]
self.nodesList.append(nodes.copy())
distance = {}
point_num , features_num = np.shape(x)
self.labels = [-1] * point_num
currentClusterid = -1
while len(nodes) > self.k:
if len(nodes) == 30:
self.nodesList.append(nodes.copy())
elif len(nodes) == 20:
self.nodesList.append(nodes.copy())
elif len(nodes) == 15:
self.nodesList.append(nodes.copy())
elif len(nodes) == 10:
self.nodesList.append(nodes.copy())
elif len(nodes) == 5:
self.nodesList.append(nodes.copy())
min_distance = math.inf
nodes_lenght = len(nodes)
closet_part = None
for i in range(nodes_lenght - 1):
for j in range(i + 1 , nodes_lenght):
d_key = (nodes[i].id , nodes[j].id)
if d_key not in distance:
distance[d_key] = ou_distance(nodes[i].vec , nodes[j].vec)
d = distance[d_key]
if d < min_distance:
min_distance = d
closet_part = (i , j)
part1 , part2 = closet_part
node1 , node2 = nodes[part1] , nodes[part2]
new_vec = [(node1.vec[i]*node1.count + node2.vec[i]*node2.count)/
(node1.count + node2.count) for i in range(features_num)]
new_node = Node(vec=new_vec ,
left=node1,
right=node2,
distance=min_distance,
id=currentClusterid,
count=node1.count + node2.count)
currentClusterid -= 1
del nodes[part2], nodes[part1]
nodes.append(new_node)
self.nodes = nodes
self.nodesList.append(self.nodes.copy())
self.calc_label()
pass
nodes = [Node(vec=v , id=i) for i ,v in enumerate(x)]
self.nodesList.append(nodes.copy())
distance = {}
point_num , features_num = np.shape(x)
self.labels = [-1] * point_num
currentClusterid = -1
這里開始解釋。首先一開始都是一個類別,每一個類別沒有做節點右節點,那么他們自己就是一個中心了。nodeList是為了畫圖的,不用管,point_num,features_num得到點的數量和特征數量。label一開始都是-1。currentClusterid就是集合的id,因為他不是數據,只是一個中間值。反正畫圖是用不到的。不要也行,這里加了有點難理解,但是我樂意。
while len(nodes) > self.k:
if len(nodes) == 30:
self.nodesList.append(nodes.copy())
elif len(nodes) == 20:
self.nodesList.append(nodes.copy())
elif len(nodes) == 15:
self.nodesList.append(nodes.copy())
elif len(nodes) == 10:
self.nodesList.append(nodes.copy())
elif len(nodes) == 5:
self.nodesList.append(nodes.copy())
while()里面這段都是畫圖準備的。不用管。
min_distance = math.inf
nodes_lenght = len(nodes)
closet_part = None
for i in range(nodes_lenght - 1):
for j in range(i + 1 , nodes_lenght):
d_key = (nodes[i].id , nodes[j].id)
if d_key not in distance:
distance[d_key] = ou_distance(nodes[i].vec , nodes[j].vec)
d = distance[d_key]
if d < min_distance:
min_distance = d
closet_part = (i , j)
這里是計算這個數據里面最小距離的點。整個類別里面距離最小最小的點。先設置min_ditance距離為無限大。代碼很簡單,應該能看懂。
最后得到的closet_part就是距離最小的兩個編號了。
part1 , part2 = closet_part
node1 , node2 = nodes[part1] , nodes[part2]
new_vec = [(node1.vec[i]*node1.count + node2.vec[i]*node2.count)/
(node1.count + node2.count) for i in range(features_num)]
new_node = Node(vec=new_vec ,
left=node1,
right=node2,
distance=min_distance,
id=currentClusterid,
count=node1.count + node2.count)
currentClusterid -= 1
print(currentClusterid)
del nodes[part2], nodes[part1]
nodes.append(new_node)
計算出新的節點,vec為兩個點的中心,然后放進數據點中,再刪掉原來的。
self.nodes = nodes
self.nodesList.append(self.nodes.copy())
self.calc_label()
pass
最后這些是為了返回的,calclabel()是得到分類的函數,后面講到。
def calc_label(self):
for i, node in enumerate(self.nodes):
self.leaf_traversal(node, i)
def leaf_traversal(self, node: Node, label):
if node.left == None and node.right == None:
self.labels[node.id] = label
if node.left:
self.leaf_traversal(node.left, label)
if node.right:
self.leaf_traversal(node.right, label)
這里是遞歸遍歷出來所有的葉子給label值。
最后還有一個畫圖函數:
然而畫圖前要進行PCA的姜維:
pca = PCA(n_components=2)
pca.fit(data)
PCA(copy=True, n_components=2, whiten=False)
data = pca.transform(data)
def leaf_print(node , j):
if node.left == None or node.right == None:
plt.scatter(data[node.id][0] , data[node.id][1] , color = colors[j] , marker=markers[j])
if node.left:
leaf_print(node.left , j)
if node.right:
leaf_print(node.right , j)
def draw(nodes):
for i , j in zip(nodes , range(len(nodes))):
leaf_print(i,(j+1)%len(markers))
plt.title(len(nodes))
plt.show()
應該很好理解的。
另外還有一些顏色啊,,形狀啊等等的,也給出來:
colors = {
'burlywood': '#DEB887',
'cadetblue': '#5F9EA0',
'chartreuse': '#7FFF00',
'chocolate': '#D2691E',
'cornflowerblue': '#6495ED',
'cornsilk': '#FFF8DC',
'crimson': '#DC143C',
'cyan': '#00FFFF',
'darkblue': '#00008B',
'darkcyan': '#008B8B',
'darkgoldenrod': '#B8860B',
'darkgray': '#A9A9A9',
'darkgreen': '#006400',
'darkkhaki': '#BDB76B',
'darkmagenta': '#8B008B',
'darkolivegreen': '#556B2F',
'darkorange': '#FF8C00',
'darkorchid': '#9932CC',
'darkred': '#8B0000',
'darksalmon': '#E9967A',
'darkseagreen': '#8FBC8F',
'darkslateblue': '#483D8B',
'darkslategray': '#2F4F4F',
'darkturquoise': '#00CED1',
'darkviolet': '#9400D3',
'deeppink': '#FF1493',
'deepskyblue': '#00BFFF',
'dimgray': '#696969',
'dodgerblue': '#1E90FF',
'firebrick': '#B22222',
'floralwhite': '#FFFAF0',
'forestgreen': '#228B22',
'fuchsia': '#FF00FF',
'gainsboro': '#DCDCDC',
'ghostwhite': '#F8F8FF',
'gold': '#FFD700',
'goldenrod': '#DAA520',
'gray': '#808080',
'green': '#008000',
'greenyellow': '#ADFF2F',
'honeydew': '#F0FFF0',
'hotpink': '#FF69B4',
'indianred': '#CD5C5C',
'indigo': '#4B0082',
'ivory': '#FFFFF0',
'khaki': '#F0E68C',
'lavender': '#E6E6FA',
'lavenderblush': '#FFF0F5',
'lawngreen': '#7CFC00',
'lemonchiffon': '#FFFACD',
'mediumpurple': '#9370DB',
'mediumseagreen': '#3CB371',
'mediumslateblue': '#7B68EE',
'mediumspringgreen': '#00FA9A',
'mediumturquoise': '#48D1CC',
'mediumvioletred': '#C71585',
'midnightblue': '#191970',
'mintcream': '#F5FFFA',
'mistyrose': '#FFE4E1',
'moccasin': '#FFE4B5',
'navajowhite': '#FFDEAD',
'navy': '#000080',
'oldlace': '#FDF5E6',
'olive': '#808000',
'olivedrab': '#6B8E23',
'orange': '#FFA500',
'orangered': '#FF4500',
'orchid': '#DA70D6',
'palegoldenrod': '#EEE8AA',
'palegreen': '#98FB98',
'paleturquoise': '#AFEEEE',
'palevioletred': '#DB7093',
'papayawhip': '#FFEFD5',
'peachpuff': '#FFDAB9',
'peru': '#CD853F',
'pink': '#FFC0CB',
'plum': '#DDA0DD',
'powderblue': '#B0E0E6',
'purple': '#800080',
'red': '#FF0000',
'rosybrown': '#BC8F8F',
'royalblue': '#4169E1',
'saddlebrown': '#8B4513',
'salmon': '#FA8072',
'sandybrown': '#FAA460',
'seagreen': '#2E8B57',
'seashell': '#FFF5EE',
'sienna': '#A0522D',
'silver': '#C0C0C0',
'skyblue': '#87CEEB',
'slateblue': '#6A5ACD',
'slategray': '#708090',
'snow': '#FFFAFA',
'springgreen': '#00FF7F',
'steelblue': '#4682B4',
'tan': '#D2B48C',
}
colors = list(colors.values())
不要標簽只要值。
markers = ['.' , ',' , 'o' , 'v' , '^', '<' , '>' , '1','2','3','4','s' , 'p' , '*' , 'h' , 'H' , 'd' , '|' , '+' , 'x']
這個是形狀。
運行:
my = Hierarchical(4)
data , label = get_data()
my.fit(data)
上面的fit數據,喂給它姜維之后或者是不姜維都可以的。
剛剛的self.nodelist就是保存過程的,分別保存了最開始的150個類別的,30,20,15,10,5個類別的過程。
for nodes in my.nodesList:
draw(nodes)
pass
效果:
最后分出來的類別:
可以看到效果其實很好的,和原分類對比其實差不了多少。隨著整個過程下來可以看到分類效果是越來越明顯了。其實距離的選擇有很多種:
==================================================================================================================================
DBSCAN密度聚類算法:
這種算法的指導思想是只要密度大于某個閾值就把他加入到附近的簇中。不必要知道要多少個分類,可以發現任意形狀的簇,包括非凸的,而且都噪音數據不敏感。
密度聚類概念:
算法流程:
1.如果一個點的領域包括了多于m個點的對象,那么就把他作為一個核心對象。
2.尋找直接密度可達的對象
3.沒有更新時結束
包含過少對象的會被認為是噪聲。
python實現:
還是使用iris數據集:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import sklearn.datasets as datasets
import math
def get_data():
data = datasets.load_iris().data
dataset = []
for d in data:
d1 = (d[0] , d[1] , d[2] , d[3])
dataset.append(d1)
return dataset
得到數據集。
#計算歐氏距離
def dist(vec1 , vec2):
distance = 0.0
for a, b in zip(vec1, vec2):
distance += math.pow(a - b, 2)
return math.sqrt(distance)
pass
計算歐氏距離
def DBSCAN(D, e, Minpts):
#初始化核心對象集合T,聚類個數k,聚類集合C, 未訪問集合P,
T = set()
k = 0
C = []
P = set(D)
for d in D:
if len([ i for i in D if dist(d, i) <= e]) >= Minpts:
T.add(d)
#開始聚類
while len(T):
P_old = P
#隨機選擇一個核心對象
o = list(T)[np.random.randint(0, len(T))]
Q = []
#加入集合
Q.append(o)
while len(Q):
q = Q[0]
Nq = [i for i in D if dist(q, i) <= e]
if len(Nq) >= Minpts:
S = P & set(Nq)
Q += (list(S))
P = P - S
Q.remove(q)
k += 1
Ck = list(P_old - P)
T = T - set(Ck)
C.append(Ck)
return C , P
聚類算法的實現,解釋一下主要步驟:
for d in D:
if len([ i for i in D if dist(d, i) <= e]) >= Minpts:
T.add(d)
選擇出所有的核心對象。
while len(T):
P_old = P
#隨機選擇一個核心對象
o = list(T)[np.random.randint(0, len(T))]
Q = []
#加入集合 Q集合是待選擇的密度可達核心點
Q.append(o)
while len(Q):
q = Q[0]
Nq = [i for i in D if dist(q, i) <= e]要計算里面的點是不是核心點,因為里面存的不僅僅是核心點,而是核心點周圍的點
if len(Nq) >= Minpts:是核心點
S = P & set(Nq)p是沒有被選擇的點 set(Nq是核心對象周圍的點) 那么S就是類別了。
Q += (list(S))重復上訴步驟,看看周圍的點哪些是核心對象點,把他周圍的點拉進來,在不斷擴張
P = P - S選出去了,可以不要了
Q.remove(q)用了 不要了
k += 1
Ck = list(P_old - P)剩下的就是被選取的
T = T - set(Ck)這些核心點已經被用過了,可以丟棄,要不永遠都不會結束的
C.append(Ck)分出來的一個類別
上面的注釋已經解釋的很清楚了。。。。。。
C , P= DBSCAN(data, 0.6, 4)
C是分類,P是噪音點,沒有被選擇的
接下來就是畫圖了:
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(data)
data2 = pca.transform(data)
def Draw(C):
for c , i in zip(C , range(len(C))):
for s in c:
s = data.index(s)
s = data2[s]
plt.scatter(s[0] , s[1] , color = colors[i])
pass
for i in P:
i = data.index(i)
i = data2[i]
plt.scatter(i[0] , i[1] , color = 'black')
pass
plt.show()
pass
使用的colors顏色是剛剛層次聚類的那個。
Draw(C)
效果:
效果其實不夠剛剛的好。其實在實現的過程中發現一個很重要的問題,參數選擇不好做什么都是假的。一開始不知道怎么選半徑和個數,每次出來都是1,最后是上網看了別人用的這個數據才知道選0.6和5的。試一個晚上了,還以為是代碼錯誤。
==================================================================================================================================
譜聚類
譜聚類是一種基于拉普拉斯矩陣的特征向量的聚類算法。
計算過程:
注意在選取特征的時候,要選擇前k小的特征,而不是最大的。
原因:
python實現:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
import sklearn.datasets as dataset
import seaborn as sns
import math
def pca(data):
pca = PCA(n_components=2)
pca.fit(data)
return pca.transform(data)
def get_data():
data = dataset.load_iris().data
data = pca(data)
return data
data = get_data()
def dist(vec1 , vec2):
distance = 0.0
for a, b in zip(vec1, vec2):
distance += math.pow(a - b, 2)
return math.sqrt(distance)
降維,得到數據,歐氏距離。
def getWbyKNN(data , K):
point_num = len(data)
dis_num = np.zeros((point_num , point_num))
W = np.zeros((point_num , point_num))
for i in range(point_num):
for j in range(i+1 , point_num):
dis_num[i][j] = dis_num[j][i] = dist(data[i] , data[j])
for idx , each in enumerate(dis_num):
index_distance = np.argsort(each)
W[idx][index_distance[1 : K+1]] = 1
tmp_W = np.transpose(W)
W = (tmp_W + W)/2
return W
pass
得到W相似矩陣。首先計算出一個距離矩陣,包含了所以的距離。然后排序,第一個小的點肯定是自己,所以1開始,前k小的設為1,其他的設為0。這時候得到的這個矩陣并一定是對稱的,所以加上轉置除2來對稱化。
#得到度矩陣
def getD(W):
point_num = len(W)
D = np.diag(np.zeros(point_num))
for i in range(point_num):
D[i][i] = sum(W[i])
return D
pass
通過W相似矩陣得到D。
#得到游走拉普拉斯矩陣 I - D-1W
def getLPLSmatrix(W , D):
D = np.linalg.inv(D)
I = np.eye(len(W[0]) , 1)
return (I - D.dot(W))
pass
得到拉普拉斯矩陣
W = getWbyKNN(data , 5)
D = getD(W)
L = getLPLSmatrix(W , D)
之后就是獲得特征值特征向量了。
def getEigVec(L,cluster_num): #從拉普拉斯矩陣獲得特征矩陣
eigval,eigvec = np.linalg.eig(L)
dim = len(eigval)
dictEigval = dict(zip(eigval,range(0,dim)))
kEig = np.sort(eigval)[0:cluster_num]
ix = [dictEigval[k] for k in kEig]
return eigval[ix],eigvec[:,ix]
eigval , eigvec = getEigVec(L , 5)
得到前5個
最后用Kmeans來計算
clk = KMeans(4)
clk.fit(eigvec)
C = clk.labels_
這是最后得到的標簽:
效果差不多吧。
colors = ['r' , 'b' , 'g' , 'y']
markers = ['x' , '^' , '+' , 's']
def draw(data , label):
for i , j in zip(data , label):
plt.scatter(i[0] , i[1] , color = colors[j] , marker=markers[j])
plt.show()
pass
畫圖:
這就是最后的效果了。
總體來說還是凝聚層次聚類好些。還有一些聚類判斷指標沒有寫,等看書了再不全吧,現在還是理論階段。
還有其他的距離模型,比如som神經網絡,GMM高斯混合模型等等,學到在說吧。