Сочетание DQN и алгоритма REINFORCE для обучения агентов
Итак, в моих предыдущих постах мы обсудили следующие концепции обучения с подкреплением.
Формирование многоруких бандитов (МАБ)
Разработка игр с использованием обучения с подкреплением и pygame
Обучение окружения тренажерного зала OpenAI с использованием алгоритма REINFORCE
Сосредоточив внимание на последних двух обсуждениях, 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 эпизодов.
- Для каждого эпизода:
- Сбросить среду
- Смоделируйте эпизод, передав состояние модели Актер-Критик и получив вероятности действий (игнорируйте выходные данные для q-значений)
- Выполнить действие в зависимости от вероятностей и сохранить текущее состояние, действие, награду, следующее состояние и флаг завершения (независимо от того, закончился ли эпизод или нет)
- Установить следующее состояние как текущее состояние
Когда '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 считает, что процент выигрыша в порядке, поэтому агент чему-то научился.
До сих пор мы рассматривали агентов с Дискретным пространством действий. Как насчет пространства для непрерывного действия? Следите за обновлениями