聚类算法(1)

一聚类算法简介

1.聚类和分类的区别

聚类 - 利用算法将相似或者相近的样本聚成一簇,这些样本都是无标签的,是一种无监督学习算法。

分类 - 首先需要从有标签样本学习出打标签逻辑,再利用学习出的逻辑对无标签样本进行分类,是一种有监督学习算法。

2.聚类的使用

聚类算法可以帮助我们认识数据,比如一批新闻文本,通过几次聚类的尝试,你可能就会知道这一批新闻文本主要分类几个类别。

聚类算法可以用于离群点检测,离群点检测可以应用在信用卡欺诈检测上 - “数据挖掘概念与技术”。

3.聚类的种类

划分法,将Ñ组数据划分到ķ个簇内。常用的算法包括K-均值,K-均值++,K-中心法等等。

层次发,自下而上或者自上而下的将Ñ组数据一步一步的划分,每一次划分的结果都将保存。

基于密度,将领域内包含超过阈值(大于设定密度)的数据点组合成可增长的簇,常用算法DBSCAN。

下面分别介绍这几种算法,并用Python的实现。


二划分法

1.K均值

1).算法简介

算法的核心思想是将Ñ组样本划分成ķ簇(K是需要人输入的,该算法并不能自动判定要聚成几类),簇和簇之间的距离和最大。这样的一个问题直接求解的难度太大,实践中经常使用迭代(EM)的思路去求解。

如何迭代求解呢?

第一步:随机选择ķ个样本作为ķ簇的中心点

第二步:计算每个样本点到ķ中心点个的距离,将样本点归到离它最近的簇中

第三步:计算每个新簇的中心点

第四步:重复第二和第三步,直到簇的中心点不再变化,中心点的变动很少或者达到你定的最大迭代次数三个条件满足任何一个迭代就会停止。

通过迭代计算出的结果,通常是一个局部最优解,该结果依赖于初始中心点的选取。

2).程序实现

第一步导入包

from numpy import *   #用于矩阵运算
import time    #计时
import csv    #读取CSV
from random import random as random1  #随机初始点

第二步随机初始化点

def randomcentroids(data, k):
    numSamples, dim = data.shape      #获取已有数据的行列
    centroids = mat(zeros((k, dim)))      #新建0矩阵存中心点
    for i in range(k):    
        index = int(random.uniform(0, numSamples))    #随机取数
        centroids[i, :] = data[index, :]    #拿出相应的行数据作为中心点
    return centroids

初始中心点的会直接影响最后聚类的结果(毕竟迭代得到是局部最优解),所以一个好的初始方法是非常重要的,我在网上看到了一个初始化的方法,转发供大家参考(出处忘了)

def randCent(dataSet, k):
    n = shape(dataSet)[1]    #获取每个样本有多少特征
    centroids = mat(zeros((k,n)))    #新建0矩阵存储特征
    for j in range(n):    #对每个特征进行处理
        rangeJ = float(max(dataSet[:,j]) - min(dataSet[:,j]))    #计算每个特征的极差
        centroids[:,j] = mat(min(dataSet[:,j]) + rangeJ * random.rand(k,1))   #一次性生成k个数字 
    return centroids

第三步确定向量和向量的距离

在上述描述中可以发现,在迭代过程中需要不停的计算样本点到中心点的距离 - 向量的距离欧氏距离,马氏距离和夹角余弦等等都可以用。

听过一个在线教育的课程,那个老师说不建议使用夹角余弦,理由是不收敛。我在文本挖掘上用过聚类算法,将每个短文表示成TF-IDF的形式,这时候每个文本的向量可能达到几千上万维度,这时候我发现欧氏距离就“失真”了。使用欧式距离会使得大部分文本被聚到一类(至少在我使用的短文本上有这个缺点) ,使用夹角余弦聚类结果会好很多,所以我下面提供良种距离计算方法。

# 计算欧式距离   
def distance(vector1, vector2):
    dis = sqrt(sum(power(vector2 - vector1, 2)))    #欧式距离计算公式
    return dis


#计算余弦夹角
def cosVector(x,y):
    result1=0.0
    result2=0.0
    result3=0.0
    for i in range(len(x)):    #这样的计算是比较慢的,可以优化
        result1+=x[i]*y[i]   #sum(X*Y)
        result2+=x[i]**2     #sum(X*X)
        result3+=y[i]**2     #sum(Y*Y)
    cosv = result1/((result2*result3)**0.5)
    return -cosv

第四步开始循环

代码是当年照着网上的抄的,可以我忘记抄的是哪几个了,这里就没办法标注来源了。

