【机器学习笔记36】蚁群算法-聚类分析

【参考资料】
【1】《模式识别与智能计算的MATLAB技术实现》
【2】《蚁群聚类算法综述》

1 算法概述

聚类数已知的算法流程
  1. 初始化蚁群参数,如蚂蚁数量、聚类数量等;每只蚂蚁对应一个解集:
样品号 1 2 N
蚂蚁 S i S_i 类别1 类别2 类别1

上述表表示蚂蚁 S i S_i 把N个样本分归属到分类中。

  1. 构建信息素矩阵
样本\类别 类别1 类别2 类别3 类别4
样本1 0.1 0.1 0.1 0.1
样本2 0.1 0.1 0.1 0.1
样本3 0.1 0.1 0.1 0.1
0.1 0.1 0.1 0.1
样本N 0.1 0.1 0.1 0.1

上述信息素 τ i j \tau_{ij} 表示把第i个样本归类到第j个类别时的信息素

  1. 构建目标函数

假设: N个样品、M个模式分类 S j , j = 1 , 2 , . . . , M |S_j,j=1,2,..., M| ,每个样品有n个特征。
目标: 将每个样品到聚类中心的距离和达到最小

m i n J ( w , c ) = j = 1 m i = 1 N p = 1 n w i j x i p c j p 2 minJ(w, c)=\sum\limits_{j=1}^{m}\sum\limits_{i=1}^{N}\sum\limits_{p=1}^{n} w_{ij}||x_{ip} - c_{jp}||^2

c j p = i = 1 N w i j x i p i = 1 N w i j c_{jp}=\dfrac{\sum\limits_{i=1}^{N}w_{ij}x_{ip}}{\sum\limits_{i=1}^{N}w_{ij}}

