Сочетание DQN и алгоритма REINFORCE для обучения агентов

Итак, в моих предыдущих постах мы обсудили следующие концепции обучения с подкреплением.

Основы обучения с подкреплением

Формирование многоруких бандитов (МАБ)

Монте-Карло на примере

Обучение временной разнице с помощью SARSA и Q Learning

Разработка игр с использованием обучения с подкреплением и pygame

Контекстные бандиты с кодами

Обучение окружения тренажерного зала OpenAI с использованием алгоритма REINFORCE

DQN для обучения тренажерных залов OpenAI

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

УКРЕПИТЬ

  • Среда должна носить эпизодический характер. Как насчет неэпизодических проблем? Как робототехника или трейдинг, где нет конца и агент постоянно занят
  • Агент получает окончательную награду за каждое действие, совершенное в конце эпизода. Если эпизод очень длинный, вы можете не захотеть ждать так долго, чтобы получить награду за действие 1, которое вы получите после выполнения действия 5829!
  • Использование REINFORCE в средах с редкими наградами (например, MountainCar, где каждая награда равна -1, за исключением того, что когда вы достигаете вершины, вы получаете 0)

Почему? Чем чаще мы обновляем информацию, тем ниже будет дисперсия вознаграждения. Например: если в эпизоде ​​более 5 000 шагов, и если мы обновляем данные после получения финальной награды, если награда была случайной, вы повлияете на вероятность всех действий в эпизодах. Следовательно, для лучшего обучения, особенно в условиях длительных эпизодов, лучше выбрать поэтапное обучение.

DQN

  • Накладные расходы, такие как воспроизведение опыта и DQN Target & Copy, необходимы для стабильного обучения.
  • Нам нужно принять решение о политике отдельно от нейронной сети, чего нельзя сказать о REINFORCE, где работу выполняет одна нейронная сеть.
  • Кроме того, воспроизведение опыта можно использовать, когда система следует марковскому свойству отсутствия памяти, то есть будущее состояние зависит от текущего состояния, а не от какого-либо исторического состояния. Но мы можем получить ситуации, когда это может не соблюдаться, и будущее также зависит от исторических состояний.

Вопрос что лучше? Или мы можем получить лучшее из двух миров, объединив эти стратегии в единый алгоритм?

Актерско-критический метод

Метод Актера-Критика делает именно то, что мы хотим, чтобы использовать полезные функции обоих алгоритмов, образуя гибрид, который может

  • Учитесь постепенно, не дожидаясь окончания всего эпизода.
  • Повтор опыта не требуется.
  • Стабильное обучение

Алгоритм, который мы собираемся обсудить из семейства актеров-критиков, — это метод Advantage Actor-Critic, также известный как

Алгоритм A2C

В AC мы будем тренировать две нейронные сети.

  • Сеть политик, аналогичная алгоритму REINFORCE
  • Сеть со значением состояния, аналогичная DQN

Отсюда и название «Актер-Критик», где «Сеть политики» выступит в роли главного героя, а «Сеть государственных ценностей» — в роли критика. Если вы читали о GAN, эта концепция может показаться немного знакомой, когда у нас есть генератор и дискриминатор, задействованные в состязательной системе.

Функция потерь для алгоритма Policy Gradient обновляется с

убыток = -1 х Σlog(вероятность) х дисконтированная_награда

to

убыток = -1 x Σlog(вероятность) x (вознаграждение+γV(S`)-V(S))

А для DQN остается среднеквадратическая ошибка. отсюда и окончательная потеря:

-1 x Σlog(вероятность)x(вознаграждение+γV(S`)-V(S))+MSE(фактическое_V(S),прогнозируемое_V(S))

