ML入门6.0 手写K-Means 聚类 (K-Means Clustering)

K-Means 简介

聚类算法通常被归类于无监督学习中,由于训练样本是未知的,目标是通过对无对无标记训练样本的学习来揭示数据的内在规律和性质,为进一步的数据分析提供基础。聚类试图将数据集中的样本划分为若干个不相交的子集,每个子集被称为一个,通过这样的划分,每个内的样本可能拥有相同或者类似的特征,从而对应于特定的类别。
k均值聚类是最著名的划分聚类算法,由于简洁和效率使得他成为所有聚类算法中最广泛使用的。给定一个数据点集合和需要的聚类数目k,k由用户指定,k均值算法根据某个距离函数反复把数据分入k个聚类中。 百度百科
K-Means应用场景十分广泛,如:物品传输优化,识别犯罪地点,客户分类,乘车数据分析,天文数据分析等
在这里插入图片描述

原理简介

聚类问题输入:数据集合
聚类问题输出:簇

聚类指标—距离

闵可夫斯基距离

d i s t ( x i , x j ) = ( ∑ u = 1 n ∣ x i u − x j u ∣ p ) dist(x_{i},x_{j}) = (\sum_{u=1}^{n}|x_{iu} - x_{ju}|^{p}) dist(xi,xj)=(u=1nxiuxjup)

当p=1时就是曼哈顿距离,p=2时就是欧式距离

Jaccard距离

d i s t ( A , B ) = ∣ A ⋂ B ∣ ∣ A ⋃ B ∣ dist(A,B) = \frac{|A \bigcap B|}{|A \bigcup B|} dist(A,B)=ABAB

K-Means算法步骤

  1. 选择初始化的 k 个样本作为初始聚类中心, a = ( a 1 , a 2 , . . . , a k ) a = (a_{1}, a_{2},...,a_{k} ) a=(a1,a2,...,ak)
  2. 针对数据集中每个样本 x i x_{i} xi计算它到 k 个聚类中心的距离并将其分到距离最小的聚类中心所对应的类中;(其中 x i = ( x i 1 , x i 2 , … , x i n ) x_{i} = (x_{i1}, x_{i2},…,x_{in} ) xi=(xi1,xi2,,xin))
  3. 针对每个类别 a j a_{j} aj , 重新计算它的聚类中心 a j = 1 m ∑ i = 1 m x i a_{j} = \frac{1}{m}\sum{i=1}^{m} x_{i} aj=m1i=1mxi
  4. 重复上面 2 3 两步操作,直到达到某个中止条件(迭代次数、最小误差变化等)

实现代码

Func1: loadDataSet(fileName) 加载数据集

def loadDataSet(fileName):  # general function to parse tab -delimited floats
    """
    加载数据
    :param fileName:
    :return: datamat
    """
    dataMat = []  # assume last column is target value
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t')
        fltLine = map(float, curLine)  # map all elements to float()
        dataMat.append(fltLine)
    return dataMat

Func2: distEclud(vecA, vecB) 计算距离

def distEclud(vecA, vecB):
    """
    计算欧式距离
    :param vecA: 向量A
    :param vecB: 向量B
    :return: 距离
    """
    return sqrt(sum(power(vecA - vecB, 2)))  # la.norm(vecA-vecB)

Func3: randCent(dataSet, k)给出随机的聚类中心

def randCent(dataSet, k):
    """
    随机聚类中心
    :param dataSet: 数据集
    :param k: 中心个数
    :return: 中心点
    """
    n = shape(dataSet)[1]
    centroids = mat(zeros((k, n)))  # create centroid mat
    for j in range(n):  # create random cluster centers, within bounds of each dimension
        minJ = min(dataSet[:, j])
        rangeJ = float(max(dataSet[:, j]) - minJ)
        centroids[:, j] = mat(minJ + rangeJ * random.rand(k, 1))
    return centroids

Func4: Kmeans(dataSet, k, distMeas=distEclud, createCent=randCent) kmeans算法实现

def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    """
    Kmeans算法实现
    :param dataSet:数据集
    :param k: 中心个数
    :param distMeas: 距离算法
    :param createCent: 得到中心的算法
    :return:
    """
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m, 2)))  # create mat to assign data points
    # to a centroid, also holds SE of each point
    centroids = createCent(dataSet, k)
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):  # for each data point assign it to the closest centroid
            minDist = inf;
            minIndex = -1
            for j in range(k):
                distJI = distMeas(centroids[j, :], dataSet[i, :])
                if distJI < minDist:
                    minDist = distJI;
                    minIndex = j
            if clusterAssment[i, 0] != minIndex: clusterChanged = True
            clusterAssment[i, :] = minIndex, minDist ** 2
        print(centroids)
        for cent in range(k):  # recalculate centroids
            ptsInClust = dataSet[nonzero(clusterAssment[:, 0].A == cent)[0]]  # get all the point in this cluster
            centroids[cent, :] = mean(ptsInClust, axis=0)  # assign centroid to mean
    return centroids, clusterAssment

Func5: clusterClubs(numClust=5) 对数据进行聚类并绘制相关图像

def clusterClubs(numClust=5):
    """
    测试K-means并绘图
    :param numClust:簇的个数
    :return: 图像
    """
    print("clusterClubs")
    datList = []
    for line in open('places.txt').readlines():
        lineArr = line.split('\t')
        datList.append([float(lineArr[4]), float(lineArr[3])])
    datMat = mat(datList)
    myCentroids, clustAssing = biKmeans(datMat, numClust, distMeas=distSLC)
    fig = plt.figure()
    rect = [0.1, 0.1, 0.8, 0.8]
    scatterMarkers = ['s', 'o', '^', '8', 'p', \
                      'd', 'v', 'h', '>', '<']
    axprops = dict(xticks=[], yticks=[])
    ax0 = fig.add_axes(rect, label='ax0', **axprops)
    imgP = plt.imread('Portland.png')
    ax0.imshow(imgP)
    ax1 = fig.add_axes(rect, label='ax1', frameon=False)
    for i in range(numClust):
        ptsInCurrCluster = datMat[nonzero(clustAssing[:, 0].A == i)[0], :]
        markerStyle = scatterMarkers[i % len(scatterMarkers)]
        ax1.scatter(ptsInCurrCluster[:, 0].flatten().A[0], ptsInCurrCluster[:, 1].flatten().A[0], marker=markerStyle,
                    s=90)
    ax1.scatter(myCentroids[:, 0].flatten().A[0], myCentroids[:, 1].flatten().A[0], marker='+', s=300)
    plt.show()

运行结果

在这里插入图片描述
在这里插入图片描述

优缺点

优点

容易理解,聚类效果不错,虽然是局部最优, 但往往局部最优就够了;
处理大数据集的时候,该算法可以保证较好的伸缩性;
当簇近似高斯分布的时候,效果非常不错;
算法复杂度低。

缺点

K 值需要人为设定,不同 K 值得到的结果不一样;
对初始的簇中心敏感,不同选取方式会得到不同结果;
对异常值敏感;
样本只能归为一类,不适合多分类任务;
不适合太离散的分类、样本类别不平衡的分类、非凸形状的分类。

完整代码见GitHub

ST-ML

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值