当前位置:   article > 正文

遗传算法实现-- 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)最大

  1. import numpy as np
  2. import random
  3. from matplotlib import cm
  4. #from mpl_toolkits.mplot3d import Axes3D
  5. import matplotlib.pyplot as plt
  6. DNA_SIZE = 10
  7. POP_SIZE = 20
  8. CROSSOVER_RATE = 0.3
  9. MUTATION_RATE = 0.2
  10. N_GENERATIONS = 30
  11. X_BOUND = [0, 10]
  12. k=2 #锦标赛 每次取K个染色体比赛
  13. def fuc(x): #问题函数
  14. return int(x**2+1/x-9*x)
  15. def get_fitness(pop):#对种群每个个体打分
  16. x=decode(pop)
  17. result=[]
  18. for i in range(len(pop)):
  19. pred=fuc(x[i])
  20. result.append(pred)
  21. return result
  22. def find_index(array): #找索引
  23. count=0
  24. for i in array:
  25. count=count+1
  26. if i==1:
  27. break
  28. count_back=count-1
  29. return count_back
  30. def decode(pop): #解码
  31. x=[]
  32. for i in pop[:]:
  33. #i1=i.tolist()
  34. z=find_index(i)+1
  35. x.append(z)
  36. return x
  37. def crossover_and_mutation(pop,CROSSOVER_RATE,MUTATION_RATE):
  38. new_pop = []
  39. for father in pop:
  40. child=[i for i in father]
  41. if np.random.rand() < CROSSOVER_RATE: #交叉:父代交叉取索引之和除以2,相当于是找他们俩中间的数
  42. mother = pop[np.random.randint(POP_SIZE-1)]
  43. new_index=(find_index(father)+find_index(mother))/2
  44. new_index=round(new_index)
  45. child[find_index(child)]=0
  46. child[new_index]=1
  47. if np.random.rand() < MUTATION_RATE: #变异,-1或+1
  48. if np.random.rand()<0.5:
  49. if find_index(father)==0:
  50. child[0]=1
  51. else:
  52. child[find_index(father)-1]=1
  53. child[find_index(father)]=0
  54. else:
  55. if find_index(father)==len(child)-1:
  56. child[-1]=1
  57. elif find_index(father)<len(child)-1:
  58. child[find_index(father)+1]=1
  59. child[find_index(father)]=0
  60. new_pop.append(child)
  61. return new_pop
  62. def select_run(pop, k): #锦标赛,随机取K个数,最大的放进赢家池
  63. seq=[]
  64. win=[]
  65. for j in range(POP_SIZE):
  66. r=random.sample(range(POP_SIZE),k)
  67. for i in list(r):
  68. seq.append( pop[i])
  69. seq_fit=get_fitness(seq)
  70. max_fitness_index = np.argmax(seq_fit)
  71. win.append(seq[max_fitness_index])
  72. seq=[]
  73. return win
  74. def print_info(pop,n):#打印出最优解
  75. na.append(n)
  76. fitness = get_fitness(pop)
  77. max_fitness_index = np.argmax(fitness)
  78. print("epo:", n)
  79. print("max_fitness:", fitness[max_fitness_index])
  80. x = decode(pop)
  81. print("(最优解):", (x[max_fitness_index]))
  82. best.append(x[max_fitness_index])
  83. plt.figure(figsize=(10,8), dpi=80)
  84. plt.plot(na,best)
  85. plt.show()
  86. n=0
  87. na=[]
  88. best=[]
  89. pop=np.random.randint(1, size=(POP_SIZE, DNA_SIZE*10)) #初始化种群
  90. for x in range(POP_SIZE):
  91. r=np.random.randint(0, DNA_SIZE*10)
  92. pop[x][r]=1
  93. for _ in range(N_GENERATIONS):#迭代N代
  94. n=n+1
  95. pop_new=select_run(pop,k) #锦标赛生成新的种群
  96. new_pop=crossover_and_mutation(pop_new,CROSSOVER_RATE,MUTATION_RATE)#交叉变异
  97. print_info(new_pop,n)
  98. 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虽然比较直观,但是如果问题函数的精确度要求比较高,染色体长度会变长,搜索效率会变低,可能不如二进制编码好

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/620718
推荐阅读
相关标签
  

闽ICP备14008679号