这是我看下来,最简单的内容,哭了,K-means,so nice
K-means,由于太过简单,不需要数学推导时,一时间甚至无从下指
首先,K-means需要提前锚定几个点,然后让所有数据样本根据与这几个点的距离,将分别选择最短距离凑成一簇
有那么点近水楼台先得月,兔子硬吃窝边草的意思
然后所有样本分别站队分组后,每个组再重新选出新的锚定点:计算出所有特征各自的均值,作为新锚定点
重复分组与计算新锚点,直到最终所有组的锚点不再改变,或是超过最大迭代次数。
- 确定初始锚点
- 数据样本分组
- 计算各组新锚点
- 重复2和3步,直到达标
但非常关键的是,如何确定初始锚点?以及,需要几个初始锚点?
需要几个初始锚点: 这个其实不好说,在scikit-learn里,默认是8个,但要想获得更好的聚类效果,一般可以调试选择最终表现较好的锚点数 N p o i n t N_{point} Npoint
如何确定初始锚点: 可以随机,但是有一个优化选择初始锚点的方式,一般被称为K-means++
K-means++:也是先确定了锚点数 N p o i n t N_{point} Npoint,然后,
- 首先随机选取一个锚点,此时,所有样本都归于当前锚点
- 计算出所有样本到锚点的距离,选择距锚点最远的样本点,作为新的锚点,
- 计算出所有样本到新锚点的距离,如果到新锚点距离更短,则分到新锚点组
- 然后再次分别计算所有样本到各自锚点的距离,选择各组中距离锚点最远的点,作为新锚点,(注意:如果已经有多个锚点,也是依次遍历多个锚点,生成新锚点)
- 依次循环234步,直到得到 N p o i n t N_{point} Npoint个锚点
K-means++,仅仅是获取 N p o i n t N_{point} Npoint个新锚点,还要重新开始进行聚类,即
1. 确定初始锚点
①. 首先随机选取一个锚点,此时,所有样本都归于当前锚点
②. 计算出所有样本到锚点的距离,选择距锚点最远的样本点,作为新的锚点,
③. 计算出所有样本到新锚点的距离,如果到新锚点距离更短,则分到新锚点组
④. 然后再次分别计算所有样本到各自锚点的距离,选择各组中距离锚点最远的点,作为新锚点,(注意:如果已经有多个锚点,也是依次遍历多个锚点,生成新锚点)
⑤. 依次循环234步,直到得到
N
p
o
i
n
t
N_{point}
Npoint个锚点
2. 数据样本分组
3. 计算各组新锚点
4. 重复2和3步,直到达标
步骤很清晰,思路很明确,下笔如有神!开敲键盘~
程序设计
数据结构
考虑到每个簇,有各自的锚点,还有分组的点,并且都有一样的计算距离的方式
用字典或是创建一个类的对象来存储各个簇(组)的数据,也是可以的
但考虑到,样本要同时与每个锚点计算并比较距离,如果是用对象的话呢,不太好在类之外进行比较
因此,不如直接用字典存储,然后在外边定义一个计算距离的方法,直接在外边进行比较,会更方便吧
那就是,一个字典,包含了多个组别,每个组别都有各自的锚点、分组数据、组均方误差
字典 = {
"组1":[xxxx],
"组2":[xxxx]
# .......[xxxx]表示对应的锚点..........
}
但在对每个样本进行分类时,遇到点小麻烦,有两个思路:
思路一:
- 维护一张所有样本与所有组锚点距离的dataframe表,每次都计算出所有样本与新锚点的距离
- 再给每个样本获取距离最小的对应锚点,并添加一个新列class,这个新列存的是每个样本的组别名
- 再对组别名新列,进行分组聚合,也就是每个组都割裂开各自的数据(这里要注意,新列里的组别名要跟字典的组名保持一致)
- 将割裂开的数据,依次计算各特征的均值作为新锚点,并对比旧锚点是否有改变
- 判断是否需要再迭代:是否超迭代次数或锚点是否无改变?
- 如果需要再迭代,那就继续循环,否则停止迭代,输出最终的分组锚点及数据
思路二:
- 所有锚点都存在一个列表里
- 对一条数据,计算各样本与新锚点的距离,并存入一个新列表里。
- 获取列表中距离最小的下标值,并获取到对应的锚点
- 用pandas里的apply函数,对所有样本数据,进行相同的操作,最终得到每个样本的锚点
如果是每个样本都单独去计算距离,再进行比较
。。。。啊算了,思路一就挺好的,思路二还有些小问题。。。。
啊,明天再说,今晚没心思敲键盘了。。。