遗传算法实现-- one-hot编码

本文探讨了将one-hot方法用于遗传算法编码的可行性。介绍了遗传算法one-hot版本的编码、交叉、变异、解码算子,给出实现代码及运行结果。指出代码存在运行慢、收敛不稳定等问题,还提到one-hot编码在高精度要求下搜索效率低,后续可借鉴动量梯度下降法优化。

        笔者近两年在实际工作中发现,制造业场景中会有很多问题可以用到优化算法(或启发算法)来解决,遗传算法作为最典型的算法可以解决绝大部分优化问题。遗传算法思想本身并不复杂,但在不同的场景中,其算子可以多种多样。

        今天想跟大家讨论的是编码问题;编码作为遗传算法最开始的算子,其作用非常关键;目前网上大部分资料都是用二进制方法来编码;前段时间我看到一些NLP的文章,了解到one-hot方法来表示特征;能不能用到遗传算法中呢? 因此我做了一个简单的遗传算法-onehot版本,实验了一下是可行的。

        先将算法思想简单陈述一下:

        一、编码:

        加入待求变量X区间为[1:10],DNA_SIZE设为10,即10个基因位;在初始化的时候,随机选取1到10以内任意一个整数作为表征;比如下图中,第五个元素为1,即该染色体代表5

0000100000

        二、交叉:

       父代染色体基因位求平均值,子基因位=(P1的基因位+P2的基因位)/2

        比如下图中,P1基因位是2,P2基因位是9 ;子代基因位=(2+9)/2=5(取整)

        

        直观的表达就是: P1代表整数2,P2代表整数9,子代就是5

        因为此方法编码的方式与传统的二进制编码不同,所以其交叉、变异算子也会不同;为什么交叉要取父代的平均值,因为这样可以大范围搜索解空间

        三、变异

         常规的二进制遗传算法中,变异算子通常是某个基因位变为0或1;本文中的变异算子为:向前或向后移动一位,比如某染色体基因位是8,根据随机数判断是向前还是向后移动一位,如果是向前移动一位,则变成8-1=7,如果是向后移动,则表示8+1=9;这个操作在交叉的基础上可以小范围精确搜索解空间

        四、解码

        解码即直接找到1所在元素的索引

     

其他算子逻辑与常规的算法逻辑相同

下面贴出实现代码:

其中 问题函数为:F(x)=x**2+1/x-9*x;x=[1:10],求F(x)最大值, 肉眼可知x=10时F(x)最大

import numpy as np
import random
from matplotlib import cm
#from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

DNA_SIZE = 10
POP_SIZE = 20
CROSSOVER_RATE = 0.3
MUTATION_RATE = 0.2
N_GENERATIONS = 30
X_BOUND = [0, 10]
k=2 #锦标赛 每次取K个染色体比赛

def fuc(x): #问题函数
   return int(x**2+1/x-9*x)
    
    
def get_fitness(pop):#对种群每个个体打分
    
    x=decode(pop)
    result=[]
    for i in range(len(pop)):
        pred=fuc(x[i])
        result.append(pred)
    
    return result
    
def find_index(array): #找索引
    count=0
    for i in array:
        count=count+1
        if i==1:
            break
    count_back=count-1
    return count_back
            
        
def decode(pop): #解码
    
    x=[]
    
    for i in pop[:]: 
        #i1=i.tolist()
        z=find_index(i)+1
        x.append(z)
    return x


def crossover_and_mutation(pop,CROSSOVER_RATE,MUTATION_RATE):
    new_pop = []
    for father in pop:
        
        
        child=[i for i in father]
        if np.random.rand() < CROSSOVER_RATE:   #交叉:父代交叉取索引之和除以2,相当于是找他们俩中间的数
            mother = pop[np.random.randint(POP_SIZE-1)]
            new_index=(find_index(father)+find_index(mother))/2
            new_index=round(new_index)
            child[find_index(child)]=0
            child[new_index]=1
        if np.random.rand() < MUTATION_RATE: 	#变异,-1或+1
            if np.random.rand()<0.5:
                if find_index(father)==0:
                    child[0]=1
                else:
                    child[find_index(father)-1]=1
                child[find_index(father)]=0
                
            else:
                
                if find_index(father)==len(child)-1:
                    child[-1]=1
                elif find_index(father)<len(child)-1:
                    child[find_index(father)+1]=1
                child[find_index(father)]=0       
        new_pop.append(child)
        
    return new_pop

def select_run(pop, k): #锦标赛,随机取K个数,最大的放进赢家池
    seq=[]
    win=[]
    for j in range(POP_SIZE):
        r=random.sample(range(POP_SIZE),k)
        
        for i in list(r):
            seq.append( pop[i])
        seq_fit=get_fitness(seq)
        
        max_fitness_index = np.argmax(seq_fit)
        win.append(seq[max_fitness_index])
        seq=[]
    return win
            
def print_info(pop,n):#打印出最优解
    
    na.append(n)
    fitness = get_fitness(pop)
    max_fitness_index = np.argmax(fitness)
    
    print("epo:", n)   
    print("max_fitness:", fitness[max_fitness_index])   
    x = decode(pop)
    print("(最优解):", (x[max_fitness_index]))
    best.append(x[max_fitness_index])
    plt.figure(figsize=(10,8), dpi=80)
    plt.plot(na,best)
    plt.show()
  

