【寻优算法】量子遗传算法(QGA) 参数寻优的python实现


遗传算法用于模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,种群通过选择、交叉和变异操作使染色体的十进制数值逼近最优参数值。 遗传算法容易陷入局部最优的陷阱。量子遗传算法是将量子计算与遗传算法结合,引入量子编码和量子旋转门,增加染色体变化的可能性。

一、量子编码

1、染色体量子编码

遗传算法中染色体的取值采用二进制编码,只要编码确定,染色体的取值就是确定的,后续的选择、交叉和变异操作都是在已经确定的二进制编码的染色体上进行。为了增加染色体取值的变化,量子遗传算法引入量子编码方法为染色体的取值进行编码。
在量子编码中,染色体每一个二进制位被称为量子比特,量子比特不是确定的0或1,而是0和1的叠加,一个量子比特的状态可以取值0或1,其状态表示为:

φ = α 0 + β 1 \left| \varphi \right\rangle = \alpha \left| 0 \right\rangle + \beta \left| 1 \right\rangle

其中 α 2 {\left| \alpha \right|^2} β 2 {\left| \beta \right|^2} 代表0和1出现的概率,初始染色体取值时,随机选择一个位于 a = ( 0 , 2 π ) a=\left( {0,2\pi } \right) 的角度,令 α = sin ( a ) \alpha = \sin \left( a \right) b = cos ( a ) b = \cos \left( a \right)

2、量子编码转换为二进制编码

如上所说,量子编码表示染色体二进制位为0或1的概率,但是具体操作还是要现将染色体量子编码转化为二进制取值进而转化为十进制取值。生成一个位于 ( 0 , 1 ) \left( {0,1} \right) 的随机数,如果该随机数小于 α 2 {\left| \alpha \right|^2} ,则该二进制位取值为1,否则取值0。

二、量子进化

1、全干扰交叉

量子遗传算法中,交叉的操作对象是种群中所有染色体取值的比特位角度。交叉是为了增加染色体的变化,防止陷入局部最优的陷阱【参考资料1】。全干扰交叉不同于其它的交叉方法,种群中所有的染色体取值都参与交叉,进一步增加了染色体的变化。假设只有一个参数(一个染色体),种群数为5,染色体长度(染色比特位数)为5,则全干扰交叉前后种群中染色体所有取值分别为:
全干扰交叉前:

染色体取值在种群中的位置 第1位 第2位 第3位 第4位 第5位
1 A(1) A(2) A(3) A(4) A(5)
2 B(1) B(2) B(3) B(4) B(5)
3 C(1) C(2) C(3) C(4) C(5)
4 D(1) D(2) D(3) B(4) D(5)
5 E(1) E(2) E(3) E(4) E(5)

全干扰交叉后:

染色体取值在种群中的位置 第1位 第2位 第3位 第4位 第5位
1 A(1) E(2) D(3) C(4) B(5)
2 B(1) A(2) E(3) D(4) C(5)
3 C(1) B(2) A(3) E(4) D(5)
4 D(1) C(2) B(3) A(4) E(5)
5 E(1) D(2) C(3) B(4) A(5)

2、量子变异

量子变异是通过量子旋转门实现的,本质是通过改变种群中所有染色体取值的每一位量子比特的量子角度,使得染色体取值向更好的染色体靠拢。具体方法可参考【资料2】

三、QGA多参数寻优的python实现

完整代码及样本地址:https://github.com/shiluqiang/QGA_python

1、训练模型及待寻优参数

本博文选用的多参数机器学习模型为非线性SVM(参考资料【3】),模型的优化问题为:

min W , e 1 2 W 2 + C 2 i = 1 m e i 2 s . t . y i ( W φ ( x i ) + b ) 1 e i , i = 1 ,   , m e 0 , i = 1 ,   , m \begin{array}{l} \mathop {\min }\limits_{W,e} \frac{1}{2}{\left\| W \right\|^2} + \frac{C}{2}\sum\limits_{i = 1}^m {{e_i}^2} \\ s.t.{y_i}\left( {W \cdot \varphi ({x_i}) + b} \right) \ge 1 - {e_i},i = 1, \cdots ,m\\ e \ge 0,i = 1, \cdots ,m \end{array}

通过Lagrange乘数法并转化为对偶问题,优化问题转换为:

