Прежде чем приступить к изучению обучения с подкреплением, давайте разберемся с его поведением. Существует 4 основных стратегии машинного обучения. Контролируемое, неконтролируемое, полу-контролируемое обучение и обучение с подкреплением. Здесь я не пытаюсь углубляться в эти стратегии. Основное различие обучения с подкреплением и других стратегий обучения состоит в том, что алгоритмы обучения с подкреплением полностью динамичны и он собирает данные из окружающей среды (термин «среда» будет объяснен позже). На следующей диаграмме показано, чем обучение с подкреплением отличается от других стратегий обучения.

Это также хорошая адаптация для прогнозов в реальном времени. Всем, кто любит изучать обучение с подкреплением, я настоятельно рекомендую сначала улучшить свои математические знания (исчисление и линейная алгебра). Итак, давайте перейдем к Multi-Armed Bandit.

В чем проблема многорукого бандита?

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

В казино есть 3 игровых автомата с неизвестным процентом выигрыша. Например что-то вроде 0.2,0.4,0.7. Итак, наша цель - максимизировать наш выигрыш, но перед тем, как приступить к достижению цели, сначала необходимо обсудить проблему. Проблема в том, что можем ли мы найти лучшего бандита (в основном игровой автомат), не тратя больше денег? Теперь мы можем узнать, как изложенные выше основы связаны с нашей проблемой бандитов?

Агент: на самом деле агент - это вы (игрок казино), который принимал решения.

Действие: какое действие предпринял агент. В качестве примера агент может выбрать первый игровой автомат, может быть вторым или может быть третьим. Это полностью зависит от агента и зависит от его интеллекта.

Вознаграждение: вознаграждение - это результат действия агента. Для нашей задачи выигрышный статус каждого игрового автомата можно ввести в качестве награды.

Окружающая среда: поведение игровых автоматов. Мы не знаем, как они действуют.

Временные шаги: сколько раз агент играет с игровыми автоматами.

Как решить проблему с бандитом?

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

Для произвольного действия (а) ожидаемая награда в виде q-звездочки является истинной ценностью действия, но мы не знаем этого значения. Так что нам просто нужно это оценить. Существует несколько способов оценки ценности действия, но здесь мы используем самый простой способ - «Метод среднего выборочного значения».

Метод выборочного среднего

Для временного шага (t) выборочное среднее для действия (a) может быть оценено с использованием приведенного выше уравнения, а 1 (предикат) обозначает случайную величину, которая равна 1, если предикат истинен, и 0, если нет. После расчета Qt (a) для всех действий, в конце временных шагов вы можете выбрать действие, которое максимизирует значения действия, и это действие под названием «Жадное действие», но это не очень рентабельно. поэтому мы улучшаем этот метод, используя правило инкрементного обновления, которое использует предыдущую оценку временного шага для оценки текущей оценки и посмотрите, как она работает.

Новая оценка ← Старая оценка + Step_size (цель - старая оценка)

Это лучшая форма правила инкрементного обновления, и вы наверняка понимаете, как легко вычислить оценку, используя предыдущую оценку.

step_size в основном обозначает использование alpha и 0 ‹alpha‹ 1 и является гиперпараметром и наиболее важным компонентом эффективности обучения нашего агента. Этого всего достаточно, чтобы решить проблему?

НЕТ. Теперь мы должны решить главную проблему: «как научить нашего агента учиться». Давайте вернемся к нашей проблеме. Для удобства назовем наши игровые автоматы A, B, C. Сначала наш агент выбирает случайное действие, потому что агент не имеет представления о вероятности выигрыша игрового автомата. Предположим, наш агент сначала выбрал игровой автомат «А». Если проигрыш агента от этого игрового автомата, агент попробует B или C, если агент выиграл заранее, агент снова выберет тот же игровой автомат (A) во второй раз, но в это время шаги B и C могут дать лучшее вознаграждение, чем A . Так что этот жадный метод не очень хорош для оценки. Поэтому нам нужно сыграть несколько временных шагов со всеми игровыми автоматами. может быть 20 шагов, может быть 50 и это полностью зависит от агента, а также напрямую влияет на вознаграждение агента. Мы не можем играть со всеми игровыми автоматами за много шагов по времени, потому что, не зная, что лучше играть в бандитскую игру, мы тратим деньги (максимизация вознаграждения не удалась бы). Итак, теперь мы остановились на вопросе , , сколько временных шагов нам просто нужно сыграть с игровыми автоматами, чтобы максимизировать нашу награду ? это называется компромисс между разведкой и эксплуатацией

Компромисс между разведкой и эксплуатацией

Исследование: улучшайте знания для получения долгосрочной выгоды. Чтобы принимать более обоснованные решения, это будет доминирующим фактором.

