在无监督学习中,训练样本的标记信息是未知的,网络是通过对无标记样本的学习来揭示数据的内在性质和规律。在无监督学习中,应用最多的就是聚类。
简单的理解聚类:聚类就是把数据划分为不同的组,组内的数据具有相似的属性和特征,组间的数据具有高度不相关的属性和特征。即把相似的东西分为一组。
那么,组内相似越大,组间差别越大,那么聚类的效果就会很好。
难点:如何评估(不知到分类结果到底怎么样),如何调参(分成多少类合适)
现在,就其中最简单的算法,kmeans进行学习。
基本概念:
簇:相似的堆的个数,用k值表示
质心:均值,即对数据各个维度取平均
距离的度量:常用欧式距离
优化目标:
具体例子:
- 图a:我们想要生成2堆数据,即簇为2,K=2。
- 图b:首先,我们先随机产生两个点,对应图中红×和蓝×。
- 图c:分别计算图中各点到红×和蓝×之间的距离,如果离红×比较近,那么这个点被归为红色点,反之,则归为蓝色点。
- 图d:经过步骤3中,分好了2个数据堆,如图d,分别计算红色堆和蓝色堆的质心,即在两个维度上的均值。对应图d中的红叉和蓝×
- 图e:同步骤3
- 图f:继续计算当前质心,更新。继续遍历所有数据,直到满足停止条件。
下图是kmean算法步骤,注意看停止条件。
这里停止条件的意思是,直到新质心的位置相比旧质心的位置不再改变,或者差值在一个很小的数内,就可以停止了。或者也可以把迭代次数作为停止条件。
- 优点:简单,适合常规的数据集
- 缺点:k值难以确定,复杂度比较高因为存在遍历,很难发现任意形状的簇。
代码实现:
testset 是一个80x2 的txt文件。总共80个点,每个点有2个特征。
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
##假设k取4
data = pd.read_table('C:/Users/red/Desktop/a1/testSet.txt', header=None, names=['x', 'y'])
##没有表头,read_table去Tab
x = data['x']
y = data['y']
plt.subplot(2,1,1)
plt.scatter(x, y)
def distance(data, centers):
# data: 80x2, centers: kx2
dist = np.zeros((data.shape[0], centers.shape[0])) ## 出来的是80*4,即每个点相对4个质心的距离
for i in range(len(data)):
for j in range(len(centers)):
dist[i, j] = np.sqrt(np.sum((data.iloc[i, :] - centers[j]) ** 2)) ##共80个样本,每个样本的在两个特征维度上,
# 分别对k个质心求距离然后求和,类似矩阵乘法,
# 所以距离矩阵为80x4
return dist
def near_center(data, centers): ##得到每个点离哪个质心最近,返回对应质心的label
dist = distance(data, centers)
near_cen = np.argmin(dist, 1) ##得到的dist行为80个点,列为每个点到4个质心的距离。然后取最小距离,得到对应质心的label。
return near_cen
def kmeans(data, k):
# step 1: init. centers
centers = np.random.choice(np.arange(-5, 5, 0.1), (k, 2)) ##随机产生质心
print(centers)
for _ in range(10): #做10次迭代
# step 2: 点归属
near_cen = near_center(data, centers)
# step 3:簇重心更新
for ci in range(k): ##每次点划分完之后,安照步骤,需要重新寻找各个簇的质心,即求平均
centers[ci] = data[near_cen == ci].mean()
return centers, near_cen
centers, near_cen = kmeans(data, 4)
print(near_cen)
plt.subplot(2,1,2)
plt.scatter(x, y, c=near_cen)
plt.scatter(centers[:, 0], centers[:, 1], marker='*', s=500, c='r')
plt.show()
对代码中distance函数及质心的更新解释如下:
假设:
- testset数据集为80x2,相当于二维平面上的80个点,每个点的坐标为(x,y)。分簇数为4,就得到图中前两个矩阵a和b,a中每个点(总共80个)都要对b中初始随机生成的4个点(质心)依次求距离,如上图,画出了2次的。所以,得到的距离矩阵c为80x4的,行为80个点,列为每个点到4个质心的距离。
- 由1可得,在near_center()函数里,是对每一行,求距离的最小值。返回每个点对应的最近质心的label。
- 在更新质心时,是对已经分好簇的点,分别求平均,来更新质心。 循环终止条件是迭代次数。
最终,得到结果如下图所示:
代码还有很多改进的地方,如替换for循环为矩阵计算,这样运算速度会快,不过以上代码理解kmeans的思想是足够了。
另外,可以通过python内置的sklearn库实现好的kmeans算法,对鸢尾花数据集进行聚类分析。
代码如下:
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data[:, :]
#绘制数据分布图
plt.subplot(2,1,1)
plt.scatter(X[:, 0], X[:, 1], c = "red", marker='o', label='see')
plt.legend(loc=2)
estimator = KMeans(n_clusters=3)#构造聚类器
estimator.fit(X)#聚类
label_pred = estimator.labels_ #获取聚类标签
#绘制k-means结果
x0 = X[label_pred == 0]
x1 = X[label_pred == 1]
x2 = X[label_pred == 2]
# x3 = X[label_pred == 3]
plt.subplot(2,1,2)
plt.scatter(x0[:, 0], x0[:, 1], c = "red", marker='o', label='label0')
plt.scatter(x1[:, 0], x1[:, 1], c = "green", marker='*', label='label1')
plt.scatter(x2[:, 0], x2[:, 1], c = "blue", marker='+', label='label2')
# plt.scatter(x3[:, 0], x3[:, 1], c = "blue", marker='x', label='label3')
plt.legend(loc=2)
plt.show()
另外,刚开始随机初始点的选择,因此最终求得的簇的划分与随机选取的“簇中心”有关,这是因为kmeans算法收敛到了局部最小值,而非全局最小值。既然对初值很敏感,可以做多次蒙特卡洛仿真求平均。