1. 程式人生 > >【機器學習】人像識別(三)——K-Means聚類

【機器學習】人像識別(三)——K-Means聚類

簡介

  K-Means聚類是一種非監督的聚類方式,原理參看資料探勘十大演算法 | k-means
  

程式碼

import sys
import random
import numpy as np
from sklearn.decomposition import IncrementalPCA

imgNum = 10 # 幾張圖片
KNum = 2 # 分成幾類
n = 2 # 每張圖片都是n×n
dimension = 2016 # 每張圖片的維數
dst_dimension = 10 # 想降到的維數
bound = 10 # 前後兩次迭代結果之差小於這個時可以停止
maxRecurseTime = 10
# 最多迭代次數 centroids = [] # 存放KNum個質心的n維座標向量 last_centroids = [] # 上一次遞迴得到的質心座標們 ori_dots = [] # 存放每個點的n維座標向量 dots = [] # 降維之後的點的座標 clusters = [] # 存放每個類中有哪些點, clusters[i]中存放的是第i類中的點的下標,第i類的中心是centroids[i] selected = [] Distance = lambda v: np.linalg.norm(v) # 初始化點的座標,並進行降維,返回降維後的向量集 def InitDots()
:
# 讀入imgNum個點的座標,存放在dots當中 for i in range(imgNum): ori_dots.append([]) # 讀入向量集並降維 ReadFiles(ori_dots) return PCA(ori_dots) # 初始化質心資訊 def InitCentroids(): # 初始化質心資訊 for i in range(KNum): centroids.append([]) centroids[i] = np.array([float(0)] * dimension) # 初始化為全零
clusters.append([]) # 隨機挑選初始時的‘質心’座標 for i in range(KNum): _ = random.randrange(imgNum) while _ in selected: _ = random.randrange(imgNum) selected.append(_) selected.sort() print('selected:' , selected) for i in range(KNum): centroids[i] = dots[selected[i]] print('centroids 0:', centroids) # 讀入imgNum個圖的座標向量 def ReadFiles(dots): path = r'C:\Users\Owner\Documents\Visual Studio 2015\Projects\Python\K-Means\K-Means\\' fd = open(path + 'input.txt', 'r') _ = fd.read() # 一次讀進所有 fd.close() _ = _.split() for i in range(len(_)): dots[i % imgNum].append(255 - float(_[i])) tmp = [2, 5] for i in tmp: for j in range(len(dots[i])): dots[i][j] *= 0.9 # 降維操作 def PCA(dots): X = np.array(dots) ipca = IncrementalPCA(n_components = dst_dimension) ipca.fit(X) Y = ipca.transform(X) print('y = ', Y, '\n') for i in range(len(Y)): Y[i] = np.array(Y[i]) return Y # 對於每個cluster,計算質心 def CalcCentroids(KNum, dimension, centroids, dots, clusters): # 先把上一次得到的質心存放到last_centroids當中 last_centroids = centroids # centroids = [] 這會導致傳不回去 for i in range(KNum): v = np.array([float(0)] * dst_dimension) for _ in clusters[i]: v += dots[_] l = len(clusters[i]) centroids[i] = (v / l) # 聚類,判斷每個點屬於哪個類 def Cluster(imgNum, KNum, dots, clusters): # 清空原有資料 for i in range(KNum): clusters[i] = [] # 計算每個點到每個質心的距離,並將他們放到相應的cluster中 for i in range(imgNum): store = [] # 存放當前的點到每個質心的距離 for j in range(KNum): store.append(Distance(dots[i] - centroids[j])) cluster_index = store.index(min(store)) # store中最小的數是min(store),找這個最小數的下標用store.index() clusters[cluster_index].append(i) dots = InitDots() InitCentroids() temp = sys.stdout log_root = r'C:\Users\Owner\Documents\Visual Studio 2015\Projects\Python\K-Means\K-Means\Log_'+str(dst_dimension)+'\\' sys.stdout = open(log_root + str(selected) + '.txt','w') for i in range(bound): print('ROUND ' + str(i) + ': ') print('centroids: ', centroids) Cluster(imgNum, KNum, dots, clusters) print('clusters', clusters, '\n') CalcCentroids(KNum, dimension, centroids, dots, clusters) print('centroids: ', centroids) print('clusters: ', clusters) sys.stdout = temp

遇到的問題

  1. 計算兩點之間的歐氏距離可以用numpy庫中的函式:
    dist = numpy.linalg.norm(vec1 - vec2)

  2. 經常發現ROUND 2之後,質心就不再變了。對此,我心中不太踏實,踟躕於聚類速度是否當真如此之快。不過資料集這樣小,結果也正確,姑且將此歸功於K-Means的有效性吧。

  3. 在閱讀log文件時,發現編號為2和5的圖總是被歸到一組,然而恢復出灰度圖後,發現這兩張圖並不是一個人,但有一個共同點——色調較暗。因此在讀入資料後,將這兩張圖的灰度值乘了係數0.9,之後聚類結果就基本穩定了。