根据训练样本是否包含标签信息,机器学习可以分为监督学习和无监督学习(这里我们不考虑半监督学习)。聚类算法是典型的无监督学习算法,它是对事务自动归类的一种算法,在聚类算法中利用样本的标签,将具有相似属性的事物聚集到一类中。
一、常用的相似性度量
K-Means算法(K-均值算法)是基于相似性的无监督学习算法,即通过比较样本之间的相似性,将较为相似的样本划分到同一个类别中。为了度量两个样本(以样本X和样本Y为例)之间的相似性,通常会定义一个距离函数d(X,Y),利用这个距离函数来定义样本X和样本Y之间的相似性。
1、闵可夫斯基距离
空间中两个点X,Y的坐标分别为:
那么,点X与点Y之间的闵可夫斯基距离可以定义为:
2、曼哈顿距离
同样的两个点X,Y其曼哈顿距离可以表示为:
3、欧氏距离
同样的,点X与点Y的欧氏距离可以表示为:
由上面的定义可知,曼哈顿距离和欧式距离是闵可夫斯基距离的特殊形式。当p=1时,闵可夫斯基距离就是曼哈顿距离;当p=2时,闵可夫斯基距离就是欧式距离。在K-Means算法中,我们使用欧氏距离作为其相似性度量。因为欧氏距离的根号存在与否对其度量性没有影响,简单起见,我们使用欧氏距离的平方作为最终的相似性度量。
首先将需要加载相关的模块:
import numpy as np
欧氏距离的平方实现代码如下:
def distance(vecA, vecB):
'''
计算两个向量之间欧氏距离的平方
:param vecA: 向量A的坐标
:param vecB: 向量B的坐标
:return: 返回两个向量之间欧氏距离的平方
'''
dist = (vecA - vecB) * (vecA - vecB).T
return dist[0, 0]
二、K-Means算法原理
首先,我们需要人为的指定最终的聚类个数,假设我们最终的聚类个数为k个,即我们需要初始化k个聚类中心,通过计算每个样本与聚类中心的相似度,将样本点划分到距离最近的聚类中心。然后,通过每个类的样本重新计算每个类的聚类中心,重复这个操作直至聚类中心不再改变即为最终的聚类结果。
三、K-Means算法步骤
1、随机初始化K个聚类中心,其代码如下:
def randomCenter(data, k):
'''
随机初始化聚类中心
:param data: 训练数据
:param k: 聚类中心的个数
:return: 返回初始化的聚类中心
'''
n = np.shape(data)[1] # 特征的个数
cent = np.mat(np.zeros((k, n))) # 初始化K个聚类中心
for j in range(n): # 初始化聚类中心每一维的坐标
minJ = np.min(data[:, j])
rangeJ = np.max(data[:, j]) - minJ
cent[:, j] = minJ * np.mat(np.ones((k, 1))) + np.random.rand(k, 1) * rangeJ # 在最大值和最小值之间初始化
return cent
2、计算每个样本与k个聚类中心的相似度,将样本划分到与之最相似的类中;
3、计算划分到每个类别中所有样本特征的均值,并将该均值作为每个类别新的聚类中心;
4、重复2、3步操作,直至聚类中心不再改变,输出最终的聚类中心。
构建K-Means算法的代码如下:
def kmeans(data, k, cent):
'''
kmeans算法求解聚类中心
:param data: 训练数据
:param k: 聚类中心的个数
:param cent: 随机初始化的聚类中心
:return: 返回训练完成的聚类中心和每个样本所属的类别
'''
m, n = np.shape(data) # m:样本的个数;n:特征的维度
subCenter = np.mat(np.zeros((m, 2))) # 初始化每个样本所属的类别
change = True # 判断是否需要重新计算聚类中心
while change == True:
change = False # 重置
for i in range(m):
minDist = np.inf # 设置样本与聚类中心的最小距离,初始值为正无穷
minIndex = 0 # 所属的类别
for j in range(k):
# 计算i和每个聚类中心的距离
dist = distance(data[i, ], cent[j, ])
if dist < minDist:
minDist = dist
minIndex = j
# 判断是否需要改变
if subCenter[i, 0] != minIndex: # 需要改变
change = True
subCenter[i, ] = np.mat([minIndex, minDist])
# 重新计算聚类中心
for j in range(k):
sum_all = np.mat(np.zeros((1, n)))
r = 0 # 每个类别中样本的个数
for i in range(m):
if subCenter[i, 0] == j: # 计算第j个类别
sum_all += data[i, ]
r += 1
for z in range(n):
try:
cent[j, z] = sum_all[0, z] / r
except:
print("ZeroDivisionError: division by zero")
return subCenter, cent
四、K-Means算法举例
1、数据集:数据集含有两个特征,如下图所示:
2、加载数据集
def load_data(file_path):
'''
导入数据
:param file_path: 文件路径
:return: 以矩阵的形式返回导入的数据
'''
f = open(file_path)
data = []
for words in f.readlines():
row = [] # 记录每一行
word = words.strip().split("\t")
for x in word:
row.append(float(x)) # 将文本中的特征转换成浮点数
data.append(row)
f.close()
return np.mat(data) # 以矩阵的形式返回
3、保存聚类结果
通过K-Means聚类之后,我们可以使用如下方法保存聚类结果:
def save_result(file_name, data):
'''
保存source中的结果到file_name文件中
:param file_name: 保存的文件名
:param data: 需要保存的数据
:return:
'''
m, n = np.shape(data)
f = open(file_name, "w")
for i in range(m):
tmp = []
for j in range(n):
tmp.append(str(data[i, j]))
f.write("\t".join(tmp) + "\n")
f.close()
4、调用K-Means算法
调用K-Means算法:
if __name__ == "__main__":
k = 4 # 聚类中心的个数
file_path = "tfidf.txt"
subCenter, center = kmeans(load_data(file_path), k, randomCenter(load_data(file_path), k))
save_result("result/kmeans_sub", subCenter)
save_result("result/kmeans_center", center)
5、结果展示
得到的聚类结果如图所示:
你们在此过程中遇到了什么问题,欢迎留言,让我看看你们都遇到了哪些问题。