Эксплуатация: улучшайте знания для получения краткосрочной выгоды (жадные действия)

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

  1. Эпсилон-жадный метод

Мы вводим параметр (эпсилон), и на основе эпсилона агент решает, как выглядит действие, будь то исследование или эксплуатация. мы генерируем случайное число под названием «p» между 0 и 1 если p ›epsilon, агент выбирает действие, а если p ≤ epsilon, агент выбирает действие исследования.

epsilon является доминирующим фактором, и в следующей статье я объясню, как выбрать гиперпараметр для улучшения модели.

2. Оптимистичные начальные значения

В исходном состоянии вы должны инициализировать предполагаемые значения действий для всех бандитов, например Q1 (A) = 0,5, Q1 (A) = 1,5, Q1 (A) = 1,0. Эта инициализация также может управлять торговлей. -off мы углубимся в это при реализации алгоритма.

3. Верхняя граница уверенности (UCB)

Вместо вычисления оценочного значения мы просто находим интервал, в котором Q (a) может быть ложью. Из-за неопределенности нашей оценки это было бы лучшим приближением. Когда диапазон интервала уменьшается, мы уверены, что наша оценка соответствует истинному значению.

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

Вы уже знаете, что первый термин представляет эксплуатацию, но как второй термин представляет эксплуатацию. Я знаю, что вы застрянете на нем. Сначала учтите, что мы используем 10000 временных шагов для нашего теста, а «c» - это гиперпараметр, определяемый пользователем. если мы выберем действие (a) 5000 временных шагов из 10000 временных шагов, вы увидите, что агент использует действие (a) и не пробовал много других действий. но если мы выберем действие (а) в 100 раз, значение термина почти в 9 раз больше, чем предыдущее. что легко доказывает, что второй член представляет разведку.

Теперь вы знаете, что вся теория, необходимая для решения проблемы многорукого бандита. Пришло время для самой интересной части и реализации на Python.

Реализация Python

Реализация состоит из нескольких частей. Сначала мы представим 2 класса s.t. класс agent и класс env. Здесь мы просто используем библиотеки numpy и matplotlib для вычислений и визуализации. Сначала просматривается класс env.

class env:
   def __init__(self,q_value):
       self.true_mean = [1.0, 2.0, 3.0]
       self.q_value = q_value
       self.arm_count = [1,1,1] 

В конструкторе env класс имеет 3 основных атрибута. В этом примере мы предполагаем некоторые истинные значения q (true_mean) для выполнения наших вознаграждений. q_value используется для обновления значений q для каждого временного шага с использованием правила инкрементного обновления, и последнее - это arm_count, которое используется для кеширования количества случаев, когда агент тянет каждую руку. (Мы инициализируем arm_count, 1 для каждого бандита, чтобы преодолеть особенность). Затем рассмотрите назначение награды, используя значения true_mean.

def reward(self,action):
      return np.random.randn() + self.true_mean[action]

для каждого игрового автомата мы добавляем внешнюю случайную часть для true_mean из-за случайности игровых автоматов. Для всех бандитов вы можете рассчитать вознаграждение с помощью метода reward класса env.

def update_action_values(self,reward,action):
        self.arm_count[action] += 1
        step_size = 1/self.arm_count[action]
        self.q_value[action] += step_size *
                                (reward -  self.q_value[action])

После выбора любого действия мы увеличиваем arm_count (сколько раз агент потянул руку). Как я уже упоминал, step_size - это значение от 0 до 1. В этом случае мы уменьшаем наш step_size с временными шагами, чтобы замедлить обучение для долгосрочных ожиданий. наконец, обновите значения действия для соответствующего действия, используя предыдущий временной шаг и вознаграждение.

class agent:
    def __init__ (self,N):
        self.arms = 3
        self.N = N # number if experiments do by agent   
        self.visualiza_data = np.empty(self.N)

Теперь просмотрим конструктор класса агента . Для нашего удобства мы используем несколько атрибутов, наиболее важным из которых является visualiza_data, потому что мы используем его для визуализации данных. Далее рассмотрим метод run_experiment.

Эта часть является наиболее важной частью реализации, поскольку она содержит оптимизаторы, которые мы использовали для компромисса между исследованием и эксплуатацией, а именно UCB, optimistic_initial_value. песок эпсилон-жадный.

def run_experiment(self,env,optimizer,epsilon=0,c=0):
       for iteration in range(self.N):
            if optimizer == "ucb" : 
                action = np.argmax([(env.q_value[i] + c * 
                         np.log(self.N)/env.arm_count[i])         
                         for i in range(self.arms)])
            elif elif optimizer in  
                ["epsilon_greedy","optimistic_initial_values"]:
           
                p = np.random.random()
                if epsilon > p :
                    action = np.random.choice(self.arms)
                else:
                    max_action_values = [i for i in range(self.arms)
                           if env.q_value[i] == np.max(env.q_value)]
                    action = np.random.choice(max_action_values)
                reward = env.reward(action)
                env.update_action_values(reward,action)
                self.visualiza_data[iteration] = reward
       cumulative_average =    
       np.cumsum(self.visualiza_data)/(np.arange(self.N) + 1 )
