机器学习--聚类(公式结论)

本文记录周志华《机器学习》聚类内容:***(文中公式内容均来自周志华《机器学习》)***

聚类是无监督学习中应用最广的方法,是根据将样本划分为k个不相交的簇,其性能度量通常使用内部指标(直接考察聚类结果而不利用参考模型)。

内部指标:通常为DB指数(DBI)和Dunn指数(DI),下式中 a v g ( C ) avg(C) avg(C)表示簇C内样本间平均距离, d i a m ( C ) diam(C) diam(C)表示簇C内样本间的最远距离, d min ⁡ ( C i , C j ) {d_{\min }}({C_i},{C_j}) dmin(Ci,Cj)表示两簇最近样本间的距离,KaTeX parse error: Undefined control sequence: \cen at position 5: {d_{\̲c̲e̲n̲}}({C_i},{C_j})表示两簇中心点间的距离。DBI值越小越好,DI越大越好。
1. D B I = 1 k ∑ i = 1 k max ⁡ j ≠ i ( a v g ( C i ) + a v g ( C j ) d c e n ( C i , C j ) ) DBI = {1 \over k}\sum\limits_{i = 1}^k {\mathop {\max }\limits_{j \ne i} ({{avg({C_i}) + avg({C_j})} \over {{d_{cen}}({C_i},{C_j})}})} DBI=k1i=1kj=imax(dcen(Ci,Cj)avg(Ci)+avg(Cj))
2. D I = max ⁡ 1 ≤ i ≤ k { min ⁡ j ≠ i ( d min ⁡ ( C i , C j ) max ⁡ 1 ≤ l ≤ k d i a m ( C l ) ) } DI = \mathop {\max }\limits_{1 \le i \le k} \{ \mathop {\min }\limits_{j \ne i} ({{{d_{\min }}({C_i},{C_j})} \over {{{\max }_{1 \le l \le k}}diam({C_l})}})\} DI=1ikmax{j=imin(max1lkdiam(Cl)dmin(Ci,Cj))}

距离计算:主要分为连续属性(可数字化的特征值)和离散属性(只存在有限个取值),其中连续属性距离计算是通过欧式距离,离散属性距离计算通过VDM(令 m u , a {m_{u,a}} mu,a表示在属性u上取值为a的样本数, m u , a , i {m_{u,a,i}} mu,a,i表示在第i个样本簇中在属性u上取值为a的样本数,k为样本族数),其距离为两种属性距离之和
V D M p ( a , b ) = ∑ i = 1 k ∣ m u , a , i m u , a − m u , b , i m u , b ∣ 2 VD{M_p}(a,b) = {\sum\limits_{i = 1}^k {|{{{m_{u,a,i}}} \over {{m_{u,a}}}} - {{{m_{u,b,i}}} \over {{m_{u,b}}}}|} ^2} VDMp(a,b)=i=1kmu,amu,a,imu,bmu,b,i2

聚类算法
1.k-means算法:
优点:简单快速,适合常规数据集
缺点:K值(分类数)难确定,复杂度与样本呈线性关系,很难发现任意形状的簇
注意点:(1)由于初始化位置的不同,可导致最后分类结果的不同,故可进行多次程序,选取得分最高的一次。(2)样本集数据要进行标准化。
选取K值时,可对多个K值的评估分数进行画图展示,选择拐点出的K值。该评估标准可选择样本对其簇中心点的距离和
在这里插入图片描述
代码:
其中数据集文件用excal创建如下文件,并改后缀名为csv即可
在这里插入图片描述

