一、第一種初始化簇中心的方法:隨機產生k個簇中心,保證簇中心的每個維度的取值都在這個緯度所有值的最小值與最大值的左閉右開區間內
import numpy as np class KMeans_1: def __init__(self,k_clusters,tol=1e-4,max_iter=300): self.k_clusters=k_clusters self.tol=tol self.max_iter=max_iter #生成隨機的k個聚類中心,每個維度都在最小最大值范圍內 def _init_centers_random(self,X,k_clusters): _,n=X.shape xmin=np.min(X,axis=0) xmax=np.max(X,axis=0) return xmin+(xmax-xmin)*np.random.rand(k_clusters,n) def _kmeans(self,X): '''K-Means核心算法''' m,n=X.shape #label存儲對每一個實例的划分標記 labels=np.zeros(m,dtype=np.int) #distance為m*k的矩陣,表示每個樣本到每個簇中心的距離 distances = np.empty((m,self.k_clusters)) #centers_old存儲之前的質心點 centers_old = np.empty((self.k_clusters,n)) #初始化簇中心 centers=self._init_centers_random(X,self.k_clusters) for _ in range(self.max_iters): #1、分類標簽 for i in range(self.k_clusters): #計算m個實例到質心點的距離 np.sum((X-centers[i])**2,axis=1,out=distances[:,i]) #將m個實例划分到距離最小的那個類中 np.argmin(distances,axis=1,out=labels) #2、計算質心點 #保存之前的質心點 np.copyto(centers_old,centers) for i in range(self.k_clusters): cluster = X[labels==i] if cluster.size==0: return None #計算新的簇中心 np.mean(cluster,axis=0,out=centers[i]) #3、判斷是否收斂,求每個簇中心與原來的位置的距離和 delta_centers = np.sqrt(np.sum((centers-centers_old)**2,axis=1)) #每個簇中心點的變化都小於閾值 if np.all(delta_centers < self.tol): break #計算簇內誤差平方和 sse=np.sum(distances[range(m),labels]) return labels,centers def predict(self,X): res = None while not res: res=self._kmeans(X) labels,self.centers_= res return labels
二、第二種K-Means算法,初始化簇中心的時候使用了概率模型,能夠選出k個相聚較遠的點。在這個算法中,我們通過十次有效的划分,計算出最少的損失函數SSE的值,將這個值對應的分類返回
import numpy as np class KMeans_2: def __init__(self,k_clusters,tol=1e-4,max_iter=300,n_init=10): self.k_clusters=k_clusters self.tol=tol self.max_iter=max_iter self.n_init = n_init def _init_centers_kpp(self,X,n_clusters): '''k-means++核心初始化算法''' m,n=X.shape #第一個點是隨機產生的,所以只需要計算n_clusters-1個點 distances = np.empty((m,n_clusters-1)) centers=np.empty((n_clusters,n)) #隨機產生一個[0,m-1]的下標,將這個樣本作為第一個聚類中心點 np.copyto(centers[0],X[np.random.randint(m)]) for j in range(1,n_clusters): for i in range(j): np.sum((X-centers[j])**2,axis=1,out=distances[:,i]) #計算各點到最近質心點的距離平方 nds=np.min(distances[:,:j],axis=1) #1、以各點到最近質心的距離平方構成的加權概率進行分布,產生下一簇質心點 r=np.sum(nds)*np.random.random() #2、判斷概率點落入那個區域,對應的樣本就是簇中心 for k in range(m): r-=nds[k] if r < 0: break np.copyto(centers[j],X[k]) return centers def _kmeans(self,X): '''K-Means++核心算法''' m,n=X.shape #label存儲對每一個實例的划分標記 labels=np.zeros(m,dtype=np.int) #distance為m*k的矩陣,表示每個樣本到每個簇中心的距離 distances = np.empty((m,self.k_clusters)) #centers_old存儲之前的質心點 centers_old = np.empty((self.k_clusters,n)) #初始化簇中心 centers=self._init_centers_kpp(X,self.k_clusters) for _ in range(self.max_iter): #1、分類標簽 for i in range(self.k_clusters): #計算m個實例到質心點的距離 np.sum((X-centers[i])**2,axis=1,out=distances[:,i]) #將m個實例划分到距離最小的那個類中 np.argmin(distances,axis=1,out=labels) #2、計算質心點 #保存之前的質心點 np.copyto(centers_old,centers) for i in range(self.k_clusters): cluster = X[labels==i] if cluster.size==0: return None #計算新的簇中心 np.mean(cluster,axis=0,out=centers[i]) #3、判斷是否收斂,求每個簇中心與原來的位置的距離和 delta_centers = np.sqrt(np.sum((centers-centers_old)**2,axis=1)) #每個簇中心點的變化都小於閾值 if np.all(delta_centers < self.tol): break #計算簇內誤差平方和 sse=np.sum(distances[range(m),labels]) return labels,centers,sse def predict(self,X): result = np.empty((self.n_init,3),dtype=np.object) #運行self.n_init次 for i in range(self.n_init): #調用self.k_means直到成功 res = None while res is None: res=self._kmeans(X) result[i]=res #選出sse最小的分類結果 k=np.argmin(result[:,-1]) labels,self.centers_,sse_ = result[k] return labels
三、加載數據
數據來源
http://archive.ics.uci.edu/ml/machine-learning-databases/00236/
import numpy as np X=np.genfromtxt('F:/python_test/data/seeds_dataset.txt',usecols=range(7)) print(X) labels=np.genfromtxt('F:/python_test/data/seeds_dataset.txt',usecols=7,dtype=np.int) print(labels)

