4 经典非监督学习算法—K-Means聚类 机器学习基础理论入门
4.1 高斯混合模型
本节内容主要是介绍单变量高斯混合模型的数学表示和集合表示,虽然在K均值聚类中没有很多用武之地,但是在期望最大算法中,五年会更深入的了解一般意义上的高斯混合模型以及高斯混合聚类。
单高斯模型(gaussian single model)
中间多,两边少时高斯模型的典型特征
高斯混合模型(gaussian mixed model)
随机变量X属于k个单高斯模型中的某一个,那么:
4.2 度量距离的几种方法
这里的距离特指两个特征向量之间的距离,是描述特征向量关系的重要指标。一般来说,距离越小,关系越接近。除了距离,还有余弦相似度衡量两个特征向量的关系。
什么是距离
(1) 距离度量了两个特征向量xi, yj的远近程度(或相似程度,距离越近,相似度越高)
(2) 记xi, yj之间的距离为dist(xi, yj)
距离的性质
闵可夫斯基距离(Minkowski distance)
欧式距离
当p=2时,闵可夫斯基距离成为欧氏距离(eculidean distance)
曼哈顿距离
当p=1时,闵可夫斯基距离成为曼哈顿距离(manhattan distance),也叫街道距离
局限性
只能处理连续实数值和有序值,无法处理无序离散值。
VDM(value difference metric)
VDM距离与闵可夫斯基距离结合
假设有nc个有序属性,n-nc个无序属性,并且假设有序属性排在无序属性之前,那么:
加权距离
4.3 评价聚类效果的几种方法
什么是聚类
聚类:非监督学习算法。把相似的对象通过静态分类的方法分成不同的组别或更多的子集,这样让在同一个子集中的成员对象都有相似的一些属性。
簇的概念(cluster)
簇;一个簇表示由多个距离相近的对象所组成的集合(聚类就是把原始混乱的数据集划分为多个簇的过程)
外部指标和内部指标
外部指标
假设
Jaccard系数(Jaccard coeddicient, 简称JC)
大小在0-1之间,且越大越好
FM指数(fowlkes and mallows index)
大小在0-1之间,且越大越好
Rand指数(Rand index)
大小在0-1之间,且越大越好
内部指标
假设
DB指数(davies-bouldin index)
DB指数越小越好
Dunn指数(Dunn index)
Dunn指数越大越好
4.4 K均值算法
原型聚类
聚类中的某个簇可以使用一个原型进行描述(就好像由一群金毛犬,其中或多或少都会混有其他犬种的基因,其中我们选出金毛特征最为明显的那只作为原型)
K-Means的损失函数
不正确的越多,损失越大。
给定数据集,聚类所得的簇划分,则最小化平方误差
如何损失最小化
(1) 损失函数没有解析解(明确写成公式的解),即无法通过令导数=0来求解,此公式无法写出解的公式来求最小值。
(2) 使用贪婪策略来迭代的求解,详见基本流程。
K-Means的基本流程
输入
数据集D={x1, x2, …, x,m}
聚类簇数k
迭代过程(第一阶段)
迭代过程(第二阶段)
更新簇的均值向量之后,返回第一阶段进行迭代。
循环结束条件:当前所有簇的均值向量趋于稳定。
K-Means的注意点
(1)最后的聚类结果高度依赖于初始均值向量的选择。
(2)比较适合连续属性的特征特征向量聚类,不太适合离散向量。
针对上面两个问题,一般解决办法如下:
1)初始均值向量的选择
(1) 简单的选取k个样本
(2) 计算所有数据的均值,将一些小的随机向量加到均值上,得到初始的k个均值向量。
2)离散属性的处理
(1)K-Modes算法:处理离散属性的聚类方法
(2)K-Prototype算法:处理混合属性(连续属性和离散数学混合)的方法
4.5 编程实现
from math import sqrt
from random import randint
class Cluster(object):
def __init__(self):
self._fcs=[]#簇的所有特征向量
self._center=None#簇的中心点
def set_center(self,new_center):
"""
初始化簇的中心点
params:
new_center->
returns:
"""
self._center=new_center
def get_center(self):
return self._center
def add_fc(self,fc):
"""
簇中添加特征向量
"""
self._fcs.qppend(fc)
def update_center(self):
"""
更新中心点
"""
feature_num = len(self._fcs[0])#特征向量中特征的数量
fc_num = len(self._fcs)#特征向量的数量
for i in range(feature_num):
#开始计算当前特征所在特征向量上的总和
current_sum = 0
for fc in self._fcs:
current_sum += fc[i]
#将中心点对应的特征向量的值更新为均值(总和/特征向量个数)
self._center[i] = current_sum/fc_num
def distance(self,fc):
"""
计算中心点和特征向量之间的距离
"""
length = len(fc)
distance =0
for i in range(length):
distance += (self._center[i]-fc[i])**2
return sqrt(distance)
class KMeans(object):
def __init__(self,k):
self._k = k
self._clusters = [cluster() for i in range(self._k)]
def _select_k_indexes(self,total_num):
"""
选择k个所有,选出来的所有用于从数据集中随机挑选k个簇的中心点
"""
k_indexes = []
while len(k_indexes)<self._k:
index = randint(0,total_num-1)#生成一个随机的索引
#如果当前的索引不在索引list中,把它加到k_indexes中
if index not in k_indexes:
k_indexes.append(index)
#循环结束的条件为k_indexes中已经有了k个随机且不重复的索引
return k_indexes
def _init_clusters(self,dataset):
"""
初始化K个簇的中心点
"""
k_indexes = self._select_k_indexes(self._k,len(dataset))
for i in range(self._k):
self._clusters[i].set_center(dataset[k_indexes[i]])
def train(self,dataset,iter_times):
"""
"""
self._init_clusters(dataset)#初始化k个簇
fc_num = len(dataset)#特征向量的个数
#进行iter_times次迭代
for i in range(iter_times):
for j in range(fc_num):#开始循环数据集中的数据
current_fc = dataset[j]#当前特征向量
#记当前特征向量的归属cluster的索引为0,最小距离为和第一个簇的距离
cluster_index = 0
min_dist = self._clusters[0].distance(current_fc)
#计算当前特征向量和所有簇的距离
for k in range(1,self._k):
current_dist = self._clusters[0].distance(current_fc)
#这里使用了打擂台算法,求得最小距离,节省了求出所有距离之和进行排序的过程
if current_dist<min_dist:
cluster_index = k
min_dist = current_dist
#所有k个距离计算完之后把当前的特征向量加入到指定的簇中
self._clusters[cluster_index].add_dc(current_fc)
#数据集中所有数据都迭代之后开始更新每个簇的中心点
for cluster in self._clusters:
cluster.update_center()
def get_cluster(self):
return self._clusters
4.6 实际应用(图像压缩)
背景分析
(1) 图像有三个颜色通道R,G,B
(2) 每个通道的值都在0-255之间(28,所以一个通道8位,1比特的数据)
(3) 减少颜色总数(16种颜色,24,0.5比特)
使用K-Means
(1)K-Means生成k个颜色簇
(2)图片种在一个颜色簇中的像素都使用该簇的中心点表示
(3)K值越小图片越失真,图片体积越小,训练实际越短
效果展示
#通过K均值聚类进行图像压缩
from cv2 import imread, imwrite
from skimage import io
from sklearn.cluster import KMeans
import numpy as np
def compress_image():
image = imread('12christma-png-10.png')
rows = image.shape[0]
cols = image.shape[1]
image = image.reshape(image.shape[0]*image.shape[1],3)
kmeans = KMeans(n_clusters=2,n_init=10,max_iter=200)
kmeans.fit(image)
clusters = np.asarray(kmeans.cluster_centers_,dtype=np.uint8)
labels = np.asarray(kmeans.labels_,dtype=np.uint8)
labels = labels.reshape(rows,cols)
np.save('codebook_img.npy',clusters)
imwrite('compressed_img.png',labels)
def reconstruct_image():
centers = np.load('codebook_img.npy')
c_image = io.imread('compressed_img.png')
image = np.zeros((c_image.shape[0],c_image.shape[1],3),dtype=np.uint8)
for i in range(c_image.shape[0]):
for j in range(c_image.shape[1]):
image[i,j,:] = centers[c_image[i,j],:]
imwrite('reconstructed_img.png',image)
compress_image()
reconstruct_image()