class Kmean(object):
    def __init__(self, data_path):
        self.data_path = data_path
        self.clusters = []
        self.center = np.zeros([2, 2])

    def get_data(self):
        data = pd.read_csv(self.data_path, encoding="gbk")
        self.inputs = data.loc[:, ['密度', '含糖率']].values
        self.labels = data.loc[:, '好瓜'].values.reshape((self.inputs.shape[0], -1))

    def train(self, k=2, max_epioch=200):
        for _ in range(k):
            cluster = []
            self.clusters.append(cluster)
        num_data = self.inputs.shape[0]
        center = self.inputs[random.sample(range(num_data), k)]
        for _ in range(max_epioch):
            for idx in range(num_data):
                data = self.inputs[idx]
                length = np.sqrt(np.sum(np.square(data - center), axis=1))
                self.clusters[np.argmin(length)].append(data)
            for i in range(k):
                center[i] = np.mean(self.clusters[i], axis=0)
            self.center = center

    def draw(self):
        fig = plt.figure()
        ax = fig.add_subplot(1, 2, 1)
        ax.plot(self.inputs[:, 0], self.inputs[:, 1], 'bo')
        bx = fig.add_subplot(1, 2, 2)
        x = np.array(self.clusters[0])
        bx.plot(x[:, 0], x[:, 1], 'bo', color='red')
        bx.plot(self.center[0, 0], self.center[0, 1], 'bo')
        x = np.array(self.clusters[1])
        bx.plot(x[:, 0], x[:, 1], 'bo', color='yellow')
        bx.plot(self.center[1, 0], self.center[1, 1], 'bo')
        plt.show()

a = Kmean('data.csv')
a.get_data()
a.train()
#a.draw()

2.学习向量量化(LVQ):
特点:LVQ假设数据样本带有类别标记,学习过程利用样本的监督信息来辅助聚类。
在这里插入图片描述
算法第一行对原型向量初始化(例如对第q个簇可从类别标记为tq的样本中随机选取一个作为原型向量),第2~12行对原型向量进行迭代优化,随机选取一个训练样本,找出与其距离最近的原型向量,并根据两者的类别标记是否一致来对原型向量进行相应更新。
主要思想:让原型向量在对不同样本训练时,靠近类别一样的样本,远离类别相异的。

3.高斯混合聚类
特点:高斯混合聚类采用概率形式来表达聚类,适用于样本由多个符合高斯分布的组合数据集。
由于高斯分布仅由均值向量μ和协方差矩阵Σ决定,再加上混合系数α(占比权重),通过迭代更新这三个组参数,可实现模拟出K个高斯公式,从而判断样本在不同高斯分布情况下的概率。
图中(9.30)式为 γ j i = P M ( z j = i ∣ x j ) = α i ∗ P ( x j ∣ μ i , Σ i ) ∑ l = 1 k α l ∗ P ( x j ∣ μ l , Σ l ) {\gamma _{ji}} = {P_M}({z_j} = i|{x_j}) = {{{\alpha _i}*P({x_j}|{\mu _i},{\Sigma _i})} \over {\sum\limits_{l = 1}^k {{\alpha _l}*P({x_j}|{\mu _l},{\Sigma _l})} }} γji=PM(zj=ixj)=l=1kαlP(xjμl,Σl)αiP(xjμi,Σi) P ( x ∣ μ , Σ ) = 1 ( 2 π ) n 2 ∣ Σ ∣ 1 2 e − 1 2 ( x − μ ) T Σ − 1 ( x − μ ) P(x|\mu ,\Sigma ) = {1 \over {{{(2\pi )}^{{n \over 2}}}|\Sigma {|^{{1 \over 2}}}}}{e^{ - {1 \over 2}{{(x - \mu )}^T}{\Sigma ^{ - 1}}(x - \mu )}} P(xμ,Σ)=(2π)2nΣ211e21(xμ)TΣ1(xμ)该式子给出了样本xj由第i个高斯混合成分生成的后验概率
(9.31)式为 λ j = arg ⁡ max ⁡ i ∈ { 1 , 2 , . . . , k } γ j i {\lambda _j} = \mathop {\arg \max }\limits_{i \in \{ 1,2,...,k\} } {\gamma _{ji}} λj=i{1,2,...,k}argmaxγji
在这里插入图片描述