min α 1 2 i m j m α i α j y i y j K ( x i , x j ) i = 1 m α i s . t . i = 1 m α i y i = 0 0 α i C , i = 1 ,   , m \begin{array}{l} \mathop {\min }\limits_\alpha \frac{1}{2}\sum\limits_i^m {\sum\limits_j^m {{\alpha _i}{\alpha _j}{y^i}{y^j}K\left( {{x_i},{x_j}} \right) - \sum\limits_{i = 1}^m {{\alpha _i}} } } \\ s.t.\sum\limits_{i = 1}^m {{\alpha _i}{y^i} = 0} \\ 0 \le {\alpha _i} \le C,i = 1, \cdots ,m \end{array}

其中: K ( x i , x j ) = exp ( x i x j 2 2 σ 2 ) K\left( {{x_i},{x_j}} \right) = \exp \left( { - \frac{{{{\left\| {{x_i} - {x_j}} \right\|}^2}}}{{2{\sigma ^2}}}} \right)

非线性SVM有两个参数:正则化参数 C C 和核参数 σ \sigma

2、QGA算法的python代码

## 2. QGA算法
class QGA(object):
###2.1 类初始化
    '''定义QGA'''
    def __init__(self,population_size,chromosome_num,chromosome_length,max_value,min_value,iter_num,deta):
        '''初始化类参数
        population_size(int):种群数
        chromosome_num(int):染色体数,对应需要寻优的参数个数
        chromosome_length(int):染色体长度
        max_value(float):染色体十进制数值最大值
        min_value(float):染色体十进制数值最小值
        iter_num(int):迭代次数
        deta(float):量子旋转角度        
        '''
        self.population_size = population_size
        self.chromosome_num = chromosome_num
        self.chromosome_length = chromosome_length
        self.max_value = max_value
        self.min_value = min_value
        self.iter_num = iter_num
        self.deta = deta 
        
### 2.2 种群的量子形式初始化        
    def species_origin_angle(self):
        '''种群初始化
        input:self(object):QGA类
        output:population_Angle(list):种群的量子角度列表
               population_Angle2(list):空的种群的量子角度列表,用于存储交叉后的量子角度列表
               
        '''
        population_Angle = []
       
        for i in range(self.chromosome_num):
            tmp1 = [] ##存储每个染色体所有取值的量子角度                    
            for j in range(self.population_size): 
                tmp2 = [] ## 存储量子角度                           
                for m in range(self.chromosome_length):
                    a = np.pi * 2 * random.random()
                    tmp2.append(a)
                tmp1.append(tmp2)
            population_Angle.append(tmp1)
        return  population_Angle
    
    def population_Q(self,population_Angle):
        '''将初始化的量子角度序列转换为种群的量子系数列表
        input:self(object):QGApopulation_Angle(list):种群的量子角度列表
        output:population_Q(list):种群的量子系数列表
        '''
        population_Q = []
       
        for i in range(len(population_Angle)):
            tmp1 = [] ##存储每个染色体所有取值的量子系数对                
            for j in range(len(population_Angle[i])): 
                tmp2 = [] ## 存储每个染色体的每个取值的量子对
                tmp3 = [] ## 存储量子对的一半
                tmp4 = [] ## 存储量子对的另一半
                for m in range(len(population_Angle[i][j])):
                    a = population_Angle[i][j][m]
                    tmp3.append(np.sin(a))
                    tmp4.append(np.cos(a))
                tmp2.append(tmp3)
                tmp2.append(tmp4)
                tmp1.append(tmp2)
            population_Q.append(tmp1)
        return population_Q     

