
Занятие №11:
Глубокое Q-обучение

Основные понятия

В некоторых средах количество состояний может быть почти бесконечным.
Проблема


Глубокое Q-обучение
| Состояние-действие | Значение |
|---|---|
| - | 0 |
| - | 0 |
| - | 0 |
| - | 0 |
| - | 0 |
| - | 0 |
| - | 0 |
| - | 0 |
| - | 0 |
Состояние
Действие
Q-значение
Q-таблица
Q-обучение

Состояние
Глубокое Q-обучение
Q-значение действия 1
Q-значение действия 2
Q-значение действия N

Как это работает
Предобработка. Частично наблюдаемый МПП

Начальный кадр

Предварительно обработанный кадр




Предварительно обработанный стек кадров
100 x 100 x 4

Как это работает

Нормализация батчей
+ ReLU


Как это работает


Поворот налево
Шаг влево
Поворот направо
Шаг вправо
Шаг вперед
Шаг назад
Выстрел
Q-значение

Сверточные нейронные сети

Автомобиль
Человек
Видимый слой (исходные пиксели)



Первый скрытый слой (границы)
Второй скрытый слой (углы и контуры)
Третий скрытый слой (части объектов)
Выход (тип объекта)
Животное










Сверточные нейронные сети
| 1 | 0 | -1 |
| 2 | 0 | -2 |
| 1 | 0 | -1 |
| 3 | 0 | -3 |
| 10 | 0 | -10 |
| 3 | 0 | -3 |
Оператор Собеля
Оператор Щарра
| 1 | 0 | -1 |
| 1 | 0 | -1 |
| 1 | 0 | -1 |
| 1 | 1 | 1 |
| 0 | 0 | 0 |
| -1 | -1 | -1 |
Вертикальный фильтр
Горизонтальный фильтр
| 10 | 10 | 10 | 0 | 0 | 0 |
| 10 | 10 | 10 | 0 | 0 | 0 |
| 10 | 10 | 10 | 0 | 0 | 0 |
| 10 | 10 | 10 | 0 | 0 | 0 |
| 10 | 10 | 10 | 0 | 0 | 0 |
| 10 | 10 | 10 | 0 | 0 | 0 |
| 1 | 0 | -1 |
| 2 | 0 | -2 |
| 1 | 0 | -1 |
*
=
| 0 | 30 | 30 | 0 |
| 0 | 30 | 30 | 0 |
| 0 | 30 | 30 | 0 |
| 0 | 30 | 30 | 0 |




Сверточные нейронные сети


Некоторые сложности
-
Забывание прошлого опыта. Использование replay buffer;
-
Корреляция между различными опытами.