#kmeancluster
def kmeanscluster(data, k, maxnum = 50):
    num = 1
    numSamples, dim = data.shape    # 计算数据的大小
    initcentroids =  randcent2(data, k)  # 初始化中心点,这边可以替换成其他初始化方法
    initcentroids = array(initcentroids)
    classes = zeros((numSamples, 2))  # 生成n*2的0矩阵,用于存储每一个样本离中心点的距离和所属类别
    state = True
    while state:
        state = False
        noaccuracy = 0
        for i in range(numSamples):
            mindis = 100000000
            minindex = 0
            for j in range(k):
                dis = cosVector(initcentroids[j], data[i])  #计算余弦夹角
                if dis < mindis:
                    mindis = dis
                    minindex = j
            # 如果数据不一样跟新数据,并且把sata状态设置为Ture(保证循环继续)
            if int(classes[i, 0]) != minindex:
                noaccuracy += 1
                state = True
                classes[i, :] = minindex, mindis
        # 跟新类别中心
        for j in range(k):
            classesj = data[nonzero(classes[:, 0] == j)[0]]
            if len(classesj) != 0:
                # 找出类别为j的所有元素
                initcentroids[j, :] = mean(classesj, axis = 0)
        # 当循环次数超过最大循环次数时停止
        if num >=  maxnum:
            state = False
        # 当各个样本点的类别基本不变时候停止,0.005可以调
        if (noaccuracy/numSamples) < 0.005:
            state = False
        print('迭代次数为%s' % num )
        print('变化率%s' % (noaccuracy/numSamples))
        num += 1
    return initcentroids, classes

2.K-意味着++

对初始化中心点的方法进行优化,就是K-means ++了。核心思想就是:距离当前已选取的聚类中心越远的点会有更高的概率被选为第n + 1个聚类中心。如何在python的实现呢?

#设置初始中心Kmeans++
def randcent2(data, k):
    numSamples, dim = data.shape
    centroids = mat(zeros((k, dim)))
    index = int(random.uniform(0, numSamples))
    centroids[0, :] = data[index, :]    #随机获取一个样本作为第一个中心点
    distance1 = zeros((numSamples, 2))  # 用于存储类别和距离
    for ii in range(1,k):    #利用距离越远被选中概率越大的思路,开始生产其他n-1个中心点
        sum = 0
        for i in range(numSamples):    # 将每个点和已选择的中心点最近距离计算出来,求和
            mindis = 100000000
            for j in range(ii):
                dis = distance(centroids[j], data[i])  #计算距离,所以你要先确认距离的定义
                if dis < mindis:
                    distance1[i, :] = j, dis
            sum += distance1[i,1]
        sum *= random1()    # 从sum上随机选一个点
        for ind, num1 in enumerate(distance1[:,1]): #距离越大,被选中的概率就越大
            sum -= num1
            if sum > 0:
                continue
            centroids[ii, :] = data[ind, :]
            break
    return centroids

距离越大,被选中概率越大是怎么实现的呢?可以理解成把一段一段距离拼凑在一起,然后随机选出一个点,观察点属于哪一段距离。如图所示,我在这样的线段上随机取一点是不是绿色被选中的概率最大.K均值++初始化看似复杂,但是好的初始点的选择能够大大降低后续循环迭代的工作。

3.K-mediods(K中心点)

对循环迭代过程中中心点的选择进行小小的改动后,就是K-中心算法了。

1).算法简介

当样本存在噪声和离群点时,K-中心算法更鲁棒。

举一个特殊的例子,下图画出了K均值的聚类结果,是不是和我们想象中的聚类结果又出入。就是因为离群点使得群的中心点左移了。改进方法就是只有确实存在的样本点才可能被选成中心点。

第一步:随机选择ķ个样本作为ķ簇的中心点

第二步:计算每个样本点到ķ中心点个的距离,将样本点归到离它最近的簇中

第三步:计算每个新簇的中心点,簇距的内各样本点距离的绝度误差最小的点,作为新的中心点

第四步:重复第二和第三步,直到簇的中心点不再变化,中心点的变动很少或者达到你定的最大迭代次数三个条件满足任何一个迭代就会停止。

通过迭代计算出的结果,通常是一个局部最优解,该结果依赖于初始中心点的选取。

2).程序实现

写重新找簇内各样本点距离的绝度误差最小的点的方法,替换红框就好。


三总结

1.现实中聚类样本的维度都很高,我们很难先验的知道哪一种划分聚类的方法更好,只有多次尝试才能找到最好的方法。

2. scikit-learn中有各种聚类方法,建议直接调用。大家可以发现我们自己写的聚类算法用到了太多的for,程序注定效率不高。

3.层次法和基于密度的聚类方法将在聚类算法(2)中介绍。

猜你喜欢

转载自blog.csdn.net/m0_38126215/article/details/82842375