Binary ACO is a modification of TACO. Touring ant colony optimization was initially desinged for handling continuous varibales in the optimization problems, and was later used for different problems such as digital filter design. In this algorithm, each solution is represented with a string of binary bits. Artificial ants search for the value of each bit in the string. At the decision stage for the value of a bit, ants only use the pheromone information. After all ants in the colony have produced their solutions and pheromone of sub-paths between the bits is updated.This is carried out by evaporating the previous pheromone amounts and depositing the new pheromone amounts on the paths.
Here I give the python codes for binary colony based on its general ideas. In the codes, only the global best ant is admissible to deposit pheromone and max-min ant system stragety is utilized for pheromone updating. Here i applied the binary ant colony algorithm for feature selection. The dataset can be dowloaded from the UCI websites.
Welcome everyone to give some advice!
Created on Tue Jan 00 00:00:00 2017
@author: Randolph.Lee
"""
from __future__ import division
import numpy as np
import random
class Graph:
def __init__(self, num_nodes, min_pheromone, max_pheromone, phro, Q):
self.nodes_number = num_nodes
self.pheromone_min = min_pheromone
self.pheromone_max = max_pheromone
self.Q = Q
self.phro = phro
self.pheromone_mat = np.ones((self.nodes_number, 2))
self.pheromone_deltaMat = np.zeros((self.nodes_number, 2))
def update_pheromone(self, result_list, fitness):
# update the pheromone according to the best ant
# result_list is a tuple
# max-min ant system
for (feat_index, bin_code) in result_list:
self.pheromone_deltaMat[feat_index, bin_code] = self.Q / fitness
self.pheromone_mat = (1 - self.phro) * self.pheromone_mat + self.pheromone_deltaMat
for index in range(self.nodes_number):
for j in [0, 1]:
if self.pheromone_mat[index, j] > self.pheromone_max:
self.pheromone_mat[index, j] = self.pheromone_max
if self.pheromone_mat[index, j] < self.pheromone_min:
self.pheromone_mat[index, j] = self.pheromone_min
class Ant:
def __init__(self, ID):
self.ID = ID
# save the result of binary string including the feature index ant corresponding status
self.result_list = []
# the next index to select
self.next_i = 0
# the subset selected
self.subset_list = []
# calculate the recall and precision for the evaluation criterion
'''
Here it's noted that the graph is directed and the ant search the feature nodes by the ordering path
'''
self.accuracy = 0.0
self.current_fitness = 0.0
def select_bin(self, alpha, beta, pheromone_mat, heuristic_mat):
# pheromone_mat: num_nodes * 2, heuristic_mat: num_nodes * 2
temp_prob0 = np.power(pheromone_mat[self.next_i, 0], alpha) * np.power(heuristic_mat[self.next_i, 0], beta)
temp_prob1 = np.power(pheromone_mat[self.next_i, 1], alpha) * np.power(heuristic_mat[self.next_i, 1], beta)
transfer_to1 = temp_prob1 / (temp_prob0 + temp_prob1)
# Roulette
if random.random() <= transfer_to1:
res = (self.next_i, 1)
else:
res = (self.next_i, 0)
return res
def add_bin(self, bin_tuple):
self.result_list.append(bin_tuple)
self.next_i += 1
def decode_bin(self, feature_num):
temp_result = filter(lambda x: x[1] == 1, self.result_list)
if len(temp_result) == 0:
self.subset_list = [random.randint(0, feature_num - 1)] # modify
else:
self.subset_list = map(lambda x: x[0], temp_result)
def evaluate_subset(self, origin_featMat, label_arr, centers=None):
# calculate the three indicators
if centers is not None:
selected_feat = self.subset_list.extend(centers)
else:
selected_feat = self.subset_list
feat_mat = origin_featMat[:, selected_feat]
self.accuracy = cal_indicator(feat_mat, label_arr)
'''
Here we encode the features except the most representative features due to that the most representative features
must be added into the subsets, which can decrease the search time of each ant
'''
self.current_fitness = cal_fitness1(len(self.subset_list), self.accuracy)
def clear_subset(self):
self.subset_list = []
self.result_list = []
class AntSearch:
def __init__(self, constructed_graph, ant_num, alpha_para, beta_para, iteration_max):
self.graph = constructed_graph
self.num_ant = ant_num
self.alpha = alpha_para
self.beta = beta_para
self.iter_max = iteration_max
self.best_subset = []
self.best_result = []
self.best_fit = 0.0
self.best_accuracy = 0.0
self.res = []
def create_ant(self):
ant_list = []
for j in range(self.num_ant):
ant_list.append(Ant(j))
return ant_list
def iter_search(self, origin_featMat, label_arr, heuristic_mat, centers=None):
for i in range(self.iter_max):
ant_list = self.create_ant()
for ant in ant_list:
# remember that the graph is directed so the search is an ordering path
while ant.next_i < self.graph.nodes_number:
next_bin = ant.select_bin(self.alpha, self.beta, self.graph.pheromone_mat, heuristic_mat)
ant.add_bin(next_bin)
ant.decode_bin(origin_featMat.shape[1])
# i, origin_featMat, label_arr, most_repList=None, params=None, property_dict=None
ant.evaluate_subset(origin_featMat, label_arr, centers)
# record the local best subset
local_fit = ant_list[0].current_fitness
local_subset = ant_list[0].subset_list
local_result = ant_list[0].result_list
local_accuracy = ant_list[0].accuracy
for ant in ant_list[1:]:
if ant.current_fitness > local_fit:
local_fit = ant.current_fitness
local_subset = ant.subset_list
local_result = ant.result_list
local_accuracy = ant.accuracy
# record the global best subset
if local_fit > self.best_fit:
self.best_fit = local_fit
self.best_subset = local_subset
self.best_result = local_result
self.best_accuracy = local_accuracy
'''
Here we employ the max-min ant system, and only the global ant can update the pheromone
'''
self.graph.update_pheromone(self.best_result, self.best_fit)
# clear up the datasets
for ant in ant_list:
ant.clear_subset()
self.res.append([self.best_fit, self.best_accuracy, self.best_subset, len(self.best_subset) / origin_featMat.shape[1]])