需要帮助设计适用于基于NEAT算法的神经网络的健身评估。

14
我正在基于 NEAT算法 的神经网络上工作,学习在Python 2.7中玩一个Atari Breakout克隆版,所有的部分都已经可以工作了,但我认为通过改进物种适应性计算的算法,进化可以大大提高。
神经网络的输入包括:
- 挡板中心的X坐标 - 球的中心X坐标 - 球的中心Y坐标 - 球的dx(X轴速度) - 球的dy(Y轴速度)
输出包括:
- 向左移动挡板 - 向右移动挡板 - 不移动挡板
我可以用以下参数来计算物种适应性:
  • breakout_model.score - int: 物种玩游戏的最终得分
  • breakout_model.num_times_hit_paddle - int: 挡板击中球的次数
  • breakout_model.hits_per_life - int: 以列表形式给出每个生命中挡板击中球的次数;例如,第一个元素是第一条命的值,第二个元素是第二条命的值,依此类推,最多有四个元素
  • breakout_model.avg_paddle_offset_from_ball - decimal: 球和挡板中心在X轴方向上的平均线性距离
  • breakout_model.avg_paddle_offset_from_center - decimal: 挡板中心和框架中心在X轴方向上的平均线性距离
  • breakout_model.time - int: 游戏的总持续时间,以帧为单位
  • breakout_model.stale - boolean: 游戏是否因为停滞不前而被人为终止(例如,球被卡住垂直弹跳且挡板不移动)
如果你认为我需要更多有关游戏最终状态的数据,那么我很可能可以轻松实现一种获取方式。这是我目前的适应度计算方法,我觉得并不是很好:
def calculate_fitness(self):
    self.fitness = self.breakout_model.score
    if self.breakout_model.num_times_hit_paddle != 0:
        self.fitness += self.breakout_model.num_times_hit_paddle / 10
    else:
        self.fitness -= 0.5
    if self.breakout_model.avg_paddle_offset_from_ball != 0:
        self.fitness -= (1 / self.breakout_model.avg_paddle_offset_from_ball) * 100
    for hits in self.breakout_model.hits_per_life:
        if hits == 0:
            self.fitness -= 0.2
    if self.breakout_model.stale:
        self.fitness = 0 - self.fitness
    return self.fitness

以下是我认为健身计算应该在语义上执行的内容:

这是我认为健身计算应该进行的操作:

  • 显然,分数对整体适应度的影响最大。也许分数为0会稍微对适应度产生负面影响?
  • 球拍在每一条命中击打球的次数应该有一定的影响,但不应该是最重要的贡献/权重。例如,如果这个数字为0,在那一条命中它根本没有试图去打球,所以应该有一个负面影响。
  • 球拍总共击打球的次数也应该有一定的影响,并且其贡献应该基于得分。例如,如果它没有多少次击打球,也没有得到很多分数,那么应该有一个显著的负面影响;如果它没有多少次击打球,但得了高分,那么应该有一个显著的正面影响。总的来说,(我认为)这个值越接近游戏得分,这个值对适应度的贡献/权重就越小。
  • 球拍中心和帧中心之间的X方向平均距离应该鼓励球拍保持中心“休息”位置。
  • 如果游戏由于无聊而人为结束,那么这可能会产生显著的负面影响,或者它应该自动强制适应度为0.0;我不确定哪种情况更好。

我不确定如何操作所有这些值,以使它们适当地影响整体适应度。

非常感谢您提供的任何帮助。


1
也许最好的适应度标准是类似以下这样的:(得分/球拍击中次数)- 球拍未击中次数。即,您希望最大化每个球拍击中的得分并最小化球拍未击中的次数。 - Alex
在你可以合理地为游戏定义适应度函数之前,你应该首先定义一下你认为“玩得好”对于这个游戏意味着什么。你想像Alex建议的那样最大化每次球拍击打的得分吗? - Doomed Mind
@Alex,我最初的想法就是这么简单,但它会导致行为看起来像是故意错过球,如果从一个挡板击中超过一个块,它就会卡在这个局部最小值中。我不确定如何帮助它逃脱这个最小值,所以我开始逐渐使健身函数变得更加复杂,希望解决这个问题。 - Mat Jones
鉴于从一个球拍击中多个方块有助于最大化“每个球拍击中的得分”,而“故意错过球”对“每个球拍击中的得分”和最小化球拍失误次数都有负面影响。除非由于某些错误,否则我不太理解您所描述的行为。 - Alex
@Alex 这是一个局部最小值。假设它打破了足够多的方块,使得球能够越过方块墙,然后在方块和天花板之间反弹几次;这种情况下,发生这种情况所需的最少挡板击打次数为3(因为有3行砖块)。然后它发现只击中了球4次并打破了一堆砖块,因此score / (num_paddle_hits)是一个高比率,所以它故意错过了(至少,它会执行一些看起来像是故意错过的行为)。 - Mat Jones
1个回答

2

我建议你在适应度函数中尽量减少条件逻辑的使用,只在需要将适应度分数强制设为0或者有重大惩罚时才使用。你可以决定每个分数组成部分的权重,并相乘得到总分数。负分数只会增加理解适应度函数的复杂性,没有实际好处;模型会从分数之间的相对差异中学习。因此,我的版本的函数看起来会像这样:

def fitness(...):
    if total_hits == 0:
        return 0
    return (game_score/max_score) * .7 \
           + game_score/total_hits * .2 \
           + game_score_per_life/hits_per_life * .1

(顺便说一句:我没有包括“距离框架中心的距离”,因为我认为这是作弊的;如果保持靠近中心是最大化游戏效率的好方法,那么代理应该自己学会这一点。如果将所有智能都隐藏在适应度函数中,那么您的代理根本不具备智能。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接