где термин (Reward+γV(S`)-V(S)) происходит от сети State-Value Network, которая называется Преимущество отсюда и название Advantage Actor-Critic. Если присмотреться, это следует из уравнения Беллмана, которое мы использовали в DQN и Q-Learning. Итак, что мы на самом деле делаем, так это

  • Делайте прогнозы с помощью нейронных сетей DQN и REINFORCE.
  • Рассчитайте новые потери, поскольку они включают условия для обеих сетей.
  • Обратное распространение в обе нейронные сети для обновления веса.

Еще одна концепция, которую нам нужно знать, прежде чем переходить к коду, т.е.

N шаг обучения

Прежде чем мы перейдем к обучению N Step, нам нужно знать, что такое

Онлайн-обучение: обновление агента после каждого предпринятого действия (DQN без воспроизведения опыта)

Монте-Карло: обновление агента после окончания эпизода (REINFORCE)

N-шаговое обучение — это что-то среднее между двумя, когда мы, когда агент делает «x» шагов, обновляем агента, где «x» — это порог, который мы можем установить, который мы будем применять в методе «актор-критик».

Окружающая среда

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

В блэкджеке есть 2 сущности, крупье и игрок, цель игры – собрать руку со значением как можно ближе к 21, но не превышая его. Игрок представлен агентом, который принимает решения на основе сданных карт и видимой карты дилера. Агент может выполнить одно из следующих двух действий:

Попадание: агент запрашивает у дилера еще одну карту для добавления в свою руку.

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

Дилер следует фиксированному набору правил в среде блэкджека, где он должен бить, пока не достигнет значения руки 17 или более, а затем остается в силе.

В среде блэкджека каждая карта имеет ценность в очках, при этом пронумерованные карты имеют свою номинальную стоимость, лицевые карты имеют ценность 10, а туз имеет ценность 1 или 11, в зависимости от текущей ценности руки агента.

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

Состояние представлено кортежем из 3 элементов (A, B, C), где A = сумма карт у игрока, B = сумма карт у дилера, C = логическое значение, представляющее, есть ли у карт туз.

Если вы никогда не играли в карты, это может быть немного сложно понять.

Давайте начнем тогда

import tensorflow as tf
import numpy as np
import gym
import math
from PIL import Image
import pygame, sys
from pygame.locals import *
from tensorflow import keras
from tensorflow.keras.layers import Input, Dense, concatenate
import math

2. Инициация среды блэкджека

env = gym.make('Blackjack-v1')

input_shape = len(env.observation_space)
num_actions = env.action_space.n

3. Проектирование сети актер-критик

# Define input layer
inputs = Input(shape=(input_shape,))

# Define shared hidden layers
hidden1 = Dense(32, activation='relu')(inputs)
hidden2 = Dense(32, activation='relu')(hidden1)

# Define separate output layers
output1 = Dense(num_actions)(hidden2)
output2 = Dense(num_actions, activation='softmax')(hidden2)

model = tf.keras.Model(inputs=inputs, outputs=[output2, output1])

optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
#model = tf.keras.models.load_model('blackjack')

Как вы могли заметить, эта сеть представляет собой нейронную сеть с несколькими выходами, дающую 2 разных выхода с использованием одной и той же сети, один для Q-значений, а другой для вероятностей действий. Мы могли бы взять две отдельные нейронные сети для Критика и Актера соответственно. Входная форма - это форма пространства состояний.

4. Объявление нескольких констант

num_episodes = 10000
gamma = tf.cast(tf.constant(0.9),tf.float64)
count = 0
n = 5 #keep n very small
min_loss = math.inf

maxlen = n
states = deque(maxlen=maxlen)
actions = deque(maxlen=maxlen)
rewards = deque(maxlen=maxlen)
next_states = deque(maxlen=maxlen)
dones = deque(maxlen=maxlen)

5. Тренировочный цикл

for episode in range(num_episodes):
    # Reset the environment and get the initial state
    state = list(env.reset())
    state[2] = 1 if state[2] else 0

    # Keep track of the states, actions, and rewards for each step in the episode

    # Run the episode
    while True:
        count=count+1
        # Get the action probabilities from the policy network
        action_probs, _ = model.predict(np.array([state]),verbose=0)

        # Choose an action based on the action probabilities
        action = np.random.choice(num_actions, p=action_probs[0])

        # Take the chosen action and observe the next state and reward
        next_state, reward, done, _ = env.step(action)
        next_state = list(next_state)
        next_state[2] = 1 if next_state[2] else 0
        # Store the current state, action, and reward
        states.append(state)
        actions.append(action)
        rewards.append(reward)
        next_states.append(next_state)
        dones.append(1 if done else 0)

        state = next_state

        # End the episode if the environment is done
        if done:
            done=False
            break

    if count>n:
            count=0
            # Convert the lists of states, actions, and discounted rewards to tensors
            states_ = tf.convert_to_tensor(states)
            actions_ = tf.convert_to_tensor(actions)
            rewards_ = tf.convert_to_tensor(rewards)
            next_states_ = tf.convert_to_tensor(next_states)

     

            with tf.GradientTape(persistent=True) as tape:
       
                action_probs,q_values1 = model(states_)
                q_value1 = tf.cast(tf.gather(q_values1,actions_,axis=1,batch_dims=1),tf.float64)
                _,q_value2 = model(next_states_)
                q_value2 = tf.cast(tf.reduce_max(q_value2),tf.float64)
                
                target_value = tf.cast(rewards_,tf.float64) + (tf.cast(1,tf.float64)-tf.cast(dones,tf.float64))*gamma*q_value2
                value_loss = tf.reduce_mean(tf.math.pow(target_value-q_value1,2))
                advantage = target_value-q_value1
      
                action_probs = tf.cast(tf.math.log(tf.gather(action_probs,actions,axis=1,batch_dims=1)),tf.float64)
                loss = -tf.reduce_mean(action_probs * advantage) + value_loss


            grads = tape.gradient(loss, model.trainable_variables)
            optimizer.apply_gradients(zip(grads, model.trainable_variables))

    
            print('Episode {} done with loss {} !!!!!!'.format(episode, loss))
            if loss<min_loss:
                min_loss = loss
                model.save('blackjack/')
  • Цикл начинается с обучения 5000 эпизодов.
  • Для каждого эпизода:
  1. Сбросить среду
  2. Смоделируйте эпизод, передав состояние модели Актер-Критик и получив вероятности действий (игнорируйте выходные данные для q-значений)
  3. Выполнить действие в зависимости от вероятностей и сохранить текущее состояние, действие, награду, следующее состояние и флаг завершения (независимо от того, закончился ли эпизод или нет)
  4. Установить следующее состояние как текущее состояние

Когда 'N' образцов объединены, то использование GradientTape

  • Повторно предскажите action_probabilites и q-значения, используя модель для сохраненных состояний для текущего тензора состояния
  • Получите прогнозы для значений q следующего состояния и выберите максимальное значение
  • Рассчитайте target_q_value и срок преимущества, как обсуждалось
  • Рассчитайте потери как для Актера (A Policy Network), так и для Критика (DQN) и добавьте
  • Применение градиентов

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

#pygame essentials
pygame.init()
DISPLAYSURF = pygame.display.set_mode((500,500),0,32)
clock = pygame.time.Clock()
pygame.display.flip()

#openai gym env
env = gym.make('Blackjack-v1')
input_shape = len(env.observation_space)
num_actions = env.action_space.n
state = list(env.reset())
state[2] = 1 if state[2] else 0

done = False
count=0
done=False
steps = 0
#loading trained model
model = tf.keras.models.load_model('blackjack/')
total_wins =0
episodes = 0


def print_summary(text,cood,size):
        font = pygame.font.Font(pygame.font.get_default_font(), size)
        text_surface = font.render(text, True, (0,0,0))
        DISPLAYSURF.blit(text_surface,cood)
     
while episodes<1000 :
    pygame.event.get()

    
    for event in pygame.event.get():
                if event.type==QUIT:
                                pygame.quit()
                                raise Exception('training ended')

    action_probs,_ = model.predict(np.array([state]))
    action = np.argmax(action_probs[0])
    next_state, reward, done, info = env.step(action) # take a step in the environment
    image = env.render(mode='rgb_array') # render the environment to the screen
   
    #convert image to pygame surface object
    image = Image.fromarray(image,'RGB')
    mode,size,data = image.mode,image.size,image.tobytes()
    image = pygame.image.fromstring(data, size, mode)

    DISPLAYSURF.blit(image,(0,0))
    pygame.display.update()
    clock.tick(100)
    if done:
        print_summary('Agent {} game'.format('won' if reward>0 else 'lost'),(50,50),20)
        if reward>0:
            total_wins+=1
        
        state = list(env.reset())
        state[2] = 1 if state[2] else 0
        
        pygame.display.update()
        pygame.time.delay(10)
        episodes+=1
    pygame.time.delay(10)
    next_state=list(next_state)
    next_state[2] = 1 if state[2] else 0
    state = next_state

pygame.quit()

Для понимания этого кода поможет это видео

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

До сих пор мы рассматривали агентов с Дискретным пространством действий. Как насчет пространства для непрерывного действия? Следите за обновлениями