简介
- 非监督学习
- 聚类算法,样本自动聚集成类,使得成本函数最小,使得样本点到聚类中心的欧式距离最小
原理
过程
- 初始化簇中心:随机选择k个数据对象作为初始簇中心
- 聚类剩余数据对象:对剩余的每个对象,根据其与各簇中心的相似度(距离),将它赋给与其最相似(距离最近)的簇中心对应的簇
- 判断成本函数(样本点到聚类中心的欧式距离)是否达到最优(样本点的聚类中心不再发生变化)或者达到最大迭代次数:达到最优或者最大迭代次数则退出,否则进入第4步
- 重新计算新簇中心:计算每个簇中所有对象的平均值,作为新的簇中心
相似度
similarity
- 相似度通过距离衡量
距离
设特征空间
X
X
X是m维实数向量空间
R
n
R^n
Rn,
x
i
,
x
j
∈
X
x_i,x_j\in X
xi,xj∈X,
x
i
=
(
x
i
(
1
)
,
x
i
(
2
)
,
.
.
.
,
x
i
(
m
)
)
T
x_i=(x_i^{(1)},x_i^{(2)},...,x_i^{(m)})^T
xi=(xi(1),xi(2),...,xi(m))T,
x
j
=
(
x
j
(
1
)
,
x
j
(
2
)
,
.
.
.
,
x
j
(
m
)
)
T
x_j=(x_j^{(1)},x_j^{(2)},...,x_j^{(m)})^T
xj=(xj(1),xj(2),...,xj(m))T
- x i , x j x_i,x_j xi,xj的 L p L_p Lp距离: L p ( x i , x j ) = ( ∑ l = 1 m ∣ x i ( l ) − x j ( l ) ∣ p ) 1 p L_p(x_i,x_j)=\Big(\sum\limits_{l=1}^m|x_i^{(l)}-x_j^{(l)}|^p\Big)^{\frac{1}{p}} Lp(xi,xj)=(l=1∑m∣xi(l)−xj(l)∣p)p1
- p = 2 p=2 p=2欧式距离: L 2 ( x i , x j ) = ( ∑ l = 1 m ∣ x i ( l ) − x j ( l ) ∣ 2 ) 1 2 L_2(x_i,x_j)=\Big(\sum\limits_{l=1}^m|x_i^{(l)}-x_j^{(l)}|^2\Big)^{\frac{1}{2}} L2(xi,xj)=(l=1∑m∣xi(l)−xj(l)∣2)21
剩余样本与各簇中心的相似度(距离)
- L 2 ( x i , x j ) = ( ∑ l = 1 m ∑ j = 1 k ∣ x i ( l ) − c j ( l ) ∣ 2 ) 1 2 L_2(x_i,x_j)=\Big(\sum\limits_{l=1}^m\sum\limits_{j=1}^k|x_i^{(l)}-c_j^{(l)}|^2\Big)^{\frac{1}{2}} L2(xi,xj)=(l=1∑mj=1∑k∣xi(l)−cj(l)∣2)21
- c i c_i ci是第i个簇中心
成本函数
成本函数: m i n ∑ i = 1 k ∑ x ∈ c i d i s t ( c i , x ) 2 min\sum\limits_{i=1}^k\sum\limits_{x\in c_i}dist(c_i,x)^2 mini=1∑kx∈ci∑dist(ci,x)2
- 最小化每个数据对象到所属中心点的距离的方差,共有k个聚类
- k是聚类个数,簇的个数
- c i c_i ci是第i个簇的中心点
- c i c_i ci是 x x x的所属的聚类中心
- d i s t ( c i , x ) dist(c_i,x) dist(ci,x)为x到 c i c_i ci的距离
代码
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import random
from sklearn.datasets import make_blobs
matplotlib.rcParams['font.family'] = 'STSong'
matplotlib.rcParams['font.size'] = 20
np.random.seed(1)
class DataSet(object):
"""
数据集
"""
def __init__(self, X, y):
self.X = X
self.y = y
def get_data_set():
"""
获取数据集
"""
# X.shape=1000x2,y.shape=1000x1;X共有1000个样本,每个样本2个特征值;y是这个样本所属的中心
X, y = make_blobs(centers=4, n_samples=1000)
print("X.shape=", X.shape)
plt.figure(figsize=(10, 8))
# X的第1列做横轴,X的第2列做纵轴;c=color 与y的值大小有关,值相同颜色相同
plt.scatter(X[:, 0], X[:, 1], c=y)
plt.title("4个簇")
plt.xlabel("X特征1")
plt.ylabel("X特征2")
plt.show()
data_set = DataSet(X, y)
return data_set
class KMeans():
"""
k-means算法
"""
def __init__(self, k=4):
"""
构造函数
:param k: 簇个数,聚类中心个数
"""
self.k = k
self.centers = None
self.initial_centers = None
def clustering(self, data, num_iter=100):
"""
聚类函数
达到最优(样本点的聚类中心不再发生变化)或者最大迭代次数则退出
:param data: X
"""
print("data.shape=", data.shape)
# 初始化簇中心:随机选择k个数据对象作为初始簇中心
self.centers = np.array(random.sample(list(data), self.k))
self.initial_centers = np.copy(self.centers)
# 记录样本点所属中心点的下标
old_belongs = None
real_iter = 0
for i in range(num_iter):
# new_belongs是长度为1000的array,存储样本点所属中心点的下标
new_belongs = [self.classify(point) for point in data]
# 达到最优(样本点的聚类中心不再发生变化);样本点的聚类中心(下标)是否发生了变化
if new_belongs == old_belongs:
print("要求迭代次数:", num_iter, ",实际迭代次数:", real_iter)
return
old_belongs = new_belongs
# 重新计算新簇中心:计算每个簇中所有对象的平均值,作为新的簇中心
for ki in range(self.k):
# 获取中心点为ki的所有样本的下标;points.shape=(point_idxs.size,2)
point_idxs = np.where(np.array(new_belongs) == ki)
points = data[point_idxs]
# 计算每个簇中所有对象的平均值,作为新的簇中心;axis=0按列求平均值
self.centers[ki] = points.mean(axis=0)
real_iter += 1
def classify(self, point):
"""
计算给定样本点与k个中心点的距离,返回最近距离的中心点下标
"""
# 样本点到聚类中心的欧式距离;centers.shape=(4,2) point.shape=(2,)是行向量(1,2);axis=1 按行求和
dists = np.sqrt(np.sum((point - self.centers) ** 2, axis=1))
# 返回最近距离的中心点下标
return np.argmin(dists)
def show_clusters(self, data_set):
plt.figure(figsize=(10, 8))
plt.title("初始聚类中心点是黑色,最终聚类中心点是红色")
# X的第1列做横轴,X的第2列做纵轴;c=color 与y的值大小有关,值相同颜色相同
plt.scatter(data_set.X[:, 0], data_set.X[:, 1], c=data_set.y)
# 聚类的初始中心点
plt.scatter(self.initial_centers[:, 0], self.initial_centers[:, 1], marker="*", c='k')
# 聚类的最终中心点
plt.scatter(self.centers[:, 0], self.centers[:, 1], marker="*", c='r')
plt.show()
def main():
data_set = get_data_set()
# k是簇数量,聚类中心数量
kmeans = KMeans(k=4)
# 开始聚类
kmeans.clustering(data_set.X, num_iter=100)
# 展示初始聚类中心,最终聚类中心
kmeans.show_clusters(data_set)
if __name__ == "__main__":
main()