这是对如何为游戏创建一个好的评估函数?问题中由@David给出的回答的回应(这是第一个回答)。
背景:我正在使用遗传算法来优化使用极小极大/alpha-beta剪枝(带有迭代加深)的游戏玩家的超参数。特别是,我想使用遗传算法来优化启发式(评估)函数的参数。我使用的评估函数是:
f(w) = w * num_my_moves – (1-w) * num_opponent_moves
唯一需要优化的参数是w,范围在[0,1]之间。
这是我如何编程实现遗传算法的:
- 创建一个随机群体,例如100个智能体
- 让它们随机进行1000场比赛,并进行替换。
- 让父母群体由表现最佳的智能体组成,并混入一些表现较差的智能体以增加遗传多样性。
- 随机选择一些父母进行繁殖以创建子代。*繁殖过程:我们定义子代的权重为其父母权重的平均值。即childWeight = 0.5(father.w+ mother.w)
- 新群体由父母和新创建的子代组成。
- 随机变异群体中1%的个体,如下所示:newWeight = agent.x + random.uniform(-0.01,0.01),并处理边界情况(即小于零和大于一的情况,适当处理)。
- 进化10次(即对新群体重复上述步骤)
我的问题:请评估上述加粗部分。特别是,有人有更好的繁殖方法吗(而不是简单地平均父母的权重),以及有人有更好的变异方法吗(而不是仅仅加上random.uniform(-0.01,0.01))?
回答:
看起来你实际上并没有对你的智能体应用遗传算法,而只是直接对表型/权重进行简单进化。我建议你尝试引入权重的遗传表示,并进化这个基因组。一个例子是将你的权重表示为二进制字符串,并对字符串的每个位进行进化,这意味着每个位都有可能发生变异。这被称为点变异。你可以应用许多其他变异,但这是一个好的开始。
你会注意到你的智能体不会那么容易陷入局部最小值,因为有时一个小的基因变化可以极大地改变表型/权重。
这听起来可能很复杂,实际上并不复杂。让我给你举个例子:
假设你有一个基数为10的权重42
。这在二进制中是101010
。现在你对二进制表示的每个位实施了1%的变异率。假设最后一位被翻转了。那么我们得到的二进制是101011
,或者十进制中的43
。变化不大。另一方面,如果对第二位进行同样的操作,你会得到二进制111010
或十进制58
。注意这个大的跳跃。这正是我们想要的,让你的智能体群体更快地搜索更大的解空间。
关于繁殖。你可以尝试交叉。假设你有许多权重,每个都有遗传编码。如果你将整个基因组(所有二进制数据)表示为一个长的二进制字符串,你可以组合两个父母基因组的部分。再次举个例子。以下是“父亲”和“母亲”的基因组和表型:
Weight Name: W1 W2 W3 W4 W5Father Phenotype: 43 15 34 17 14Father Genome: 101011 001111 100010 010001 001110Mother Genome: 100110 100111 011001 010100 101000Mother Phenotype: 38 39 25 20 40
你可以做的是在两个基因组的相同位置画任意线,并任意分配这些段给子代。这是一种交叉版本。
Weight Name: W1 W2 W3 W4 W5Father Genome: 101011 00.... ...... .....1 001110Mother Genome: ...... ..0111 011001 01010. ......Child Genome: 101011 000111 011001 010101 001110Child Phenotype: 43 7 25 21 14
这里前8位和最后7位来自父亲,中间部分来自母亲。注意W1和W5完全来自父亲,而W3完全来自母亲。而W2和W4是组合的。W4几乎没有变化,而W2变化很大。
我希望这能给你一些关于如何进行遗传算法的见解。话虽如此,我建议使用现代库而不是自己实现,除非你是为了学习。
编辑:更多关于处理权重/二进制表示的内容:
- 如果你需要分数,可以通过将分子和分母作为不同的权重来实现,或者将其中一个作为常数,例如,
42
和10
给出4.2
。) - 大于0的约束是免费的。要得到负数,你需要对权重取负。
- 小于1的约束可以通过将权重除以该位字符串长度的最大可能值来实现。在上面的例子中,你有6位,可以达到最大值63。如果你然后在变异后得到一个二进制字符串
101010
或基数10中的42
,你可以做42/63得到0.667,并且只能达到1.0,当63/63时。 - 两个权重的总和等于1?如果你得到
101010
和001000
分别为W1
和W2
,它给出42和8,然后你可以去W1_scaled = W1 / (W1 + W2) = 0.84
和W2_scaled = W2 / (W1 + W2) = 0.16
。这应该总是给你W1_scaled + W2_scaled = 1
。