我已经实现了一个使用变异操作符训练的神经网络的遗传算法,如下所示:
def mutation(chromosome, mutation_rate): for gene in chromosome: if random.uniform(0.00, 1.00) <= mutation_rate: gene = random.uniform(-1.00, 1.00)
染色体最初是随机初始化的:
def make_chromosome(chromosome_length): chromosome = [] for _ in range(chromosome_length): chromosome.append(random.uniform(-1.00, 1.00)) return chromosome
在进行交叉操作时,后代染色体的基因只能在区间[-1, 1]
内,因为父代染色体的基因也在这个区间内。当后代发生变异时,它的基因同样会保持在这个区间内。
这种方法在某些问题上有效,但在其他问题上则不然。如果神经元的最优权重在[-1, 1]
内,那么遗传算法就能正常工作,但如果神经元的最优权重在其他区间内呢?
例如,如果我使用反向传播训练网络,并以分类错误率低于5%作为终止条件,我可以查看网络权重,看到类似-1.49
、1.98
、2.01
等值。我的遗传算法永远无法生成这些基因,因为基因是在[-1, 1]
内初始化的,并且交叉和变异操作也无法生成超出该范围的基因。
看起来我需要更好地定义搜索空间,像这样:
# 搜索空间边界S_MIN = -1.00S_MAX = 1.00# 在mutation()中gene = random.uniform(S_MIN, S_MAX)# 在make_chromosome()中chromosome.append(random.uniform(S_MIN, S_MAX))
然后我可以根据问题设置搜索空间的边界。但我如何确定搜索空间呢?这一信息不是先验已知的,而是通过训练网络得出的。但如果训练需要已知的搜索空间,我就会陷入僵局。
我可以将搜索空间设定为任意大的(例如,确保足够大),但这样算法收敛速度会很慢。我至少需要知道搜索空间的大致范围,才能使遗传算法高效运行。
在反向传播中,搜索空间不是先验已知的,这并不重要,但对于遗传算法来说,这确实重要。
回答:
这似乎是对使用神经网络进行强化学习的核心挑战的重申。你有一个损失函数,它以数值方式量化当前解空间局部可能动作的好坏,从而当采取动作时会使你更接近/远离全局最优解(答案)。(即相对于损失函数的梯度)
在开始之前,你无法确切知道答案在哪里,所以你需要定义一个作为算法一部分的探索策略。这将驱动对可能解空间的探索,根据某些动作在接近答案方面的改进程度来指导探索,如损失函数所定义的。
在开始时,探索非常积极,会采取大胆的行动,以便快速探索解空间。然后,当解空间的某些区域显示出更有希望时,探索就会变得不那么大胆,试图收敛到解上。
在你的情况下,探索策略将改变变异大小、变异率和染色体的交叉。变异大小和率将代表在局部内的移动大小,而交叉将代表解空间中的维度转换。
因此,与其设定最大/最小值,你将有一个在解空间中的起始位置,假设解空间的特征是统一缩放和归一化的,一个最佳猜测将是在单位空间中的任何随机点。
然后,探索策略将选择变异大小、率和交叉,初步探索广泛。选择后续代将偏好那些更接近答案且探索策略不那么积极的代。因此,后来的代将倾向于更接近“答案”,并且探索策略也将不那么积极,从而倾向于收敛。
这篇文章对这些概念进行了更正式的审查。