一、算法简介

Affinity Propagation聚类算法简称AP,是一个在07年发表在Science上的聚类算法。它实际属于message-passing algorithms的一种。算法的基本思想将数据看成网络中的节点,通过在数据点之间传递消息,分别是吸引度(responsibility)和归属度(availability),不断修改聚类中心的数量与位置,直到整个数据集相似度达到最大,同时产生高聚类中心,并将其余各点分配到相应的聚类中。

二、算法描述

1、相关概念

  • Exemplar:指的是聚类中心,该聚类中心实际存在,并不是如同K-Means算法由计算生成的。

  • Similarity:数据点i和点j的相似度记为s(i, j),是指点j作为点i的聚类中心的相似度。一般使用欧氏距离来计算;相似度值越大说明点与点的距离越近,便于后面的比较计算。

  • Preference:数据点i的参考度称为p(i)或s(i,i),是指点i作为聚类中心的参考度。一般取s相似度值的中值。

  • Responsibility:r(i,k)用来描述点k适合作为数据点i的聚类中心的程度。

  • Availability:a(i,k)用来描述点i选择点k作为其聚类中心的适合程度。

  • Damping factor(阻尼因子)λ:主要是起收敛作用的。

2、算法步骤

2.1 具体算法步骤

AP算法可能需要指定一些参数,如Preference与Damping factor与最大迭代次数maxIte​rNum.

step 1: 初始化参数Damping factor与maxIterNum,并读取数据;

step 2:计算相似度矩阵Similarity[i,j],一般使用欧氏距离,并求出相似度矩阵的中位值并赋给Preference;

step 3: 更新吸引度矩阵;

step 4: 更新归属度矩阵;

setp 4: 判断是否达到最大迭代次数或达到终止条件,如未达到跳转step 2,否则继续下一步;

setp 5: 生成最终Exemplar,并将各数据分配到相应的聚类中。

2.2 算法详解

AP算法有两个关键步骤,即更新吸引度矩阵与更新归属度矩阵。

更新吸引度矩阵:

更新归属度矩阵:

为了避免振荡,AP算法更新信息时引入了衰减系数λ。每条信息被设置为它前次迭代更新值的λ倍加上本次信息更新值的1-λ倍。其中,衰减系数

到1之间的实数。即第t+1次r(i,k)与a(i,k)的迭代值:

2.3 算法优缺点

优点:

  • 不需要事先指定聚类的数量

  • 聚类结果很稳定

  • 适用于非对称相似性矩阵

  • 初始值不敏感

缺点:

  • 算法复杂度较高,为O(N*N*logN),该算法比较慢,对于大量数据,计算很久