return cumulative_average

В методе run_experiment мы используем два наших основных оптимизатора для реализации значений действий. После вычисления значений Action он передается для расчета вознаграждения. наконец, мы обновляем значения действий в классе env. Затем вычислите кумулятивное среднее значение для визуализации. На этом реализация двух классов завершена. Теперь посмотрим, как агент взаимодействует с окружающей средой в заключительной части нашего кода.

if __name__ == '__main__':
      import argparse
       parser = argparse.ArgumentParser(
                description='Multi Arm Bnandit problem based 
                different stratergies for exploration vs   
                exploitation dilellma')
       parser.add_argument('--optimizer', required = True,
                           metavar='optimizer=ucb',
                           help='ucb or epsilon greedy')
       parser.add_argument('--N', required = False,default = 100000,
                           metavar='N=100000',
                           help='Number of time steps used')
       args = parser.parse_args()
       optimizer = str(args.optimizer)
       N = int(args.N)

Мы используем несколько аргументов для оптимизатора и общего времени шагов. Давайте сравним результаты каждого оптимизатора.

  1. Жадный к эпсилону
# python multi_arm_bandit.py --optimizer=epsilon_greedy --N=10000
if optimizer == "epsilon_greedy":
  q_value = [1.0,2.0,1.5]
  exp_1 = agent(N).run_experiment( env(q_value), optimizer, 0.1)
  exp_2 = agent(N).run_experiment( env(q_value), optimizer, 0.05)
  exp_3 = agent(N).run_experiment( env(q_value), optimizer, 0.01)
  exp_4 = agent(N).run_experiment( env(q_value), optimizer, 0)
  plt.plot(exp_1 , label = "eps = 0.1")
  plt.plot(exp_2 , label = "eps = 0.05")
  plt.plot(exp_3 , label = "eps = 0.01")
  plt.plot(exp_4 , label = "greedy")
  plt.legend()
  plt.xscale('log')
  plt.show()

На этом этапе мы визуализируем данные для уникальных начальных значений q на основе нескольких значений эпсилон. Давайте посмотрим на визуализацию вывода.

2. Верхняя граница уверенности (UCB)

#python multi_arm_bandit.py --optimizer=epsilon_greedy
elif optimizer == "ucb":
  q_value = [1.0,2.0,1.5] 
  exp_0 = agent(N).run_experiment( env(q_value),optimizer, 0.01)
  exp_1 = agent(N).run_experiment( env(q_value),optimizer, 0.15)
  exp_2 = agent(N).run_experiment( env(q_value),optimizer, 0.25)
  plt.plot(exp_0 , label = "c = 0.01")
  plt.plot(exp_1 , label = "c = 0.15")
  plt.plot(exp_2 , label = "c = 0.25")
  plt.legend()
  plt.xscale('log')
  plt.show()

Вы можете видеть, что, когда c увеличивается, конечный результат становится намного лучше из-за увеличения экспозиции.

3. Оптимистичные начальные значения с жадным эпсилон

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

elif optimizer == "optimistic_initial_values":
    q_value1 = [1.0,2.0,1.5]
    q_value2 = [2.0,1.0,3.0]
    exp_1 = agent(N).run_experiment( env(q_value1), optimizer, 0.1 )
    exp_2 = agent(N).run_experiment( env(q_value1), optimizer, 0.01)
    exp_3 = agent(N).run_experiment( env(q_value2), optimizer, 0.1 )
    exp_4 = agent(N).run_experiment( env(q_value2), optimizer, 0.01)
    plt.plot(exp_1 , label = "eps = 0.1 and q_value =   
                             [1.0,2.0,1.5]")
    plt.plot(exp_1 , label = "eps = 0.01 and q_value =   
                             [1.0,2.0,1.5]")
    plt.plot(exp_1 , label = "eps = 0.1 and q_value =   
                             [2.0,1.0,3.0]")
    plt.plot(exp_1 , label = "eps = 0.01 and q_value =   
                             [2.0,1.0,3.0]")
    plt.legend()
    plt.xscale('log')
    plt.show()

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

Поздравляем! Надеюсь, теперь вы лучше знакомы с самыми фундаментальными концепциями обучения с подкреплением. Вот полная реализация проблемы Multi Arm Bandit, и в следующей статье я, вероятно, попытаюсь объяснить настройку гиперпараметров для задач обучения с усилением !!!

Благодарю вас !