在这里我之前已经写过了一个遗传算法的模型,核心部分直接用上就好。
我们关心的是神经网络的参数如何转化成个体的基因信息。
我之前尝试的是直接将权重和偏执以实数矩阵的形式作为基因信息来利用,但是发现这样范围太大了,因为遗传算法的进化完全依靠变异,实数的范围太大了,不知要优化多少代才能找到一个可用的神经网络。于是我还是改用了二进制编码的形式,不过是带小数的,至少能做到取到一定的精度。
其中之前的参数就给了一个indv_acc=13,是指12位的二进制小数和以为判断正负的,其中6位是整数部分,6位是小数部分。这样初始化的函数代码很容易写出:
def getfirstgeneration(a,b):
g=np.random.random((a,b))
for i in range(a):
for j in range(b):
if g[i,j]<0.5:
g[i,j]=0
else:
g[i,j]=1
return g
以及用于解码(从二进制小数到实数)的函数:
def bec2real(x):
y=0
for i in range(indv_acc-1):
y+=(2**(i-6))*x[i]
if x[indv_acc-1]==1:
return y
return -1*y
之后就是用可以解码的二进制矩阵确定我们的神经网络参数了。同样我们在之前的参数展示中给出了input_num和hid_num用于自定义神经网络的尺寸。这样对于第一层网络,权重w1的数量应为input_num*hid_num,偏执b1数量应为hid_num,第二层网络,权重w2的数量应为hid_num,偏执b2的数量应为1。所以所有参数的数量为(input_num+2)*hid_num+1,那么用二进制存储的个体基因信息长度应为((input_num+2)*hid_num+1)*indv_acc。这样我们依次读取单位indv_acc长度的基因,转换为实数之后加入进w1,w2,b1,b2的数组中,再将其reshape为相应尺寸的矩阵即可用于神经网络输出的计算了。
以下是nn_layers()的代码:
def sigmod(x):
return 1/(1+np.exp(-1*x))
def nn_layers(Carr,index):
inputlayer=np.array([[Sensor1(Carr)],[Sensor2(Carr)],[Sensor3(Carr)]])
w1=[]
w2=[]
b1=[]
b2=[]
for i in range((input_num+2)*hid_num+1):
temp=bec2real(individuals[index-1,indv_acc*i:indv_acc*(i+1)])
if i<input_num*hid_num:
w1.append(temp)
elif i<(input_num+1)*hid_num:
b1.append(temp)
elif i<(input_num+2)*hid_num:
w2.append(temp)
else:
b2.append(temp)
w1=np.array(w1)
b1=np.array(b1)
w2=np.array(w2)
w1=w1.reshape((hid_num,input_num))
b1=b1.reshape((hid_num,1))
w2=w2.reshape((1,hid_num))
hiddenlayer=sigmod(np.dot(w1,inputlayer)+b1)
outputlayer=sigmod(np.dot(w2,hiddenlayer)+b2)
return outputlayer[0,0]
这样的话,我们直接用遗传算法的代码即可,由于适应度函数确定都为正,因此不需要再对fit_func进行调整。
#应用遗传算法
fit_prob=[]
for i in range(indv_num):
fit_prob.append(sum(fit_func[0:i+1])/sum(fit_func))
parents=np.zeros((2*parents_num,indv_info))
for i in range(2*parents_num):
choose=np.random.random()
if choose<fit_prob[0]:
parents[i,:]=individuals[0,:]
else:
for j in range(indv_num-1):
if choose>fit_prob[j] and choose<fit_prob[j+1]:
parents[i,:]=individuals[j,:]
break;
#储存需要被保留的较强的个体
child=np.zeros((indv_num,indv_info))
nowat=0
stay_num=indv_num-2*parents_num
for i in range(stay_num):
for j in range(indv_num):
if fit_func[j]==max(fit_func):
child[nowat]=individuals[j,:]
fit_func[j]=0
nowat+=1
break
#采用单点交叉法产生子代
for i in range(parents_num):
temptry=np.random.random()
if temptry<cross_prob:
cross_point=int((indv_info-1)*np.random.random()) #小于等于此片段进行交换
child[nowat,0:cross_point]=parents[2*i,0:cross_point]
child[nowat,cross_point:indv_info]=parents[2*i+1,cross_point:indv_info]
nowat+=1
child[nowat,0:cross_point]=parents[2*i+1,0:cross_point]
child[nowat,cross_point:indv_info]=parents[2*i,cross_point:indv_info]
nowat+=1
else:
child[nowat,:]=parents[2*i,:]
nowat+=1
child[nowat,:]=parents[2*i+1,:]
nowat+=1
#考虑子代的变异
for i in range(stay_num,indv_num):
temptry=np.random.random()
if(temptry<mute_prob):
temptry=int(20*np.random.random())+1
for j in range(temptry):
mute_point=int(indv_info*np.random.random())
child[i,mute_point]=1-child[i,mute_point]
#子代形成下一个种群
individuals=child
print('generation:',generation,' best score:',score)
generation+=1
值得一提的是,在这里我们的基因信息长度应为((3+2)*8+1)*13=533,变异时只采取单点的反转对整个个体的影响并不大,因此我加强了变异的力度,在提高变异概率mute_prob的同时,也采取了多点反转的原则,随机取一10以内的非负整数,以此为数量对随机位置的基因反转。但在实际过程中也总会出现小车总是卡在一个地方的情况,可能连着持续一百多代神经网络都不收敛,说明变异的基因太少,或者并不足以找到优化的方案,这也从侧面说明遗传算法对神经网络优化的局限性。
至此,我们的代码已经可以运行并尝试出结果了。下一章将是对结果的讨论以及对优化的尝试与探讨。