def reward(self, state, collision): """ Вычисление награды агента, находящегося в состоянии state. Эту функцию можно (и иногда нужно!) менять, чтобы обучить вашу сеть именно тем вещам, которые вы от неё хотите :param state: текущее состояние агента :param collision: произошло ли столкновение со стеной на прошлом шаге :return reward: награду агента (возможно, отрицательную) """ a = np.sin(angle(-state.position, state.heading)) heading_score = self.HEADING_REWARD * np.tanh(-2 * a) idle_penalty = 0 if abs(state.velocity) > self.MIN_SPEED else -self.IDLENESS_PENALTY speeding_penalty = 0 if abs(state.velocity) < self.MAX_SPEED else -self.SPEEDING_PENALTY * abs(state.velocity) collision_penalty = - max(abs(state.velocity), 0.1) * int(collision) * self.COLLISION_PENALTY dist = np.infty sectors = len(self.map) for j in range(sectors): inner_wall = self.map[j - 1][0], self.map[j][0] outer_wall = self.map[j - 1][1], self.map[j][1] intersect = intersect_ray_with_segment((state.position, state.heading), inner_wall) intersect = abs(intersect - state.position) if intersect is not None else np.infty if intersect < dist: dist = intersect intersect = intersect_ray_with_segment((state.position, state.heading), outer_wall) intersect = abs(intersect - state.position) if intersect is not None else np.infty if intersect < dist: dist = intersect dist_penalty = 0 if collision else - max(abs(state.velocity), 1) * self.COLLISION_PENALTY * np.exp(-dist) return heading_score + collision_penalty \ + idle_penalty + speeding_penalty + dist_penalty
def visualize(self, scale, step): """ Рисует картинку. Этот и все "приватные" (начинающиеся с _) методы необязательны для разбора. """ for i, agent in enumerate(self.agents): state = self.agent_states[agent] collisions = self.collisions[agent] surface = self._agent_surfaces[i] rays_lengths = self.vision_for(agent)[-agent.rays:] self._agent_images[i] = [self._draw_ladar(rays_lengths, state, scale), self._get_agent_image(surface, state, scale)] if len(self.agents) == 1: a = self.agents[0] draw_text("Step: %i" % step, self._info_surface, scale, self.size, text_color=white, bg_color=black, tlpoint=(self._info_surface.get_width() - 100, self._info_surface.get_height() - 30)) draw_text("Reward: %.3f" % a.reward_history[-1], self._info_surface, scale, self.size, text_color=white, bg_color=black) steer, acc = a.chosen_actions_history[-1] state = self.agent_states[a] draw_text("Action: steer.: %.2f, accel: %.2f" % (steer, acc), self._info_surface, scale, self.size, text_color=white, bg_color=black, tlpoint=(self._info_surface.get_width()/2-200, self._info_surface.get_height()/2-50)) draw_text("Inputs: |v|=%.2f, sin(angle): %.2f" % ( abs(state.velocity), np.sin(angle(-state.position, state.heading))), self._info_surface, scale, self.size, text_color=white, bg_color=black, tlpoint=(self._info_surface.get_width()/2-200, self._info_surface.get_height()/2)) draw_text("Circle: %.2f, collisions: %i" % (self.circles[a]/2/pi, collisions), self._info_surface, scale, self.size, text_color=white, bg_color=black, tlpoint=(self._info_surface.get_width() / 2 - 200, self._info_surface.get_height() / 2 + 50))
def evaluate_agent(self, agent, Physics, steps=1000, visual=True, **physics_pars): """ Прогонка цикла мира для конкретного агента (см. пример использования в комментариях после if _name__ == "__main__") :param agent: SimpleCarAgent :param steps: количество итераций цикла :param visual: рисовать картинку или нет :return: среднее значение награды агента за шаг """ agent.evaluate_mode = True self.set_agents([agent]) rewards = [] if visual: scale, screen = self._prepare_visualization() for step in range(steps): self.obs = generate_obstacles(8, self.radii, self.angles, step) self.physics = Physics(self.map, self.obs, **physics_pars) #plot_obstacles(self.map, self.obs, screen) vision = self.vision_for(agent) action = agent.choose_action(vision) next_agent_state, collision = self.physics.move( self.agent_states[agent], action ) if collision: self.collisions[agent] += 1 self.circles[agent] += angle(self.agent_states[agent].position, next_agent_state.position) self.agent_states[agent] = next_agent_state rewards.append(self.reward(next_agent_state, collision)) agent.receive_feedback(rewards[-1]) if visual: self.visualize(scale, step) if self._update_display() == pygame.QUIT: break #sleep(0.05) return self.circles[agent], self.collisions[agent] # np.mean(rewards)
def evaluate_agent(self, agent, steps=1000, visual=True): """ Прогонка цикла мира для конкретного агента (см. пример использования в комментариях после if __name__ == "__main__") :param agent: SimpleCarAgent :param steps: количество итераций цикла :param visual: рисовать картинку или нет :return: среднее значение награды агента за шаг """ agent.evaluate_mode = True self.set_agents([agent]) rewards = [] if visual: scale = self._prepare_visualization() self.steps = steps for step in range(steps): self.step = step vision = self.vision_for(agent) action = agent.choose_action(vision) next_agent_state, collision = self.physics.move( self.agent_states[agent], action ) self.circles[agent] += angle(self.agent_states[agent].position, next_agent_state.position) / (2*pi) self.agent_states[agent] = next_agent_state rewards.append(self.eval_reward(next_agent_state, collision)) agent.receive_feedback(rewards[-1]) if visual: self.visualize(scale) if self._update_display() == pygame.QUIT: break sleep(0.05) return np.mean(rewards)
def eval_reward(self, state, collision): """ Награда "по умолчанию", используется в режиме evaluate Удобно, чтобы не приходилось отменять свои изменения в функции reward для оценки результата """ a = -np.sin(angle(-state.position, state.heading)) heading_reward = 1 if a > 0.1 else a if a > 0 else 0 heading_penalty = a if a <= 0 else 0 idle_penalty = 0 if abs(state.velocity) > self.MIN_SPEED else -self.IDLENESS_PENALTY speeding_penalty = 0 if abs(state.velocity) < self.MAX_SPEED else -self.SPEEDING_PENALTY * abs(state.velocity) collision_penalty = - max(abs(state.velocity), 0.1) * int(collision) * self.COLLISION_PENALTY return heading_reward * self.HEADING_REWARD + heading_penalty * self.WRONG_HEADING_PENALTY + collision_penalty \ + idle_penalty + speeding_penalty
def vision_for(self, agent): """ Строит видение мира для каждого агента :param agent: машинка, из которой мы смотрим :return: список из модуля скорости машинки, направленного угла между направлением машинки и направлением на центр и `agent.rays` до ближайших стен трека (запустите картинку, и станет совсем понятно) """ state = self.agent_states[agent] vision = [abs(state.velocity), np.sin(angle(-state.position, state.heading))] extras = len(vision) delta = pi / (agent.rays - 1) start = rotate(state.heading, - pi / 2) sectors = len(self.map) for i in range(agent.rays): # define ray direction ray = rotate(start, i * delta) # define ray's intersections with walls vision.append(np.infty) for j in range(sectors): inner_wall = self.map[j - 1][0], self.map[j][0] outer_wall = self.map[j - 1][1], self.map[j][1] intersect = intersect_ray_with_segment((state.position, ray), inner_wall) intersect = abs(intersect - state.position) if intersect is not None else np.infty if intersect < vision[-1]: vision[-1] = intersect intersect = intersect_ray_with_segment((state.position, ray), outer_wall) intersect = abs(intersect - state.position) if intersect is not None else np.infty if intersect < vision[-1]: vision[-1] = intersect for obstacle in self.obs: for j in range(len(obstacle)): obstacle_wall = obstacle[j - 1], obstacle[j] intersect = intersect_ray_with_segment((state.position, ray), obstacle_wall) intersect = abs(intersect - state.position) if intersect is not None else np.infty if intersect < vision[-1]: vision[-1] = intersect assert vision[-1] < np.infty, \ "Something went wrong: {}, {}".format(str(state), str(agent.chosen_actions_history[-1])) assert len(vision) == agent.rays + extras, \ "Something went wrong: {}, {}".format(str(state), str(agent.chosen_actions_history[-1])) return vision
def reward(self, state, collision): """ Вычисление награды агента, находящегося в состоянии state. Эту функцию можно (и иногда нужно!) менять, чтобы обучить вашу сеть именно тем вещам, которые вы от неё хотите :param state: текущее состояние агента :param collision: произошло ли столкновение со стеной на прошлом шаге :return reward: награду агента (возможно, отрицательную) """ a = np.sin(angle(-state.position, state.heading)) heading_reward = 1 if a < 0.1 else a if a < 0 else 0 heading_penalty = -a if a >= 0 else 0 idle_penalty = 0 if abs(state.velocity) > self.MIN_SPEED else -self.IDLENESS_PENALTY speeding_penalty = 0 if abs(state.velocity) < self.MAX_SPEED else -self.SPEEDING_PENALTY * abs(state.velocity) collision_penalty = - max(abs(state.velocity), 0.1) * int(collision) * self.COLLISION_PENALTY return heading_reward * self.HEADING_REWARD + heading_penalty * self.WRONG_HEADING_PENALTY + collision_penalty \ + idle_penalty + speeding_penalty
def transition(self): """ Логика основного цикла: подсчёт для каждого агента видения агентом мира, выбор действия агентом, смена состояния и обработка реакции мира на выбранное действие """ for a in self.agents: vision = self.vision_for(a) action = a.choose_action(vision) next_agent_state, collision = self.physics.move( self.agent_states[a], action ) self.circles[a] += angle(self.agent_states[a].position, next_agent_state.position) self.agent_states[a] = next_agent_state a.receive_feedback(self.reward(next_agent_state, collision))
def eval_reward(self, state, collision): """ Награда "по умолчанию", используется в режиме evaluate Удобно, чтобы не приходилось отменять свои изменения в функции reward для оценки результата """ a = -np.sin(angle(-state.position, state.heading)) heading_reward = 1 if a > 0.1 else a if a > 0 else 0 heading_penalty = a if a <= 0 else 0 idle_penalty = 0 if abs( state.velocity) > self.MIN_SPEED else -self.IDLENESS_PENALTY speeding_penalty = 0 if abs( state.velocity) < self.MAX_SPEED else -self.SPEEDING_PENALTY * abs( state.velocity) collision_penalty = -max(abs(state.velocity), 0.1) * int(collision) * self.COLLISION_PENALTY return heading_reward * self.HEADING_REWARD + heading_penalty * self.WRONG_HEADING_PENALTY + collision_penalty \ + idle_penalty + speeding_penalty
def vision_for(self, agent, state=None): """ Строит видение мира для каждого агента :param agent: машинка, из которой мы смотрим :return: список из модуля скорости машинки, направленного угла между направлением машинки и направлением на центр и `agent.rays` до ближайших стен трека (запустите картинку, и станет совсем понятно) :state: Force the position of the agent, otherwise the current position is used """ if state is None: state = self.agent_states[agent] vision = [abs(state.velocity), np.sin(angle(-state.position, state.heading))] extras = len(vision) delta = pi / (agent.rays - 1) start = rotate(state.heading, - pi / 2) sectors = len(self.map) for i in range(agent.rays): # define ray direction ray = rotate(start, i * delta) # define ray's intersections with walls vision.append(np.infty) for j in range(sectors): inner_wall = self.map[j - 1][0], self.map[j][0] outer_wall = self.map[j - 1][1], self.map[j][1] intersect = intersect_ray_with_segment((state.position, ray), inner_wall) intersect = abs(intersect - state.position) if intersect is not None else np.infty if intersect < vision[-1]: vision[-1] = intersect intersect = intersect_ray_with_segment((state.position, ray), outer_wall) intersect = abs(intersect - state.position) if intersect is not None else np.infty if intersect < vision[-1]: vision[-1] = intersect assert vision[-1] < np.infty, \ "Something went wrong: {}, {}".format(str(state), str(agent.chosen_actions_history[-1])) assert len(vision) == agent.rays + extras, \ "Something went wrong: {}, {}".format(str(state), str(agent.chosen_actions_history[-1])) return vision
def transition(self): """ Логика основного цикла: подсчёт для каждого агента видения агентом мира, выбор действия агентом, смена состояния и обработка реакции мира на выбранное действие """ for a in self.agents: vision = self.vision_for(a) action = a.choose_action(vision) next_agent_state, collision = self.physics.move( self.agent_states[a], action) self.circles[a] += angle(self.agent_states[a].position, next_agent_state.position) / (2 * pi) self.agent_states[a] = next_agent_state a.receive_feedback(self.reward(next_agent_state, collision), train_every=self._train_every, epochs=self._epochs, eta=self._eta)
def reward(self, state, collision): """ Вычисление награды агента, находящегося в состоянии state. Эту функцию можно (и иногда нужно!) менять, чтобы обучить вашу сеть именно тем вещам, которые вы от неё хотите :param state: текущее состояние агента :param collision: произошло ли столкновение со стеной на прошлом шаге :return reward: награду агента (возможно, отрицательную) """ a = np.sin(angle(-state.position, state.heading)) heading_reward = 1 if a > 0.1 else a if a > 0 else 0 heading_penalty = a if a <= 0 else 0 idle_penalty = 0 if abs( state.velocity) > self.MIN_SPEED else -self.IDLENESS_PENALTY speeding_penalty = 0 if abs( state.velocity) < self.MAX_SPEED else -self.SPEEDING_PENALTY * abs( state.velocity) collision_penalty = -max(abs(state.velocity), 0.1) * int(collision) * self.COLLISION_PENALTY return heading_reward * self.HEADING_REWARD + heading_penalty * self.WRONG_HEADING_PENALTY + collision_penalty \ + idle_penalty + speeding_penalty
def evaluate_agent(self, agent, Physics, steps=1000, visual=True, **physics_pars): """ Прогонка цикла мира для конкретного агента (см. пример использования в комментариях после if _name__ == "__main__") :param agent: SimpleCarAgent :param steps: количество итераций цикла :param visual: рисовать картинку или нет :return: среднее значение награды агента за шаг """ agent.evaluate_mode = True self.set_agents([agent]) rewards = [] if visual: scale, screen = self._prepare_visualization() for step in range(steps): self.obs = generate_obstacles(8, self.radii, self.angles, step) self.physics = Physics(self.map, self.obs, **physics_pars) #plot_obstacles(self.map, self.obs, screen) vision = self.vision_for(agent) action = agent.choose_action(vision) next_agent_state, collision = self.physics.move( self.agent_states[agent], action) if collision: self.collisions[agent] += 1 self.circles[agent] += angle(self.agent_states[agent].position, next_agent_state.position) self.agent_states[agent] = next_agent_state rewards.append(self.reward(next_agent_state, collision)) agent.receive_feedback(rewards[-1]) if visual: self.visualize(scale, step) if self._update_display() == pygame.QUIT: break #sleep(0.05) return self.circles[agent], self.collisions[agent] # np.mean(rewards)
def visualize(self, scale): """ Рисует картинку. Этот и все "приватные" (начинающиеся с _) методы необязательны для разбора. """ for i, agent in enumerate(self.agents): state = self.agent_states[agent] surface = self._agent_surfaces[i] rays_lengths = self.vision_for(agent)[-agent.rays:] self._agent_images[i] = [ self._draw_ladar(rays_lengths, state, scale), self._get_agent_image(surface, state, scale) ] if len(self.agents) == 1: a = self.agents[0] draw_text("Reward: %.3f" % a.reward_history[-1], self._info_surface, scale, self.size, text_color=white, bg_color=black) steer, acc = a.chosen_actions_history[-1] state = self.agent_states[a] draw_text("Action: steer.: %.2f, accel: %.2f" % (steer, acc), self._info_surface, scale, self.size, text_color=white, bg_color=black, tlpoint=(self._info_surface.get_width() - 500, 10)) draw_text("Inputs: |v|=%.2f, sin(angle): %.2f, circle: %.2f" % (abs(state.velocity), np.sin(angle(-state.position, state.heading)), self.circles[a]), self._info_surface, scale, self.size, text_color=white, bg_color=black, tlpoint=(self._info_surface.get_width() - 500, 50))
def reward(self, previous_state, state, collision, previous_vision, vision): """ Вычисление награды агента, находящегося в состоянии state. Эту функцию можно (и иногда нужно!) менять, чтобы обучить вашу сеть именно тем вещам, которые вы от неё хотите :param state: текущее состояние агента :param collision: произошло ли столкновение со стеной на прошлом шаге :return reward: награду агента (возможно, отрицательную) """ a = -np.sin(angle(-state.position, state.heading)) heading_reward = 1 if a > 0.1 else a if a > 0 else 0 heading_penalty = a if a <= 0 else 0 idle_penalty = 0 if abs(state.velocity) > self.MIN_SPEED else -self.IDLENESS_PENALTY speeding_penalty = 0 if abs(state.velocity) < self.MAX_SPEED else -self.SPEEDING_PENALTY * abs(state.velocity) collision_penalty = - max(abs(state.velocity), 0.1) * int(collision) * self.COLLISION_PENALTY along_longest_ray_reward = 0 towards_longest_ray_reward = 0 # мы должны двигаться вдоль самого длинного луча (самый длинный луч должен быть по середине) # возможно он уже посередине? rays = np.array(vision[2:]) maxIndex = np.argmax(rays) raysNum = rays.size centerRayIndex = raysNum // 2 previous_rays = np.array(previous_vision[2:]) previous_maxIndex = np.argmax(previous_rays) if maxIndex == centerRayIndex & previous_maxIndex == centerRayIndex: along_longest_ray_reward = 1 else: if (abs(centerRayIndex - maxIndex) < abs(centerRayIndex - previous_maxIndex)): towards_longest_ray_reward = 1 # награда за движение вдоль длинного луча только если движение в правильном направлении # награда за движение в сторону длинного луча только если движение в правильном направлении return heading_reward * self.HEADING_REWARD + heading_penalty * self.WRONG_HEADING_PENALTY + collision_penalty \ + idle_penalty + speeding_penalty \ + heading_reward * along_longest_ray_reward * self.ALONG_LONGEST_RAY_REWARD \ + heading_reward * towards_longest_ray_reward * self.TOWARDS_LONGEST_RAY_REWARD \ + 0.5
def transition(self): """ Логика основного цикла: подсчёт для каждого агента видения агентом мира, выбор действия агентом, смена состояния и обработка реакции мира на выбранное действие """ rewards = [] for a in self.agents: vision = self.vision_for(a) action = a.choose_action(vision) next_agent_state, collision = self.physics.move( self.agent_states[a], action) self.circles[a] += angle(self.agent_states[a].position, next_agent_state.position) / (2 * pi) self.agent_states[a] = next_agent_state reward = self.reward(vision, next_agent_state, collision, a.print_logs) rewards += [reward] a.receive_feedback(reward) return rewards
def transition(self): """ Логика основного цикла: подсчёт для каждого агента видения агентом мира, выбор действия агентом, смена состояния и обработка реакции мира на выбранное действие """ for a in self.agents: vision = self.vision_for(a) # s_t action = a.choose_action(vision) # a_t next_agent_state, collision = self.physics.move( self.agent_states[a], action ) self.circles[a] += angle(self.agent_states[a].position, next_agent_state.position) / (2*pi) self.agent_states[a] = next_agent_state reward = self.reward(next_agent_state, collision) # r_t if collision: self.learner.update_final_qvalue(vision, action, reward) raise EpisodeFinished() else: next_vision = self.vision_for(a, next_agent_state) self.learner.update_qvalue(vision, action, reward, a, next_vision)