Список работ

Перевернутый маятник

Содержание

Введение

Рассматривается задача стабилизации перевернутого маятника [1], расположенного на подвижной платформе (рис. 1).

CartPole-v1

Рис. 1. Перевернутый маятник

Маятник удерживается в перевернутом состоянии за счет изменения скорости тележки.
Управление маятником можно описать марковским процессом. В таких процессах управляющее действие определяется только текущим состоянием объекта управления и не зависит от предшествующих состояний.
Для таких процессов применим принцип оптимальности Беллмана.
Задача решается с использованием объекта CartPole-v1 библиотеки gym [2].
Для решения задачи необходимо в каждом состоянии s объекта управления правильно определять действие a, оказываемое на тележку с маятником.
Возможны два действия: толкнуть тележку влево и толкнуть вправо.
Задача стабилизации перевернутого маятника решается с помощью агента, взаимодействующего с средой, фиксирующей в том числе и состояние объекта – тележки с перевернутым маятником.
Среда передает агенту состояние объекта управления и награду за действие агента, переведшее объект в текущее состояние. Агент, получив от среды информацию о состоянии объекта и свою награду, определяет следующее действие, которое он считает наиболее правильным в сложившихся обстоятельствах. Сведения о своем решении он передает среде, которая и предпринимает соответствующее действие (рис. 2).

Марковский процесс

Рис. 2. Схема взаимодействия агента со средой

Описание CartPole-v1

CartPole-v1 это модель среды, в которой можно управлять тележкой, по центру которой посредством шарнира прикреплен шест (маятник) (см. рис. 1).
Тележка перемещается по горизонтальному рельсу. Силы трения и сопротивления отсутствуют. Маятник в момент начала симуляции (игры) перевернут.
Цель игры – предотвратить падение маятника. Это достигается за счет изменения скорости тележки. Скорость меняется в результате приложения к тележке горизонтальной силы, равной +1 или -1.
Среда задается состоянием, действием, наградой, начальным состоянием и флагом завершения эпизода.

Состояние описывается следующими величинами:

Действие может принимать два значения – 0 и 1:

Награда на каждом шаге равна 1, включая и последний шаг.

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

Случаи завершения эпизода:

Задача считается решенной, если средняя награда среды в течение 100 последовательных эпизодов не менее 195.
Замечание. Описание CartPole-v0, версии, предшествующей CartPole-v1, приведено в [3].

Обучение агента

В рассматриваемой ниже программе используется метод Q-обучения агента. Метод реализует обучение с подкреплением.
При Q-обучении агент на основе сведений о состоянии s объекта управления и полученного от среды вознаграждения r, за действие a, переведшее объект в следующее состояние, вычисляет значение функции Q(s, a), оценивающей полезность действия a в состоянии s.
Функция Q вычисляется следующим образом:

Q(st, at) := Q(st, at) + α * (rt + 1 + γ * maxa Q(st + 1, at) – Q(st, at)),      (*)

где α – скорость обучения агента;
rt + 1 – награда, полученная от окружающей среды за действие a;
γ – коэффициент уменьшения вознаграждения.
Соотношение (*) выражает принцип оптимальности Беллмана.
Полученное значение Q используется, во-первых, для обучения агента, а во-вторых, для определения следующего действия.
В приводимой ниже реализации полезность Q определяется нейронной сетью (НС) – многослойным перцептроном (по аналогии с [4]).
Обучение агента состоит в подготовке данных для обучения НС и ее последующего обучения по этим данным.
НС имеет следующую структуру:

Layer (type)              Output Shape        Param #
=======================================
dense_1 (Dense)        (None, 24)              120
____________________________________________
dense_2 (Dense)        (None, 24)              600
____________________________________________
dense_3 (Dense)        (None, 2)                  50
=======================================
Total params: 770
Trainable params: 770
Non-trainable params: 0

На вход сети подается состояние объекта (state – массив формы(1, 4)). В качестве цели обучения определяются значения полезности действий 0 и 1 в состоянии state.
Для обучения агента формируется коллекция memory следующих значений:

Обучение агента выполняется по следующему алгоритму:

Выбрать случайным образом из memory коллекцию minibatch из batch_size значений.
Для каждого state, action, reward, state_next, done из minibatch Цикл
    получить по state прогноз НС: q_values = self.model.predict(state);
    определить текущую оценку полезности действия action: Qsa = q_values[0][action];
    получить по state_next прогноз НС: q_values_next = self.model.predict(state_next);
    вычислить по соотношению (*) новую оценку Qsa полезности действия action;
    сформировать цель обучения НС: q_values[0][action] = Qsa;
    провести обучение НС: self.model.fit(state, q_values, epochs = 1, verbose = 0).
КонецЦикла

Замечание. Форма входа НС – (1, 4); форма выхода НС – (1, 2).

