前言:
K-均值聚类是无监督学习,聚类算法中的一种,也比较简单。主要用于视觉图像分类,视频分析,基因分析,
自然语言处理文本分析,图像分割等算法(二值化)
主要应用场景,图片聚类,行程安排(一群人安排班车,选择合适的地点,总距离最小)
目录
- 算法流程
- 后处理提高聚类性能
- 二分K-均值算法
- 例子
一 算法流程
也叫基于质心的算法。它的样本划分为k个类,参数由人工设定。
假设有m个n维样本,
初始化k个中心向量
循环(终止条件 样本中心点变化小于预设值)
1.1 根据当前的类中心,确定每个样本所属于的类
分配样本:
循环:
对每个样本
将样本分配到距离最近的那个点
结束循环
更新样本中心:
1.2 循环,对每个类
根据上面的分配方案更新每个类的中心
为样本j的类别
为第i类的样本数
# -*- coding: utf-8 -*-
"""
Created on Thu Dec 12 16:37:40 2019
@author: chengxf2
"""
from numpy import *
import os
import matplotlib.pyplot as plt
"""
加载数据集
Args
None
return
dataMat: 数据集
"""
def LoadData():
path = os.path.abspath("testSet.txt")
f = open(path)
dataMat =[]
for line in f.readlines():
curLine = line.strip().split("\t")
floatLine = list(map(float, curLine))
dataMat.append(floatLine)
return np.mat(dataMat)
"""
初始化聚类中心
Args:
dataMat: 数据集
k: 分类种类
return
U: 聚类中心点
"""
def RandCent(dataMat, k):
m,n = np.shape(dataMat)
U = np.mat(np.zeros((k,n)))
for j in range(n):
minJ = min(dataMat[:,j])
maxJ = max(dataMat[:,j])
rangeJ = float(maxJ-minJ)
U[:,j]=minJ+rangeJ*np.random.rand(k,1)
print("\n U ",U)
return U
"""
欧式距离计算
Args
vecA
vecB
"""
def Dist(vecA, vecB):
vecC = vecA-vecB
dist = vecC*vecC.T
return float(dist)
"""
获得欧式距离
Args
dataMat: 数据集
k: 分类种类
distMeas: 欧式距离计算
createCent: 随机选择中心
"""
def kMean(dataMat, k, distMeas = Dist, createCent = RandCent):
m,n = np.shape(dataMat)
search = True
U = RandCent(dataMat, k) ##聚类中心点
cluster = np.mat(np.zeros((m,2)))##第一个是当前样本点归类,第二个欧式聚类
iter = 0
while search:
search = False
iter +=1
for i in range(m):
minDist = np.inf
minIndex = -1
vecA = dataMat[i,]
for j in range(k):
vecB = U[j,]
distIJ = distMeas(vecA, vecB)
if distIJ< minDist:
minDist = distIJ
minIndex = j
if cluster[i,0]!= minIndex:
search = True
cluster[i,:]= [minIndex,minDist**2]
for j in range(k):
ClusterJ = dataMat[np.nonzero(cluster[:,0].A == j)[0]]
U[j,:]= np.mean(ClusterJ,axis =0)
print("\n iter::::::::\n ",iter)
return U, cluster
def Test():
dataMat = LoadData()
k =4
U, cluster =kMean(dataMat, k)
marker =["^","o","<","s","x","8"]
color = ["b","c","g","r","y","k"]
m,n = np.shape(dataMat)
print("====================")
for i in range(m):
t = int(cluster[i,0])
# print("k ",k)
co = color[t]
mk = marker[t]
x = dataMat[i,0]
y = dataMat[i,1]
plt.scatter(x,y,c=co, marker = mk,s=10)
for j in range(k):
x = U[j,0]
y = U[j,1]
co = color[j]
mk = marker[j]
print("x: ",x,"y: ",y)
plt.scatter(x,y,c=co, marker = mk,s=200)
plt.show()
#print("\n dist ",U,"\n cluster : \n ",cluster)
Test()
二 后处理提高性能
K均值算法收敛但聚类效果差的原因:
由于质心的随机初始化,K均值算法可能会收敛到局部最小值。
一种度量聚类效果的指标是SSE(误差平方和),但必须在保持簇数目不变的情况下提高簇的质量。
聚类效果评价指标SSE
SSE(Sum of Squared Error,误差平方和)
SE值越小表示数据点越接近于它们的质心,聚类效果也越好。因为对误差取了平方,因此更加重视那些远离中心的点。所以,应该想办法使得SSE尽可能的小。
改进的方法是对生成的簇进行后处理,将最大SSE值的簇划分成两个(K=2的K-均值算法),然后再进行相邻的簇合并。具体方法有两种:
1、合并最近的两个质心(合并使得SSE增幅最小的两个质心)
2、遍历簇 合并两个然后计算SSE的值,找到使得SSE最小的情况。
具体可以参照下面的二分K-均值算法
三 二分-均值算法
所有的点看作一个簇
当簇小于数目k时候
对每个簇:
计算总误差
在给定的的簇上面进行K-均值聚类(K=2)
计算将该簇一份为2后的总误差
选择使得误差最小的簇 进行划分操作
"""
二分K 均值算法
"""
def MeanK(dataMat, k):
m,n = np.shape(dataMat)
cluster =np.mat(np.zeros((m,2))) ##第一个当前的类,第二个当前距离
u= np.mean(dataMat, axis=0).tolist()[0] ##开始只有一个中心点
print("\n ************** u0 ********\n ",u)
u_List =[u]
vecB = np.mat(u)
for j in range(m):
vecA = dataMat[j,]
dist = Dist(vecA, vecB)
cluster[j,1]= dist
#print("\n i ",i,"\t dist: \t ",dist)
iter = 0
while (len(u_List)<k): ##小于聚类总数
minSSE = np.inf
n = len(u_List)
iter =iter+1
#print("\n **********iter: **********",iter)
for i in range(n):
print("\n i: ",i)
curCluster = dataMat[np.nonzero(cluster[:,0].A==i)[0],:]
u, splitCluster = kMean(curCluster, 2)
SSE1 = np.sum(splitCluster[:,1])
SSE2Index = np.nonzero(cluster[:,0].A!=i)[0]
#print("\n SSE2Index: ",SSE2Index)
if len(SSE2Index)==0:
SSE2 = 0.0
else:
SSE2 = np.sum(cluster[SSE2Index,1])
if (SSE1+SSE2)<minSSE:
bestIndex = i
bestU = u
bestCluster = splitCluster.copy()
minSSE = SSE1+SSE2
bestCluster[np.nonzero(bestCluster[:,0].A == 1)[0],0] = int(len(u_List)) ##新的
bestCluster[np.nonzero(bestCluster[:,0].A == 0)[0],0] = int(bestIndex) ##old
u_List[bestIndex]=bestU[0,:]
u_List.append(bestU[1,:])
cluster[np.nonzero(cluster[:,0].A == bestIndex)[0],:] = bestCluster
print("\n ********uList*****\n",u_List)
return u_List,cluster
参考文档
《机器学习与应用》