非监督学习: 无类别标记的
一、 K-means 算法:
1. Clustering 中的经典算法,数据挖掘十大经典算法之一
2. 参数k
已知参数 k ;然后将事先输入的n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。
3. 算法思路:以空间中k个点为中心进行聚类,对最靠近他们的对象归类。通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果。
4. 算法描述:
(1)任意适当选择c个类的初始中心;
(2)在第k次迭代中,对任意一个样本,求其到c各中心的距离,将该样本归到距离最短的中心所在的类;
(3)利用均值等方法更新该类的中心值;
(4)对于所有的c个聚类中心,如果利用(2)(3)的迭代法更新后,值保持不变,则迭代结束, 否则继续迭代。
5. 算法流程
输入:类的数量k、 数据data[n];
(1) 选择k个初始中心点,例如c[0]=data[0],…c[k-1]=data[k-1];
(2) 对于data[0]….data[n], 分别与c[0]…c[k-1]比较,假定与c[i]差值最少,就标记为i;
(3) 对于所有标记为i点,重新计算c[i]={ 所有标记为i的data[j]之和}/标记为i的个数;
(4) 重复(2)(3),直到所有c[i]值的变化小于给定阈值。
6. 优点:速度快,简单
缺点:最终结果跟初始点选择相关,容易陷入局部最优,需直到k值
二、举例
将上述四个药片归为两类:
蓝色为药片,五角星为随机选取的中心点,四个点到c1(1,1)的距离分别为0、1、3.61、5 ; 四个点到c2(2,1)的距离为1、0、2.83、4.24;
得到第一个点为一类,第234个为第二类,再重新找中心点。
新的中心点:c1>>(1,1); c2>>(11/3,8/3) 新的图示如下:
完成分类,迭代停止。(停止条件:分类不变,或分类变化小于一个值,或指定迭代次数)
三、python实现
import numpy as np def kmeans(x, k, maxIt): # maxIt是迭代次数 numPoints, numDim = x.shape # 传入的行数 dataSet = np.zeros((numPoints, numDim + 1)) # 多添加一列,作为标记 dataSet[:,:-1] = x # 除了最后一列其他的和x一样 centroids = dataSet[np.random.randint(numPoints, size= k), :] # 选出k个行数,作为中心点 centroids[:, -1] = range(1, k+1) iterations = 0 # 第多少次循环 oldCentroids = None # 旧的中心点 while not shouldstop(oldCentroids, centroids, iterations, maxIt): print("iteration: ", iterations) print("dataSet: \n", dataSet) print("centroids: \n", centroids) oldCentroids = np.copy(centroids) iterations += 1 updataLabels(dataSet, centroids) # 重新归类label centroids = getCentroids(dataSet, k) # 更新中心点 return dataSet def shouldstop(oldCentroids, centroids, iterations, maxIt): if iterations > maxIt: # 是否到预设迭代次数 return True return np.array_equal(oldCentroids, centroids) # 比较值中心点是否相等 def updataLabels(dataSet, centroids): numPoints, numDim = dataSet.shape for i in range(0, numPoints): # 计算 dataSet[i, -1] = getLabelFromClosestCentroid(dataSet[i,:-1], centroids) # 对比距离,返回最近中心点的标记 def getLabelFromClosestCentroid(dataSetRow, centroids): label = centroids[0, -1] minDist = np.linalg.norm(dataSetRow - centroids[0,:-1]) # 返回两个向量的距离 for i in range(1, centroids.shape[0]): dist = np.linalg.norm(dataSetRow - centroids[i,:-1]) if dist < minDist: minDist = dist label = centroids[i, -1] print("-"*10) print("minDistance: ", minDist) return label def getCentroids(dataSet, k): result = np.zeros((k, dataSet.shape[1])) # 初始化 shape[1] 为列数 for i in range(1, k+1): # 将所有标签相同的点找出来,求均值 oneCluster = dataSet[dataSet[:, -1] == i, :-1] # 等于某一列的所有标签找出来 result[i - 1, :-1] = np.mean(oneCluster, axis=0) # 求均值,赋到除了最后一列的所有,axis=0 每一行所有列 result[i - 1, -1] = i # 赋标签 return result x1 = np.array([1, 1]) x2 = np.array([2, 1]) x3 = np.array([4, 3]) x4 = np.array([5, 4]) testX = np.vstack((x1, x2, x3, x4)) # 将四个点堆成一个矩阵 result = kmeans(testX, 2, 10) print("final result:\n", result)