Пример:
state = [[-0.006 0.411 -0.073 -0.772]];
q_values = self.model.predict(state) = [[ 0.053 0.528]];
state_next = [[ 0.002 0.217 -0.0889 -0.504]];
q_values_next = self.model.predict(state_next) = [[ 0.039 0.027]];
reward = 1.0;
action = 0;
γ = 0.95;
&alpha = 1.0, поэтому
Qsa = 1.0 + 0.95 * max(0.039, 0.027) = 1.037;
q_values = [[1.037 0.0528]] (после корректировки – цель обучения);
q_values = [[0.07063997 0.04742151]] (после обучения НС; на входе [[-0.006 0.411 -0.073 -0.772]]).

Выбор очередного действия

Задача сети – предсказать по текущему состоянию значения полезности возможных действий: толкнуть тележку влево или вправо.
Сеть получает на входе состояние – массив state формы (1, 4), содержащий позицию тележки, ее скорость, угол отклонения шеста и скорость изменения этого угла.
Выход сети – это массив формы (1, 2):

q_values = self.model.predict(state)

Значение следующего действия, передаваемое окружению, – это индекс (0 или 1) массива q_values[0], отвечающий максимальному элементу массива, то есть действию с наибольшей полезностью:

action = np.argmax(q_values[0])

Общая схема функционирования агента

Работа и обучение агента организована по следующей схеме:

Создать среду: env = gym.make('CartPole-v1')
Создать агента: dqn_agent = DQNAgent(4, 2)
scores = {} // Хранит длительность игры в последних 100 эпизодах. Тип collections.deque
Неудача = Истина
Для Эпизод = 1 По 1000 Цикл // Предельное число эпизодом может быть иным
    Получить начальное состояние объекта: state = env.reset()
    ВремяИгры = 0
    Пока Истина Цикл // Начинаем очередную игру
        ВремяИгры = ВремяИгры + 1
        Определить очередное действие: action = dqn_agent.findAction(state)
        Получить от среды состояние объекта, награду и флаг завершения игры
        после выполнения действия action: state_next, reward, done = env.step(action)
        Если done = Истина Тогда // Игра завершена
            reward = - reward
        КонецЕсли
        Запоминаем предыдущее состояние объекта, действие, награду, текущее состояние и done:
        dqn_agent.remember(state, action, reward, state_next, done)
    КонецЦикла
    Запоминаем время игры: scores.append(ВремяИгры)
    Если Эпизод > 100 Тогда
        Вычислить score_mean – среднее время последних 100 игр
        Если score_mean > 195 Тогда
            Сообщить('Цель достигнута. Средняя продолжительность игры: ', score_mean)
            Неудача = Ложь
            Выйти из цикла
        КонецЕсли
    КонецЕсли
    Выполнить обучение агента
КонецЦикла
Если Неудача = Истина Тогда
    Сообщить('Задача не решена')
КонецЕсли

Реализация

Выполнена на Python.

import gym
import numpy as np
from collections import deque
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import Adam
import sys # Для sys.exit()
import random
#
# Агент с глубоким Q-обучением
class DQNAgent():
    #
    def __init__(self, observation_space, action_space):
        self.state_size = observation_space
        self.action_size = action_space
        self.memory = deque(maxlen = 20000) # Тип collections.deque
        self.alpha = 1.0 # Скорость обучения агента
        self.gamma = 0.95 # Коэффициент уменьшения вознаграждения агента
        # Уровень обучения повышается с коэффициентом exploration_decay
        # Влияет на выбор действия action (0 или 1)
        self.exploration_rate = 1.0
        self.exploration_min = 0.01
        self.exploration_decay = 0.995
        self.learning_rate = 0.001 # Скорость обучения сети
        self.model = self.build_model()
        print(self.model.summary())
    #
    # Создает модель сети
    def build_model(self):
        model = Sequential()
        model.add(Dense(24, input_dim = self.state_size, activation = 'relu'))
        model.add(Dense(24, activation = 'relu'))
        model.add(Dense(2, activation = 'linear'))
        model.compile(loss = 'mse', optimizer = Adam(lr = self.learning_rate))
        return model
    #
    # Запоминаем историю
    def remember(self, state, action, reward, state_next, done):
        self.memory.append((state, action, reward, state_next, done))
    #
    # Определяет и возвращает действие
    def findAction(self, state):
        # Случайный выбор действия - 0 или 1
        if np.random.rand() <= self.exploration_rate: return random.randrange(self.action_size) # или random.randint(0, 1)
        # Выбор действия по состоянию объекта
        q_values = self.model.predict(state)
        return np.argmax(q_values[0]) # Возвращает действие
    #
    def replay(self, batch_size):
        if len(self.memory) < batch_size: return
        # Обучение агента
        # Случайная выборка batch_size элементов для обучения агента
        minibatch = random.sample(self.memory, batch_size)
        for state, action, reward, state_next, done in minibatch:
            # Пример (done = False):
            # state: [[-0.00626842 0.41118423 -0.07340495 -0.77232979]]
            # q_values (до корректировки): [[0.052909 0.05275263]] - numpy.ndarray
            # state_next: [[ 0.00195526 0.21714493 -0.08885155 -0.50361631]]
            # q_values_next: [[0.03970249 0.02732118]]
            # Qsa = 1.0377173654735088
            # reward = 1.0
            # action = 0
            # q_values (после корректировки): [[1.0377173 0.05275263]]
            # q_values (после обучения НС): [[0.07063997 0.04742151]]
            q_values = self.model.predict(state)
            if done:
                Qsa = reward
            else:
                q_values_next = self.model.predict(state_next)[0]
                # Текущая оценка полезности действия action
                Qsa = q_values[0][action]
                # Уточненная оценка полезности действия action
                Qsa = Qsa + self.alpha * (reward + self.gamma * np.amax(q_values_next) - Qsa)
            # Формируем цель обучения сети
            q_values[0][action] = Qsa
            # Обучение сети
            self.model.fit(state, q_values, epochs = 1, verbose = 0)
        if self.exploration_rate > self.exploration_min: self.exploration_rate *= self.exploration_decay
