-在机器学习领域有一个非常特殊的存在——无监督学习, 其中最为经典就是聚类算法了。聚类算法因为其不需要先验标签,因此在很多领域应用都较为广泛,其中最经典的算法就是Kmean Cluster。
对于给定的样本集,K-means按照样本之间的距离大小,将样本集划分为K个簇。让簇内的点尽量紧密的连在一起,而让簇间的距离尽量的大。
Kmean聚类的算法如下:
K-mean聚类算法中可以使用不同的距离计算方式,除了最常见的欧式距离,还可以使用曼哈顿距离,余弦距离等。对这几种距离的定义如下。
欧式距离(Euclidean Distance)
这就是最为常用的距离计算方法,计算公式如下
V 1 = x 0 , x 1 , . . . x n , V 2 = y 0 , y 1 , . . . y n V1={x_0,x_1,...x_n},V2={y_0,y_1,...y_n} V1=x0,x1,...xn,V2=y0,y1,...yn
d 12 = ∑ i = 0 n ( x i − y i ) 2 d_{12}=\sqrt{\sum_{i=0}^n(x_i-y_i)^2} d12=i=0∑n(xi−yi)2
曼哈顿距离(Manhattan Distance)
纽约
曼哈顿的街道规划是非常整齐的块状,街道纵横交错,从一个地方到另一个地方,需要经过的距离是走过的纵向和横向街道的距离和,这就是所谓的曼哈顿距离。
计算公式如下:
V 1 = x 0 , x 1 , . . . x n , V 2 = y 0 , y 1 , . . . y n V1={x_0,x_1,...x_n},V2={y_0,y_1,...y_n} V1=x0,x1,...xn,V2=y0,y1,...yn
d 12 = ∑ i = 0 n ∣ x i − y i ∣ d_{12} = \sum_{i=0}^{n}|x_i-y_i| d12=i=0∑n∣xi−yi∣
余弦距离(Cosine)
c o s i n e ( θ ) = V 1. V 2 ∣ V 1 ∣ ∣ V 2 ∣ cosine(\theta)= \frac{V1.V2}{|V1||V2|} cosine(θ)=∣V1∣∣V2∣V1.V2
因为Kmean算法是通过计算离散的点之间的距离来判定类别的,因此对数据的预处理,归一化就变得很重要。如果输入的数据值差异很大,那么在计算距离时一些值比较大的特征就会对距离的影响比较大,反之一些数值较小的特征对于距离计算的影响就会比较小。比如,年龄的范围在0-100,而收入的范围可能时1000-100,000之间,显然在聚类时收入的形象就会比年龄大出10-1000倍。为了排除这种特征本身数值范围对聚类的影响,对输入特征数据进行归一化就显得非常的重要了。
数值型数据的归一化有两种方式:
对于类别型的特征数据,分两类进行处理。
索引 | 姓名 | 性别 |
---|---|---|
1 | 张三 | 男 |
2 | 李四 | 女 |
3 | 刘五 | 男 |
转换后数据:
索引 | 姓名 | 性别男 | 性别女 |
---|---|---|---|
1 | 张三 | 1 | 0 |
2 | 李四 | 0 | 1 |
3 | 刘五 | 1 | 0 |
把一个类别特征数据转换成哑码,在这个类别中有多少个不同的类别值,那么就要产生多少个新数据栏,每个数据栏仅有两个值,0或1。
对聚类算法的评估指标有silhouette score和calinski harabasz scores两种。
定义a:一个样本与同一簇类中的其他样本点的平均距离;
定义b:一个样本与距离最近簇类中所有样本点的平均距离;
s i l h o u e t t e s c o r e = b − a m a x ( a , b ) silhouette score=\frac{b-a}{max(a,b)} silhouettescore=max(a,b)b−a
轮廓系数处于[-1,1]的范围内,-1表示错误的聚类,1表示高密度的聚类,0附近表示重叠的聚类。
此外,常用的方法还有 手肘法(Elbow),这个方法适用于K值比较小的时候,查看在不同K值下的损失函数的值,并作图。选取损失函数值下降趋势明显变小的这个点,就是最佳的K值选取点。
我们可以根据我们的需要设定一个聚类评估标准,然后根据这个评估标准来选取最佳的K值。此处我们以轮廓系数为例,来看一个实列。
首先,我们用make_blobs来生成一组数据,1000个数据点,5个中心。
x, y = make_blobs(n_samples=1000, n_features=2, centers=5)
plt.scatter(x[:,0],x[:,1],c=y)
我们对它用kmean算法进行聚类,分别设分类为3,4,5,6,7,然后来看一下使用手肘法得到的图像结果。
Sum_of_squared_distance = []
for n_clusters in range(3,7)
model = KMeans(n_clusters=n_clusters, init="random", random_state=8).fit(x)
labels = model.predict(x)
Sum_of_squared_distance.append(model.inertia_)
plt.plot([3,4,5,6,7], Sum_of_squared_distance, 'bx-')
在Kmean模型中inertia_参数是每个样本到它们最近的聚类中心的距离平方和,在sklearn的KMeans对象中inertia_属性就记录了该值。作图,从图中可以容易的发现,从k=5起该平方和值的递减变小,由此可以确定选择K值为5是最佳的。
同时,我们使用轮廓系数来确定K值。计算轮廓系数可以直接使用sklean.metrics对象下的方法silhouette_score, 其参数包括x值,kmean输出的类别值,以及计算距离的方法,此处我们选择euclidean即使用欧氏空间的距离计算规则。
from sklearn import metrics
for n_clusters in range(3,8)
model = KMeans(n_clusters=n_clusters, init="random", random_state=8).fit(x)
labels = model.predict(x)
silhouette_score = metrics.silhouette_score(
x,
labels,
metric='euclidean'
)
print("K=%d, silhouette_score=%.6f"%(n_clusters,silhouette_score))
得到输出:
K=3, silhouette_score=0.559099
K=4, silhouette_score=0.639296
K=5, silhouette_score=0.701195
K=6, silhouette_score=0.630837
K=7, silhouette_score=0.542077
可见当K=5的时候, 轮廓系数分值最高,这个结论也是和手肘法是相互验证的。
但情况并不总是如此,在一些边界模糊的分类中,轮廓系数显示的结果和手肘法不一样。比如,我们生成的数据如下所示。
当K分别等于2到7时,我们得到的聚类结果如下图所示:
由于这个数据分布中三个类别聚集在图像左边,二另外两个类别聚集在图像右边,因此从图像上看,分成两大类或四类(左三类,右一类)都是合理的。那如果参考手肘法和轮廓系数又会得到什么样的结论呢?
首先我们来看一下轮廓系数,确实如先前所说,K=2和4时轮廓系数都是最大的。
K=2, silhouette_score=0.773288
K=3, silhouette_score=0.618731
K=4, silhouette_score=0.667729
K=5, silhouette_score=0.513711
K=6, silhouette_score=0.441556
K=7, silhouette_score=0.409318
再看手肘法的结果,你会惊讶的发现手肘法依旧显示在K=5的时候是最优,K值继续增加后距离平方和的减小不再明显。
由此可见,轮廓系数和手肘法在评估上的侧重点是不同的的,轮廓系数同时侧重类别内部的聚合度以及和其他类别的的距离,仅从图像上看该数据分成两类也是有其合理性的,但是手肘法的结果却非常一致,他仅考虑点和中心的距离,而不考虑不同类别之间的距离,因此他给出的结果始终是5个类。因此,在考虑K值时还是主要从实际需求出发,考虑多种评估手段的结果,做出判断。
PCA降维算法是将高维的数据降维到低维数据,并尽可能的保留最多的特征。比如,我们的数据有100个特征,对100个特征的数据进行聚类,那计算效率比如很低,但如果我们可以将其降维到5个特征且最大程度的保留原有100个特征中的信息,再进行聚类就会很大程度上提高计算效率。
这里需要注意的是这5个降维后的特征和原来100个特征不存在一对一的对于关系,也就是说这5个新的特征中的任何一个都是原来100个特征经过信息提取综合后的综合体。
PCA降维的核心思想就是矩阵的特征值和特征向量。读书时学习矩阵的特征值和特征向量,那是就纳闷什么叫特征,现在终于明白了就是包括矩阵最大的信息量的向量。
PCA降维的基本步骤如下:
以下为numpy实现代码:
import numpy as np
# Generate a numpy array with 10000 recorders and each recorder contains 12 features
x = np.random.randn(10000, 12)
# For each value in the matrix, reduce its row mean
x -= x.mean(axis=0)
# Calculate the cov matrix
C = np.dot(x.T,x)
eigvalue, eigvector = np.linalg.eig(C)
# Get the sort index of eigen values (large -> small)
new_index = np.argsort(eigvalue)[::-1]
# Sort the eigen vetors according to the eigen value index, choose to the top 30 (reduce the deminsion to 30)
A = -eigvector[:,new_index].T[: 30]
x_cal_reduced_dimension = x.dot(A[:30].T)
# Print out the first data recorders
print("result from manual calculation")
print(x_cal_reduced_dimension[0])
print("components")
print(A)
print("explained variance ")
print(eigvalue[:3])
print("explained variance ratio ")
print((eigvalue/eigvalue.sum())[:3])