人工智能笔记08 聚类算法

本文深入探讨了聚类算法的基本原理和应用,包括划分法中的K-means算法及其优缺点,以及密度基聚类算法DBSCAN的工作机制。K-means通过迭代寻找样本的簇中心,而DBSCAN利用样本密度来发现聚类结构。两种算法各有特点,适用于不同的数据集和场景。

简介

将数据集中的样本划分为若干个通常不相交的子集(“簇”,cluster).

形式描述

假定样本集合D={x1,……,xm}D=\{x_1,……,x_m\}D={x1,xm}包含m个无标记样本,每个样本xi={xi1……xin}x_i=\{x_{i1}……x_{in}\}xi={xi1xin}十一i个n维得特征向量,聚类算法将样本集D划分为k个不相交的簇

{Cl∣l=1,2……k}\{C_l|l=1,2……k\}{Cll=1,2k}
其中Cl′∩l′≠lCl=∅C_{l'}\cap_{l'\neq l}C_l=\emptyCll=lCl=并且D=∪l=1kClD=\cup^k_{l=1}C_lD=l=1kCl

相应地,用λj∈{1,2,……k}\lambda_j\in\{1,2,……k\}λj{1,2,k}表示样本xjx_jxj的簇标记(cluster label)
xj∈Cλx_j\in C_\lambdaxjCλ,于是聚类结果可以用包含mmm个元素的簇标记向量λ={λ1,……λm}\lambda=\{\lambda_1,……\lambda_m\}λ={λ1,λm}

• 单独过程:
Ø 用于找寻数据内在的分布特征,exploratory data analysis
• 前驱过程:
Ø 作为分类等其他学习任务的数据预处理,feature pre-processing
• 应对大数据的有效方式:
Ø 无监督方

聚类类别

按照聚类算法的主要思路
Ø 划分法(Partitioning Methods):基于一定标准构建数据的划分。
• 属于该类的聚类方法有:k-means、k-modes、k-prototypes、kmedoids、PAM、CLARA、CLARANS等。

Ø 层次法(Hierarchical Methods):对给定数据对象集合进行层次分解

Ø 密度法(density-based Methods):基于数据对象的相连密度评价

Ø 网格法(Grid-based Methods):将数据空间划分成为有限个单元(
Cell)的网格结构,基于网格结构进行聚类。

Ø 模型法(Model-Based Methods):给每一个簇假定一个模型,然后
去寻找能够很好的满足这个模型的数据集。

Ø 基于距离的聚类算法
• 用各式各样的距离来衡量数据对象之间的相似度,如k-means、k-medoids、BIRCH、CURE等算法。

Ø 基于密度的聚类算法
• 相对于基于距离的聚类算法,基于密度的聚类方法主要是依据合适的密度函数等。如DBSCAN

Ø 基于互连性(Linkage-Based)的聚类算法
• 通常基于图或超图模型。高度连通的数据聚为一类,如复杂网络中的社团发现。

划分聚类算法

给定数据集D={x1,……,xm}D=\{x_1,……,x_m\}D={x1,,xm},k均值算法针对聚类所得的簇划分C={C1,……Ck}C=\{C_1,……C_k\}C={C1,Ck}最小化平方误差

E=∑i=1k∑x∈Cj∣∣x−μi∣∣22E=\sum^k_{i=1}\sum_{x\in C_j}||x-\mu_i||^2_2E=i=1kxCjxμi22
其中μi\mu_iμi是簇CiC_iCi的均值向量
E值在一定程度上刻画了簇内样本围绕均值向量的紧密程度,E值越小,簇内样本相似度越高

1.从D中随机选择k个样本作为初始的均值向量{mu1....muk}
2.		令Ci = ∅( 0 < i < k+1)
3.	 	for j in range(1,m+1):
4. 		计算xj到均值向量mui的距离:dji = ||xj-mui||
5. 		跟距离最近均值确定xj的簇值:lambdaj = arg min{dji}
6. 		样本 xj 划入相应的簇 C_{lambda_j} += xj(把xj放进去)
7. 	end for
8. 	
9. 	for i in range(1, k+1)
10. 		计算新均值向量 mui' = sum{x} / |Ci|				(x in Ci)
11.		if ui' != ui
12.			ui = ui'
13.		else
14.			保持
15.		end if
16.	end for
17.until  当前均值向量未更新
18.return 簇划分结果

