Я работаю над проектом, в котором я пытаюсь научить машину вождению с помощью Q-learning в Python. Но у меня проблема в том, что кажется, что машина никогда ничего не изучает (даже после 1000000 эпизодов). Поскольку я действительно не могу понять, в чем заключается моя проблема, я публикую большую часть кода (который, я думаю, может иметь отношение квопрос).
На данный момент у меня есть класс Car и класс Game в качестве структуры моего проекта. Игра построена с использованием PyGame и представляет собой сетку с фиксированным размером плиток 16 пикселей. Для более быстрого обучения была создана простая матрица коллизий, чтобы сэкономить время выполнения вместо использования спрайтов. Я также внедрил различные системы вознаграждений, чтобы побудить автомобиль двигаться в определенном направлении, что можно увидеть ниже. (Хлебные крошки и награждение Автомобиля за то, что он не оставался в одном и том же положении в течение длительного времени)
Следует отметить, что движение автомобиля осуществляется таким образом, что оно не привязано к сетке (для более плавного движения). Тем не менее, позиция автомобиля отображается в сетке путем деления позиции на размер сетки.
Скриншот игры можно увидеть ниже: ![The Car Game](https://i.stack.imgur.com/uIRnR.png)
Используемая мной 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](https://i.stack.imgur.com/HIoaK.png)
До сих пор, несмотря на различные системы вознаграждений, я также пытался использовать броски лучей, которые зависят от того, насколько близко машина находится кСтена, вознаграждение за эту итерацию зависит, однако эта реализация, похоже, не имеет никакого значения. Поэтому из-за большого времени вычислений с использованием столкновения спрайтов я больше не использую этот код.
Так что, похоже, что я все перепробовал, и все равно ничего не получилось, я надеялся, чтоМожет быть, кто-нибудь из вас мог видеть, в чем моя проблема.
Заранее спасибо, и я надеюсь, что предоставил достаточно информации о проблеме.
Редактировать POST : Так как этот вопрос был опубликован, было сделано обходное решение. То, что я сделал, чтобы заставить агента работать, как и предполагалось, я изменил с того, чтобы быть «похожим на автомобиль», чтобы стать движением блока. Это заставило агента учиться правильно. Поэтому, если у кого-то еще есть такая же проблема, посмотрите на свое движение или окружающую среду и посмотрите, не слишком ли это сложно.