本博客将以wine数据集,一步一步的演示K-means实战的全过程,让大家学会和使用k-means方法进行聚类(大佬勿锤)
1.数据集来源
特征分别为:酒精 苹果酸 艾熙 灰分碱性 镁 总酚类 黄酮类 非淀粉酚类 原花青素 颜色强度 色调 稀释葡萄酒的OD280/OD315 脯氨酸
数据来自uci,点击此处即可下载
2.背景知识
1.评价聚类的常用指标指标
- 兰德指数(Rand index)
- 互信息(MI)
- 同质化得分(Homogeneity)
- 完整性得分(Complenteness)
- v_meansure_score
- 轮廓系数(Silhouette)
- calinski-harabaz Index
这里解释两个常用的兰德指数和轮廓系数
兰德指数:假设U是外部评价标准即true_label,而V是聚类结果。设定四个统计量:
a为在U中为同一类且在V中也为同一类别的数据点对数
b为在U中为同一类但在V中却隶属于不同类别的数据点对数
c为在U中不在同一类但在V中为同一类别的数据点对数
d为在U中不在同一类且在V中也不属于同一类别的数据点对数
此时兰德指数为:
A R T = a + d a + b + c + d ART=\frac{a+d}{a+b+c+d} ART=a+b+c+da+d
轮廓系数:适用于实际类别信息未知的情况,用来计算所有样本的平均轮廓系数。对于单个样本,设a是该样本与它同类别中其他样本的平均距离,b是与它距离最近不同类别中样本的平均距离.
轮廓系数为:
s
(
i
)
=
b
−
a
m
a
x
(
a
,
b
)
s(i)=\frac{b-a}{max(a,b)}
s(i)=max(a,b)b−a
2.数据预处理的常用方法
- 标准化,均值去除和按方差比例缩放
- scale 零均值单位方差
- StandardScaler计算训练集的平均值和标准差
- 归一化,将数据特征缩放至某一范围
- MinMaxScaler(最小最大值标准化)
- MaxAbsScaler(绝对值最大标准化)
这些方法适用于不同地方:
- 数据集的标准化:当个体特征太过或明显不遵从高斯正态分布时,标准化表现的效果较差。实际操作中,经常忽略特征数据的分布形状,移除每个特征均值,划分离散特征的标准差,从而等级化,进而实现数据中心化。
- 数据集的归一化:有时数据集中数字差最大的属性对计算结果影响较大,或者有时数据集的标准差非常非常小,有时数据中有很多很多零(稀疏数据)需要保存住0元素。
这里介绍一下常用的归一化处理:
归一化:将任意取值范围内的特征值传换为[0,1]或者[-1,1]内的值
公式
n
e
w
v
a
l
u
e
s
=
o
l
d
v
a
l
u
e
s
−
m
i
n
m
a
x
−
m
i
n
newvalues=\frac{oldvalues - min}{max-min}
newvalues=max−minoldvalues−min
min和max分别为数据集的最小值和最大值
3.k的选择
常用方法是:肘方法
K_means参数的最优解是以成本函数最小化为目标,成本函数为各个类畸变程度之和,
每个类的畸变程度等于该类重心与其内部成员位置距离的平方和,
但是平均畸变程度会随着K的增大先减小后增大,所以可以求出最小的平均畸变程度。
3.实战应用
对于wine的数据,我们希望可以根据特征值聚类出不同的红酒。
第一次尝试,我们简单的读取数据,利用knn算法,计算兰德指数,同质化得分和平均轮廓系数,并绘制聚类图。
我们发现效果并不好
改进:
通过观察不难发现,特征值之间的数值相差过大,导致在利用欧式距离计算的时候,会使得部分特征值失去效果。
所以采用了归一化处理数据。
min_max_scaler = preprocessing.MinMaxScaler()
X = min_max_scaler.fit_transform(X)
另外,对于k的选择,本文也加上了肘方法。
K = range(1, 10)
meandistortions = []
for k in K:
kmeans = KMeans(n_clusters=k)
kmeans.fit(X)
meandistortions.append(sum(np.min(cdist(X, kmeans.cluster_centers_, 'euclidean'), axis=1)) / X.shape[0])
plt.plot(K, meandistortions, 'bx-')
plt.xlabel('k')
plt.ylabel('meandistortions')
plt.title('best K of the model');
plt.show()
如图k选择3
此时加上。上面两步以后。效果得到了明显的提升。
但是数据聚类效果依然不够好。
分享原因可能是uci的数据特征值不够精准,数据存在噪声。
从下文可以看出wine原本特征值有30个,可是现在只有13个。
等到博主功力更深厚在回来改进。
I think that the initial data set had around 30 variables, but
for some reason I only have the 13 dimensional version.
I had a list of what the 30 or so variables were, but a.)
I lost it, and b.), I would not know which 13 variables
are included in the set.
下面是源码
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn import preprocessing
from scipy.spatial.distance import cdist
from sklearn import metrics
# 读取原始数据
X = []
y_true = []
id = []
f = open('wine.data')
for line in f:
y = []
for index,item in enumerate(line.split(",")):
if index == 0:
id.append(int(item))
continue
y.append(float(item))
X.append(y)
# 转化为numpy array
X = np.array(X)
y_true = np.array(id)
min_max_scaler = preprocessing.MinMaxScaler()
X = min_max_scaler.fit_transform(X)
K = range(1, 10)
meandistortions = []
for k in K:
kmeans = KMeans(n_clusters=k)
kmeans.fit(X)
meandistortions.append(sum(np.min(cdist(X, kmeans.cluster_centers_, 'euclidean'), axis=1)) / X.shape[0])
plt.plot(K, meandistortions, 'bx-')
plt.xlabel('k')
plt.ylabel('meandistortions')
plt.title('best K of the model');
plt.show()
n_clusters = 3
cls = KMeans(n_clusters).fit(X)
y_pre = cls.predict(X)
n_samples,n_features=X.shape #总样本量,总特征数
inertias = cls.inertia_ #样本距离最近的聚类中心的总和
adjusted_rand_s=metrics.adjusted_rand_score(y_true,y_pre) #调整后的兰德指数
homogeneity_s=metrics.homogeneity_score(y_true,y_pre) #同质化得分
silhouette_s=metrics.silhouette_score(X,y_pre,metric='euclidean') #平均轮廓系数
print("兰德指数ART",adjusted_rand_s)
print("同质化得分homo",homogeneity_s)
print("平均轮廓系数",silhouette_s)
centers=cls.cluster_centers_ #各类别中心
colors=['#ff0000','#00ff00','#0000ff'] #设置不同类别的颜色
plt.figure() #建立画布
for i in range(n_clusters): #循环读取类别
index_sets=np.where(y_pre==i) #找到相同类的索引集合、
cluster=X[index_sets] #将相同类的数据划分为一个聚类子集
plt.scatter(cluster[:,0],cluster[:,1],c=colors[i],marker='.') #展示聚类子集内的样本点
plt.plot(centers[i][0],centers[i][1],'*',markerfacecolor=colors[i],markeredgecolor='k',markersize=6)
plt.show()