【機器學習】人像識別(三)——K-Means聚類
阿新 • • 發佈:2019-01-02
簡介
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
遇到的問題
計算兩點之間的歐氏距離可以用numpy庫中的函式:
dist = numpy.linalg.norm(vec1 - vec2)
經常發現ROUND 2之後,質心就不再變了。對此,我心中不太踏實,踟躕於聚類速度是否當真如此之快。不過資料集這樣小,結果也正確,姑且將此歸功於K-Means的有效性吧。
在閱讀log文件時,發現編號為2和5的圖總是被歸到一組,然而恢復出灰度圖後,發現這兩張圖並不是一個人,但有一個共同點——色調較暗。因此在讀入資料後,將這兩張圖的灰度值乘了係數0.9,之後聚類結果就基本穩定了。