Q-Learning无法改善自动驾驶汽车

时间:2019-10-14 12:50:14

标签: python machine-learning pygame artificial-intelligence q-learning

我正在一个项目中,我试图教一辆车如何通过Python中的Q学习进行驾驶。但是我有一个问题,那就是汽车似乎永远都不会学任何东西(即使在1000000集之后) 由于我真的不知道问题出在哪里,因此我发布了大多数代码(我认为可能与问题有关)。

目前,我有一个Car类和一个Game类作为我的项目结构。该游戏是使用PyGame构建的,基本上是 具有固定像素大小为16px的网格。为了更快地学习,我们制作了一个简单的Collison矩阵来节省运行时间,而不是使用Sprite Collions。 我还实施了各种奖励制度,以鼓励汽车向特定方向行驶,如下所示。 (面包屑和奖励汽车长时间不在同一位置)

  

要注意的一件事是,汽车的移动方式是这样实现的,即它不会锁定在网格上(以实现平稳的移动)。但是,通过将汽车位置除以网格尺寸,可以将汽车的位置映射到网格。

该游戏的屏幕截图如下: The Car Game

我使用的q表的大小与此网格相同,每个图块上的可能移动量如下:

for x in range(0, int(GRIDWIDTH)+1):
            for y in range(0, int(GRIDHEIGHT)+1):
                    q_table[(y,x)] = [np.random.uniform(-5, 0) for i in range(NUMBER_OF_ACTIONS)]

在我改变了NUMBER_OF_ACTIONS的情况下,只允许汽车以恒定的速度向前行驶。但同时也要让Car向前迈进。 **值得注意的是,在使用用户输入时,所有这些操作均按预期进行。 Car类中的动作函数是这样写的:

def action(self, choice):
    self.rot_speed = 0
    if choice == 0:
        #self.move(x=1, y=0)
        self.rot_speed = PLAYER_ROT_SPEED
        self.rot = (self.rot + self.rot_speed * self.game.dt)
    elif choice == 1:
        #self.move(x=-1, y=0)
        self.rot_speed = -PLAYER_ROT_SPEED
        self.rot = (self.rot + self.rot_speed * self.game.dt)
    elif choice == 2:
        self.vel = vec(PLAYER_SPEED, 0).rotate(-self.rot + ROTATE_SPRITE_DEG)

    If NUMBER_OF_ACTIONS == 2
    self.vel = vec(PLAYER_SPEED, 0).rotate(-self.rot + ROTATE_SPRITE_DEG)   

    self.pos += self.vel * self.game.dt

Q学习算法如下(不是每个情节都被循环调用)。

def qLearning(self):
#Random Starting Positons on EVERY EPISODE
    StartingPositions = self.playerPositions[np.random.randint(0,len(self.playerPositions))]
    self.player = Player(self, StartingPosition[0] * TILESIZE , StartingPosition[1] * TILESIZE)
    food = self.goal

    episode_reward = 0

#RESET BREADCRUMBS FOR EVERY EPISODE
    for bread in range(len(self.breadCrumb_array)):
        self.wallPositions[self.breadCrumb_array[bread][0]][self.breadCrumb_array[bread][1]] = 3

    self.breadCrumb_array = []
    self.lastPosition = self.player.posToTile
    self.update()
    self.dt = 0.1

    for i in range(ITERATIONS):
        obs = (int(self.player.posToTile.x), int(self.player.posToTile.y))
        if np.random.random() > self.epsilon:
            action = np.argmax(self.q_table[obs])
        else:
            action = np.random.randint(0, NUMBER_OF_ACTIONS)

        self.player.action(action)
        self.update()

        if not LEARNING:
            self.draw()

        if(self.wallPositions[int(self.player.posToTile.x)][int(self.player.posToTile.y)] == 1):
            self.player.hitWall = True
        elif(self.wallPositions[int(self.player.posToTile.x)][int(self.player.posToTile.y)] == 2):
            self.player.hitGoal = True
        elif (self.wallPositions[int(self.player.posToTile.x)][int(self.player.posToTile.y)] == 3):
            self.wallPositions[int(self.player.posToTile.x)][int(self.player.posToTile.y)] = 0
            self.breadCrumb_array.append((int(self.player.posToTile.x),int(self.player.posToTile.y)))
            self.player.hitReward = True


        if self.player.hitWall:
            reward = -DEATH_PENALTY
        elif self.player.hitGoal:
            reward = FOOD_REWARD
        elif self.player.hitReward:
            reward = BREADCRUMB_REWARD
            self.player.hitReward = False
        else:
            reward = -MOVE_PENALTY #+ self.distanceTo((player.pos), food)


        if i % 100 == 0 and not i == 0 and not reward == -DEATH_PENALTY or reward == FOOD_REWARD:
    # Checks how far the distance is between the last position and current.
            distance = self.distanceTo(self.lastPosition, self.player.posToTile)
            self.lastPosition = self.player.posToTile
            if (distance > RADIUS):
                if (distance <= 5):
                    reward += distance
                else:
                    reward += 5


        new_obs = (int(self.player.posToTile.x), int(self.player.posToTile.y))
        max_future_q = np.max(self.q_table[new_obs])

        current_q = self.q_table[obs][action]

        if reward == FOOD_REWARD:
            new_q = FOOD_REWARD
        elif reward == -DEATH_PENALTY:
            new_q = -DEATH_PENALTY
        else:
            new_q = (1 - LEARNING_RATE) * current_q + LEARNING_RATE * (reward + DISCOUNT * max_future_q)

        self.q_table[obs][(action)] = new_q

        episode_reward += reward

        if reward == FOOD_REWARD or reward == -DEATH_PENALTY:
            break

#For plotting later        
self.episode_rewards.append(episode_reward)
self.epsilon *= EPS_DECAY

在运行q学习时,我尝试将所有的常量更改为不同的值以获得更好的结果,但是,结果似乎 保持不变,即不学习任何东西。一整夜,我尝试使用以下常量,

ITERATIONS = 5000
HM_EPISODES = 1000000
MOVE_PENALTY = 1
DEATH_PENALTY = ITERATIONS * 2
FOOD_REWARD = ITERATIONS
RADIUS = 10
BREADCRUMB_REWARD = 300
EPS_DECAY = (1 - 1/(HM_EPISODES))

LEARNING_RATE = 0.8 
DISCOUNT = 0.95    
EPSILON_START = 1       

但是,如下图所示,即使Epilon正在衰减(几乎达到0),平均结果也永远不会变好(甚至更差)。

Graph over the Episode Rewards

到目前为止,尽管有各种奖励系统,但我还尝试使用射线投射,具体取决于汽车与墙壁的距离,该迭代的奖励会受到影响,但是此实现似乎并没有任何区别。因此,由于使用Sprite冲突的计算时间很长,我不再使用该代码。

因此,既然看起来我已经尝试了一切,但无论如何都没有成功,所以我希望也许你们中的任何人都可以看到我的问题所在。

预先感谢您,希望我能提供有关此问题的足够信息。

  

编辑POST :由于发布了此问题,因此已解决。为了使代理程序正常工作,我将机芯的运动方式从“类似于汽车”更改为方块运动。这使代理能够正确学习。所以如果有人   遇到同样的问题,看看您的动作或环境,看看它是否太复杂。

0 个答案:

没有答案