w i j = { 1 , N i S j 0 , N i S j w_{ij} = \begin{cases} 1, & N_i \in S_j \\ 0, & N_i \notin S_j \end{cases} 样本属于该类则为1,否则为0

其中 x i p x_{ip} 为第i个样本的p属性、 c j p c_{jp} 为第j个分类的p属性

  1. 更新蚁群

每一只蚂蚁在对自己解集中样本归属判断时采用两种策略(随机选择):

  1. 根据当前时刻信息素表,选择信息素高的;(直接基于既有知识)
  2. 按照当前信息素的概率,即信息素高的类别有更大的可能性被选择;(也是基于既有知识,但非直接,而是一定程度上的随机选择)

4.1 参考第一步每只蚂蚁所具备的解集;根据目标函数公式计算每只蚂蚁的目标值;
4.2 根据目标值将蚂蚁进行排序;
4.3 取最优的L只蚂蚁进行“局部搜索”,遍历这L只蚂蚁:

4.3.1 随机选择其中第i个样本,重新计算目标函数,确定一个新的分类;
4.3.2 当这只蚂蚁的若干个样本被重新分类后,再计算一次该蚂蚁的总目标函数,若解更优;则替换原解;
4.3.3 遍历L只蚂蚁后,前L只蚂蚁中具备最优解的作为当前全局最优解;

  1. 更新信息素表
    τ i j ( t + 1 ) = ( 1 ρ ) τ i j ( t ) + s = 1 l Δ τ i j s \tau_{ij}(t+1)=(1-\rho)\tau_{ij}(t) + \sum\limits_{s=1}^{l}\Delta \tau_{ij}^s
    其中若蚂蚁s中的样品i属于分类j,则 Δ τ i j s = Q J \Delta \tau_{ij}^s=\dfrac{Q}{J} ,否则为0。这里Q是一个超参数;J是蚂蚁s的目标函数值。

  2. 多次迭代后达到全局最优解

2 算法实现

实际在实现过程中感觉蚁群算法还是非常收到超参数影响。包括信息素的挥发参数,以及超参数Q等等,而且效果差距非常大。当前代码中的参数是一个具备比较好效果的设置。

# -*- coding: utf-8 -*-
import numpy  as np
import sklearn.datasets as ds
import matplotlib.pyplot as plt
import random
import math
import operator

SAMPLE_NUM  = 18    #样本数量
FEATURE_NUM = 2     #每个样本的特征数量
CLASS_NUM   = 2     #分类数量
ANT_NUM     = 200    #蚂蚁数量


"""
初始化测试样本,sample为样本,target_classify为目标分类结果用于对比算法效果
"""
sample, target_classify = ds.make_blobs(SAMPLE_NUM, n_features=FEATURE_NUM, centers=CLASS_NUM, random_state=3)

"""
信息素矩阵
"""
tao_array =  [[random.random() for col in range(FEATURE_NUM)] for row in range(SAMPLE_NUM)] 

"""
蚁群解集
"""
ant_array = [[0 for col in range(SAMPLE_NUM)] for row in range(ANT_NUM)] 

t_ant_array = [[0 for col in range(SAMPLE_NUM)] for row in range(ANT_NUM)] #存储局部搜索时的临时解

"""
聚类中心点
"""
center_array = [[0 for col in range(FEATURE_NUM)] for row in range(CLASS_NUM)] 

"""
当前轮次蚂蚁的目标函数值,前者是蚂蚁编号、后者是目标函数值
"""
ant_target = [(0, 0) for col in range(ANT_NUM)] 




change_q   = 0.3       #更新蚁群时的转换规则参数,表示何种比例直接根据信息素矩阵进行更新
L          = 2         #局部搜索的蚂蚁数量
change_jp  = 0.03      #局部搜索时该样本是否变动
change_rho = 0.02      #挥发参数
Q          = 0.1      #信息素浓度参数


def _init_test_data():

    """
    初始化蚁群解集,随机确认每只蚂蚁下每个样本的分类为1或者0
    """
    for i in range(0, ANT_NUM):
        for j in range(0, SAMPLE_NUM):

            tmp = random.randint(0, FEATURE_NUM - 1)

            ant_array[i][j] = tmp

    """
    将前两个样本作为聚类中心点的初始值
    """
    for i in range(0, CLASS_NUM):

        center_array[i][0] = sample[random.randint(0, SAMPLE_NUM-1)][0]
        center_array[i][1] = sample[random.randint(0, SAMPLE_NUM-1)][1]

def _get_best_class_by_tao_value(sampleid):

    max_value = np.max(tao_array[sampleid])

    for i in range(0, CLASS_NUM):

        if max_value == tao_array[sampleid][i]:

            return i


def random_pick(some_list, probabilities):
    x = random.uniform(0,1)
    cumulative_probability = 0.0
    for item, item_probability in zip(some_list, probabilities):
        cumulative_probability += item_probability
        if x < cumulative_probability:break
    return item


def _get_best_class_by_tao_probablity(sampleid):

    tarray = np.array(tao_array[sampleid])

    parray = tarray/np.sum(tarray)

    return random_pick([0,1], parray)


def _update_ant():

    """
    更新蚁群步骤
    """

    #产生一个随机数矩阵
    r = np.random.random((ANT_NUM, SAMPLE_NUM))

    for i in range(0, ANT_NUM):
        for j in range(0, SAMPLE_NUM):

            if r[i][j] > change_q:

                tmp_index = _get_best_class_by_tao_value(j)

                #选择该样本中信息素最高的做为分类
                ant_array[i][j] = tmp_index

            else:
                #计算概率值,根据概率的大小来确定一个选项
                tmp_index = _get_best_class_by_tao_probablity(j)

                ant_array[i][j] = tmp_index

    #print(ant_array[i])
    #1. 确定一个新的聚类中心
    f_value_feature_0 = 0
    f_value_feature_1 = 0

    for i in range(0, CLASS_NUM):

        
        f_num   = 0

        for j in range(0, ANT_NUM):

            for k in range(0, SAMPLE_NUM):

                if ant_array[j][k] == 0:

                    f_num   += 1
                    f_value_feature_0 += sample[k][0] #特征1

                else:

                    f_num   += 1
                    f_value_feature_1 += sample[k][1] #特征2

        if i == 0:
            center_array[i][0] = f_value_feature_0/f_num
        else:
            center_array[i][1] = f_value_feature_1/f_num
    
    

        #print(center_array[i], f_num)


def _judge_sample(sampleid):

    """
    计算与当前聚类点的举例,判断该sample应所属的归类
    """
    target_value_0 = 0
    target_value_1 = 0

    f1 = math.pow((sample[sampleid][0] - center_array[0][0]),2)
    f2 = math.pow((sample[sampleid][1] - center_array[0][1]),2)
    target_value_0 = math.sqrt(f1 + f2)


    f1 = math.pow((sample[sampleid][0] - center_array[1][0]),2)
    f2 = math.pow((sample[sampleid][1] - center_array[1][1]),2)
    target_value_1 = math.sqrt(f1 + f2)

    if target_value_0 > target_value_1:
        return 1
    else:
        return 0


def _local_search():

    """
    局部搜索逻辑
    """
    


    #2. 根据新的聚类中心计算每个蚂蚁的目标函数

    for i in range(0, ANT_NUM):

        target_value = 0

        for j in range(0, SAMPLE_NUM):

            if ant_array[i][j] == 0:

                #与分类0的聚类点计算距离
                f1 = math.pow((sample[j][0] - center_array[0][0]),2)
                f2 = math.pow((sample[j][1] - center_array[0][1]),2)
                target_value += math.sqrt(f1 + f2)

            else:
                #与分类1的聚类点计算距离
                f1 = math.pow((sample[j][0] - center_array[1][0]),2)
                f2 = math.pow((sample[j][1] - center_array[1][1]),2)
                target_value += math.sqrt(f1 + f2) 

        #保存蚂蚁i当前的目标函数
        ant_target[i] = (i, target_value)

    #3. 对全部蚂蚁的目标进行排序,选择最优的L只蚂蚁

    ant_target.sort(key= operator.itemgetter(1)) #对ant进行排序

    #4. 对这L只蚂蚁进行解的优化
    for i in range(0, L):

        ant_id = ant_target[i][0]

        target_value = 0

        for j in range(0, SAMPLE_NUM):

            #对于该蚂蚁解集中的每一个样本
            if random.random() < change_jp:

                #将该样本调整到与当前某个聚类点最近的位置
                t_ant_array[ant_id][j] = _judge_sample(j)


        #判断是否保留这个临时解
        for j in range(0, SAMPLE_NUM):

            if t_ant_array[ant_id][j] == 0:

                #与分类0的聚类点计算距离
                f1 = math.pow((sample[j][0] - center_array[0][0]),2)
                f2 = math.pow((sample[j][1] - center_array[0][1]),2)
                target_value += math.sqrt(f1 + f2)

            else:
                #与分类1的聚类点计算距离
                f1 = math.pow((sample[j][0] - center_array[1][0]),2)
                f2 = math.pow((sample[j][1] - center_array[1][1]),2)
                target_value += math.sqrt(f1 + f2) 


        if target_value < ant_target[i][1]:

            #更新最优解
            ant_array[ant_id] = t_ant_array[ant_id]


def _update_tau_array():

    """
    更新信息素表
    """
    for i in range(0, SAMPLE_NUM):

        for j in range(0, CLASS_NUM):

            tmp = tao_array[i][j] #当前的信息素

            tmp = (1 - change_rho) * tmp #处理信息素挥发

            J = 0

            #处理信息素浓度增加
            for k in range(0, ANT_NUM):

                if ant_array[k][i] == j:

                    f1 = math.pow((sample[i][0] - center_array[j][0]),2)
                    f2 = math.pow((sample[i][1] - center_array[j][1]),2)
                    J += math.sqrt(f1 + f2)

            if J != 0:
                tmp += Q/J 

                #print(tmp, Q/J)

            tao_array[i][j] = tmp


    #print(np.var(tao_array))






"""
说明:

简单蚁群算法解决聚类问题,参考笔记《蚁群算法-聚类算法》

作者:fredric

日期:2018-12-21

"""
if __name__ == "__main__":

    _init_test_data();

    for i in range(0, 100):

        print("iterate No. {} target {}".format(i, ant_target[0][1]))

        _update_ant()

        _local_search()

        _update_tau_array()



    #画出分类
    pre = ant_array[ant_target[0][0]]

    plt.figure(figsize=(5, 6), facecolor='w')
    plt.subplot(211)
    plt.title('origin classfication')
    plt.scatter(sample[:, 0], sample[:, 1], c=target_classify, s=20, edgecolors='none')

    plt.subplot(212)
    plt.title('ant classfication')
    plt.scatter(sample[:, 0], sample[:, 1], c=pre, s=20, edgecolors='none')

    plt.plot(center_array[0][0], center_array[0][1],'ro')
    plt.plot(center_array[1][0], center_array[1][1],'bo')

    plt.show()

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Fredric_2014/article/details/85167556