三、算法实现(Java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
package cang.algorithms.clustering.ap;
 
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
 
/**
 * 近邻传播算法,半监督聚类算法<br>
 * 优点:不需事先指定类的个数;对初值的选取不敏感;对距离矩阵的对称性没要求<br>
 * 缺点:算法复杂度较高,为O(N*N*logN)
 *
 * @author cang
 *
 */
public class AP {
 
    private int maxIterNum;
    // 聚类结果不变次数
    private int changedCount;
    private int unchangeNum;
    private int dataNum;
    private Point[] dataset;
    // 相似度矩阵,数据点i和点j的相似度记为s(i, j),是指点j作为点i的聚类中心的相似度
    private double similar[][];
    // 吸引信息矩阵,r(i,k)用来描述点k适合作为数据点i的聚类中心的程度
    private double r[][];
    // 归属信息矩阵,a(i,k)用来描述点i选择点k作为其聚类中心的适合程度
    private double a[][];
    // 衰减系数,主要是起收敛作用的
    private double lambda;
    // 聚类中心
    private List<Integer> exemplar;
    private List<Integer> oldExemplar;
 
    public AP() {
        this(1000, 0.9);
    }
 
    public AP(int maxIterNum, double lambda) {
        this.maxIterNum = maxIterNum;
        this.lambda = lambda;
    }
 
    /**
     * 数据初始化
     */
    public void init() {
        oldExemplar = new ArrayList<Integer>();
        exemplar = new ArrayList<Integer>();
        similar = new double[dataNum][dataNum];
        r = new double[dataNum][dataNum];
        a = new double[dataNum][dataNum];
        for (int i = 0; i < dataset.length; i++) {
            for (int j = i + 1; j < dataset.length; j++) {
                similar[i][j] = distance(dataset[i].dimensioin,
                        dataset[j].dimensioin);
                similar[j][i] = similar[i][j];
            }
        }
        setPreference(3);
    }
 
    /**
     * 获取数据点i的参考度<br>
     * 称为p(i)或s(i,i) 是指点i作为聚类中心的参考度。一般取s相似度值的中值
     *
     * @param prefType 参考度类型
     */
    private void setPreference(int prefType) {
        List<Double> list = new ArrayList<Double>();
        // find the median
        for (int i = 0; i < dataNum; i++) {
            for (int j = i + 1; j < dataNum; j++) {
                list.add(similar[i][j]);
            }
        }
        Collections.sort(list);
        double pref = 0;
        // use the median as preference
        if (prefType == 1) {
            if (list.size() % 2 == 0) {
                pref = (list.get(list.size() / 2)
                        + list.get(list.size() / 2 - 1)) / 2;
            } else {
                pref = list.get((list.size()) / 2);
            }
            // use the minimum as preference
        } else if (prefType == 2) {
            pref = list.get(0);
            // use the 0.5 * (min + max) as preference
        } else if (prefType == 3) {
            pref = list.get(0)
                    + (list.get(list.size() - 1) + list.get(0)) * 0.5;
            // use the maximum as preference
        } else if (prefType == 4) {
            pref = list.get(list.size() - 1);
        } else {
            System.out.println("prefType error");
            System.exit(-1);
        }
        System.out.println(pref);
        for (int i = 0; i < dataNum; i++) {
            similar[i][i] = pref;
        }
    }
 
    public void clustering() {
        for (int i = 0; i < maxIterNum; i++) {
            updateResponsible();
            updateAvailable();
 
            oldExemplar.clear();
            if (!exemplar.isEmpty()) {
                for (Integer v : exemplar) {
                    oldExemplar.add(v);
                }
            }
            exemplar.clear();
 
            changedCount = 0;
            // 获取聚类中心
            for (int k = 0; k < dataNum; k++) {
                if (r[k][k] + a[k][k] > 0) {
                    exemplar.add(k);
                }
            }
            // data point assignment
            assignCluster();
 
            if (changedCount == 0) {
                unchangeNum++;
                if (unchangeNum > 10) {
                    maxIterNum = i;
                    break;
                }
            } else {
                unchangeNum = 0;
            }
 
        }
        // 生成预测标签
        setPredictLabel();
    }
 
    /**
     * 将各数据点分配到聚类中心
     */
    private void assignCluster() {
        for (int i = 0; i < dataNum; i++) {
            double max = Double.MIN_VALUE;
            int index = 0;
            for (Integer k : exemplar) {
                if (max < similar[i][k]) {
                    max = similar[i][k];
                    index = k;
                }
            }
            if (dataset[i].cid != index) {
                dataset[i].cid = index;
                changedCount++;
            }
        }
    }
 
    /**
     * 更新吸引信息矩阵
     */
    private void updateResponsible() {
        for (int i = 0; i < dataNum; i++) {
            for (int k = 0; k < dataNum; k++) {
                double max = Double.MIN_VALUE;
                for (int j = 0; j < dataNum; j++) {
                    if (j != k) {
                        if (max < a[i][j] + similar[i][j]) {
                            max = a[i][j] + similar[i][j];
                        }
                    }
                }
                r[i][k] = (1 - lambda) * (similar[i][k] - max)
                        + lambda * r[i][k];
            }
        }
    }
 
    /**
     * 更新归属信息矩阵
     */
    private void updateAvailable() {
        for (int i = 0; i < dataNum; i++) {
            for (int k = 0; k < dataNum; k++) {
                if (i == k) {
                    double sum = 0;
                    for (int j = 0; j < dataNum; j++) {
                        if (j != k) {
                            if (r[j][k] > 0) {
                                sum += r[j][k];
                            }
                        }
                    }
                    a[k][k] = sum;
                } else {
                    double sum = 0;
                    for (int j = 0; j < dataNum; j++) {
                        if (j != i && j != k) {
                            if (r[j][k] > 0) {
                                sum += r[j][k];
                            }
                        }
                    }
                    a[i][k] = (1 - lambda) * (r[k][k] + sum) + lambda * a[i][k];
                    if (a[i][k] > 0) {
                        a[i][k] = 0;
                    }
                }
            }
        }
    }
 
    /**
     * 生成数据点的聚类标签
     */
    private void setPredictLabel() {
        Map<Integer, String> labelMap = new HashMap<Integer, String>();
        for (int cid : exemplar) {
            Map<String, Integer> tempMap = new HashMap<String, Integer>();
            for (Point p : dataset) {
                if (cid == p.cid) {
                    if (tempMap.get(p.label) == null) {
                        tempMap.put(p.label, 1);
                    } else {
                        tempMap.put(p.label, tempMap.get(p.label) + 1);
                    }
                }
            }
            String maxLabel = null;
            int temp = 0;
            for (Entry<String, Integer> iter : tempMap.entrySet()) {
                if (temp < iter.getValue()) {
                    temp = iter.getValue();
                    maxLabel = iter.getKey();
                }
            }
            labelMap.put(cid, maxLabel);
        }
 
        for (Point p : dataset) {
            p.predictLabel = labelMap.get(p.cid);
        }
    }
 
    /**
     * 计算数据点之间的距离
     *
     * @param a 数据的坐标
     * @param b 另一个数据的坐标
     * @return
     */
    private double distance(double[] a, double[] b) {
        if (a.length != b.length) {
            throw new IllegalArgumentException("Arrry a not equal array b!");
        }
        double sum = 0;
        for (int i = 0; i < a.length; i++) {
            double dp = a[i] - b[i];
            sum += dp * dp;
        }
        return (double) Math.sqrt(sum);
    }
 
 
    /**
     * 读取数据集<br>
     * 将数据集保存到数据集中
     *
     * @param fileName 文件名
     * @param split 分隔符
     * @param labelAtHead 标签是否在头部
     * @throws IOException
     */
    public void importDataWithLabel(String fileName, String split,
            boolean labelAtHead) throws IOException {
        int dimensionNum = 0;
        List<Point> dataList = new ArrayList<Point>();
        // 读取数据文件
        BufferedReader reader = new BufferedReader(new FileReader(fileName));
        String line = null;
        while ((line = reader.readLine()) != null) {
            if (line.trim().equals("")) {
                continue;
            }
            // 字符串以split拆分
            String[] splitStrs = line.split(split);
            dimensionNum = splitStrs.length - 1;
            double[] temp = new double[dimensionNum];
 
            String label = splitStrs[dimensionNum];
            if (labelAtHead) {
                label = splitStrs[0];
                for (int i = 0; i < dimensionNum; i++) {
                    temp[i] = Double.parseDouble(splitStrs[i + 1]);
                }
            } else {
                for (int i = 0; i < dimensionNum; i++) {
                    temp[i] = Double.parseDouble(splitStrs[i]);
                }
            }
            dataList.add(new Point(temp, label));
            dataNum++;
        }
        reader.close();
        Collections.shuffle(dataList);
        dataset = new Point[dataList.size()];
        dataList.toArray(dataset);
    }
 
    /**
     * 打印输出聚类信息
     */
    public void printInfo() {
        System.out.println("迭代次数:" + maxIterNum);
        System.out.println("聚类数目为:" + exemplar.size());
        for (int j = 0; j < exemplar.size(); j++) {
            System.out.println(j + ": " + exemplar.get(j));
        }
        for (Point point : dataset) {
            System.out.println(point);
        }
    }
 
 
    static class Point {
        // 数据标签
        private String label;
        // 聚类预测的标签
        private String predictLabel;
        // 数据点所属簇id
        private int cid;
        // 数据点的维度
        private double dimensioin[];
 
        public Point(double dimensioin[], String label) {
            this.label = label;
            init(dimensioin);
        }
 
        public Point(double dimensioin[]) {
            init(dimensioin);
        }
 
        public void init(double value[]) {
            dimensioin = new double[value.length];
            for (int i = 0; i < value.length; i++) {
                dimensioin[i] = value[i];
            }
        }
 
        @Override
        public String toString() {
            return "Point [label=" + label + ", predictLabel=" + predictLabel
                    + ", cid=" + cid + ", dimensioin="
                    + Arrays.toString(dimensioin) + "]";
        }
 
    }
 
public static void main(String[] args) throws IOException {
        AP ap = new AP(10000, 0.6);
        ap.importDataWithLabel(FILEPATH, ",", false);
        ap.init();
        ap.clustering();
        ap.printInfo();
    }
}

AP聚类算法的更多相关文章

  1. AP聚类算法(Affinity propagation Clustering Algorithm )

    AP聚类算法是基于数据点间的"信息传递"的一种聚类算法.与k-均值算法或k中心点算法不同,AP算法不需要在运行算法之前确定聚类的个数.AP算法寻找的"examplars& ...

  2. AP聚类算法(转)

    Affinity Propagation (AP) 聚类是2007年在Science杂志上提出的一种新的聚类算法.它根据N个数据点之间的相似度进行聚类,这些相似度可以是对称的,即两个数据点互相之间的相 ...

  3. 机器学习:Python实现聚类算法(一)之AP算法

    1.算法简介 AP(Affinity Propagation)通常被翻译为近邻传播算法或者亲和力传播算法,是在2007年的Science杂志上提出的一种新的聚类算法.AP算法的基本思想是将全部数据点都 ...

  4. Python实现聚类算法AP

    1.算法简介 AP(Affinity Propagation)通常被翻译为近邻传播算法或者亲和力传播算法,是在2007年的Science杂志上提出的一种新的聚类算法.AP算法的基本思想是将全部数据点都 ...

  5. 机器学习:Python实现聚类算法(二)之AP算法

    1.算法简介 AP(Affinity Propagation)通常被翻译为近邻传播算法或者亲和力传播算法,是在2007年的Science杂志上提出的一种新的聚类算法.AP算法的基本思想是将全部数据点都 ...

  6. AP聚类

    基于代表点的聚类算法可以说是聚类算法中"最经典的,最流行的,也是最前沿的". "最经典"是因为K均值是最早出现的聚类算法之一; "最流行"是 ...

  7. DBSCAN密度聚类算法

    DBSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)是一种很典型的密度聚类算法,和K-M ...

  8. 基于改进人工蜂群算法的K均值聚类算法(附MATLAB版源代码)

    其实一直以来也没有准备在园子里发这样的文章,相对来说,算法改进放在园子里还是会稍稍显得格格不入.但是最近邮箱收到的几封邮件让我觉得有必要通过我的博客把过去做过的东西分享出去更给更多需要的人.从论文刊登 ...

  9. 挑子学习笔记:两步聚类算法(TwoStep Cluster Algorithm)——改进的BIRCH算法

    转载请标明出处:http://www.cnblogs.com/tiaozistudy/p/twostep_cluster_algorithm.html 两步聚类算法是在SPSS Modeler中使用的 ...

