Использование простого усреднения для обучения подкреплению - PullRequest
6 голосов
/ 20 сентября 2019

Я сейчас читаю Обучение усилению: Введение (RL: AI) и пытаюсь воспроизвести первый пример с n-вооруженным бандитом и простым усреднением награды.

Усреднение

new_estimate = current_estimate + 1.0 / step * (reward - current_estimate)

Чтобы воспроизвести график из PDF, я генерирую 2000 бандитских игр и позволяю различным агентам воспроизводить 2000 бандитов за 1000 шагов (как описано в PDF), а затем усреднятьвознаграждение, а также процент оптимальных действий.

В PDF результат выглядит следующим образом:

enter image description here

Однако я не могу воспроизвести это.Если я использую простое усреднение, все агенты с разведкой (epsilon > 0) на самом деле играют хуже, чем агенты без разведки.Это странно, потому что возможность исследования должна позволить агентам чаще выходить из локального оптимума и достигать лучших действий.

Как вы можете видеть ниже, это не относится к моей реализации.Также обратите внимание, что я добавил агентов, которые используют взвешенное усреднение.Это работает, но даже в этом случае повышение epsilon приводит к снижению производительности агентов.

enter image description here

Есть идеи, что не так в моем коде?

Код (MVP)

from abc import ABC
from typing import List

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from multiprocessing.pool import Pool


class Strategy(ABC):

    def update_estimates(self, step: int, estimates: np.ndarray, action: int, reward: float):
        raise NotImplementedError()


class Averaging(Strategy):

    def __str__(self):
        return 'avg'

    def update_estimates(self, step: int, estimates: np.ndarray, action: int, reward: float):
        current = estimates[action]
        return current + 1.0 / step * (reward - current)


class WeightedAveraging(Strategy):

    def __init__(self, alpha):
        self.alpha = alpha

    def __str__(self):
        return 'weighted-avg_alpha=%.2f' % self.alpha

    def update_estimates(self, step: int, estimates: List[float], action: int, reward: float):
        current = estimates[action]
        return current + self.alpha * (reward - current)


class Agent:

    def __init__(self, nb_actions, epsilon, strategy: Strategy):
        self.nb_actions = nb_actions
        self.epsilon = epsilon
        self.estimates = np.zeros(self.nb_actions)
        self.strategy = strategy

    def __str__(self):
        return ','.join(['eps=%.2f' % self.epsilon, str(self.strategy)])

    def get_action(self):
        best_known = np.argmax(self.estimates)
        if np.random.rand() < self.epsilon and len(self.estimates) > 1:
            explore = best_known
            while explore == best_known:
                explore = np.random.randint(0, len(self.estimates))
            return explore
        return best_known

    def update_estimates(self, step, action, reward):
        self.estimates[action] = self.strategy.update_estimates(step, self.estimates, action, reward)

    def reset(self):
        self.estimates = np.zeros(self.nb_actions)


def play_bandit(agent, nb_arms, nb_steps):

    agent.reset()

    bandit_rewards = np.random.normal(0, 1, nb_arms)

    rewards = list()
    optimal_actions = list()

    for step in range(1, nb_steps + 1):

        action = agent.get_action()
        reward = bandit_rewards[action] + np.random.normal(0, 1)
        agent.update_estimates(step, action, reward)

        rewards.append(reward)
        optimal_actions.append(np.argmax(bandit_rewards) == action)

    return pd.DataFrame(dict(
        optimal_actions=optimal_actions,
        rewards=rewards
    ))


def main():
    nb_tasks = 2000
    nb_steps = 1000
    nb_arms = 10

    fig, (ax_rewards, ax_optimal) = plt.subplots(2, 1, sharex='col', figsize=(8, 9))

    pool = Pool()

    agents = [
        Agent(nb_actions=nb_arms, epsilon=0.00, strategy=Averaging()),
        Agent(nb_actions=nb_arms, epsilon=0.01, strategy=Averaging()),
        Agent(nb_actions=nb_arms, epsilon=0.10, strategy=Averaging()),
        Agent(nb_actions=nb_arms, epsilon=0.00, strategy=WeightedAveraging(0.5)),
        Agent(nb_actions=nb_arms, epsilon=0.01, strategy=WeightedAveraging(0.5)),
        Agent(nb_actions=nb_arms, epsilon=0.10, strategy=WeightedAveraging(0.5)),
    ]

    for agent in agents:

        print('Agent: %s' % str(agent))

        args = [(agent, nb_arms, nb_steps) for _ in range(nb_tasks)]
        results = pool.starmap(play_bandit, args)

        df_result = sum(results) / nb_tasks
        df_result.rewards.plot(ax=ax_rewards, label=str(agent))
        df_result.optimal_actions.plot(ax=ax_optimal)

    ax_rewards.set_title('Rewards')
    ax_rewards.set_ylabel('Average reward')
    ax_rewards.legend()
    ax_optimal.set_title('Optimal action')
    ax_optimal.set_ylabel('% optimal action')
    ax_optimal.set_xlabel('steps')
    plt.xlim([0, nb_steps])
    plt.show()


if __name__ == '__main__':
    main()

1 Ответ

4 голосов
/ 20 сентября 2019

В формуле для правила обновления

new_estimate = current_estimate + 1.0 / step * (reward - current_estimate)

параметр step должен быть числом раз, которое был принят конкретный action, а не общим числом шагов моделирования.Таким образом, вам нужно сохранить эту переменную вместе со значениями действия, чтобы использовать ее для обновления.

Это также можно увидеть в поле псевдокода в конце главы 2.4. Инкрементная реализация:

Screenshot

(источник: Ричард С. Саттон и Эндрю Дж. Барто: Обучение усилению - Введение, второе издание, 2018 г., глава 2.4.Осуществление)

...