西瓜例子:
Ø 接下来以的西瓜数据集4.0为例,来演示k均值算法的学习过程。将编号
iii 的样本称为 xix_ixi.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最优性:
Ø NP难问题
Ø 贪心,依赖初始值
耗时:
Ø 较快停止
Ø 某些情况下复杂度高
K值选择:
Ø Loss-k曲线
标准k-means(1957)不足:
Ø 依赖初始值、计算复杂度过高
k-means++(SODA 2007):
Ø 初始化环节,添加seed节点迭代随机筛选
Ø 效果:
• 极大降低时间复杂度
• 提高准确度

密度聚类

• 适用场景:
Ø 聚类结构能通过样本分布的紧密程度来确定。
• 主要过程:
Ø 从样本密度的角度来考察样本之间的可连接性,
Ø 基于可连接样本不断扩展聚类簇来获得最终的聚类结果
代表算法:DBSCAN

基本概念

ϵ\epsilonϵ邻域,对于样本xj∈Dx_j\in DxjD他的ϵ\epsilonϵ邻域包含样本集D中与xjx_jxj的距离不大于ϵ\epsilonϵ的样本

核心对象:若样本xjx_jxjϵ\epsilonϵ邻域至少包含MinPts个样本,该样本点为一个核心对象

密度直达:若样本xjx_jxj位于样本xjx_jxjϵ\epsilonϵ邻域中,xjx_jxj是一个核心对象,称样本xjx_jxjxix_ixi密度直达

密度可达:对样本xix_ixixjx_jxj,若存在样本序列p1,……pnp_1,……p_np1,pn其中p1=xi,pn=xjp_1=x_i,p_n=x_jp1=xipn=xj 并且pi+1p_{i+1}pi+1pip_ipi密度直达,则这两个密度可达

密度相连:对样本xix_ixixjx_jxj,若存在样本xkx_kxk与他们密度可达,则xixj密度相连
在这里插入图片描述
由密度可达关系导出最大密度相连样本集合

给定领域参数,簇是满足以下性质的非空样本子集
连接性:
xi∈C,xj∈C⇒xi和xj密度相连x_i\in C,x_j\in C\Rightarrow x_i和x_j密度相连xiC,xjCxixj
最大性
xi∈C,xi和xj密度可达⇒xj∈Cx_i\in C,x_i和x_j密度可达\Rightarrow x_j\in CxiC,xixjxjC
实际上若x为核心对象,由x密度可达的所有样本组成的集合记为X={x′∈D∣x′由x密度可达}X=\{x'\in D|x'由x密度可达\}X={xDxx}则X是满足连接性和最大性的簇
在这里插入图片描述

在这里插入图片描述
• 经典工作:
Ø KDD 1996
Ø O(n logn)复杂度
• 理论复杂度争议:
Ø SIGMOD 2015 Best Paper
Ø 实际:O(n^2) 时间复杂度
Ø Grid网格划分新方法
• 实践建议平反:
Ø TODS 2017
Ø 理论复杂度现实较为罕见
Ø 结合高效索引一起使用,
在大规模数据上,经典方
法更有优势

层次聚类

• 主要思路:
Ø 在不同层次对数据集进行划分,从而形成树形的聚类结构。
Ø 数据集划分既可采用“自底向上”的聚合策略,也可采用“自顶向下”
的分拆策略

代表算法,AGNES算法
在这里插入图片描述
两个聚类簇的距离度量方式
最小距离
dmin(Ci,Cj)=minx∈Ci,z∈Cjdist(x,z)d_{min}(C_i,C_j)=\underset{x\in C_i,z\in C_j}{min}dist(x,z)dmin(Ci,Cj)=xCi,zCjmindist(x,z)

最大距离
dmax(Ci,Cj)=maxx∈Ci,z∈Cjdist(x,z)d_{max}(C_i,C_j)=\underset{x\in C_i,z\in C_j}{max}dist(x,z)dmax(Ci,Cj)=xCi,zCjmaxdist(x,z)

平均距离
dave(Ci,Cj)=1∣Ci∣∣Cj∣∑x∈Ci∑z∈Cjdist(x,z)d_{ave}(C_i,C_j)=\frac{1}{|C_i||C_j|}\sum_{x\in C_i}\sum_{z\in C_j}dist(x,z)dave(Ci,Cj)=CiCj1xCizCjdist(x,z)

Ward距离
Δ(A,B)=∑i∈A∪B∣∣xi→−mA→∣∣2−∑i∈B∣∣xi→−mB→∣∣2=nAnBnB+nA∣∣mA→−mB→∣∣2\Delta(A,B) = \sum_{i\in A\cup B}||\mathop{x_i}\limits^{\rightarrow}-\mathop{m}\limits^{\rightarrow}_{A}||^2 - \sum_{i\in B}|| \mathop{x_i}\limits^{\rightarrow}-\mathop{m}\limits^{\rightarrow} _{B} ||^2 = \frac{n_An_B}{n_B+n_A}|| \mathop{m}\limits^{\rightarrow}_A-\mathop{m}\limits^{\rightarrow} _{B} ||^2Δ(A,B)=iABxiAm2iBxiBm2=nB+nAnAnBAmBm2

