本文内容、数据参考周志华《机器学习》,代码部分为个人实现,如有错误还请指出。
K-means(K均值)算法是最简单的一种聚类算法,它期望最小化平方误差
E
=
∑
i
=
1
k
∑
x
∈
C
i
∣
∣
x
−
μ
i
∣
∣
2
2
E=\sum\limits_{i=1}^k\sum\limits_{x\in C_i}||\pmb x-\pmb\mu_i||_2^2
E=i=1∑kx∈Ci∑∣∣xxx−μμμi∣∣22
其中
μ
i
=
1
∣
C
i
∣
∑
x
∈
C
i
x
\pmb\mu_i=\frac{1}{|C_i|}\sum_{x \in C_i}\pmb x
μμμi=∣Ci∣1∑x∈Cixxx是簇(cluster)
C
i
C_i
Ci的均值向量,
∣
∣
x
−
μ
i
∣
∣
2
||\pmb x-\pmb\mu_i||_2
∣∣xxx−μμμi∣∣2即一般意义下向量的模长。
K-means算法的一般步骤如下:
输入:样本集
D
=
{
x
1
,
x
2
,
.
.
.
,
x
m
}
;
D=\{\pmb x_1,\pmb x_2,...,\pmb x_m\};
D={xxx1,xxx2,...,xxxm};聚类簇数
k
.
k.
k.
过程:
1
:
从
D
中
随
机
选
择
k
个
样
本
作
为
初
始
均
值
向
量
{
μ
1
,
μ
2
,
.
.
.
,
μ
k
}
\ \ 1:从D中随机选择k个样本作为初始均值向量\{\pmb \mu_1, \pmb \mu_2,...,\pmb \mu_k\}
1:从D中随机选择k个样本作为初始均值向量{μμμ1,μμμ2,...,μμμk}
2
:
repeat
\ \ 2:\texttt{repeat}
2:repeat
3
:
令
C
i
=
∅
(
1
≤
i
≤
k
)
\ \ 3:\hspace{0.5cm}令C_i=\varnothing(1\le i \le k)
3:令Ci=∅(1≤i≤k)
4
:
for
j
=
1
,
2
,
.
.
.
,
m
do
\ \ 4:\hspace{0.5cm}\texttt{for}\hspace{0.2cm} j=1,2,...,m \hspace{0.2cm}\texttt{do}
4:forj=1,2,...,mdo
5
:
计
算
样
本
x
j
与
各
均
值
向
量
μ
i
(
1
≤
i
≤
k
)
的
距
离
:
d
j
i
=
∣
∣
x
−
μ
i
∣
∣
2
;
\ \ 5:\hspace{1.0cm}计算样本\pmb x_j与各均值向量\pmb \mu_i(1\le i \le k)的距离:d_{ji}=||\pmb x-\pmb\mu_i||_2;
5:计算样本xxxj与各均值向量μμμi(1≤i≤k)的距离:dji=∣∣xxx−μμμi∣∣2;
6
:
根
据
距
离
最
近
的
均
值
向
量
确
定
x
j
的
簇
标
记
:
λ
j
=
argmin
i
∈
{
1
,
2
,
.
.
.
,
k
}
d
j
i
;
\ \ 6:\hspace{1.0cm}根据距离最近的均值向量确定x_j的簇标记:\lambda_j=\texttt{argmin}_{i\in \{1,2,...,k\}}d_{ji};
6:根据距离最近的均值向量确定xj的簇标记:λj=argmini∈{1,2,...,k}dji;
7
:
将
样
本
x
j
划
入
相
应
的
簇
:
C
λ
j
=
C
λ
j
∪
{
x
j
}
;
\ \ 7:\hspace{1.0cm}将样本\pmb x_j划入相应的簇:C_{\lambda_j}=C_{\lambda_j}\cup\{\pmb x_j\};
7:将样本xxxj划入相应的簇:Cλj=Cλj∪{xxxj};
8
:
end
for
\ \ 8:\hspace{0.5cm}\texttt{end for}
8:end for
9
:
for
i
=
1
,
2
,
.
.
.
,
k
do
\ \ 9:\hspace{0.5cm}\texttt{for}\hspace{0.2cm}i=1,2,...,k\hspace{0.2cm}\texttt{do}
9:fori=1,2,...,kdo
10
:
计
算
新
均
值
向
量
:
μ
i
′
=
1
∣
C
i
∣
∑
x
∈
C
i
x
;
10:\hspace{1.0cm}计算新均值向量:\pmb \mu_i^{'}=\frac{1}{|C_i|}\sum_{x \in C_i}\pmb x;
10:计算新均值向量:μμμi′=∣Ci∣1∑x∈Cixxx;
11
:
if
μ
i
′
≠
μ
i
then
11:\hspace{1.0cm}\texttt{if} \hspace{0.2cm}\pmb \mu_i^{'}\neq\pmb \mu_i\hspace{0.2cm}\texttt{then}
11:ifμμμi′=μμμithen
12
:
将
当
前
均
值
向
量
μ
i
更
新
为
μ
i
′
12:\hspace{1.5cm}将当前均值向量\pmb \mu_i更新为\pmb \mu_i^{'}
12:将当前均值向量μμμi更新为μμμi′
13
:
else
13:\hspace{1.0cm}\texttt{else}
13:else
14
:
保
持
当
前
均
值
向
量
不
变
14:\hspace{1.5cm}保持当前均值向量不变
14:保持当前均值向量不变
15
:
end
if
15:\hspace{1.0cm}\texttt{end if}
15:end if
16
:
end
for
16:\hspace{0.5cm}\texttt{end for}
16:end for
17
:
until
当
前
均
值
向
量
均
未
更
新
17:\texttt{until}\hspace{0.2cm}当前均值向量均未更新
17:until当前均值向量均未更新
输出:簇划分
C
=
{
C
1
,
C
2
,
.
.
.
,
C
k
}
\mathcal{C}=\{C_1,C_2,...,C_k\}
C={C1,C2,...,Ck}
注:为避免运行时间过长,通常设置一个最大运行轮数或最小调整幅度阈值,若到达最大轮数或调整幅度小于阈值,则停止运行。
下面我们用python来实现一下K-means算法:我们先尝试手动实现这个算法,再用sklearn库中的KMeans类来实现。数据我们采用《机器学习》的西瓜数据(P202表9.1):
# 下面的内容保存在 melons.txt 中
# 第一列为西瓜的密度;第二列为西瓜的含糖率。我们要把这30个西瓜分为3类
0.697 0.460
0.774 0.376
0.634 0.264
0.608 0.318
0.556 0.215
0.403 0.237
0.481 0.149
0.437 0.211
0.666 0.091
0.243 0.267
0.245 0.057
0.343 0.099
0.639 0.161
0.657 0.198
0.360 0.370
0.593 0.042
0.719 0.103
0.359 0.188
0.339 0.241
0.282 0.257
0.748 0.232
0.714 0.346
0.483 0.312
0.478 0.437
0.525 0.369
0.751 0.489
0.532 0.472
0.473 0.376
0.725 0.445
0.446 0.459
我们用到的库有matplotlib和numpy,如果没有需要先用pip安装一下。
import random
import numpy as np
import matplotlib.pyplot as plt
下面定义一些数据:
k = 3 # 要分的簇数
rnd = 0 # 轮次,用于控制迭代次数(见上文)
ROUND_LIMIT = 100 # 轮次的上限
THRESHOLD = 1e-10 # 单轮改变距离的阈值,若改变幅度小于该阈值,算法终止
melons = [] # 西瓜的列表
clusters = [] # 簇的列表,clusters[i]表示第i簇包含的西瓜
从melons.txt读取数据,保存在列表中:
f = open('melons.txt', 'r')
for line in f:
# 把字符串转化为numpy中的float64类型
melons.append(np.array(line.split(' '), dtype = np.string_).astype(np.float64))
从 m m m个数据中随机挑选出 k k k个,对应上面算法的第 1 1 1行:
# random的sample函数从列表中随机挑选出k个样本(不重复)。我们在这里把这些样本作为均值向量
mean_vectors = random.sample(melons, k)
下面是算法的主要部分。
# 这个while对应上面算法的2-17行
while True:
rnd += 1 # 轮次增加
change = 0 # 把改变幅度重置为0
# 清空对簇的划分,对应上面算法的第3行
clusters = []
for i in range(k):
clusters.append([])
# 这个for对应上面算法的4-8行
for melon in melons:
'''
argmin 函数找出容器中最小的下标,在这里这个目标容器是
list(map(lambda vec: np.linalg.norm(melon - vec, ord = 2), mean_vectors)),
它表示melon与mean_vectors中所有向量的距离列表。
(numpy.linalg.norm计算向量的范数,ord = 2即欧几里得范数,或模长)
'''
c = np.argmin(
list(map( lambda vec: np.linalg.norm(melon - vec, ord = 2), mean_vectors))
)
clusters[c].append(melon)
# 这个for对应上面算法的9-16行
for i in range(k):
# 求每个簇的新均值向量
new_vector = np.zeros((1,2))
for melon in clusters[i]:
new_vector += melon
new_vector /= len(clusters[i])
# 累加改变幅度并更新均值向量
change += np.linalg.norm(mean_vectors[i] - new_vector, ord = 2)
mean_vectors[i] = new_vector
# 若超过设定的轮次或者变化幅度<预先设定的阈值,结束算法
if rnd > ROUND_LIMIT or change < THRESHOLD:
break
print('最终迭代%d轮'%rnd)
最后我们绘图来观察一下划分的结果:
colors = ['red', 'green', 'blue']
# 每个簇换一下颜色,同时迭代簇和颜色两个列表
for i, col in zip(range(k), colors):
for melon in clusters[i]:
# 绘制散点图
plt.scatter(melon[0], melon[1], color = col)
plt.show()
划分结果(由于最开始的
k
k
k个均值向量随机选取,每次划分的结果可能会不同):

完整代码:
import random
import numpy as np
import matplotlib.pyplot as plt
k = 3
rnd = 0
ROUND_LIMIT = 10
THRESHOLD = 1e-10
melons = []
clusters = []
f = open('melons.txt', 'r')
for line in f:
melons.append(np.array(line.split(' '), dtype = np.string_).astype(np.float64))
mean_vectors = random.sample(melons, k)
while True:
rnd += 1
change = 0
clusters = []
for i in range(k):
clusters.append([])
for melon in melons:
c = np.argmin(
list(map( lambda vec: np.linalg.norm(melon - vec, ord = 2), mean_vectors))
)
clusters[c].append(melon)
for i in range(k):
new_vector = np.zeros((1,2))
for melon in clusters[i]:
new_vector += melon
new_vector /= len(clusters[i])
change += np.linalg.norm(mean_vectors[i] - new_vector, ord = 2)
mean_vectors[i] = new_vector
if rnd > ROUND_LIMIT or change < THRESHOLD:
break
print('最终迭代%d轮'%rnd)
colors = ['red', 'green', 'blue']
for i, col in zip(range(k), colors):
for melon in clusters[i]:
plt.scatter(melon[0], melon[1], color = col)
plt.show()
这种经典算法显然不需要我们反复地造轮子,被广泛使用的python机器学习库sklearn已经提供了该算法的实现。sklearn的官方文档中给了我们一个示例:
>>> from sklearn.cluster import KMeans
>>> import numpy as np
>>> X = np.array([[1, 2], [1, 4], [1, 0],
... [10, 2], [10, 4], [10, 0]])
>>> kmeans = KMeans(n_clusters=2, random_state=0).fit(X)
>>> kmeans.labels_
array([1, 1, 1, 0, 0, 0], dtype=int32)
>>> kmeans.predict([[0, 0], [12, 3]])
array([1, 0], dtype=int32)
>>> kmeans.cluster_centers_
array([[10., 2.],
[ 1., 2.]])
可以看出,X即要聚类的数据(1,2),(1,4),(1,0)等。
KMeans类的初始化参数n_clusters即簇数
k
k
k;
random_state是用于初始化选取
k
k
k个向量的随机数种子;
kmeans.labels_即每个点所属的簇;
kmeans.predict方法预测新的数据属于哪个簇;
kmeans.cluster_centers_返回每个簇的中心。
我们就改造一下这个简单的示例,完成对上面西瓜的聚类。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
X = []
f = open('melons.txt', 'r')
for line in f:
X.append(np.array(line.split(' '), dtype = np.string_).astype(np.float64))
kmeans = KMeans(n_clusters = 3, random_state = 0).fit(X)
colors = ['red', 'green', 'blue']
for i, cluster in enumerate(kmeans.labels_):
plt.scatter(X[i][0], X[i][1], color = colors[cluster])
plt.show()
运行结果如下,可以看到和我们手写的聚类结果基本一致。

关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO