K-means算法
简介
K-Means算法,也被称为K-平均或K-均值算法,是一种广泛使用的聚类算法。
与分类、序列标注等任务不同,聚类是在事先并不知道任何样本标签的情况下,通过数据之间的内在关系把样本划分为若干类别,使得同类别样本之间的相似度高,不同类别之间的样本相似度低(即增大类内聚,减少类间距)。
他是用于处理无监督的问题,以距离作为数据对象间相似性度量的标准,即数据对象间的距离越小,则它们的相似性越高,则它们越有可能在同一个类簇。之所以被称为K-Means是因为它可以发现k个不同的簇,且每个簇的中心采用簇中所含值的均值计算而成。
K-Means算法步骤:
- 初始化常数K,随机初始化k个聚类中心
- 重复计算以下过程,知道聚类中心不再改变
- 计算每个样本与每个聚类中心点的距离,将样本划分到最近的中心点
- 计算划分到每个类别中的所有样本特征的均值,并将该均值作为每个类新的聚类中心 输出最终的聚类中心以及每个样本所属的类别。
代码原理:
import numpy as np
from matplotlib import pyplot as plt
def euclidean_distance(vecA, vecB):
'''计算vecA与vecB之间的欧式距离'''
# return np.sqrt(np.sum(np.square(vecA - vecB)))
return np.linalg.norm(vecA - vecB)
def random_centroids(data, k):
''' 随机创建k个中心点'''
dim = np.shape(data)[1] # 获取向量的维度
centroids = np.mat(np.zeros((k, dim)))
for j in range(dim): # 随机生成每一维中最大值和最小值之间的随机数
min_j = np.min(data[:, j])
range_j = np.max(data[:, j]) - min_j
centroids[:, j] = min_j * np.mat(np.ones((k, 1))) + np.random.rand(k, 1) * range_j
return centroids
def KMeans(data, k, distance_func=euclidean_distance):
'''根据k-means算法求解聚类的中心'''
m = np.shape(data)[0] # 获得行数m
cluster_assment = np.mat(np.zeros((m, 2))) # 初试化一个矩阵,用来记录簇索引和存储距离平方
centroids = random_centroids(data, k) # 生成初始化点
cluster_changed = True # 判断是否需要重新计算聚类中心
while cluster_changed:
cluster_changed = False
for i in range(m):
distance_min = np.inf # 设置样本与聚类中心之间的最小的距离,初始值为正无穷
index_min = -1 # 所属的类别
for j in range(k):
distance_ji = distance_func(centroids[j, :], data[i, :])
if distance_ji &distance_min:
distance_min = distance_ji
index_min = j
if cluster_assment[i, 0] != index_min:
cluster_changed = True
cluster_assment[i, :] = index_min, distance_min ** 2 # 存储距离平方
for cent in range(k): # 更新质心,将每个族中的点的均值作为质心
pts_in_cluster = data[np.nonzero(cluster_assment[:, 0].A == cent)[0]]
centroids[cent, :] = np.mean(pts_in_cluster, axis=0)
return centroids, cluster_assment
def show_cluster(data, k, centroids, cluster_assment):
num, dim = data.shape
mark = ['or', 'ob', 'og', 'oy', 'oc', 'om']
for i in range(num):
mark_index = int(cluster_assment[i, 0])
plt.plot(data[i, 0], data[i, 1], mark[mark_index])
for i in range(k):
plt.plot(centroids[i, 0], centroids[i, 1], 'o', markeredgecolor='k', markersize=16)
plt.show()
if __name__ == "__main__":
data = []
f = open("sz.txt", 'r')
for line in f:
data.append([float(line.split(',')[0]), float(line.split(',')[1])])
data = np.array(data)
k = 4
centroids = random_centroids(data, k)
centroids, cluster_assment = KMeans(data, k)
show_cluster(data, k, centroids, cluster_assment)
我们可以用Scikit-Learn 库来实现 K-means 算法
from sklearn.cluster import KMeans
import numpy as np
# 示例数据
X = np.array([[1, 2],
[1.5, 1.8],
[5, 8],
[8, 8],
[1, 0.6],
[9, 11]])
# 创建 KMeans 实例,设置簇的数量
kmeans = KMeans(n_clusters=2)
# 拟合模型
kmeans.fit(X)
# 打印质心
print(kmeans.cluster_centers_)
# 对每个数据点进行簇分配
print(kmeans.labels_)
# 预测新数据点的簇
predictions = kmeans.predict([[0, 0], [12, 12]])
print(predictions)
在 Scikit-Learn 的 KMeans 类中,有多个参数可以根据您的需求进行调整。这些参数可以帮助您优化聚类的性能和结果。以下是一些常见的参数及其作用:
n_clusters:
默认值: 8
描述: 要形成的簇的数量,也是要生成的质心(中心点)的数量。
init:
默认值: 'k-means++'
描述: 初始化质心的方法。可选项包括 'k-means++'(默认)、'random'(随机选择初始质心),或者传递一个 ndarray 来指定初始质心。
n_init:
默认值: 10
描述: 算法运行具有不同质心初始值的次数。最终解是在 n_init 运行中产生最佳结果的输出。
max_iter:
默认值: 300
描述: 单次运行中的最大迭代次数。
tol:
默认值: 1e-4
描述: 关于簇内误差平方和的相对容忍度,用于确定算法的收敛。
precompute_distances:
默认值: 'deprecated'
描述: 此参数不再使用,其功能在 0.23 版本后已被弃用。
verbose:
默认值: 0
描述: 冗长模式。
random_state:
默认值: None
描述: 随机数生成器的种子。用于初始化质心。
copy_x:
默认值: True
描述: 计算时是否应复制数据,如果不复制,则原始数据可能会被覆盖。
n_jobs:
默认值: None
描述: 用于计算的作业数。这个参数已经在 0.23 版本中被弃用。
algorithm:
默认值: 'auto'
描述: K-means 的实现算法。可选项包括 'auto'、'full' 或 'elkan'。'full' 是经典的 EM 风格算法,'elkan' 是 Elkan 变体。
选择合适的参数取决于您的数据集的大小和特性。例如,对于大数据集,增加 n_init 的值或选择更快的 algorithm(如 ‘elkan’)可能会提高性能。对于有些应用,设置适当的 random_state 可以重现可重复的结果。在使用时,您可能需要根据实际情况调整这些参数,以达到最佳的聚类效果。
Kmeans++
K-Means算法中的聚类中心的个数k需要事先指定,这一点对于一些未知数据存在很大的局限性。其次,在利用K-Means算法进行聚类之前,需要初始化k个聚类中心,在上述的K-Means算法的过程中,使用的是在数据集中随机选择最大值和最小值之间的数作为其初始的聚类中心,但是聚类中心选择不好,对于K-Means算法有很大的影响。
K-Means++算法在聚类中心的初始化过程中的基本原则是使得初始的聚类中心之间的相互距离尽可能远,这样可以避免出现上述的问题。
K-Means++ 算法步骤:
- 在数据集中随机选择一个样本点作为第一个初始化的聚类中心
- 选择出其余的聚类中心:
- 计算样本中的每一个样本点与已经初始化的聚类中心之间的距离,并选择其中最短的距离,记为d_i
- 以概率选择距离最大的样本作为新的聚类中心,重复上述过程,直到k个聚类中心都被确定
- 对k个初始化的聚类中心,利用K-Means算法计算最终的聚类中心。
代码实现
def nearest(data, cluster_centers, distance_func=euclidean_distance):
min_dist = np.inf
m = np.shape(cluster_centers)[0] # 当前已经初始化的聚类中心的个数
for i in range(m):
d = distance_func(data, cluster_centers[i, ]) # 计算point与每个聚类中心之间的距离
if min_dist > d: # 选择最短距离
min_dist = d
return min_dist
def get_centroids(data, k, distance_func=euclidean_distance):
m, n = np.shape(data)
cluster_centers = np.mat(np.zeros((k, n)))
index = np.random.randint(0, m) # 1、随机选择一个样本点为第一个聚类中心
cluster_centers[0, ] = np.copy(data[index, ])
d = [0.0 for _ in range(m)] # 2、初始化一个距离的序列
for i in range(1, k):
sum_all = 0
for j in range(m):
d[j] = nearest(data[j, ], cluster_centers[0:i, ], distance_func) # 3、对每一个样本找到最近的聚类中心点
sum_all += d[j] # 4、将所有的最短距离相加
sum_all *= random() # 5、取得sum_all之间的随机值
for j, di in enumerate(d): # 6、获得距离最远的样本点作为聚类中心点
sum_all -= di
if sum_all > 0:
continue
cluster_centers[i] = np.copy(data[j, ])
break
return cluster_centers
在 Scikit-Learn 的 KMeans 类中,默认使用 K-means++ 进行初始化。这种方法的主要优势是它能够更好地初始化质心,从而有可能提高最终聚类质量,并加快算法收敛。
当您在 Scikit-Learn 中使用 KMeans 类时,默认的 init 参数值就是 ‘k-means++’。这意味着如果您不特别指定其他初始化方法,就会自动使用 K-means++。以下是如何显式地设置这个参数的示例:
from sklearn.cluster import KMeans
import numpy as np
# 示例数据
X = np.array([[1, 2],
[1.5, 1.8],
[5, 8],
[8, 8],
[1, 0.6],
[9, 11]])
# 创建 KMeans 实例,并显式指定使用 k-means++ 初始化质心
kmeans = KMeans(n_clusters=2, init='k-means++')
# 拟合模型
kmeans.fit(X)
# 打印质心
print(kmeans.cluster_centers_)
# 对每个数据点进行簇分配
print(kmeans.labels_)
二分K-means
K-Means算法收敛,但是聚类效果较差的原因是,K-Means算法收敛到了局部最小值,而非全局最小值(局部最小值指结果还可以但并非最好结果,全局最小值是可能的最好结果)。
一种用于度量聚类效果的指标是SSE(Sum of Squared Error,误差平方和)。SSE值越小表示数据点越接近他们的质心,聚类效果也最好。因为对误差取了平方,因此更加重视远离中心的点。一种肯定可以降低SSE的方法是增加族的个数,但这违背了聚类的目标。聚类的目标是在保持簇数目不变的情况下提高簇的质量。
二分 K-means(Bisecting K-means)是 K-means 算法的一个变体,它采用一种分层方法来进行聚类。这种方法的主要思想是重复地将簇分成两个较小的簇,直到达到所需的簇数量。这种方法通常能产生比传统 K-means 更优的聚类结果,尤其是在处理不均匀大小的簇或非球形簇时。
- 所有点作为一个簇
- 将该簇一分为二
- 选择能最大限度降低聚类代价函数(也就是误差平方和)的簇划分为两个簇。
- 以此进行下去,直到簇的数目等于用户给定的数目k为止。
代码实现
from sklearn.cluster import KMeans
import numpy as np
def bisecting_kmeans(X, num_clusters):
clusters = [X]
centroids = []
while len(centroids) < num_clusters:
largest_cluster = max(clusters, key=len)
clusters.remove(largest_cluster)
kmeans = KMeans(n_clusters=2, random_state=0).fit(largest_cluster)
centroids.extend(kmeans.cluster_centers_)
clusters.extend([largest_cluster[kmeans.labels_ == i] for i in range(2)])
return centroids
# 示例数据
X = np.array([[1, 2], [1.5, 1.8], [5, 8], [8, 8], [1, 0.6], [9, 11]])
# 使用二分 K-means
centroids = bisecting_kmeans(X, 3)
print(centroids)
在这个例子中,bisecting_kmeans 函数接受数据集 X 和目标簇的数量 num_clusters 作为输入。该函数首先将整个数据集作为一个簇。然后,它迭代地选择最大的簇,并使用 K-means 将其分成两个簇,直到达到所需数量的簇。这个实现是基本的,并且可以根据需要进行优化和调整。
k-medoids(k-中心聚类算法)
K-medoids和K-means是有区别的,不一样的地方在于中心点的选取
K-means中,将中心点取为当前cluster中所有数据点的平均值,对异常点很敏感!K-medoids中,将从当前cluster 中选取到其他所有(当前cluster中的)点的距离之和最小的点作为中心点。
算法流程:
- 总体n个样本点中任意选取k个点作为medoids
- 按照与medoids最近的原则,将剩余的n-k个点分配到当前最佳的medoids代表的类中
- 对于第i个类中除对应medoids点外的所有其他点,按顺序计算当其为新的medoids时,代价函数的值,遍历所有可能,选取代价函数最小时对应的点作为新的medoids
- 重复2-3的过程,直到所有的medoids点不再发生变化或已达到设定的最大迭代次数
- 产出最终确定的k个类
优缺点
k-medoids对噪声鲁棒性好。
例:当一个cluster样本点只有少数几个,如(1,1)(1,2)(2,1)(1000,1000)。其中(1000,1000)是噪声。如果按照k-means质心大致会处在(1,1)(1000,1000)中间,这显然不是我们想要的。这时k-medoids就可以避免这种情况,他会在(1,1)(1,2)(2,1)(1000,1000)中选出一个样本点使cluster的绝对误差最小,计算可知一定会在前三个点中选取。
k-medoids只能对小样本起作用,样本大,速度就太慢了,当样本多的时候,少数几个噪音对k-means的质心影响也没有想象中的那么重,所以k-means的应用明显比k-medoids多。
K-medoids 是一种与 K-means 类似的聚类算法,不同之处在于 K-medoids 选择数据点中的实际观测点作为簇的中心(即 medoids),而不是使用簇内数据点的均值作为中心。这使得 K-medoids 对于异常值更加鲁棒,尤其适用于异常值或噪声较多的数据集。
代码实现
在 Python 中,您可以使用 Scikit-Learn-Extra 库来实现 K-medoids。Scikit-Learn-Extra 是一个补充于 Scikit-Learn 的库,提供了一些额外的算法,包括 K-medoids。首先,您需要安装这个库:
pip install scikit-learn-extra
from sklearn_extra.cluster import KMedoids
import numpy as np
# 示例数据
X = np.array([[1, 2],
[1.5, 1.8],
[5, 8],
[8, 8],
[1, 0.6],
[9, 11]])
# 创建 KMedoids 实例
kmedoids = KMedoids(n_clusters=2, random_state=0)
# 拟合模型
kmedoids.fit(X)
# 打印 medoids
print(kmedoids.cluster_centers_)
# 对每个数据点进行簇分配
print(kmedoids.labels_)
在这个例子中,KMedoids 类被用来创建一个实例,其中指定了簇的数量(n_clusters=2)和随机状态(random_state=0)。然后,使用示例数据集 X 来拟合这个模型。与 K-means 类似,K-medoids 也提供了 cluster_centers_ 属性来查看每个簇的中心(即 medoids),以及 labels_ 属性来查看每个数据点的簇分配。
K-medoids 的一个关键优点是其对异常值的鲁棒性。由于簇中心是实际的数据点,因此算法对于数据中的异常值不太敏感。这使得它在某些应用中比 K-means 更可靠,尤其是在数据集包含异常值或噪声时。
Reference
http://t.csdnimg.cn/gcwOs
http://t.csdnimg.cn/5G5BL