聚类算法与K-means实现
聚类算法与K-means实现
一、聚类算法的数学描述:
区别于监督学习的算法(回归,分类,预测等),无监督学习就是指训练样本的 label 未知,只能通过对无标记的训练样本的学习来揭示数据的内在规律和性质。无监督学习任务中研究最多的就是聚类算法(clustering)。我们假定一个样本集:
编号 | 色泽 | 根蒂 | 敲声 | 纹理 | 脐部 | 触感 | 密度 | 含糖率 | 好瓜 |
---|---|---|---|---|---|---|---|---|---|
1 | 青绿 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 0.697 | 0.46 | 是 |
2 | 乌黑 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | 硬滑 | 0.774 | 0.376 | 是 |
3 | 乌黑 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 0.634 | 0.264 | 是 |
4 | 青绿 | 蜷缩 | 沉闷 | 清晰 | 凹陷 | 硬滑 | 0.608 | 0.318 | 是 |
5 | 浅白 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 0.556 | 0.215 | 是 |
该样本集中包含5个样本数据,聚类算法的目的就是将这 5 个样本划分为互不相交的子集,每个子集被称为一个簇。比如,我们以色泽对样本进行划分,可以得到3个簇,分别为:。值得说明的是,在实际的聚类过程中,没有 “色泽” 的概念,聚类过程仅仅能自动形成簇结构,而簇所对应的概念语义是人为赋予的。用数学语言描述上述过程: |
假设样本集 包含 个无标记样本,每个样本由 维特征来描述,即 。那么聚类算法的目的就是将样本集 划分为 个不相交的簇:,且 。我们用 来代表 个簇的标记,因此第 个簇的所有元素可以标记为 。对应上述过程,。
二、聚类算法的度量
每一个样本可能有 个特征描述,那么有没有一个度量标准来判断聚类算法的好坏呢?其中一个最重要的原则就是:相同簇的样本尽可能相似,不同簇的样本尽可能不同。聚类的性能度量有两类:1)将聚类结果与某个参考模型进行比较,称为外部指标;2)直接考察聚类结果而步利用任何参考模型,称为内部指标。
2.1 外部指标法:
对数据集 ,假设通过聚类给出的簇划分为 ,而参考模型给出的簇划分为。相应地,用 来分别表示与 对应的标记向量,于是我们定义如下:
刚一看到上述公式会感觉有点懵,那么我们来直观地感受一下上述表达式描述的是什么。以 为例, 代表在 中属于同一类且在 中属于同一类的样本,那么 就等于 中样本的个数。以此类推, 代表在 中属于同一类,而在 中属于不同类的样本个数; 代表在 中属于不同类,而在 中属于同一类的样本个数; 代表在 中属于不同类,而在 中也属于不同类的样本个数。看到这,有没有类似于评价指标中的精准率,召回率?https://www.cnblogs.com/zhaozhibo/p/14954685.html。由于每个样本对 只能出现在一个集合中,因此:。基于此,我们给出几个常用的聚类性能度量外部指标:
- Jaccard系数(JC系数):
- Rand指数(RI指数):
- FM指数(FMI):
其实不难理解,JC系数描述的是分类正确的样本数占总样本数的比例,RI指数描述的是分类正确(正样本和负样本)的个数占总的样本比例,FMI描述的是分类正确占 “分类正确+分类错误” 的比例。因此这三个指标都是在 ,且越大越好。
2.2 内部指标法:
仅仅考虑聚类结果的划分:,定义:
其中, 计算两个向量之间的距离,定义为:
- 当 时,等效为曼哈顿距离:
- 当 时,等效为欧氏距离:
代表第 个簇的中心点,即:。显然, 代表簇 中任意两个样本距离的平均值, 代表簇 内任意两个样本之间距离的最大值, 代表不同的簇中最近的两个样本之间的距离, 代表不同簇中心点之间的距离。根据我们 “相同簇的样本尽可能相似,不同簇的样本尽可能不同” 的原则, 越小越好,而 越大越好; 越大越好, 越小越好。因此我们有如下的内部度量指标:
- DB指数:
- Dunn指数:
显然,DB越小越好,而DI越大越好。
三、K-means聚类算法的实现
3.1 算法描述
给定样本集 ,“K 均值” 算法针对聚类所得簇划分 最小化平方误差:
其中, 是簇 的均值向量,于是 描述了簇内样本围绕簇均值向量的紧密程度, 值越小,则簇内样本的相似度越高。但是优化起来并不容易,需要考虑样本集 的所有可能的簇划分,因此 均值采取了贪心策略,通过迭代优化来近似求解。具体的流程如下:
- 从样本集中随机选取 个向量作为初始均值向量 ,且簇 设置为空;
- 分别计算将所有的样本与初始均值向量 的距离,将与 最小距离的样本 划分入 ,并以此类推得到第一轮的迭代结果
- 重新更新簇 的均值,,然后重复步骤2
- 直到下一轮与上一轮的结果相同,停止更新
3.2 代码实现
# k-means cluster
def kmeans(dataSet, k):
numSamples = dataSet.shape[0]
# first column stores which cluster this sample belongs to,
# second column stores the error between this sample and its centroid
clusterAssment = mat(zeros((numSamples, 2)))
clusterChanged = True
## step 1: init centroids
centroids = initCentroids(dataSet, k)
while clusterChanged:
clusterChanged = False
## for each sample
for i in xrange(numSamples):
minDist = 100000.0
minIndex = 0
## for each centroid
## step 2: find the centroid who is closest
for j in range(k):
distance = euclDistance(centroids[j, :], dataSet[i, :])
if distance < minDist:
minDist = distance
minIndex = j
## step 3: update its cluster
if clusterAssment[i, 0] != minIndex:
clusterChanged = True
clusterAssment[i, :] = minIndex, minDist**2
## step 4: update centroids
for j in range(k):
pointsInCluster = dataSet[nonzero(clusterAssment[:, 0].A == j)[0]]
centroids[j, :] = mean(pointsInCluster, axis = 0)
print 'Congratulations, cluster complete!'
return centroids, clusterAssment
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构