Автомобиль с самостоятельным вождением не улучшается с Q-Learning - PullRequest
3 голосов
/ 14 октября 2019

Я работаю над проектом, в котором я пытаюсь научить машину вождению с помощью Q-learning в Python. Но у меня проблема в том, что кажется, что машина никогда ничего не изучает (даже после 1000000 эпизодов). Поскольку я действительно не могу понять, в чем заключается моя проблема, я публикую большую часть кода (который, я думаю, может иметь отношение квопрос).

На данный момент у меня есть класс Car и класс Game в качестве структуры моего проекта. Игра построена с использованием PyGame и представляет собой сетку с фиксированным размером плиток 16 пикселей. Для более быстрого обучения была создана простая матрица коллизий, чтобы сэкономить время выполнения вместо использования спрайтов. Я также внедрил различные системы вознаграждений, чтобы побудить автомобиль двигаться в определенном направлении, что можно увидеть ниже. (Хлебные крошки и награждение Автомобиля за то, что он не оставался в одном и том же положении в течение длительного времени)

Следует отметить, что движение автомобиля осуществляется таким образом, что оно не привязано к сетке (для более плавного движения). Тем не менее, позиция автомобиля отображается в сетке путем деления позиции на размер сетки.

Скриншот игры можно увидеть ниже: 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 написана так:

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-learning я пытался изменить все константы на разные значения, чтобы получить лучший результат, однако результаты, похоже, остались прежними, то есть ничего не изучали. За ночь я попытался использовать следующие константы,

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       

, но, как видно на графике ниже, несмотря на то, что с эпилоном, который затухает (почти достигает 0), результат avg никогда не улучшается (даже хуже).

Graph over the Episode Rewards

До сих пор, несмотря на различные системы вознаграждений, я также пытался использовать броски лучей, которые зависят от того, насколько близко машина находится кСтена, вознаграждение за эту итерацию зависит, однако эта реализация, похоже, не имеет никакого значения. Поэтому из-за большого времени вычислений с использованием столкновения спрайтов я больше не использую этот код.

Так что, похоже, что я все перепробовал, и все равно ничего не получилось, я надеялся, чтоМожет быть, кто-нибудь из вас мог видеть, в чем моя проблема.

Заранее спасибо, и я надеюсь, что предоставил достаточно информации о проблеме.

Редактировать POST : Так как этот вопрос был опубликован, было сделано обходное решение. То, что я сделал, чтобы заставить агента работать, как и предполагалось, я изменил с того, чтобы быть «похожим на автомобиль», чтобы стать движением блока. Это заставило агента учиться правильно. Поэтому, если у кого-то еще есть такая же проблема, посмотрите на свое движение или окружающую среду и посмотрите, не слишком ли это сложно.

...