机器学习-聚类

288 阅读4分钟

一、概念

聚类是将数据集中的样本划分为若干个不相交的子集,每个子集称为一个簇。同一簇中的样本尽可能相似,不同簇中的样本尽可能不同。

二、目标与用途

1. 目标

  • 使得簇内的样本之间具有较高的相似度,而簇间的样本具有较低的相似度。
  • 通常使用某种距离度量来衡量样本之间的相似性,常见的距离度量有欧氏距离、曼哈顿距离等。

2. 用途

  • 数据探索:帮助理解数据的分布和结构。
  • 图像分割、文本分类等领域:可以将相似的图像区域或文本归为一类。
  • 作为其他机器学习任务的预处理步骤:例如在分类任务中,可以先对数据进行聚类,然后对每个簇分别进行分类,提高分类的准确性。

三、常见聚类算法

1. K 均值(K-Means)算法

  • 算法步骤:
  • 随机选择 K 个样本作为初始聚类中心。
  • 对于每个样本,计算它到每个聚类中心的距离,将其分配到距离最近的聚类中心所在的簇。
  • 对于每个簇,重新计算其聚类中心,即该簇中所有样本的均值。
  • 重复步骤 2 和 3,直到聚类中心不再变化或达到最大迭代次数。
  • 特点:简单、快速,适用于处理大规模数据,但需要预先指定簇的个数 K,且对初始聚类中心敏感。

image.png 2. 层次聚类算法

  • 算法分为凝聚层次聚类和分裂层次聚类两种。
  • 凝聚层次聚类:从每个样本作为一个单独的簇开始,逐步合并最相似的簇,直到满足某种停止条件。
  • 分裂层次聚类:从所有样本作为一个簇开始,逐步分裂成更小的簇,直到满足停止条件。
  • 特点:可以得到不同层次的聚类结果,便于观察数据的层次结构,但计算复杂度较高。

image.png

相似度计算

  • 层次聚类使用欧式距离来计算不同类别数据点间的距离(相似度)。

image.png

四、计算距离

image.png

五、评估指标

1. 内部指标

  • 常用的内部指标有误差平方和(SSE)等。SSE 是指每个样本到其所属簇中心的距离之和,SSE 越小,说明聚类效果越好。

image.png

2. 外部指标

  • 当有外部的类别标注信息时,可以使用外部指标来评估聚类结果。例如,兰德指数(Rand Index)衡量了聚类结果与真实类别标注之间的一致性。

image.png

六、python实现

import queue
import math
import copy
import numpy as np
import matplotlib.pyplot as plt


class clusterNode:
    def __init__(self, value, id=[], left=None, right=None, distance=-1, count=-1, check=0):
        '''
        value: 该节点的数值,合并节点时等于原来节点值的平均值
        id:节点的id,包含该节点下的所有单个元素
        left和right:合并得到该节点的两个子节点
        distance:两个子节点的距离
        count:该节点所包含的单个元素个数
        check:标识符,用于遍历时记录该节点是否被遍历过
        '''
        self.value = value
        self.id = id
        self.left = left
        self.right = right
        self.distance = distance
        self.count = count
        self.check = check

    def show(self):
        # 显示节点相关属性
        print(self.value, ' ', self.left.id if self.left != None else None, ' ', \
              self.right.id if self.right != None else None, ' ', self.distance, ' ', self.count)


class hcluster:
    def distance(self, x, y):
        # 计算两个节点的距离,可以换成别的距离
        return math.sqrt(pow((x.value - y.value), 2))

    def minDist(self, dataset):
        # 计算所有节点中距离最小的节点对
        mindist = 1000
        for i in range(len(dataset) - 1):
            if dataset[i].check == 1:
                # 略过合并过的节点
                continue
            for j in range(i + 1, len(dataset)):
                if dataset[j].check == 1:
                    continue
                dist = self.distance(dataset[i], dataset[j])
                if dist < mindist:
                    mindist = dist
                    x, y = i, j
        return mindist, x, y

    # 返回最小距离、距离最小的两个节点的索引

    def fit(self, data):
        dataset = [clusterNode(value=item, id=[(chr(ord('a') + i))], count=1) for i, item in enumerate(data)]
        # 将输入的数据元素转化成节点,并存入节点的列表
        length = len(dataset)
        Backup = copy.deepcopy(dataset)
        # 备份数据
        while (True):
            mindist, x, y = self.minDist(dataset)
            dataset[x].check = 1
            dataset[y].check = 1
            tmpid = copy.deepcopy(dataset[x].id)
            tmpid.extend(dataset[y].id)
            dataset.append(clusterNode(value=(dataset[x].value + dataset[y].value) / 2, id=tmpid, \
                                       left=dataset[x], right=dataset[y], distance=mindist,
                                       count=dataset[x].count + dataset[y].count))
            # 生成新节点
            if len(tmpid) == length:
                # 当新生成的节点已经包含所有元素时,退出循环,完成聚类
                break
        for item in dataset:
            item.show()
        return dataset

    def show(self, dataset, num):
        plt.figure(1)
        showqueue = queue.Queue()
        # 存放节点信息的队列
        showqueue.put(dataset[len(dataset) - 1])
        # 存入根节点
        showqueue.put(num)
        # 存入根节点的中心横坐标
        while not showqueue.empty():
            index = showqueue.get()
            # 当前绘制的节点
            i = showqueue.get()
            # 当前绘制节点中心的横坐标
            left = i - (index.count) / 2
            right = i + (index.count) / 2
            if index.left != None:
                x = [left, right]
                y = [index.distance, index.distance]
                plt.plot(x, y)
                x = [left, left]
                y = [index.distance, index.left.distance]
                plt.plot(x, y)
                showqueue.put(index.left)
                showqueue.put(left)
            if index.right != None:
                x = [right, right]
                y = [index.distance, index.right.distance]
                plt.plot(x, y)
                showqueue.put(index.right)
                showqueue.put(right)
        plt.show()


def setData(num):
    # 生成num个随机数据
    Data = list(np.random.randint(1, 100, size=num))
    return Data


if __name__ == '__main__':
    num = 20
    dataset = setData(num)
    h = hcluster()
    resultset = h.fit(dataset)
    h.show(resultset, num)
  • 运行结果

image.png

总之,聚类是机器学习中一种重要的无监督学习方法,在数据挖掘、模式识别等领域有着广泛的应用。