### 2.3 计算适应度函数值    
    def translation(self,population_Q):
        '''将种群的量子列表转换为二进制列表
        input:self(object):QGApopulation_Q(list):种群的量子列表
        output:population_Binary:种群的二进制列表
        '''
        population_Binary = []
        for i in range(len(population_Q)):
            tmp1 = [] # 存储每个染色体所有取值的二进制形式
            for j in range(len(population_Q[i])):
                tmp2 = [] ##存储每个染色体每个取值的二进制形式
                for l in range(len(population_Q[i][j][0])):
                    if np.square(population_Q[i][j][0][l]) > random.random():
                        tmp2.append(1)
                    else:
                        tmp2.append(0)
                tmp1.append(tmp2)
            population_Binary.append(tmp1)
        return population_Binary
    
    def fitness(self,population_Binary):
        '''求适应度函数数值列表,本实验采用的适应度函数为RBF_SVM3_fold交叉验证平均值
        input:self(object):QGApopulation_Binary(list):种群的二进制列表
        output:fitness_value(list):适应度函数值类表
               parameters(list):对应寻优参数的列表
        '''
        ##1.染色体的二进制表现形式转换为十进制并设置在[min_value,max_value]之间
        parameters = [] ##存储所有参数的可能取值
        for i in range(len(population_Binary)):
            tmp1 = []  ##存储一个参数的可能取值
            for j in range(len(population_Binary[i])):
                total = 0.0
                for l in range(len(population_Binary[i][j])):
                    total += population_Binary[i][j][l] * math.pow(2,l)  ##计算二进制对应的十进制数值
                value = (total * (self.max_value - self.min_value)) / math.pow(2,len(population_Binary[i][j])) + self.min_value ##将十进制数值坐落在[min_value,max_value]之间
                tmp1.append(value)
            parameters.append(tmp1)
        
        ##2.适应度函数为RBF_SVM3_fold交叉校验平均值
        fitness_value = []
        for l in range(len(parameters[0])):
            rbf_svm = svm.SVC(kernel = 'rbf', C = parameters[0][l], gamma = parameters[1][l])
            cv_scores = cross_validation.cross_val_score(rbf_svm,trainX,trainY,cv =3,scoring = 'accuracy')
            fitness_value.append(cv_scores.mean())
            
        ##3.找到最优的适应度函数值和对应的参数二进制表现形式
        best_fitness = 0.0
        
        best_parameter = []        
        best_parameter_Binary = []
        for j in range(len(population_Binary)):
            tmp2 = []
            best_parameter_Binary.append(tmp2) 
            best_parameter.append(tmp2)
            
        for i in range(len(population_Binary[0])):
            if best_fitness < fitness_value[i]:
                best_fitness = fitness_value[i]
                for j in range(len(population_Binary)):
                    best_parameter_Binary[j] = population_Binary[j][i]
                    best_parameter[j] = parameters[j][i]
        
        return parameters,fitness_value,best_parameter_Binary,best_fitness,best_parameter
        
### 2.4 全干扰交叉
    def crossover(self,population_Angle):
        '''对种群量子角度列表进行全干扰交叉
        input:self(object):QGApopulation_Angle(list):种群的量子角度列表
        '''
        ## 初始化一个空列表,全干扰交叉后的量子角度列表
        population_Angle_crossover = []
       
        for i in range(self.chromosome_num):
            tmp11 = []                    
            for j in range(self.population_size): 
                tmp21 = []                             
                for m in range(self.chromosome_length):
                    tmp21.append(0.0)
                tmp11.append(tmp21)
            population_Angle_crossover.append(tmp11)
        

        for i in range(len(population_Angle)):
            for j in range(len(population_Angle[i])):
                for m in range(len(population_Angle[i][j])):
                    ni = (j - m) % len(population_Angle[i])
                    population_Angle_crossover[i][j][m] = population_Angle[i][ni][m]
        return population_Angle_crossover

