You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

k-means.py 17 kB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. # -*- coding: utf-8 -*-
  2. # ---
  3. # jupyter:
  4. # jupytext_format_version: '1.2'
  5. # jupytext_formats: ipynb,py
  6. # kernelspec:
  7. # display_name: Python 3
  8. # language: python
  9. # name: python3
  10. # language_info:
  11. # codemirror_mode:
  12. # name: ipython
  13. # version: 3
  14. # file_extension: .py
  15. # mimetype: text/x-python
  16. # name: python
  17. # nbconvert_exporter: python
  18. # pygments_lexer: ipython3
  19. # version: 3.5.2
  20. # ---
  21. # # k-means
  22. # ## Theory
  23. #
  24. # 由于具有出色的速度和良好的可扩展性,K-Means聚类算法算得上是最著名的聚类方法。K-Means算法是一个重复移动类中心点的过程,把类的中心点,也称重心(centroids),移动到其包含成员的平均位置,然后重新划分其内部成员。
  25. #
  26. # K是算法计算出的超参数,表示类的数量;K-Means可以自动分配样本到不同的类,但是不能决定究竟要分几个类。
  27. #
  28. # K必须是一个比训练集样本数小的正整数。有时,类的数量是由问题内容指定的。例如,一个鞋厂有三种新款式,它想知道每种新款式都有哪些潜在客户,于是它调研客户,然后从数据里找出三类。也有一些问题没有指定聚类的数量,最优的聚类数量是不确定的。
  29. #
  30. # K-Means的参数是类的重心位置和其内部观测值的位置。与广义线性模型和决策树类似,K-Means参数的最优解也是以成本函数最小化为目标。K-Means成本函数公式如下:
  31. # $$
  32. # J = \sum_{k=1}^{K} \sum_{i \in C_k} | x_i - u_k|^2
  33. # $$
  34. #
  35. # $u_k$是第$k$个类的重心位置,定义为:
  36. # $$
  37. # u_k = \frac{1}{|C_k|} \sum_{x \in C_k} x
  38. # $$
  39. #
  40. #
  41. # 成本函数是各个类畸变程度(distortions)之和。每个类的畸变程度等于该类重心与其内部成员位置距离的平方和。若类内部的成员彼此间越紧凑则类的畸变程度越小,反之,若类内部的成员彼此间越分散则类的畸变程度越大。
  42. #
  43. # 求解成本函数最小化的参数就是一个重复配置每个类包含的观测值,并不断移动类重心的过程。
  44. # 1. 首先,类的重心是随机确定的位置。实际上,重心位置等于随机选择的观测值的位置。
  45. # 2. 每次迭代的时候,K-Means会把观测值分配到离它们最近的类,然后把重心移动到该类全部成员位置的平均值那里。
  46. # 3. 若达到最大迭代步数或两次迭代差小于设定的阈值则算法结束,否则重复步骤2。
  47. #
  48. #
  49. # +
  50. % matplotlib inline
  51. import matplotlib.pyplot as plt
  52. import numpy as np
  53. X0 = np.array([7, 5, 7, 3, 4, 1, 0, 2, 8, 6, 5, 3])
  54. X1 = np.array([5, 7, 7, 3, 6, 4, 0, 2, 7, 8, 5, 7])
  55. plt.figure()
  56. plt.axis([-1, 9, -1, 9])
  57. plt.grid(True)
  58. plt.plot(X0, X1, 'k.');
  59. # -
  60. # 假设K-Means初始化时,将第一个类的重心设置在第5个样本,第二个类的重心设置在第11个样本.那么我们可以把每个实例与两个重心的距离都计算出来,将其分配到最近的类里面。计算结果如下表所示:
  61. # ![data_0](images/data_0.png)
  62. #
  63. # 新的重心位置和初始聚类结果如下图所示。第一类用X表示,第二类用点表示。重心位置用稍大的点突出显示。
  64. #
  65. #
  66. #
  67. C1 = [1, 4, 5, 9, 11]
  68. C2 = list(set(range(12)) - set(C1))
  69. X0C1, X1C1 = X0[C1], X1[C1]
  70. X0C2, X1C2 = X0[C2], X1[C2]
  71. plt.figure()
  72. plt.title('1st iteration results')
  73. plt.axis([-1, 9, -1, 9])
  74. plt.grid(True)
  75. plt.plot(X0C1, X1C1, 'rx')
  76. plt.plot(X0C2, X1C2, 'g.')
  77. plt.plot(4,6,'rx',ms=12.0)
  78. plt.plot(5,5,'g.',ms=12.0);
  79. # 现在我们重新计算两个类的重心,把重心移动到新位置,并重新计算各个样本与新重心的距离,并根据距离远近为样本重新归类。结果如下表所示:
  80. #
  81. # ![data_1](images/data_1.png)
  82. #
  83. # 画图结果如下:
  84. C1 = [1, 2, 4, 8, 9, 11]
  85. C2 = list(set(range(12)) - set(C1))
  86. X0C1, X1C1 = X0[C1], X1[C1]
  87. X0C2, X1C2 = X0[C2], X1[C2]
  88. plt.figure()
  89. plt.title('2nd iteration results')
  90. plt.axis([-1, 9, -1, 9])
  91. plt.grid(True)
  92. plt.plot(X0C1, X1C1, 'rx')
  93. plt.plot(X0C2, X1C2, 'g.')
  94. plt.plot(3.8,6.4,'rx',ms=12.0)
  95. plt.plot(4.57,4.14,'g.',ms=12.0);
  96. # 我们再重复一次上面的做法,把重心移动到新位置,并重新计算各个样本与新重心的距离,并根据距离远近为样本重新归类。结果如下表所示:
  97. # ![data_2](images/data_2.png)
  98. #
  99. # 画图结果如下:
  100. #
  101. C1 = [0, 1, 2, 4, 8, 9, 10, 11]
  102. C2 = list(set(range(12)) - set(C1))
  103. X0C1, X1C1 = X0[C1], X1[C1]
  104. X0C2, X1C2 = X0[C2], X1[C2]
  105. plt.figure()
  106. plt.title('3rd iteration results')
  107. plt.axis([-1, 9, -1, 9])
  108. plt.grid(True)
  109. plt.plot(X0C1, X1C1, 'rx')
  110. plt.plot(X0C2, X1C2, 'g.')
  111. plt.plot(5.5,7.0,'rx',ms=12.0)
  112. plt.plot(2.2,2.8,'g.',ms=12.0);
  113. # 再重复上面的方法就会发现类的重心不变了,K-Means会在条件满足的时候停止重复聚类过程。通常,条件是前后两次迭代的成本函数值的差达到了限定值,或者是前后两次迭代的重心位置变化达到了限定值。如果这些停止条件足够小,K-Means就能找到最优解。不过这个最优解不一定是全局最优解。
  114. #
  115. #
  116. # ## Program
  117. # +
  118. # This line configures matplotlib to show figures embedded in the notebook,
  119. # instead of opening a new window for each figure. More about that later.
  120. # If you are using an old version of IPython, try using '%pylab inline' instead.
  121. # %matplotlib inline
  122. # import librarys
  123. from numpy import *
  124. import matplotlib.pyplot as plt
  125. import pandas as pd
  126. # Load dataset
  127. names = ['sepal-length', 'sepal-width', 'petal-length', 'petal-width', 'class']
  128. dataset = pd.read_csv("iris.csv", header=0, index_col=0)
  129. dataset.head()
  130. # -
  131. #对类别进行编码,3个类别分别赋值0,1,2
  132. dataset['class'][dataset['class']=='Iris-setosa']=0
  133. dataset['class'][dataset['class']=='Iris-versicolor']=1
  134. dataset['class'][dataset['class']=='Iris-virginica']=2
  135. def originalDatashow(dataSet):
  136. #绘制原始的样本点
  137. num,dim=shape(dataSet)
  138. marksamples=['ob'] #样本图形标记
  139. for i in range(num):
  140. plt.plot(datamat.iat[i,0],datamat.iat[i,1],marksamples[0],markersize=5)
  141. plt.title('original dataset')
  142. plt.xlabel('sepal length')
  143. plt.ylabel('sepal width')
  144. plt.show()
  145. # + {"scrolled": true}
  146. #获取样本数据
  147. datamat = dataset.loc[:, ['sepal-length', 'sepal-width']]
  148. # 真实的标签
  149. labels = dataset.loc[:, ['class']]
  150. #原始数据显示
  151. originalDatashow(datamat)
  152. # -
  153. def randChosenCent(dataSet,k):
  154. """初始化聚类中心:通过在区间范围随机产生的值作为新的中心点"""
  155. # 样本数
  156. m=shape(dataSet)[0]
  157. # 初始化列表
  158. centroidsIndex=[]
  159. #生成类似于样本索引的列表
  160. dataIndex=list(range(m))
  161. for i in range(k):
  162. #生成随机数
  163. randIndex=random.randint(0,len(dataIndex))
  164. #将随机产生的样本的索引放入centroidsIndex
  165. centroidsIndex.append(dataIndex[randIndex])
  166. #删除已经被抽中的样本
  167. del dataIndex[randIndex]
  168. #根据索引获取样本
  169. centroids = dataSet.iloc[centroidsIndex]
  170. return mat(centroids)
  171. # +
  172. def distEclud(vecA, vecB):
  173. """算距离, 两个向量间欧式距离"""
  174. return sqrt(sum(power(vecA - vecB, 2))) #la.norm(vecA-vecB)
  175. def kMeans(dataSet, k):
  176. # 样本总数
  177. m = shape(dataSet)[0]
  178. # 分配样本到最近的簇:存[簇序号,距离的平方] (m行 x 2 列)
  179. clusterAssment = mat(zeros((m, 2)))
  180. # step1: 通过随机产生的样本点初始化聚类中心
  181. centroids = randChosenCent(dataSet, k)
  182. print('最初的中心=', centroids)
  183. # 标志位,如果迭代前后样本分类发生变化值为Tree,否则为False
  184. clusterChanged = True
  185. # 查看迭代次数
  186. iterTime = 0
  187. # 所有样本分配结果不再改变,迭代终止
  188. while clusterChanged:
  189. clusterChanged = False
  190. # step2:分配到最近的聚类中心对应的簇中
  191. for i in range(m):
  192. # 初始定义距离为无穷大
  193. minDist = inf;
  194. # 初始化索引值
  195. minIndex = -1
  196. # 计算每个样本与k个中心点距离
  197. for j in range(k):
  198. # 计算第i个样本到第j个中心点的距离
  199. distJI = distEclud(centroids[j, :], dataSet.values[i, :])
  200. # 判断距离是否为最小
  201. if distJI < minDist:
  202. # 更新获取到最小距离
  203. minDist = distJI
  204. # 获取对应的簇序号
  205. minIndex = j
  206. # 样本上次分配结果跟本次不一样,标志位clusterChanged置True
  207. if clusterAssment[i, 0] != minIndex:
  208. clusterChanged = True
  209. clusterAssment[i, :] = minIndex, minDist ** 2 # 分配样本到最近的簇
  210. iterTime += 1
  211. sse = sum(clusterAssment[:, 1])
  212. print('the SSE of %d' % iterTime + 'th iteration is %f' % sse)
  213. # step3:更新聚类中心
  214. for cent in range(k): # 样本分配结束后,重新计算聚类中心
  215. # 获取该簇所有的样本点
  216. ptsInClust = dataSet.iloc[nonzero(clusterAssment[:, 0].A == cent)[0]]
  217. # 更新聚类中心:axis=0沿列方向求均值。
  218. centroids[cent, :] = mean(ptsInClust, axis=0)
  219. return centroids, clusterAssment
  220. # -
  221. # 进行k-means聚类
  222. k = 3 # 用户定义聚类数
  223. mycentroids, clusterAssment = kMeans(datamat, k)
  224. # +
  225. def datashow(dataSet, k, centroids, clusterAssment): # 二维空间显示聚类结果
  226. from matplotlib import pyplot as plt
  227. num, dim = shape(dataSet) # 样本数num ,维数dim
  228. if dim != 2:
  229. print('sorry,the dimension of your dataset is not 2!')
  230. return 1
  231. marksamples = ['or', 'ob', 'og', 'ok', '^r', '^b', '<g'] # 样本图形标记
  232. if k > len(marksamples):
  233. print('sorry,your k is too large,please add length of the marksample!')
  234. return 1
  235. # 绘所有样本
  236. for i in range(num):
  237. markindex = int(clusterAssment[i, 0]) # 矩阵形式转为int值, 簇序号
  238. # 特征维对应坐标轴x,y;样本图形标记及大小
  239. plt.plot(dataSet.iat[i, 0], dataSet.iat[i, 1], marksamples[markindex], markersize=6)
  240. # 绘中心点
  241. markcentroids = ['o', '*', '^'] # 聚类中心图形标记
  242. label = ['0', '1', '2']
  243. c = ['yellow', 'pink', 'red']
  244. for i in range(k):
  245. plt.plot(centroids[i, 0], centroids[i, 1], markcentroids[i], markersize=15, label=label[i], c=c[i])
  246. plt.legend(loc='upper left')
  247. plt.xlabel('sepal length')
  248. plt.ylabel('sepal width')
  249. plt.title('k-means cluster result') # 标题
  250. plt.show()
  251. # 画出实际图像
  252. def trgartshow(dataSet, k, labels):
  253. from matplotlib import pyplot as plt
  254. num, dim = shape(dataSet)
  255. label = ['0', '1', '2']
  256. marksamples = ['ob', 'or', 'og', 'ok', '^r', '^b', '<g']
  257. # 通过循环的方式,完成分组散点图的绘制
  258. for i in range(num):
  259. plt.plot(datamat.iat[i, 0], datamat.iat[i, 1], marksamples[int(labels.iat[i, 0])], markersize=6)
  260. for i in range(0, num, 50):
  261. plt.plot(datamat.iat[i, 0], datamat.iat[i, 1], marksamples[int(labels.iat[i, 0])], markersize=6,
  262. label=label[int(labels.iat[i, 0])])
  263. plt.legend(loc='upper left')
  264. # 添加轴标签和标题
  265. plt.xlabel('sepal length')
  266. plt.ylabel('sepal width')
  267. plt.title('iris true result') # 标题
  268. # 显示图形
  269. plt.show()
  270. # label=labels.iat[i,0]
  271. # -
  272. # 绘图显示
  273. datashow(datamat, k, mycentroids, clusterAssment)
  274. trgartshow(datamat, 3, labels)
  275. # ## How to use sklearn to do the classifiction
  276. #
  277. # +
  278. from sklearn.datasets import load_digits
  279. import matplotlib.pyplot as plt
  280. from sklearn.cluster import KMeans
  281. # load digital data
  282. digits, dig_label = load_digits(return_X_y=True)
  283. # draw one digital
  284. plt.gray()
  285. plt.matshow(digits[0].reshape([8, 8]))
  286. plt.show()
  287. # calculate train/test data number
  288. N = len(digits)
  289. N_train = int(N*0.8)
  290. N_test = N - N_train
  291. # split train/test data
  292. x_train = digits[:N_train, :]
  293. y_train = dig_label[:N_train]
  294. x_test = digits[N_train:, :]
  295. y_test = dig_label[N_train:]
  296. # +
  297. # do kmeans
  298. kmeans = KMeans(n_clusters=10, random_state=0).fit(x_train)
  299. # kmeans.labels_ - output label
  300. # kmeans.cluster_centers_ - cluster centers
  301. # draw cluster centers
  302. fig, axes = plt.subplots(nrows=1, ncols=10)
  303. for i in range(10):
  304. img = kmeans.cluster_centers_[i].reshape(8, 8)
  305. axes[i].imshow(img)
  306. # -
  307. # ## Exerciese - How to caluate the accuracy?
  308. #
  309. # 1. How to match cluster label to groundtruth label
  310. # 2. How to solve the uncertainty of some digital
  311. # ## 评估聚类性能
  312. #
  313. # 方法1: 如果被用来评估的数据本身带有正确的类别信息,则利用Adjusted Rand Index(ARI),ARI与分类问题中计算准确性的方法类似,兼顾了类簇无法和分类标记一一对应的问题。
  314. #
  315. #
  316. # +
  317. from sklearn.metrics import adjusted_rand_score
  318. ari_train = adjusted_rand_score(y_train, kmeans.labels_)
  319. print("ari_train = %f" % ari_train)
  320. # -
  321. # Given the contingency table:
  322. # ![ARI_ct](images/ARI_ct.png)
  323. #
  324. # the adjusted index is:
  325. # ![ARI_define](images/ARI_define.png)
  326. #
  327. # * [ARI reference](https://davetang.org/muse/2017/09/21/adjusted-rand-index/)
  328. #
  329. #
  330. # 方法2: 如果被用来评估的数据没有所属类别,则使用轮廓系数(Silhouette Coefficient)来度量聚类结果的质量,评估聚类的效果。轮廓系数同时兼顾了聚类的凝聚都和分离度,取值范围是[-1,1],轮廓系数越大,表示聚类效果越好。
  331. #
  332. # 轮廓系数的具体计算步骤:
  333. # 1. 对于已聚类数据中第i个样本$x_i$,计算$x_i$与其同一类簇内的所有其他样本距离的平均值,记作$a_i$,用于量化簇内的凝聚度
  334. # 2. 选取$x_i$外的一个簇$b$,计算$x_i$与簇$b$中所有样本的平均距离,遍历所有其他簇,找到最近的这个平均距离,记作$b_i$,用于量化簇之间分离度
  335. # 3. 对于样本$x_i$,轮廓系数为$sc_i = \frac{b_i−a_i}{max(b_i,a_i)}$
  336. # 4. 最后,对所以样本集合$\mathbf{X}$求出平均值,即为当前聚类结果的整体轮廓系数。
  337. # +
  338. import numpy as np
  339. from sklearn.cluster import KMeans
  340. from sklearn.metrics import silhouette_score
  341. import matplotlib.pyplot as plt
  342. plt.rcParams['figure.figsize']=(10,10)
  343. plt.subplot(3,2,1)
  344. x1=np.array([1,2,3,1,5,6,5,5,6,7,8,9,7,9]) #初始化原始数据
  345. x2=np.array([1,3,2,2,8,6,7,6,7,1,2,1,1,3])
  346. X=np.array(list(zip(x1,x2))).reshape(len(x1),2)
  347. plt.xlim([0,10])
  348. plt.ylim([0,10])
  349. plt.title('Instances')
  350. plt.scatter(x1,x2)
  351. colors=['b','g','r','c','m','y','k','b']
  352. markers=['o','s','D','v','^','p','*','+']
  353. clusters=[2,3,4,5,8]
  354. subplot_counter=1
  355. sc_scores=[]
  356. for t in clusters:
  357. subplot_counter +=1
  358. plt.subplot(3,2,subplot_counter)
  359. kmeans_model=KMeans(n_clusters=t).fit(X) #KMeans建模
  360. for i,l in enumerate(kmeans_model.labels_):
  361. plt.plot(x1[i],x2[i],color=colors[l],marker=markers[l],ls='None')
  362. plt.xlim([0,10])
  363. plt.ylim([0,10])
  364. sc_score=silhouette_score(X,kmeans_model.labels_,metric='euclidean') #计算轮廓系数
  365. sc_scores.append(sc_score)
  366. plt.title('k=%s,silhouette coefficient=%0.03f'%(t,sc_score))
  367. plt.figure()
  368. plt.plot(clusters,sc_scores,'*-') #绘制类簇数量与对应轮廓系数关系
  369. plt.xlabel('Number of Clusters')
  370. plt.ylabel('Silhouette Coefficient Score')
  371. plt.show()
  372. # -
  373. # ## How to determin the 'k'?
  374. #
  375. # 利用“肘部观察法”可以粗略地估计相对合理的聚类个数。K-means模型最终期望*所有数据点到其所属的类簇距离的平方和趋于稳定,所以可以通过观察这个值随着K的走势来找出最佳的类簇数量。理想条件下,这个折线在不断下降并且趋于平缓的过程中会有斜率的拐点,这表示从这个拐点对应的K值开始,类簇中心的增加不会过于破坏数据聚类的结构*。
  376. #
  377. #
  378. # +
  379. import numpy as np
  380. from sklearn.cluster import KMeans
  381. from scipy.spatial.distance import cdist
  382. import matplotlib.pyplot as plt
  383. cluster1=np.random.uniform(0.5,1.5,(2,10))
  384. cluster2=np.random.uniform(5.5,6.5,(2,10))
  385. cluster3=np.random.uniform(3,4,(2,10))
  386. X=np.hstack((cluster1,cluster2,cluster3)).T
  387. plt.scatter(X[:,0],X[:,1])
  388. plt.xlabel('x1')
  389. plt.ylabel('x2')
  390. plt.show()
  391. # +
  392. K=range(1,10)
  393. meandistortions=[]
  394. for k in K:
  395. kmeans=KMeans(n_clusters=k)
  396. kmeans.fit(X)
  397. meandistortions.append(sum(np.min(cdist(X,kmeans.cluster_centers_,'euclidean'),axis=1))/X.shape[0])
  398. plt.plot(K,meandistortions,'bx-')
  399. plt.xlabel('k')
  400. plt.ylabel('Average Dispersion')
  401. plt.title('Selecting k with the Elbow Method')
  402. plt.show()
  403. # -
  404. # 从上图可见,类簇数量从1降到2再降到3的过程,更改K值让整体聚类结构有很大改变,这意味着新的聚类数量让算法有更大的收敛空间,这样的K值不能反映真实的类簇数量。而当K=3以后再增大K,平均距离的下降速度显著变缓慢,这意味着进一步增加K值不再会有利于算法的收敛,同时也暗示着K=3是相对最佳的类簇数量。

机器学习越来越多应用到飞行器、机器人等领域,其目的是利用计算机实现类似人类的智能,从而实现装备的智能化与无人化。本课程旨在引导学生掌握机器学习的基本知识、典型方法与技术,通过具体的应用案例激发学生对该学科的兴趣,鼓励学生能够从人工智能的角度来分析、解决飞行器、机器人所面临的问题和挑战。本课程主要内容包括Python编程基础,机器学习模型,无监督学习、监督学习、深度学习基础知识与实现,并学习如何利用机器学习解决实际问题,从而全面提升自我的《综合能力》。