随机推荐

  1. android java epson串口打印机

    package com.common.util.portprinter; import java.io.IOException; import java.io.OutputStream; import ...

  2. three levels of abstraction

    DATABASESYSTEM CONCEPTS SIXTH EDITION Abraham Silberschatz Yale University Henry F. KorthLehigh Univ ...

  3. 设计模式之——flyweight模式

    flyweight模式,又叫做享元模式. 顾名思义,享元模式就是共享一个元素. 百度百科 解释为: 享元模式(英语:Flyweight Pattern)是一种软件设计模式.它使用共享物件,用来尽可能减 ...

  4. 不再以讹传讹,GET和POST的真正区别(转)

    add by zhj:按照restful的定义,GET是用于获取记录(幂等),POST用于创建记录(不幂等).GET也能带消息体?这个我没试过,文中说用浏览器发GET请求 是没法带的.另外,在< ...

  5. 扩展的friend语法

    Java中没有定义friend. friend用于声明类的友元,可以无视类中成员的属性.无论成员3p中的哪一种,友元类或友元函数都可以访问,破坏了封装性,此关键字备受争议.专家会建议程序员使用get/ ...

  6. POJ:2528(Mayor's posters)离散化成段更新+简单哈希

    http://poj.org/problem?id=2528 Description The citizens of Bytetown, AB, could not stand that the ca ...

  7. kvm日常管理

    创建虚拟机 快速启动虚拟机 [root@localhost ~]# yum install kvm libvirt python-virtinst qemu-kvm virt-viewer bridg ...

  8. 实现对多维数组按照某个键值排序的两种方法(array_multisort和array_sort)

    实现对多维数组按照某个键值排序的两种解决方法(array_multisort和array_sort): 第一种:array_multisort()函数对多个数组或多维数组进行排序.    //对数组$ ...

  9. js随机点名系统

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. BootStrap的布局学习

    布局组件无数可复用的组件,包括字体图标.下拉菜单.导航.警告框.弹出框等更多功能. Bootstrap的使用非常灵活,可以对各种组件进行合并使用(如:为标签页项 添加带下拉菜单),下面的知识点中将逐个 ...