n=0
na=[]
best=[]
pop=np.random.randint(1, size=(POP_SIZE, DNA_SIZE*10)) #初始化种群
for x in range(POP_SIZE):
    r=np.random.randint(0, DNA_SIZE*10)
    pop[x][r]=1
for _ in range(N_GENERATIONS):#迭代N代    
    n=n+1
    pop_new=select_run(pop,k) #锦标赛生成新的种群
    new_pop=crossover_and_mutation(pop_new,CROSSOVER_RATE,MUTATION_RATE)#交叉变异
    print_info(new_pop,n)
    pop=[i for i in new_pop] #更新种群进入下一次迭代

代码说明:

DNA_SIZE = 10 ;染色体长度为10,但是在代码中,我把长度做了处理(DNA_SIZE*10)相当于是100,虽然解的范围还是【1,10】但是其精确度可以达到0.1,如果长度变为1000,精确度达到0.01

POP_SIZE = 20 ;染色体数量

CROSSOVER_RATE = 0.3 ;交叉概率

MUTATION_RATE = 0.2 ;变异概率

N_GENERATIONS = 30 ;迭代次数

X_BOUND = [0, 10] ;X的取值范围

k=2 ;选择算子使用锦标赛来淘汰不佳的染色体,k表示每次比赛选几个染色体来PK

运行结果:

横轴代表迭代次数;纵轴代表X的值

 第29代即可收敛

总结:

1、代码运行慢,里面有很多重复的变量、for循环,后期改进时可以提高代码质量

2、运行效果,并不是每次都是30代收敛,有的需要50代以上,说明交叉、变异算子还有优化的空间;下一步可以借鉴动量梯度下降法的思想,在迭代初期可以把移动步幅变大,比如每次移动三步,在迭代后期减小移动步幅

3、one-hot虽然比较直观,但是如果问题函数的精确度要求比较高,染色体长度会变长,搜索效率会变低,可能不如二进制编码好

### 使用MATLAB实现遗传算法与BP神经网络结合进行多输出处理 #### 遗传算法简介 遗传算法是一种基于自然选择和遗传学机制的优化方法,通过模拟生物进化过程中的选择、交叉和变异操作来寻找最优解。该算法适用于解决复杂的全局优化问题。 #### BP神经网络概述 反向传播(Backpropagation, BP)神经网络是一种广泛应用的人工神经网络模型,能够学习输入数据到输出数据之间的映射关系。BP网络由多个层次组成,每一层包含若干节点,各层之间存在连接权重。训练过程中调整这些权重使得预测误差最小化[^1]。 #### 结合策略 为了提高BP神经网络性能并克服其局部极小值缺陷,在初始化阶段利用遗传算法随机生成一组初始权值;随后采用GA对BPNN参数进一步寻优。具体流程如下: - 初始化种群规模Np以及最大迭代次数Gmax; - 对于每一代个体i=1:Np, - 构建具有不同结构配置(隐含层数目Lh及其宽度Nh) 的BPNN实例; - 应用标准梯度下降法完成一轮前馈计算得到当前适应度fi; - 执行选择算子选出优秀父辈Psel用于繁殖下一代Offspring; - 进行单点或多点交叉重组产生新后代Crs; - 实施一定概率下的基因突变Mutate形成最终候选方案Evolvedsolns; - 更新精英保留Elitepool并将之加入至下轮循环直至满足终止条件或达到预设世代数。 #### MATLAB代码示例 下面给出了一段简单的Matlab程序片段展示如何集成这两种技术来进行多目标分类任务: ```matlab % 加载样本集 load fisheriris; species = grp2idx(species); % 将类别标签转换成索引形式 X = meas; % 特征矩阵作为输入变量 Y = full(sparse(species)); % one-hot编码后的期望输出 % 定义遗传算法选项 options = gaoptimset('PopulationSize',50,'Generations',30); % 创建自定义适应函数句柄 fitnessfcn = @(netparams)customFitnessFunction(netparams,X,Y); % 调用ga求解器获取最佳网络参数组合 bestParams = ga(fitnessfcn,length(X),[],[],[],[],lb,ub,[],options); function fval = customFitnessFunction(params,x,y) net = patternnet([8 7]); % 设定隐藏层单元数量 setwb(net,params); % 设置网络权重偏置项 yhat = sim(net,x'); % 前向传播获得估计结果 [~,predIdxs] = max(yhat,[],1); % 获取最高响应位置即预测类别编号 errRate = sum(predIdxs ~= find(diag(y')))/size(x,1); fval = errRate; % 返回错误率作为评价指标 end ``` 此脚本实现了Iris花卉数据集上的物种识别实验,其中`patternnet()` 函数创建了一个带有两个隐藏层的标准模式识别型BPNN架构,并调用了内置工具箱提供的 `sim()`, `train()`, 和其他辅助功能简化了开发工作量。而外部包裹着一层遗传搜索框架负责探索更合理的超参空间从而加速收敛速度提升泛化能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值