本文通过K均值算法作为例子研究数据聚类分析
无监督学习可以从给定的数据集中找到感兴趣的模式。
无监督学习,一般不给出模式的相关信息。所以,无监督学习算法需要自动探索信息是怎样组成的,并识别数据中的不同结构。
聚类就是对大量未知标注的数据集,按数据的内在相似性将数据集划分为多个类别,使类别内的数据相似度较大而类别间的数据相似度较小。
聚类中没有任何指导信息,完全按照数据的分布进行类别划分。
聚类的大小和结构都没有事先假定。
聚类属于无监督学习。
k均值算法试图将给定的数据分割为k个不相交的组(group)或者簇(cluster),每个簇的指标就是该组所有成员的均值。这个点通常称为质心,指具有相同名称的算术实体,并且可以被表示为任意维度中的向量。
判据和目标是最小化簇成员到包含该成员的簇的实际质心的平方距离的总和。这也称为惯性最小化,k均值的损失函数如下:
(1)设置初始类别数K,人为设置K个类别中心;
(2)根据样本和类别中心的距离进行类别划分,样本划分到距离最近的类别;
(3)重新计算当前类别划分下每类的中心(类别样本平均值);
(4)在得到类别中心下继续进行类别划分;
(5)如果连续两次的类别划分结果不变则停止算法;否则循环2~5 ;
可以采用欧式距离、曼哈顿距离、余弦相似度等等。这里就不对具体的相似度计算进行探讨。
首先导入数据集,使用sklearn生成数据集
blobs数据集设置类别K为4
circle数据集设置类别K为2
import tensorflow as tf
import numpy as np
import time
import matplotlib
import matplotlib.pyplot as plt
from sklearn.datasets.samples_generator import make_blobs
from sklearn.datasets.samples_generator import make_circles
DATA_TYPE = 'blobs'
N=200
# Number of clusters, if we choose circles, only 2 will be enough
if (DATA_TYPE == 'circle'):
K=2
else:
K=4
centers为我们指定的初始质心
生成数据集并且使用matplotlib绘制数据集
centers = [(-2, -2), (-2, 1.5), (1.5, -2), (2, 1.5)]
if (DATA_TYPE == 'circle'):
data, features = make_circles(n_samples=200, shuffle=True, noise= 0.01, factor=0.4)
else:
data, features = make_blobs (n_samples=200, centers=centers, n_features = 2, cluster_std=0.8, shuffle=False, random_state=42)
fig, ax = plt.subplots()
ax.scatter(np.asarray(centers).transpose()[0], np.asarray(centers).transpose()[1], marker = 'o', s = 250)
plt.show()
fig, ax = plt.subplots()
if (DATA_TYPE == 'blobs'):
ax.scatter(np.asarray(centers).transpose()[0], np.asarray(centers).transpose()[1], marker = 'o', s = 250)
ax.scatter(data.transpose()[0], data.transpose()[1], marker = 'o', s = 100, c = features, cmap=plt.cm.coolwarm )
plt.show()
points用于存放数据集的坐标
cluser_assignments用于存放每个数据的类别
centroids用于存储每个组质心的坐标
points=tf.Variable(data)
cluster_assignments = tf.Variable(tf.zeros([N], dtype=tf.int64)) # 样本归属聚类中心……
centroids = tf.Variable(tf.slice(points.initialized_value(), [0,0], [K,2])) # 初始聚类中心……
初始化模型的参数
sess = tf.Session()
sess.run(tf.global_variables_initializer())
sess.run(centroids)
计算所有样本与聚类中心的距离。
对所有的质心做N次复制,对每个 样本做K次复制,这样,样本点和质心的形状都是N*K*2。我们就可以计算每一个样本到每一个质心点之间在所有维度上的距离
rep_centroids = tf.reshape(tf.tile(centroids, [N, 1]), [N, K, 2])
rep_points = tf.reshape(tf.tile(points, [1, K]), [N, K, 2])
对所有维度求和,得到“和最小”的那个索引(这个索引就是每个点所属的新的类)Centroids也会在每个迭代之后由bucket_mean函数更新。
sum_squares = tf.reduce_sum(tf.square(rep_points - rep_centroids),
reduction_indices=2)
best_centroids = tf.argmin(sum_squares, 1) # 样本对应的聚类中心索引
停止条件是所有的质心不再变化
did_assignments_change = tf.reduce_any(tf.not_equal(best_centroids, cluster_assignments))
def bucket_mean(data, bucket_ids, num_buckets):
total = tf.unsorted_segment_sum(data, bucket_ids, num_buckets)
count = tf.unsorted_segment_sum(tf.ones_like(data), bucket_ids, num_buckets)
return total / count
means = bucket_mean(points, best_centroids, K)
使用control_dependencies来控制是否更新质心
with tf.control_dependencies([did_assignments_change]):
do_updates = tf.group(
centroids.assign(means),
cluster_assignments.assign(best_centroids))
更新质心
changed = True
iters = 0
fig, ax = plt.subplots()
if (DATA_TYPE == 'blobs'):
colourindexes=[2,1,4,3]
else:
colourindexes=[2,1]
while changed and iters < MAX_ITERS:
fig, ax = plt.subplots()
iters += 1
[changed, _] = sess.run([did_assignments_change, do_updates])
[centers, assignments] = sess.run([centroids, cluster_assignments])
ax.scatter(sess.run(points).transpose()[0], sess.run(points).transpose()[1], marker = 'o', s = 200, c = assignments, cmap=plt.cm.coolwarm )
打印结果
ax.scatter(centers[:,0],centers[:,1], marker = '^', s = 550, c = colourindexes, cmap=plt.cm.plasma)
ax.set_title('Iteration ' + str(iters))
plt.savefig("kmeans" + str(iters) +".png")
ax.scatter(sess.run(points).transpose()[0], sess.run(points).transpose()[1], marker = 'o', s = 200, c = assignments, cmap=plt.cm.coolwarm )
plt.show()
end = time.time()
print(("Found in %.2f seconds" % (end-start)), iters, "iterations")
print("Centroids:")
print(centers)
print("Cluster assignments:", assignments)
运行结果