俗话说“物以类聚,人以群分”,在地理学中有个概念叫做空间相关性,距离越是近的两个地理对象越是相关。这个思想很好的诠释了下面我们介绍的一个非监督分类算法,K-Means聚类算法,如何把相似的事物关联到一起呢?就通过事物的距离来判断,当然这个距离不仅仅局限于欧氏距离。
如何判断两个事物之间的距离呢?这就需要一个参照系,比如说两棵树之间的距离,那么放到坐标系中赋予x,y坐标系就能很容易计算出距离。那如果是抽象的事物呢?对,抽取特征,抽取这个事物的特征组成n维的特征向量,这就是赋予这个事物参照系了。以图像分类为例吧,抽取灰度值后组成特征向量,但是图像这么多我该跟那个图像比较距离呢?不用惆怅当然不会图像之间两两比较了。
举个愉快的例子说一下这个算法,话说派别划分少林寺与峨眉派,怎么区分这两个派别的人呢?看性别啊,衣服啊,武功路数啊,武器啊。。。。淡定,没错你说的这么都是特征,但是计算机不知道啊,实际上我们只需要看他们的老大就是主持,看看这些人的特征跟哪个老大类似,那么他肯定就是那个派别的。没错,但是我怎么知道谁是老大,拿破仑说的好不想当主持的和尚不是好和尚,主持也是和尚是由和尚中选出来的,那么这个老大我们也照做,随机选两个人作为老大,然后所有的点分别跟这个老大比,跟谁最近这个点就标记为哪个门派,然后在把所有标记好的弟子特征取均值,再把以这个均值作为老大的标配所以相应门派的人向老大看起,然后一直迭代下去,直到老大的特征不再改变,说明选上了正在的老大找到了真正的门派传承人。这个时候,就能体现出“近朱者赤近墨者黑”这句话的含义了,跟两个正在的老大对比,离谁近就跟谁。这个时候门派的划分终于化好了。
K-Means聚类算法,特点是简单,速度快,但是缺点也明显,容易陷入局部最优,在做实验过程中经常得到的值不是全局最优解,并且得设置到底聚多少类。这个就比较尴尬了,拿咏春来说吧,肯定谁离叶问最近谁就是咏春派的,但是当我们把类别设置为两类的时候,冷不丁多出来一个李小龙独创一派。
言归正传,在我做实验的过程中,图像数据分为建筑用地与森林两类,最好的一次聚类中,准确率达到81%,这个结果很是棒,因为总共有100样例,正负各50张,k-means算法达到比朴素贝叶斯还要高的准确度。
源码如下,欢迎指正交流:
# -*- coding: utf-8 -*-
"""
Created on Fri Mar 2 08:55:57 2018
@author: Administrator
"""
import numpy as np
def kmeans(X,k,maxIt):
numPoints,numDim=X.shape
dataSet=np.zeros((numPoints,numDim+1))
dataSet[:,:-1]=X
#随机初始化中心点
centroids=dataSet[np.random.randint(numPoints,size=k),:]
#随机的赋值标签
centroids[:,-1]=range(k)
iterations=0
oldCentroids=np.zeros((k,numDim+1))
#运行kmeans算法
while not shouldStop(oldCentroids,centroids,iterations,maxIt):
print("iteration:\n",iterations)
print("dataset:\n",dataSet)
print("centroids:\n",centroids)
#保存老中心点用于比较
oldCentroids=np.copy(centroids)
iterations+=1
#基于中心点更新lable
updateLabels(dataSet,centroids)
#重新更新中心点
centroids=getCentroids(dataSet,k)
return dataSet
def shouldStop(oldCentroids,centroids,iterations,maxIt):
"""
当迭代次数大于最大迭代次数时候停止
当中心点不在更新的时候迭代停止
"""
if iterations>maxIt:
return True
return np.array_equal(centroids,oldCentroids)
#return False
def updateLabels(dataSet,centroids):
"""
根据中心点进行更新lables
"""
numPoints,numDim=centroids.shape
distanceMatric=[]
for i in range(0,numPoints):
distanceMatric.append(np.linalg.norm(dataSet[:,:-1]-np.atleast_2d(centroids[i,:-1]),axis=1))
distanceMatric=np.atleast_2d(distanceMatric)
labelIndex=np.argmin(distanceMatric,axis=0)
dataSet[:,-1]=labelIndex.T
def getCentroids(dataSet,k):
centroids=[]
for i in range(k):
mean=np.mean(dataSet[dataSet[:,-1]==i,:-1],axis=0)
centroids.append(np.hstack((mean,[i])))
return np.atleast_2d(centroids)
import h5py
def loadDataSet():
'''
加载图片数据集
'''
pictureSet=[];pictureClasses=[]
with h5py.File(r'F:\python_project\ml_dl\NBClassfiyLand\bdata\Land.h5') as h5f:
forest=h5f['forest'][:]
urban=h5f['urban'][:]
pictureSet=np.vstack((forest,urban))
pictureClasses=[0]*50+[1]*50
return pictureSet,pictureClasses
#x1 = np.array([1, 1])
#x2 = np.array([2, 1])
#x3 = np.array([4, 3])
#x4 = np.array([5, 4])
#testX = np.vstack((x1, x2, x3, x4))
testX,testY=loadDataSet()
result = kmeans(testX, 2, 1000)
print(np.sum(np.equal(testY,result[:,-1]))/100.)
关注我的公共号,去github上领取源码与数据