本文所有實(shí)現(xiàn)代碼均來自《Python機(jī)器學(xué)習(xí)及實(shí)戰(zhàn)》
創(chuàng)新互聯(lián)主營舟山網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,成都app軟件開發(fā)公司,舟山h5小程序開發(fā)搭建,舟山網(wǎng)站營銷推廣歡迎舟山等地區(qū)企業(yè)咨詢
#-*- coding:utf-8 -*-
#分別導(dǎo)入numpy、matplotlib、pandas,用于數(shù)學(xué)運(yùn)算、作圖以及數(shù)據(jù)分析
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
#第一步:使用pandas讀取訓(xùn)練數(shù)據(jù)和測(cè)試數(shù)據(jù)
digits_train = pd.read_csv('',header=None)
digits_test = pd.read_csv('',header=None)
#第二步:已知原始數(shù)據(jù)有65個(gè)特征值,前64個(gè)是像素特征,最后一個(gè)是每個(gè)圖像樣本的數(shù)字類別
#從訓(xùn)練集和測(cè)試集上都分離出64維度的像素特征和1維度的數(shù)字目標(biāo)
X_train = digits_train[np.arange(64)]
y_train = digits_train[64]
X_test = digits_test[np.arange(64)]
y_test = digits_test[64]
#第三步:使用KMeans模型進(jìn)行訓(xùn)練并預(yù)測(cè)
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=10)
kmeans.fit(X_train)
kmeans_y_predict = kmeans.predict(X_test)
#第四步:評(píng)估KMeans模型的性能
#如何評(píng)估聚類算法的性能?
#1.Adjusted Rand Index(ARI) 適用于被用來評(píng)估的數(shù)據(jù)本身帶有正確類別的信息,ARI指標(biāo)和計(jì)算Accuracy的方法類似
#2.Silhouette Coefficient(輪廓系數(shù)) 適用于被用來評(píng)估的數(shù)據(jù)沒有所屬類別 同時(shí)兼顧了凝聚度和分散度,取值范圍[-1,1],值越大,聚類效果越好
from sklearn.metrics import adjusted_rand_score
print 'The ARI value of KMeans is',adjusted_rand_score(y_test,kmeans_y_predict)
#到此為止,手寫體數(shù)字圖像聚類--kmeans學(xué)習(xí)結(jié)束,下面單獨(dú)討論輪廓系數(shù)評(píng)價(jià)kmeans的性能
#****************************************************************************************************
#拓展學(xué)習(xí):利用輪廓系數(shù)評(píng)價(jià)不同累簇?cái)?shù)量(k值)的K-means聚類實(shí)例
from sklearn.metrics import silhouette_score
#分割出3*2=6個(gè)子圖,并且在1號(hào)子圖作圖 subplot(nrows, ncols, plot_number)
plt.subplot(3,2,1)
#初始化原始數(shù)據(jù)點(diǎn)
x1 = np.array([1,2,3,1,5,6,5,5,6,7,8,9,7,9])
x2 = np.array([1,3,2,2,8,6,7,6,7,1,2,1,1,3])
# a = [1,2,3] b = [4,5,6] zipped = zip(a,b) 輸出為元組的列表[(1, 4), (2, 5), (3, 6)]
X = np.array(zip(x1,x2)).reshape(len(x1),2)
#X輸出為:array([[1, 1],[2, 3],[3, 2],[1, 2],...,[9, 3]])
#在1號(hào)子圖作出原始數(shù)據(jù)點(diǎn)陣的分布
plt.xlim([0,10])
plt.ylim([0,10])
plt.title('Instances')
plt.scatter(x1,x2)
colors = ['b','g','r','c','m','y','k','b']
markers = ['o','s','D','v','^','p','*','+']
clusters = [2,3,4,5,8]
subplot_counter = 1
sc_scores = []
for t in clusters:
subplot_counter += 1
plt.subplot(3,2,subplot_counter)
kmeans_model = KMeans(n_clusters=t).fit(X)
for i,l in enumerate(kmeans_model.labels_):
plt.plot(x1[i],x2[i],color=colors[l],marker=markers[l],ls='None')
plt.xlim([0,10])
plt.ylim([0,10])
sc_score = silhouette_score(X,kmeans_model.labels_,metric='euclidean')
sc_scores.append(sc_score)
#繪制輪廓系數(shù)與不同類簇?cái)?shù)量的直觀顯示圖
plt.title('K=%s,silhouette coefficient = %0.03f'%(t,sc_score))
#繪制輪廓系數(shù)與不同類簇?cái)?shù)量的關(guān)系曲線
plt.figure() #此處必須空一行,表示在for循環(huán)結(jié)束之后執(zhí)行!?。?/p>
plt.plot(clusters,sc_scores,'*-') #繪制折線圖時(shí)的樣子
plt.xlabel('Number of Clusters')
plt.ylabel('Silhouette Coefficient Score')
plt.show()
#****************************************************************************************************
#總結(jié):
#k-means聚類模型所采用的迭代式算法,直觀易懂并且非常實(shí)用,但是有兩大缺陷
#1.容易收斂到局部最優(yōu)解,受隨機(jī)初始聚類中心影響,可多執(zhí)行幾次k-means來挑選性能最佳的結(jié)果
#2.需要預(yù)先設(shè)定簇的數(shù)量,
K-MEANS算法:
k-means 算法接受輸入量 k ;然后將n個(gè)數(shù)據(jù)對(duì)象劃分為 k個(gè)聚類以便使得所獲得的聚類滿足:同一聚類中的對(duì)象相似度較高;而不同聚類中的對(duì)象相似度較小。聚類相似度是利用各聚類中對(duì)象的均值所獲得一個(gè)“中心對(duì)象”(引力中心)來進(jìn)行計(jì)算的。
k-means 算法的工作過程說明如下:首先從n個(gè)數(shù)據(jù)對(duì)象任意選擇 k 個(gè)對(duì)象作為初始聚類中心;而對(duì)于所剩下其它對(duì)象,則根據(jù)它們與這些聚類中心的相似度(距離),分別將它們分配給與其最相似的(聚類中心所代表的)聚類;然后再計(jì)算每個(gè)所獲新聚類的聚類中心(該聚類中所有對(duì)象的均值);不斷重復(fù)這一過程直到標(biāo)準(zhǔn)測(cè)度函數(shù)開始收斂為止。一般都采用均方差作為標(biāo)準(zhǔn)測(cè)度函數(shù). k個(gè)聚類具有以下特點(diǎn):各聚類本身盡可能的緊湊,而各聚類之間盡可能的分開。
具體如下:
輸入:k, data[n];
(1) 選擇k個(gè)初始中心點(diǎn),例如c[0]=data[0],…c[k-1]=data[k-1];
(2) 對(duì)于data[0]….data[n], 分別與c[0]…c[n-1]比較,假定與c[i]差值最少,就標(biāo)記為i;
(3) 對(duì)于所有標(biāo)記為i點(diǎn),重新計(jì)算c[i]=/標(biāo)記為i的個(gè)數(shù);
(4) 重復(fù)(2)(3),直到所有c[i]值的變化小于給定閾值。
算法實(shí)現(xiàn)起來應(yīng)該很容易,就不幫你編寫代碼了。
幾種典型的聚類融合算法:
1.基于超圖劃分的聚類融合算法
(1)Cluster-based Similarity Partitioning Algorithm(GSPA)
(2)Hyper Graph-Partitioning Algorithm(HGPA)
(3)Meta-Clustering Algorithm(MCLA)
2.基于關(guān)聯(lián)矩陣的聚類融合算法
Voting-K-Means算法。
3.基于投票策略的聚類融合算法
w-vote是一種典型的基于加權(quán)投票的聚類融合算法。
同時(shí)還有基于互信息的聚類融合算法和基于有限混合模型的聚類融合算法。
二、基于關(guān)聯(lián)矩陣的聚類融合算法——Voting-K-Means算法
Voting-K-Means算法是一種基于關(guān)聯(lián)矩陣的聚類融合算法,關(guān)聯(lián)矩陣的每一行和每一列代表一個(gè)數(shù)據(jù)點(diǎn),關(guān)聯(lián)矩陣的元素表示數(shù)據(jù)集中數(shù)據(jù)點(diǎn)對(duì)共同出現(xiàn)在同一個(gè)簇中的概率。
算法過程:
1.在一個(gè)數(shù)據(jù)集上得到若干個(gè)聚類成員;
2.依次掃描這些聚類成員,如果數(shù)據(jù)點(diǎn)i和j在某個(gè)聚類成員中被劃分到同一個(gè)簇中,那么就在關(guān)聯(lián)矩陣對(duì)應(yīng)的位置計(jì)數(shù)加1;關(guān)聯(lián)矩陣中的元素值越大,說明該元素對(duì)應(yīng)的兩個(gè)數(shù)據(jù)點(diǎn)被劃分到同一個(gè)簇中的概率越大;
3.得到關(guān)聯(lián)矩陣之后,Voting-K-Means算法依次檢查關(guān)聯(lián)矩陣中的每個(gè)元素,如果它的值大于算法預(yù)先設(shè)定的閥值,就把這個(gè)元素對(duì)應(yīng)的兩個(gè)數(shù)據(jù)點(diǎn)劃分到同一個(gè)簇中。
Voting-K-Means算法的優(yōu)缺點(diǎn):
Voting-K-Means算法不需要設(shè)置任何參數(shù),在聚類融合的過程中可以自動(dòng)地的選擇簇的個(gè)數(shù) 并且可以處理任意形狀的簇。因?yàn)閂oting-K-Means算法在聚類融合過程中是根據(jù)兩個(gè)數(shù)據(jù)點(diǎn)共同出現(xiàn)在同一個(gè)簇中的可能性大小對(duì)它們進(jìn)行劃分的,所以只要兩個(gè)數(shù)據(jù)點(diǎn)距離足夠近,它們就會(huì)被劃分到一個(gè)簇中。
Voting-K-Means算法的缺點(diǎn)是時(shí)間復(fù)雜度較高,它的時(shí)間復(fù)雜度是O(n^2);需要較多的聚類成員,如果聚類成員達(dá)不到一定規(guī)模,那么關(guān)聯(lián)矩陣就不能準(zhǔn)確反映出兩個(gè)數(shù)據(jù)點(diǎn)出現(xiàn)在同一個(gè)簇的概率。
package clustering;import java.io.FileWriter;import weka.clusterers.ClusterEvaluation;import weka.clusterers.SimpleKMeans;import weka.core.DistanceFunction;import weka.core.EuclideanDistance;import weka.core.Instances;import weka.core.converters.ConverterUtils.DataSource;import weka.filters.unsupervised.attribute.Remove;public class Votingkmeans2 extends SimpleKMeans { /** 生成的序列號(hào) */ private static final long serialVersionUID = 1557181390469997876L; /** 劃分的簇?cái)?shù) */ private int m_NumClusters; /** 每個(gè)劃分的簇中的實(shí)例的數(shù)量 */ public int[] m_ClusterSizes; /** 使用的距離函數(shù),這里是歐幾里德距離 */ protected DistanceFunction m_DistanceFunction = new EuclideanDistance(); /** 實(shí)例的簇號(hào)賦值 */ protected int[] m_Assignments; /** 設(shè)定聚類成員融合閥值 */ private final static double THREASOD = 0.5; /** 生成一個(gè)聚類器 */ public void buildClusterer(Instances data) throws Exception{ final int numinst = data.numInstances(); // 數(shù)據(jù)集的大小 double [][]association = new double[numinst][numinst]; // 定義并初始化一個(gè)關(guān)聯(lián)矩陣 int numIteration = 40; // 設(shè)置生成的聚類成員數(shù) final int k = (int)Math.sqrt(numinst); // 設(shè)置K-Means聚類算法參數(shù)——簇?cái)?shù) for(int i = 0; i numIteration; i++) { if(data.classIndex() == -1) data.setClassIndex(data.numAttributes() - 1); // 索引是從0開始 String[] filteroption = new String[2]; filteroption[0] = "-R"; filteroption[1] = String.valueOf(data.classIndex() + 1);// 索引是從1開始 Remove remove = new Remove(); remove.setOptions(filteroption); remove.setInputFormat(data); /* 使用過濾器模式生成新的數(shù)據(jù)集;新數(shù)據(jù)集是去掉類標(biāo)簽之后的數(shù)據(jù)集 */ Instances newdata = weka.filters.Filter.useFilter(data, remove); /* 生成一個(gè)K-Means聚類器 */ SimpleKMeans sm = new SimpleKMeans(); sm.setNumClusters(k); sm.setPreserveInstancesOrder(true); // 保持?jǐn)?shù)據(jù)集實(shí)例的原始順序 sm.setSeed(i); // 通過設(shè)置不同的種子,設(shè)置不同的簇初始中心點(diǎn),從而得到不同的聚類結(jié)果 sm.buildClusterer(newdata); int[] assigm = sm.getAssignments(); // 得到數(shù)據(jù)集各個(gè)實(shí)例的賦值 /* 建立關(guān)聯(lián)矩陣 */ for(int j = 0; j numinst; j++) { for(int m = j; m numinst; m++) { if(assigm[j] == assigm[m]) { association[j][m] = association[j][m] + 1.0 / numIteration ; } } } } System.out.println(); /* 將生成的關(guān)聯(lián)矩陣寫入.txt文件(注:生成的txt文本文件在e:/result.txt中) */ FileWriter fw = new FileWriter("e://result.txt"); for(int j = 0; j numinst; j++) { for(int m = j; m numinst; m++) { //由于關(guān)聯(lián)矩陣是對(duì)稱的,為了改進(jìn)算法的效率,只計(jì)算矩陣的上三角 String number = String.format("%8.2f", association[j][m]); fw.write(number); } fw.write("\n"); } /* 處理關(guān)聯(lián)矩陣,分別考慮了兩種情況 :1.關(guān)聯(lián)矩陣中某個(gè)元素對(duì)應(yīng)的兩個(gè)數(shù)據(jù)點(diǎn)已經(jīng)被劃分到了不同的簇中 * 2.兩個(gè)數(shù)據(jù)點(diǎn)中有一個(gè)或者兩個(gè)都沒有被劃分到某個(gè)簇中。 */ int[] flag = new int[numinst]; int[] flagk = new int[k]; int[] finallabel = new int[numinst]; for(int m = 0; m numinst; m++) { for(int n = m; n numinst; n++) { if(association[m][n] THREASOD) { if(flag[m] == 0 flag[n] == 0) { // 兩個(gè)數(shù)據(jù)點(diǎn)都沒有被劃分到某個(gè)簇中, int i = 0; // 將他們劃分到同一個(gè)簇中即可 while (i k flagk[i] == 1) i = i + 1; finallabel[m] = i; finallabel[n] = i; flag[m] = 1; flag[n] = 1; flagk[i] = 1; } else if (flag[m] == 0 flag[n] == 1) { // 兩個(gè)數(shù)據(jù)點(diǎn)中有一個(gè)沒有被劃分到某個(gè)簇中, finallabel[m] = finallabel[n]; // 將他們劃分到同一個(gè)簇中即可 flag[m] = 1; } else if (flag[m] == 1 flag[n] == 0) { finallabel[n] = finallabel[m]; flag[n] = 1; } else if (flag[m] == 1 flag[n] == 1 finallabel[m] != finallabel[n]) { // 兩個(gè)數(shù)據(jù)點(diǎn)已被劃分到了不同的簇中, flagk[finallabel[n]] = 0; // 將它們所在的簇合并 int temp = finallabel[n]; for(int i = 0; i numinst; i++) { if(finallabel[i] == temp) finallabel[i] = finallabel[m]; } } } } } m_Assignments = new int[numinst]; System.out.println("基于關(guān)聯(lián)矩陣的聚類融合算法——Voting-K-Means算法的最終聚類結(jié)果"); for(int i = 0; i numinst; i++) { m_Assignments[i] = finallabel[i]; System.out.print(finallabel[i] + " "); if((i+1) % 50 == 0) System.out.println(); } for(int i = 0; i k; i++) { if(flagk[i] == 1) m_NumClusters++; } } /** * return a string describing this clusterer * * @return a description of the clusterer as a string */ public String toString() { return "Voting-KMeans\n"; } public static void main(String []args) { try {String filename="e://weka-data//iris.arff"; Instances data = DataSource.read(filename); Votingkmeans2 vk = new Votingkmeans2(); vk.buildClusterer(data); /* 要生成Voting-K-Means的聚類評(píng)估結(jié)果包括準(zhǔn)確率等需要覆蓋重寫toString()方法; * 因?yàn)闆]有覆蓋重寫,所以這里生產(chǎn)的評(píng)估結(jié)果沒有具體內(nèi)容。 */ ClusterEvaluation eval = new ClusterEvaluation(); eval.setClusterer(vk); eval.evaluateClusterer(new Instances(data)); System.out.println(eval.clusterResultsToString()); } catch (Exception e) { e.printStackTrace(); }}}
分析代碼時(shí)注意:得到的類成員變量m_Assignments就是最終Voting-K-Means聚類結(jié)果;由于是采用了開源機(jī)器學(xué)習(xí)軟件Weka中實(shí)現(xiàn)的SimpleKMeans聚類算法,初始時(shí)要指定簇的個(gè)數(shù),這里是數(shù)據(jù)集大小開根號(hào)向下取整;指定的閥值為0.5,即當(dāng)關(guān)聯(lián)矩陣元素的值大于閥值時(shí),才對(duì)該元素對(duì)應(yīng)的兩個(gè)數(shù)據(jù)點(diǎn)進(jìn)行融合,劃分到一個(gè)簇中,考慮兩種情況,代碼注釋已有,這里不再詳述。但聚類融合的實(shí)驗(yàn)結(jié)果并不理想,鶯尾花數(shù)據(jù)集irsi.arff是數(shù)據(jù)挖掘?qū)嶒?yàn)中最常用的數(shù)據(jù)集,原數(shù)據(jù)集共有三個(gè)類;但本實(shí)驗(yàn)進(jìn)行四十個(gè)聚類成員的融合,其最終聚類結(jié)果劃分成兩個(gè)簇;其原因可能有兩個(gè):一是算法本身的問題,需要使用其他更加優(yōu)化的聚類融合算法;二是實(shí)現(xiàn)上的問題,主要就在聚類結(jié)果的融合上,需要進(jìn)行一步對(duì)照關(guān)聯(lián)矩陣進(jìn)行邏輯上的分析,找出代碼中的問題。關(guān)聯(lián)矩陣文本文件
---------------------
本文來自 Turingkk 的CSDN 博客 ,全文地址請(qǐng)點(diǎn)擊:
以前做項(xiàng)目時(shí)候?qū)懙拇a,數(shù)據(jù)是一維的,多維的也一樣,把距離計(jì)算的改一改就行int?term?=?Math.abs(dotlist.get(centerIndex[j]).x-?dotlist.get(i).x);
[java]?view?plaincopy
package?uestc.dmlab.call;??
import?java.io.BufferedReader;??
import?java.io.FileReader;??
import?java.security.KeyStore.Entry;??
import?java.util.HashMap;??
import?java.util.HashSet;??
import?java.util.Iterator;??
import?java.util.LinkedList;??
import?java.util.List;??
import?java.util.Map;??
import?java.util.Random;??
import?java.util.Set;??
public?class?Clustering?{??
/**?
*??
*?@param?fileName?
*????????????文件中每個(gè)字段對(duì)應(yīng)一個(gè)概率?
*?@param?k?
*????????????聚成k個(gè)類?
*?@param?minDistance?
*????????????聚類中心位移小于minDistance時(shí)停止迭代?
*?@return?
*/??
public?static?HashMapString,?Integer?cluster(String?fileName,?int?k,??
int?minDistance)?{??
try?{??
BufferedReader?br?=?new?BufferedReader(new?FileReader(fileName));??
ListDot?dotlist?=?new?LinkedListDot();??
String?line;??
int?count?=?0;//?行數(shù)??
while?((line?=?br.readLine())?!=?null)?{??
String?s[]?=?line.split(",");??
Dot?dot?=?new?Dot();??
dot.isCenter?=?false;??
dot.isVirtual?=?false;??
dot.name?=?s[0];??
//?if(s.length4){??
//?System.out.println(line);??
//?}??
dot.x?=?Integer.parseInt(s[3]);??
dotlist.add(dot);??
count++;??
}??
if?(count??k)?{??
k?=?count;??
}??
//?隨機(jī)初始化k個(gè)聚類中心??
int?centerIndex[]?=?new?int[k];?//?存儲(chǔ)k個(gè)中心點(diǎn)在dotlist中的索引??
int?centerNum?=?k;??
while?(centerNum??0)?{??
int?index?=?new?Random().nextInt(count);??
if?(!dotlist.get(index).isCenter)?{??
centerNum--;??
dotlist.get(index).isCenter?=?true;??
centerIndex[centerNum]?=?index;??
}??
}??
//?K個(gè)聚類??
Cluster[]?clusers?=?new?Cluster[k];??
boolean?flag?=?true;??
while?(flag)?{??
flag?=?false;??
clusers?=?new?Cluster[k];??
for?(int?i?=?0;?i??clusers.length;?i++)?{??
clusers[i]?=?new?Cluster();??
}??
//System.out.println(clusers.length);??
//?找到離第i個(gè)點(diǎn)最近的聚類中心??
for?(int?i?=?0;?i??dotlist.size();?i++)?{??
//?該點(diǎn)不是中心點(diǎn)也不是虛擬點(diǎn)就計(jì)算它與所有中心點(diǎn)的距離并取最小值??
//?if(!dotlist.get(i).isCenter!dotlist.get(i).isVirtual){??
if?(!dotlist.get(i).isVirtual)?{??
int?distance?=?Integer.MAX_VALUE;??
int?c?=?0;//?記錄離該節(jié)點(diǎn)最近的中心點(diǎn)的索引??
for?(int?j?=?0;?j??k;?j++)?{??
int?term?=?Math.abs(dotlist.get(centerIndex[j]).x??
-?dotlist.get(i).x);??
if?(distance??term)?{??
distance?=?term;??
c?=?j;??
}??
}??
clusers[c].dots.add(i);??
}??
}??
//?重新計(jì)算聚類中心??
for?(int?i?=?0;?i??k;?i++)?{??
Cluster?cluster?=?clusers[i];??
if?(cluster.dots.size()??0)?{?//若該類中有點(diǎn)??
int?sum?=?0;??
for?(int?j?=?0;?j??cluster.dots.size();?j++)?{??
sum?+=?dotlist.get(cluster.dots.get(j)).x;??
}??
Dot?dot?=?new?Dot();??
dot.x?=?sum?/?cluster.dots.size();??
dot.isCenter?=?true;??
dot.isVirtual?=?true;??
//?新舊聚類中心的距離??
int?term?=?Math.abs(dotlist.get(centerIndex[i]).x??
-?dot.x);??
if?(term??minDistance)??
flag?=?true;??
dotlist.add(dot);??
centerIndex[i]?=?dotlist.indexOf(dot);?//?第i個(gè)聚類的中心改變??
}??
}??
}??
//?生成分類映射??
HashMapString,?Integer?map?=?new?HashMapString,?Integer();??
for?(Dot?dot?:?dotlist)?{??
if?(dot.isVirtual?==?false)?{??
int?className?=?-1;??
for?(int?i?=?0;?i??k;?i++)?{??
if?(clusers[i].dots.contains(dotlist.indexOf(dot)))??
className?=?i;??
}??
map.put(dot.name,?className);??
}??
}??
return?map;??
}?catch?(Exception?e)?{??
e.printStackTrace();??
}??
return?new?HashMapString,?Integer();??
}??
public?static?void?main(String[]?args)?{??
MapString,?Integer?map?=?Clustering.cluster(??
"C:/Documents?and?Settings/Administrator/桌面/123.txt",?2,?0);??
IteratorMap.EntryString,?Integer?it?=?map.entrySet().iterator();??
while(it.hasNext()){??
Map.EntryString,?Integer?entry?=?it.next();??
System.out.println(entry.getKey()+","+entry.getValue());??
}??
}??
}??
class?Dot?{??
String?name;??
int?x;??
boolean?isCenter;??
boolean?isVirtual;??
}??
class?Cluster?{??
//?記錄了該類中點(diǎn)的索引值??
LinkedListInteger?dots?=?new?LinkedListInteger();