三類標簽分別有七十個
print(labels[:70]) print(labels[70:140]) print(labels[140:210])

kmeans=KMeans_2(3) label_pred=kmeans.predict(X) print(label_pred)

大體上分成了三類,但是效果怎么樣還有待評估,使用ARI指標進行評估,
from sklearn.metrics import adjusted_rand_score ari=adjusted_rand_score(labels,label_pred) print(ari)
得到ARI=0.7166198557361053
再考察外部指標FM
from sklearn.metrics import fowlkes_mallows_score fm=fowlkes_mallows_score(labels,label_pred) print(fm)

性能可以說還不錯,我們緊接着對每個屬性的取值都標准化再使用K-means算法
from sklearn .preprocessing import StandardScaler ss=StandardScaler() X_std=ss.fit_transform(X) kmeans = KMeans_2(3) label_pred=kmeans.predict(X_std) ari=adjusted_rand_score(labels,label_pred) print(ari) fm=fowlkes_mallows_score(labels,label_pred) print(fm)

可見ARI指標和FM指標分別提高到了77.3%和84.8%
下面測試一下上述兩種算法的能力
第一種:
w=range(200) ari_arr=[] fm_arr=[] import matplotlib matplotlib.use('TkAgg') import matplotlib.pyplot as plt for i in w: ss=StandardScaler() X_std=ss.fit_transform(X) kmeans = KMeans_1(3) label_pred=kmeans.predict(X_std) ari=adjusted_rand_score(labels,label_pred) ari_arr.append(ari) fm=fowlkes_mallows_score(labels,label_pred) fm_arr.append(fm) plt.plot(w,ari_arr,label='ari',linestyle='--',color='red') plt.plot(w,fm_arr,label='fm',linestyle='-.',color='blue') plt.show()

可以發現平均下來還是很不錯的
第二種:(非常穩定,但是好像沒有上面第一種方法好)
w=range(200) ari_arr=[] fm_arr=[] import matplotlib matplotlib.use('TkAgg') import matplotlib.pyplot as plt for i in w: ss=StandardScaler() X_std=ss.fit_transform(X) kmeans = KMeans_2(3) label_pred=kmeans.predict(X_std) ari=adjusted_rand_score(labels,label_pred) ari_arr.append(ari) fm=fowlkes_mallows_score(labels,label_pred) fm_arr.append(fm) plt.plot(w,ari_arr,label='ari',linestyle='--',color='red') plt.plot(w,fm_arr,label='fm',linestyle='-.',color='blue') plt.xlabel('iterations') plt.ylabel('ratio') plt.legend() plt.show()