4.密度聚类(DBSCAN)
特点:此类算法从样本密度的角度来考察样本之间的可连接性,并基于可连接样本不断扩展聚类簇以获得最终聚类结果。
优点:不需要指定簇个数K,可以发现任意形状的簇,擅长找到离群点(检测任务),参数少(半径e和范围内样本点MinPts)
缺点:高维数据有些困难(可做降维),参数难以选择(参数对结果影响极大)
基本概念:
在这里插入图片描述
在这里插入图片描述
算法思想:首先找出所有核心对象点,然后由任一核心对象,根据密度MinPts和半径e找出密度可达的样本点作为同一簇,循环此过程,直到核心对象均被访问,其余未被访问的样本点为噪声点。
代码:(该代码是这位大神原创DBSCAN聚类算法(附代码)

# -*- coding: utf-8 -*-
import math
import random
import matplotlib.pyplot as plt


class DBSCAN(object):

    STATUS_UNVISITED = 'unvisited'
    STATUS_VISITED = 'visited'

    STATUS_GROUP = 1
    STATUS_NOGROUP = 0

    data = dict()

    def __init__(self, e, minPts):
        """
        e 最小距离
        minPts 最少样本数量
        """
        self.e = e
        self.minPts = minPts

    def nearby(self, id):
        nearby_points = list()
        for link_id in self.scores[id]:
            if self.scores[id][link_id] <= self.e:
                nearby_points.append(link_id)

        return nearby_points

    def visit_nearby_points(self, points, group):
        for id in points:
            if self.data[id]['is_visited'] == self.STATUS_VISITED \
                    and self.data[id]['is_group'] == self.STATUS_GROUP:
                continue
            self.data[id]['is_visited'] = self.STATUS_VISITED

            if self.data[id]['is_group'] == self.STATUS_NOGROUP:
                group.append(id)
                self.data[id]['is_group'] = self.STATUS_GROUP

            nearby_points = self.nearby(id)
            if len(nearby_points) >= self.minPts:
                self.visit_nearby_points(nearby_points, group)

    def fit(self, data_set, scores):
        self.scores = scores
        groups = list()

        for index, item in enumerate(data_set):
           self.data[index] = {'id': index,
                                'is_visited': self.STATUS_UNVISITED,
                                'is_group': self.STATUS_NOGROUP
                                }

        for id in self.data:
            if self.data[id]['is_visited'] == self.STATUS_VISITED:
                continue

            self.data[id]['is_visited'] = self.STATUS_VISITED
            nearby_points = self.nearby(id)

            if len(nearby_points) >= self.minPts:
                group = list()
                group.append(id)
                self.data[id]['is_group'] = self.STATUS_GROUP
                self.visit_nearby_points(nearby_points, group)
                groups.append(group)

        for id in self.data:
            if self.data[id]['is_group'] == self.STATUS_NOGROUP:
                groups.append([id])

        return groups


def init_data(num, min, max):
    data = []
    for i in range(num):
        data.append([random.randint(min, max), random.randint(min, max)])

    return data


def mat_score(data_set):
    score = dict()
    for i in range(len(data_set)):
        score[i] = dict()

    for i in range(len(data_set) - 1):
        j = i + 1
        while j < len(data_set):
            score[i][j] = math.sqrt(abs(data_set[i][0] - data_set[j][0]) ** 2 + abs(data_set[i][1] - data_set[j][1]) ** 2)
            score[j][i] = score[i][j]
            j += 1

    return score


def show_cluster(data_set, groups):
    plt.title(u'DBSCAN')
    mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']
    for index, group in enumerate(groups):
        for i in group:
            plt.plot(data_set[i][0], data_set[i][1], mark[index])

    plt.xlim(0.0, 100)
    plt.ylim(0.0, 100)
    plt.show()


if __name__ == '__main__':
    data_set1 = init_data(20, 0, 30)
    data_set2 = init_data(20, 40, 60)
    data_set3 = init_data(20, 70, 100)
    data_set = data_set1 + data_set2 + data_set3

    score_mat = mat_score(data_set)

    groups = DBSCAN(20, 3).fit(data_set, score_mat)
    show_cluster(data_set, groups)
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值