您的位置:首页 > 其它

【机器学习】人像识别(三)——K-Means聚类

2017-02-08 19:11 197 查看

简介

  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',
4000
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,之后聚类结果就基本稳定了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息