### 2.4 变异
    def mutation(self,population_Angle_crossover,population_Angle,best_parameter_Binary,best_fitness):
        '''采用量子门变换矩阵进行量子变异
        input:self(object):QGApopulation_Angle_crossover(list):全干扰交叉后的量子角度列表
        output:population_Angle_mutation(list):变异后的量子角度列表
        '''
        ##1.求出交叉后的适应度函数值列表
        population_Q_crossover = self.population_Q(population_Angle_crossover)  ## 交叉后的种群量子系数列表
        population_Binary_crossover = self.translation(population_Q_crossover)  ## 交叉后的种群二进制数列表
        parameters,fitness_crossover,best_parameter_Binary_crossover,best_fitness_crossover,best_parameter = self.fitness(population_Binary_crossover)           ## 交叉后的适应度函数值列表
        ##2.初始化每一个量子位的旋转角度
        Rotation_Angle = []
        for i in range(len(population_Angle_crossover)):
            tmp1 = []
            for j in range(len(population_Angle_crossover[i])):
                tmp2 = []
                for m in range(len(population_Angle_crossover[i][j])):
                    tmp2.append(0.0)
                tmp1.append(tmp2)
            Rotation_Angle.append(tmp1)
            
        deta = self.deta

        ##3. 求每个量子位的旋转角度
        for i in range(self.chromosome_num):
            for j in range(self.population_size):
                if fitness_crossover[j] <= best_fitness:
                    for m in range(self.chromosome_length):
                        s1 = 0
                        a1 = population_Q_crossover[i][j][0][m]
                        b1 = population_Q_crossover[i][j][1][m]
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 0 and a1 * b1 > 0:
                            s1 = -1
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 0 and a1 * b1 < 0:
                            s1 = 1
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 0 and a1 * b1 == 0:
                            s1 = 1
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 1 and a1 * b1 > 0:
                            s1 = 1
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 1 and a1 * b1 < 0:
                            s1 = -1
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 1 and a1 * b1 == 0:
                            s1 = 1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 0 and a1 * b1 > 0:
                            s1 = -1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 0 and a1 * b1 < 0:
                            s1 = 1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 0 and a1 * b1 == 0:
                            s1 = -1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 1 and a1 * b1 > 0:
                            s1 = 1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 1 and a1 * b1 < 0:
                            s1 = -1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 1 and a1 * b1 == 0:
                            s1 = 1
                        Rotation_Angle[i][j][m] = deta * s1
                else:
                    for m in range(self.chromosome_length):
                        s2 = 0
                        a2 = population_Q_crossover[i][j][0][m]
                        b2 = population_Q_crossover[i][j][1][m]
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 0 and a2 * b2 > 0:
                            s2 = -1
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 0 and a2 * b2 < 0:
                            s2 = 1
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 0 and a2 * b2 == 0:
                            s2 = 1
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 1 and a2 * b2 > 0:
                            s2 = -1
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 1 and a2 * b2 < 0:
                            s2 = 1
                        if population_Binary_crossover[i][j][m] == 0 and best_parameter_Binary[i][m] == 1 and a2 * b2 == 0:
                            s2 = 1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 0 and a2 * b2 > 0:
                            s2 = 1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 0 and a2 * b2 < 0:
                            s2 = -1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 0 and a2 * b2 == 0:
                            s2 = 1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 1 and a2 * b2 > 0:
                            s2 = 1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 1 and a2 * b2 < 0:
                            s2 = -1
                        if population_Binary_crossover[i][j][m] == 1 and best_parameter_Binary[i][m] == 1 and a2 * b2 == 0:
                            s2 = 1
                        Rotation_Angle[i][j][m] = deta * s2
        
        ###4. 根据每个量子位的旋转角度,生成种群新的量子角度列表
        
        for i in range(self.chromosome_num):
            for j in range(self.population_size):
                for m in range(self.chromosome_length):
                    population_Angle[i][j][m] = population_Angle[i][j][m] + Rotation_Angle[i][j][m]
                    
        return population_Angle
                        
### 2.5 画出适应度函数值变化图
    def plot(self,results):
        '''画图
        '''
        X = []
        Y = []
        for i in range(self.iter_num):
            X.append(i + 1)
            Y.append(results[i])
        plt.plot(X,Y)
        plt.xlabel('Number of iteration',size = 15)
        plt.ylabel('Value of CV',size = 15)
        plt.title('QGA_RBF_SVM parameter optimization')
        plt.show()  

### 2.6 主函数
    def main(self):
        results = []
        best_fitness = 0.0
        best_parameter = []
        ## 种群初始化
        population_Angle= self.species_origin_angle()        
        ## 迭代
        for i in range(self.iter_num):
            population_Q = self.population_Q(population_Angle)
            ## 将量子系数转换为二进制形式
            population_Binary = self.translation(population_Q)
            ## 计算本次迭代的适应度函数值列表,最优适应度函数值及对应的参数
            parameters,fitness_value,current_parameter_Binary,current_fitness,current_parameter = self.fitness(population_Binary)
            ## 找出到目前为止最优的适应度函数值和对应的参数
            if current_fitness > best_fitness:
                best_fitness = current_fitness
                best_parameter = current_parameter
            print('iteration is :',i+1,';Best parameters:',best_parameter,';Best fitness',best_fitness)
            results.append(best_fitness)
            
            ## 全干扰交叉
            population_Angle_crossover = self.crossover(population_Angle)
            ## 量子旋转变异
            population_Angle = self.mutation(population_Angle_crossover,population_Angle,current_parameter_Binary,current_fitness)
        ## 结果展示
        results.sort()
        self.plot(results)
        print('Final parameters are :',parameters[-1])

在这里插入图片描述

参考资料

1、杨淑媛, 焦李成, 刘芳. 量子进化算法[J]. 工程数学学报, 2006, 23(2):235-246.
2、YangJunan, LiBin等. RESEARCH OF QUANTUM GENETIC ALGORITH AND ITS APPLICATION IN BLIND SOURCE SEPARATION[J]. 电子科学学刊(英文版), 2003, 20(1):62-68.
3、https://blog.csdn.net/google19890102/article/details/35989959

猜你喜欢

转载自blog.csdn.net/Luqiang_Shi/article/details/84672658