K_means

在这里插入图片描述
程序主体如下所示。



#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;

const double eps = 1e-6;

struct Point {
    vector<double> coordinate;
    int groupId;
    /**
     * @brief 计算两个 Point 之间的欧氏距离的平方,
     *          要求两个 Point coordinate 长度相等
     */
    double distance(const Point &other) const {
        // 采用欧氏距离平方作为指标.
        double ans = 0;
        int sz = max(coordinate.size(), other.coordinate.size());
        for (int i = 0; i < sz; ++i)
            ans += (coordinate[i] - other.coordinate[i]) * (coordinate[i] - other.coordinate[i]);
        return ans;
    }
    /**
     * @brief 将另一个 Point 赋值给当前 Point
     */
    Point &operator=(const Point &other) {
        if (this == &other) return *this;
        coordinate = other.coordinate;
        groupId = other.groupId;
        return *this;
    }
    /**
     * @brief 按 coordinate 顺序比较两个 Point 的大小,
     *          要求两个 Point coordinate 长度相等
     */
    bool operator<(const Point &other) const {
        auto sz = max(coordinate.size(), other.coordinate.size());
        for (auto i = 0; i < sz; ++i)
            if (fabs(coordinate[i] - other.coordinate[i]) > eps)
                return coordinate[i] < other.coordinate[i];
        return false;
    }
};

// 你的代码会被嵌入在这

bool vectorIsEqual(const vector<Point> &centers, const vector<Point> &temp) {
    auto v1 = centers;
    sort(v1.begin(), v1.end());
    auto v2 = temp;
    sort(v2.begin(), v2.end());
    auto k = max(v1.size(), v2.size());
    for (auto i = 0; i < k; ++i)
        if (v1[i] < v2[i] || v2[i] < v1[i])
            return false;
    return true;
}

vector<Point> kMeans(vector<Point> &points, const int k) {
    vector<Point> centers(k);
    vector<Point> temp;
    for (auto i = 0; i < k; ++i)
        centers[i] = points[i];
    do {
        temp = centers;
        centers = update(points, temp);
    } while (!vectorIsEqual(centers, temp));
    return centers;
}

double score(const vector<Point> &points, const vector<Point> &centers) {
    auto n = points.size();
    auto ans = 0.0;
    for (auto i = 0; i < n; ++i)
        ans += points[i].distance(centers[points[i].groupId]);
    return ans;
}

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    vector<int> target(n);
    vector<Point> points(n);
    for (int i = 0; i < n; ++i) {
        points[i].coordinate.resize(m);
        for (int j = 0; j < m; ++j) {
            scanf("%lf", &(points[i].coordinate[j]));
        }
        scanf("%d", &target[i]);
    }
    auto centers = kMeans(points, 2);
    printf("%f\n", (double)score(points, centers));
    return 0;
}

代码

vector<Point> update(vector<Point> &points, const vector<Point> &last)
{
    vector<Point> newest(last);
    vector<int> count;

    int lenl = last.size();
    int lenp = points.size();
    int cor_l = last[0].coordinate.size();

    //进行迭代
    //修改分组
    for(int i = 0; i < lenp; i++)
    {
        double dis = 0xffffffff;
        int id = 0;
        for(int j = 0; j < lenl; j++)
        {
            double temp = points[i].distance(last[j]);
            if(temp < dis)
            {
                dis = temp;
                id = j;
            }
        }
        points[i].groupId = id;

    }//一开始只进行迭代 就能获得6分
    //记录新的组的情况
    //清零
    
    int* cnt = new int[lenl];
    for(int i = 0; i < lenl; i++)
    {
        cnt[i] = 0;
        for(int j = 0; j < cor_l; j++)
        {
            newest[i].coordinate[j] = 0;
        }
    }//完成归零这一步能上到35分

    //更新最近点
    //每个点都加进去,并计算一个集合的数量
    for(int i = 0; i < lenp; i++)
    {
        cnt[points[i].groupId] ++;
        for(int j = 0; j < cor_l; j++)
        {
            int id = points[i].groupId;
            newest[id].coordinate[j] += points[i].coordinate[j];
        }
    }
    //相除
    for(int i = 0; i < lenl; i++)
    {
        for(int j = 0; j < cor_l; j++)
        {
         //   if(cnt[i])
                newest[i].coordinate[j] /= cnt[i];
        }
    }
    delete[] cnt;
    
    return newest;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值