机器学习实战(二)-聚类


1、场景

回想项目中遇到的,多商家信息融合的场景。我们如何把多商家的信息进行合理的匹配呢?首先想到的是人工匹配和程序匹配相结合。这样也可以实现,但费心费力,且实际匹配效果很差,如果商家融合信息呈指数级增长,这样的方式就太low了。
那如何把这个流程做的智能化些呢?我们可以引入机器学习中的聚类,它可以将商家相关属性进行聚集,并分类。

2、相关术语

簇: 所有数据点点集合,簇中对象相似。
质心: 簇中所有点的中心(计算所有点的均值).

3、距离计算

采用欧式距离,两点间直线距离.也可采用其他距离计算(后期会做相应的介绍)
    

4、评价指标: 平方误差和(SSE)

     
SSE 值越小,表示越接近它们的质心.由于代价函数(SSE)是非凸函数,所以在运用Kmeans算法时,不能保证收敛到一个全局的最优解,我们得到的一般是一个局部的最优解。
为了取得比较好的效果,用不同的初始质心运行,得到多个局部最优解,比较它们的SSE,选取SSE最小的那个。

5、算法

#加载数据
def loadDataSet(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t')
        ftlLine = map(float,curLine)  #数据初始化float类型
        dataMat.append(ftlLine)
    return dataMat

#欧式距离
def distEclud(vecA, vecB):
    return sqrt(sum(power(vecA -vecB,2)))
    # power 样本点平方((x1-x2)^2+(y1-y2)^2)
    # sqrt 求和后开方

#构建k个随机质心
def randCent(dataSet, k):
    n = shape(dataSet)[1]         #获得列数
    centroids = mat(zeros((k,n))) #k行n列的零阶矩阵
    for j in range(n):
        minJ = min(dataSet[:,j])
        rangeJ = float(max(dataSet[:,j]) - minJ)
        centroids[:,j] = minJ + rangeJ * random.rand(k,1)
    return centroids


(1)K-均值
  • 创建 k 个点作为起始质心(通常是随机选择)
  • 当任意一个点的簇分配结果发生改变
  •     对数据集中的每个数据点
  •             对每个质心
  •              计算质心与数据点之间的距离
  •      将数据点分配到距其最近的簇
  • 对每一个簇, 计算簇中所有点的均值并将均值作为质心
#Kmeans
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    m = shape(dataSet)[0]  #样本数
    clusterAssment = mat(zeros((m,2))) #获得m*2矩阵(一列簇分类结果,一列误差)
    centroids = createCent(dataSet,k)  #初始化K个质心
    clusterChanged = True #簇更改标记
    while clusterChanged:
        clusterChanged = False
        #样本点加入到最近的簇
        for i in range(m):
            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
            #每轮结束后调整簇心
            for cent in range(k):
                if clusterAssment[i, 0] != minIndex: clusterChanged = True
                clusterAssment[i, :] = minIndex, minDist ** 2 #计算每列平均值
        return centroids, clusterAssment

(2)二分K-均值
  • 将所有点看成一个簇
  • 当簇数目小雨 k 时
  • 对于每一个簇
  •        计算总误差
  • 在给定的簇上面进行 KMeans 聚类(k=2)
  • 计算将该簇一分为二之后的总误差
  • 选择使得误差最小的那个簇进行划分操作

# k:簇个数   distMeas:距离生成器
def biKmeans(dataSet, k, distMeas=distEclud):
    m = shape(dataSet)[0]  #数据集矩阵的行数
    clusterAssment = mat(zeros((m,2)))
    centroid0 = mean(dataSet, axis=0).tolist()[0]   # 创建一个初始簇
    centList =[centroid0] #create a list with one centroid
    for j in range(m):  # 计算每个样本点到初始簇的距离
        clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2
    while (len(centList) < k):  # 迭代直到簇的数目等于k
        lowestSSE = inf
        for i in range(len(centList)):
            # 尝试划分每一簇
            ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]
            centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
            sseSplit = sum(splitClustAss[:,1])
            # 剩余数据集的误差
            sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])
            # 记录总误差最小的那个簇
            if (sseSplit + sseNotSplit) < lowestSSE:
                bestCentToSplit = i
                bestNewCents = centroidMat
                bestClustAss = splitClustAss.copy()
                lowestSSE = sseSplit + sseNotSplit
        # 因为二分均值,所以结果簇编号只能是0或1,修改结果簇编号为:原簇号和新增簇号
        bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList)
        bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit
        # 更新簇列表centlist和样本点分配簇结果矩阵clusterAssment
        centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]
        centList.append(bestNewCents[1,:].tolist()[0])
        clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss
    return mat(centList), clusterAssment;

if __name__=="__main__":
    dataMat = loadDataSet('testSet.txt')
    randCent(mat(dataMat), 3)
    #kMeans(mat(dataMat),3,distEclud,randCent)
    #biKmeans(mat(dataMat),3,distEclud)

6、优缺点

优点: 容易实现
缺点:可能收敛到局部最小值, 在大规模数据集上收敛较慢,高维空间有局限

7、如何选取K(借鉴)

(1)分析数据
(2)基于变化的算法:即定义一个函数,随着K的改变,认为在正确的K时会产生极值。
(3)基于结构的算法:即比较类内距离、类间距离以确定K。
(4) 基于一致性矩阵的算法:即认为在正确的K时,不同次聚类的结果会更加相似
(5)基于层次聚类:即基于合并或分裂的思想,在一定情况下停止从而获得K
(6)基于采样的算法:即对样本采样,分别做聚类;根据这些结果的相似性确定K。
(7)使用Canopy Method算法进行初始划分
(8)使用BIC算法进行初始划分


目前了解的还比较浅显,后期将不断地优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值