Алгоритм глубокого Q-обучения
Новое Q-значение для состояния и действия
Изменение весов
Награда за действие в этом состоянии
Максимально ожидаемая будущая награда с учетом новых и всех возможных действий в этом новом состоянии
Доп картинка для магов
Максимально возможное Q-значение для следующего состояния
Текущее прогнозируемое q-значение
Ошибка TD
Градиент нашего текущего прогнозируемого значения Q
Текущее Q-значение
Код
import tensorflow as tf
from tensorflow.keras.layers import Input, Dense, Conv2D, Flatten, BatchNormalization, ReLU, InputLayer
from tensorflow.keras.optimizers import Adam
from keras.models import Model, load_model
import gym
import numpy as np
from collections import deque
import random
import vizdoomgym
from datetime import datetime
env = gym.make('VizdoomCorridor-v0')
n_outputs = env.action_space.n
observation = env.reset()
plt.imshow(observation)
plt.show()
Код preprocess_frame
def preprocess_frame(frame):
# Добавляем оттенок серого
frame = np.mean(frame,-1)
# Обрезать экран (удалить часть, не содержащую информации)
# [вверх: вниз, влево: вправо]
cropped_frame = frame[15:-5,20:-20]
# Нормализация значений пикселей
normalized_frame = cropped_frame/255.0
# Изменение размера
preprocessed_frame = transform.resize(cropped_frame, [240,320])
return preprocessed_frameКод stack_frames
stack_size = 4 # Складываем 4 кадра
stacked_frames = deque([np.zeros((240,320), dtype=np.int)
for i in range(stack_size)], maxlen=4)
def stack_frames(stacked_frames, state, is_new_episode):
frame = preprocess_frame(state)
if is_new_episode:
# Очистить наш stacked_frames
stacked_frames = deque([np.zeros((240,320), dtype=np.int)
for i in range(stack_size)], maxlen=4)
stacked_frames.append(frame)
stacked_frames.append(frame)
stacked_frames.append(frame)
stacked_frames.append(frame)Код stack_frames
# Сложить кадры
stacked_state = np.stack(stacked_frames, axis=2)
else:
# Добавить кадр в двухстороннюю очередь,
# автоматически удалив самый старый фрейм
stacked_frames.append(frame)
# Создайте сложенное (stacked) состояние
stacked_state = np.stack(stacked_frames, axis=2)
return stacked_state, stacked_framesКод
print(stacked_frames)array([[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]])Гиперпараметры
# Гиперпараметры
state_size = [240,320,4]
action_size = env.action_space.n
learning_rate = 0.00025
total_episodes = 50 # Общее количество эпизодов
max_steps = 50 # Максимально возможное количество шагов в эпизоде
batch_size = 64
explore_start = 1.0 # Вероятность исследования на старте
explore_stop = 0.01 # Минимальная вероятность исследования
decay_rate = 0.0001 # Скорость затухания для исследованийГиперпараметры
# Q learning параметры
gamma = 0.95 # Коэффициент дисконтирования
# Количество опытов, сохраненных в памяти при первой инициализации
pretrain_length = batch_size
# Количество опытов, которые может сохранить память
memory_size = 1000000 Код
possible_actions = np.identity(action_size, dtype=int).tolist()
print(possible_actions)[[1, 0, 0],
[0, 1, 0],
[0, 0, 1]]Код ReplayBuffer
img_array= []
img_array_test= []
class ReplayBuffer:
def __init__(self, capacity=10000):
self.buffer = []
self.buffer_size = capacity
def add(self, state, action, reward, next_state, done):
if len(self.buffer) + 1 >= self.buffer_size:
self.buffer[0:(1+len(self.buffer))-self.buffer_size] = []
self.buffer.append((state, action, reward, next_state, done))Код ReplayBuffer
def sample(self):
buffer_size = len(self.buffer)
index = np.random.choice(np.arange(buffer_size),
size = 32,
replace = False)
return [self.buffer[i] for i in index]
def size(self):
return len(self.buffer)Код
#Веса модели будут сохраняться в папку Models.
Save_Path = 'Models'
if not os.path.exists(Save_Path):
os.makedirs(Save_Path)
path = '{}_DQN'.format("Name")
Model_name = os.path.join(Save_Path, path)Код DQN
class DQN:
def __init__(self, state_dim, aciton_dim):
self.state_dim = state_dim
self.action_dim = aciton_dim
self.epsilon = 1.0
self.model = self.create_model()Код DQN
def create_model(self):
model = tf.keras.Sequential([
Input(shape=(*self.state_dim,), name="inputs"),
Conv2D(filters = 32, kernel_size = [8,8], strides = [4,4], padding = "VALID"),
BatchNormalization(epsilon = 1e-5),
ReLU(),
Flatten(),
Dense(units = 320, activation="relu"),
Dense(units = self.action_dim, activation = None)
])
model.compile(loss='mse', optimizer=RMSprop(learning_rate=0.03))
return modelКод DQN
def predict(self, state):
return self.model.predict(state)
def get_action(self, explore_start, explore_stop, decay_rate, decay_step, state, possible_actions):
state = np.reshape(state, (1, *state.shape))
self.epsilon = explore_stop + (explore_start - explore_stop) * np.exp(-decay_rate * decay_step)
q_value = self.predict(state)
if np.random.random() < self.epsilon:
action = np.random.choice(range(self.action_dim), 1)[0]
return action
action = np.max(q_value)
index = np.where(q_value == action)
return int(index[3])Код DQN
def train(self, states, targets):
self.model.fit(states, targets, epochs=10, verbose=0)
def load(self, Model_name):
self.model = load_model(Model_name + ".h5", compile=True)
def save(self, Model_name):
self.model.save(Model_name + ".h5")
# Не входят в класс DQN
print(env.action_space.n)
print(env.observation_space.shape)
3
(240, 320, 3)
Код Agent
class Agent:
def __init__(self, env):
self.env = env
self.state_dim = [240,320,4]
self.action_dim = self.env.action_space.n
self.model = DQN(self.state_dim, self.action_dim)
self.buffer = ReplayBuffer()Код Agent
def replay(self):
for _ in range(10):
# Выбор значений по индексам
batch = self.buffer.sample()
states = np.array([each[0] for each in batch], ndmin=3)
actions = np.array([each[1] for each in batch])
rewards = np.array([each[2] for each in batch])
next_states = np.array([each[3] for each in batch], ndmin=3)
done = np.array([each[4] for each in batch])
targets = self.model.predict(states)
target_next = self.model.predict(next_states)
Код Agent
for i in range(0, 32):
terminal = done[i]
if terminal:
targets[i] = rewards[i]
else:
targets[i] = rewards[i] + 0.95 * np.max(target_next[i])
self.model.train(states, targets)
Код Agent
def train(self, max_episodes=10, stacked_frames=None):
if os.path.exists("Models/Name_DQN.h5"):
self.model.load("Models/Name_DQN")
print("Модель загружена")
decay_step = 0
for ep in range(max_episodes):
done, total_reward = False, 0
state = self.env.reset()
state, stacked_frames = stack_frames(stacked_frames, state, True)
while not done:
decay_step += 1
action = self.model.get_action(explore_start, explore_stop, decay_rate, decay_step, state, possible_actions)
next_state, reward, done, _ = self.env.step(action)
img_array.append(next_state)
next_state, stacked_frames = stack_frames(stacked_frames, next_state, False)
self.buffer.add(state, action, reward*0.01, next_state, done)
total_reward += reward
state = next_state
if self.buffer.size() >= 32:
self.replay()
self.target_update()
print('Эпизод {}; Награда за эпизод = {}; Эпсилион={} '.format(ep, total_reward, self.model.epsilon))
if ep % 5 == 0:
self.model.save("Models/Name_DQN")Код Agent
def test(self, Model_name, stacked_frames = stacked_frames):
self.model.load(Model_name)
decay_step = 0
for e in range(2):
done, total_reward = False, 0
state = self.env.reset()
state, stacked_frames = stack_frames(stacked_frames, state, True)
while not done:
decay_step += 1
action = self.model.get_action(explore_start, explore_stop, decay_rate, decay_step, state, possible_actions)
next_state, reward, done, _ = self.env.step(action)
img_array_test.append(next_state)
next_state, stacked_frames = stack_frames(stacked_frames, next_state, False)
total_reward += reward
state = next_state
if done:
print("Эпизод: {}/{}, общая награда: {}".format(e, 10, total_reward))
break
self.env.close()Наконец начинаем тренировку
env = gym.make('VizdoomCorridor-v0')
agent = Agent(env)
agent.train(max_episodes=20, stacked_frames = stacked_frames)
#Тест
agent.test("Models/Name_DQN", stacked_frames = stacked_frames)Записываем видео
from random import choice
from google.colab.patches import cv2_imshow
import skvideo.io
out_video = np.empty([len(img_array), 240, 320, 3],
dtype = np.uint8)
out_video = out_video.astype(np.uint8)
for i in range(len(img_array)):
frame = img_array[i]
out_video[i] = frame
skvideo.io.vwrite("doom_corridor.avi", out_video)
Результат



Спасибо за понимание!
Лекция №11. Глубокое Q-обучение
By Protectornaldo
Лекция №11. Глубокое Q-обучение
- 94