一、分组教学优化算法的实现思路
分组教学优化算法(Group teaching optimization algorithm,GTOA)是2020年由Zhang Yiying等人提出的一种群智能优化算法,该算法借鉴了教师在教学过程中针对不同的学生采用不同的教学方法这一思路,将学生获得的知识信息类比于优化问题的解,学生对应的知识水平类比于适应度值,通过让学生获得更多知识信息来获得更高的适应度值,即最优解。该算法的实现思路主要涉及到学生的分组、教师的选择、教师的教学以及学生的学习这四个阶段,下面将分别从这四个阶段进行介绍(以求解最小化问题为例)。
1、学生的分组阶段
分组教学优化算法通过对学生的知识水平进行排序将整个班级的学生分成了数量相同的两组,排名靠前的一组作为出色组,排名靠后的一组作为潜力组,针对不同组别的学生采用不同的方法进行教学,同时这一分组过程是一个动态的过程,在每次算法迭代过程均会对学生进行重新分组。
2、教师的选择阶段
在进行教师的确定时,需要通过以下计算公式来确定教师的教学水平:
在上式中,教师水平T_t需要通过比较学生中知识水平排名第一学生的知识水平和排名前三学生知识信息平均值的对应水平来获得,知识水平即为优化问题的适应度值,且为最小值优化问题,即知识水平越高则计算得到的适应度值越低。
3、教师的教学阶段
教师在教学过程中针对不同分组的学生采用不同的方式,因此不同分组的学生知识获取方式不同。出色组学生的知识获取方式如下:
其中x为学生知识信息,t表示迭代次数,i表示第i个学生,n表示学生总数,T_t为上一阶段计算的教师水平,M_t为当前平均知识信息,F表示教师教学因子,通常取值为1或2,a、b、c为0到1之间的随机数,其中b+c=1。
潜力组学生的知识获取方式如下:
其中d为0到1之间的随机数。
此外学生在本次教学阶段可能不会获得知识,即知识水平不会提高,那么将保持之前的知识水平,判断公式如下:
4、学生的学习阶段
除了教师教学外,学生还会通过相互之间学习以及自学来获取更多的知识,此学习方式分为两种情况,即当学习的同学知识水平高于自己和低于自己时采用不同的知识获取方式,当学生i向学生j进行学习时,若学生i的适应度值小于或等于学生j,即:
则学生的知识获取方式如下:
其中e和g为0到1之间的随机值,学生j是在学生群体中进行随机选择获得,公式中第二部分为向其他人学习的部分,第三部分为自学部分。
若学生i的适应度值大于学生j,即:
则学生的知识获取方式如下:
此外学生同样本次学习阶段可能不会获得知识,即知识水平不会提高,那么将保持之前的知识水平,判断公式如下:
二、算法步骤
使用分组教学优化算法对优化问题进行求解时的具体步骤可以归纳如下:
- 以学生的知识信息作为待优化问题的解,根据待优化问题的解的范围,随机初始化班级所有学生额知识信息;
- 设置班级人数、教师的教学因子F等参数;
- 根据待优化问题,计算每个学生的适应度值即知识水平,并对其进行排序,通过排序结果将学生分为出色组和潜力组;
- 根据学生的适应度值排序获取前三的学生知识信息,以此计算教师的教学水平T_t;
- 教学对两组学生进行教学,对两组学生知识信息及水平进行更新;
- 两组学生进行相互学习于自学,对两组学生知识信息及水平进行更新;
- 更新班级历史最优的学生知识信息及知识水平;
- 根据迭代的次数重复步骤3到步骤7,当达到最大迭代次数时停止迭代过程,输出历史最优的学生知识信息,该知识信息即为算法优化后获得的最优解。
三、实例
待求解问题:
Rosenbrock’s,取值范围为[-10,10],取值范围内的理想最优解为0,将其搜索的空间维度设为20。
实现源码:
# 库的导入
import numpy as np
import heapq
import matplotlib.pyplot as plt
#待求解问题,求解问题为求最小值
def function(x):
y1 = 0
for i in range(len(x)-1):
y2 = 100*((x[i+1] - x[i]**2)**2)+(x[i]-1)**2
y1 = y1 + y2
y = abs(0 - y1)
return y
m = 30 #学生群体数量
h = 15 #学生群体的一半
imax = 100 #迭代次数
dimen = 20 #解的维度
F = 2 #教师的教学因子
rangelow = -10 #解的最小取值
rangehigh = 10 #解的最大取值
#pop用于存储学生个体的信息,pop_fitness用于存储个体对应的适应度值
pop = np.zeros((m,dimen))
pop_fitness = np.zeros(m)
#betterpop和badpop分别用来存储学生中的杰出个体与潜力个体,均为学生群体的一半
betterpop = np.zeros((h,dimen))
badpop = np.zeros((h,dimen))
#better_fitness和bad_fitness分别用来存储学生中的杰出个体与潜力个体对应的适应度值
better_fitness = np.zeros(h)
bad_fitness = np.zeros(h)
#his_bestfit存储每次迭代时学生群体历史适应度值最优的个体适应度
his_bestfit=np.zeros(imax)
#初始化学生群体并计算每个个体的适应度值,allbestpop,allestfit分别存储群体在历史迭代过程中最优个体解及对应适应度
for j in range(m):
pop[j] = np.random.uniform(low=rangelow, high=rangehigh,size=(1, dimen))
pop_fitness[j] = function(pop[j])
allbestpop,allestfit = pop[pop_fitness.argmin()].copy(),pop_fitness.min()
#开始训练
for i in range(imax):
print("The iteration is:", i + 1)
#计算学生个体知识信息平均值
popave = np.sum(pop)
popave = popave / 30
#获取适应度前三的学生个体知识信息以及他们的平均值,同时计算他们的适应度值
pop_fitness1 = pop_fitness.flatten()
pop_fitness1 = pop_fitness1.tolist()
three = list(map(pop_fitness1.index, heapq.nsmallest(3, pop_fitness1)))
X1 = pop[three[0]]
X2 = pop[three[1]]
X3 = pop[three[2]]
Xave = (X1 + X2 + X3)/3
fitX1 = function(X1)
fitXave = function(Xave)
#通过比较获取当前教师的知识水平
if fitX1 < fitXave:
Xtea = X1
else:
Xtea = Xave
#对学生群体按照适应度值进行划分,halfbetter为杰出个体,halfbad为潜力个体
halfbetter = list(map(pop_fitness1.index, heapq.nsmallest(h, pop_fitness1)))
halfbad = list(map(pop_fitness1.index, heapq.nlargest(h, pop_fitness1)))
#对betterpop、badpop、better_fitness、bad_fitness进行更新
for l in range(h):
betterpop[l] = pop[halfbetter[l]]
badpop[l] = pop[halfbad[l]]
better_fitness[l] = pop_fitness[halfbetter[l]]
bad_fitness[l] = pop_fitness[halfbad[l]]
#旧的学生个体知识信息
betterpopold = betterpop.copy()
badpopold = badpop.copy()
#对学生个体位置信息进行更新
for l in range(h):
a = np.random.rand()
b = np.random.rand()
c = 1 - b
d = np.random.rand()
#对杰出个体进行知识信息更新并计算适应度值
pop_newbetter1 = betterpop[l] + a * (Xtea - F * (b * popave + c * betterpop[l]))
fitness_newbetter1 = function(pop_newbetter1)
#若适应度值降低,则接受新的个体知识信息及适应度值
if fitness_newbetter1 < better_fitness[l]:
better_fitness[l] = fitness_newbetter1
betterpop[l] = pop_newbetter1
#对潜力个体进行知识信息更新并计算适应度值
pop_newbad1 = badpop[l] + 2 * d * (Xtea - badpop[l])
fitness_newbad1 = function(pop_newbad1)
#若适应度值降低,则接受新的个体知识信息及适应度值
if fitness_newbad1 < bad_fitness[l]:
bad_fitness[l] = fitness_newbad1
badpop[l] = pop_newbad1
#进行学生之间知识信息的学习
rangeM = list(range(0, h))
#针对杰出学生和潜力学生同时进行个体知识信息更新
for l in range(h):
e = np.random.rand()
g = np.random.rand()
#随机选择两个个体j1和j2
j1 = np.random.choice(rangeM)
j2 = np.random.choice(rangeM)
#当杰出学生l的适应度值小于个体杰出学生j1时个体知识信息更新
if better_fitness[l] < better_fitness[j1]:
pop_newbetter2 = betterpop[l] + e * (betterpop[l] - betterpop[j1]) + g * (betterpop[l] - betterpopold[l])
#当杰出学生l的适应度值大于或等于个体杰出学生j1时个体知识信息更新
else:
pop_newbetter2 = betterpop[l] - e * (betterpop[l] - betterpop[j1]) + g * (betterpop[l] - betterpopold[l])
fitness_newbetter2 = function(pop_newbetter2)
# 若适应度值降低,则接受新的个体知识信息及适应度值
if fitness_newbetter2 < better_fitness[l]:
better_fitness[l] = fitness_newbetter2
betterpop[l] = pop_newbetter2
#当潜力学生l的适应度值小于潜力杰出学生j2时个体知识信息更新
if bad_fitness[l] < bad_fitness[j2]:
pop_newbad2 = badpop[l] + e * (badpop[l] - badpop[j2]) + g * (badpop[l] - badpopold[l])
#当潜力学生l的适应度值大于或等于潜力杰出学生j2时个体知识信息更新
else:
pop_newbad2 = badpop[l] - e * (badpop[l] - badpop[j2]) + g * (badpop[l] - badpopold[l])
fitness_newbad2 = function(pop_newbad2)
# 若适应度值降低,则接受新的个体知识信息及适应度值
if fitness_newbad2 < bad_fitness[l]:
bad_fitness[l] = fitness_newbad2
badpop[l] = pop_newbad2
#将更新后的杰出学生与潜力学生组合成新的学生群体
for l in range(h):
pop[l] = betterpop[l]
pop[h + l] = badpop[l]
pop_fitness[l] = better_fitness[l]
pop_fitness[h + l] = bad_fitness[l]
#对学生群体在历史迭代过程中最优个体解及对应适应度进行更新
allbestpop, allbestfit = pop[pop_fitness.argmin()].copy(), pop_fitness.min()
#存储当前迭代下的学生群体历史最优适应度值并输出
his_bestfit[i] = allbestfit
print("The best fitness is:", allbestfit)
print("After iteration, the best pop is:", allbestpop)
print("After iteration, the best fitness is:", allbestfit)
#输出训练后学生群体中个体适应度值的均值与标准差
mean = np.sum(pop_fitness)/m
std = np.std(pop_fitness)
print("After iteration, the mean fitness of the swarm is:","%e"%mean)
print("After iteration, the std fitness of the swarm is:","%e"%std)
#将结果进行绘图
fig=plt.figure(figsize=(12, 10), dpi=300)
plt.title('The change of best fitness',fontdict={'weight':'normal','size': 30})
x=range(1,101,1)
plt.plot(x,his_bestfit,color="red",label="GTOA",linewidth=3.0, linestyle="-")
plt.tick_params(labelsize=25)
plt.xlim(0,101)
plt.xlabel("Epoch",fontdict={'weight':'normal','size': 30})
plt.ylabel("Fitness value",fontdict={'weight':'normal','size': 30})
plt.xticks(range(0,101,10))
plt.yscale("log")
plt.legend(loc="upper right",prop={'size':20})
plt.savefig("GTOA.png")
plt.show()
图中横轴为迭代次数,纵轴为最优适应度值。
参考源码