#
if __name__ == "__main__":
    env = gym.make('CartPole-v1') # Создаем среду. Тип: # <TimeLimit<CartPoleEnv<CartPole-v1>>>
    observation_space = env.observation_space.shape[0] # 4
    action_space = env.action_space.n # 2
    # DQN - глубокая Q-нейронная сеть
    dqn_agent = DQNAgent(observation_space, action_space) # Создаем агента
    episodes = 1001 # Число игровых эпизодов + 1
    # scores - хранит длительность игры в последних 100 эпизодах
    # После достижения maxlen новые значения, добавляемые в scores, будут вытеснять прежние
    scores = deque(maxlen = 100) # Тип collections.deque.
    fail = True
    seed = 2
    np.random.seed(seed)
    random.seed(seed)
    env.seed(seed)
    for e in range(episodes):
        # Получаем начальное состояние объекта перед началом каждой игры (каждого эпизода)
        state = env.reset() # Как вариант: state = [0.0364131 -0.02130403 -0.03887796 -0.01044108]
        # state[0] - позиция тележки
        # state[1] - скорость тележки
        # state[2] - угол отклонения шеста от вертикали в радианах
        # state[3] - скорость изменения угла наклона шеста
        state = np.reshape(state, (1, observation_space))
        # Начинаем игру
        # frame - текущий кадр (момент) игры
        # Цель - как можно дольше не допустить падения шеста
        frames = 0
        while True:
            #env.render() # Графическое отображение симуляции
            frames += 1
            action = dqn_agent.findAction(state) # Определяем очередное действие
            # Получаем от среды, в которой выполнено действие action, состояние объекта, награду и значение флага завершения игры
            # В каждый момент игры, пока не наступило одно из условий ее прекращения, награда равна 1
            state_next, reward, done, info = env.step(action)
            state_next = np.reshape(state_next, (1, observation_space))
            reward = reward if not done else -reward
            # Запоминаем предыдущее состояние объекта, действие, награду за это действие, текущее состояние и значение done
            dqn_agent.remember(state, action, reward, state_next, done)
            state = state_next # Обновляем текущее состояние
            # done становится равным True, когда завершается игра, например, отклонение угла превысило допустимое значение
            if done:
                # Печатаем продолжительность игры и покидаем внутренний цикл while
                print("Эпизод: {}/{}, продолжительность игры в кадрах: {}".format(e, episodes - 1, frames))
                break
        scores.append(frames)
        if e > 100:
            score_mean = np.mean(scores)
            if score_mean > 195:
                print('Цель достигнута. Средняя продолжительность игры: ', score_mean)
                fail = False
                break
        # Продолжаем обучать агента
        dqn_agent.replay(24)
    if fail: print('Задача не решена ')

Заключение

Библиотека gym предоставляет среды, в которых можно обучать агентов решать следующие задачи [5]:

Литература

1. Inverted pendulum. [Электронный ресурс] URL: https://en.wikipedia.org/wiki/Inverted_pendulum (дата обращения: 29.10.2018).
2. Getting Started with Gym. [Электронный ресурс] URL: https://gym.openai.com/docs/ (дата обращения: 29.10.2018).
3. CartPole v0. [Электронный ресурс] URL: https://github.com/openai/gym/wiki/CartPole-v0 (дата обращения: 29.10.2018).
4. Cartpole - Introduction to Reinforcement Learning. [Электронный ресурс] URL: https://towardsdatascience.com/cartpole-introduction-to-reinforcement-learning-ed0eb5b58288 (дата обращения: 29.10.2018).
5. Algorithms. [Электронный ресурс] URL: https://gym.openai.com/envs/#algorithmic (дата обращения: 29.10.2018).

Список работ

Рейтинг@Mail.ru