深度学习之强化学习(1)强化学习案例

深度学习之强化学习(1)强化学习案例

人工智能=深度学习+强化学习——David Silver

强化学习是继监督学习和非监督学习之后,机器学习领域的又一个研究分支。它主要使用智能体与环境进行交互,从而学习能够取得良好效果的策略。与监督学习不同的是,强化学习的动作并不清楚地标示信息,只有来自环境反馈的奖励信息,通常具有一定的滞后性,用来反映动作的好坏。

[En]

Reinforcement learning is another research branch in the field of machine learning in addition to supervised learning and unsupervised learning. it mainly uses agents to interact with the environment so as to learn strategies that can achieve good results. Different from supervised learning, the action of reinforcement learning does not clearly mark the information, only the reward information from the feedback from the environment, which usually has a certain lag and is used to reflect the “good and bad” of the action.

随着深度学习神经网络的兴起,强化学习这一领域也获得了蓬勃的发展。2015年,英国DeepMind公司提出了基于深度神经网络的强化学习DQN,在太空入侵者、打砖块、乒乓球等49个Atari游戏中取得了与人类相当的游戏水平;2017年,DeepMind提出的AlphaGo程序以3:0的比分战胜当时围棋世界排名第一的选手柯洁;同年,AlphaGo的新版本AlphaGo Zero在无任何人类知识的条件下,通过自我博弈训练的方式以100:0战胜了AlphaGo;2019年,OpenAI Five程序以2:0战胜Dota2世界冠军OG队伍,尽管这次比赛的游戏规则有所限制,但是对于Dota2这种对于需要超强个体只能水平和良好团队协作的游戏,这次胜利无疑再次坚定了人类对于AGI的信念。

本章我们将介绍强化学习中的主流算法,其中包含在太空入侵者等游戏上取得类人水平的DQN算法、制胜Dota2的主要功臣PPO算法等。

强化学习案例

强化学习算法的设计不同于传统的监督学习,包括大量新的数学公式的推导。在进入强化学习算法的学习过程之前,我们首先通过一个简单的例子来感受强化学习算法的魅力。

[En]

The design of reinforcement learning algorithm is different from the traditional supervised learning, including a large number of new mathematical formula derivation. Before entering the learning process of reinforcement learning algorithm, we first feel the charm of reinforcement learning algorithm through a simple example.

这一部分不需要掌握每一个细节,主要是直观的感觉,就可以得到第一印象。

[En]

This section does not need to master every detail, mainly intuitive feeling, to get the first impression.

1. 平衡杆游戏

平衡杆游戏系统由三个物体组成:滑轨、汽车和杆子。如图1所示,小车可以在滑轨上自由移动,拉杆的一侧通过轴承固定在小车上。在初始状态下,车厢位于滑轨的中间,杆子直立在车厢上,代理人通过控制车厢的左右运动来控制杆子的平衡。当杆子与垂直方向的夹角大于一定角度或小车偏离滑轨中心一定距离时,则认为比赛结束。博弈时间越长,给予的奖励越多,代理人的控制水平越高。

[En]

The balance bar game system consists of three objects: the slide rail, the car and the rod. As shown in figure 1, the car can move freely on the slide rail, and one side of the rod is fixed on the car through the bearing. In the initial state, the car is located in the middle of the slide rail, the rod is upright on the car, and the agent controls the balance of the rod by controlling the left and right movement of the car. when the angle between the rod and the vertical direction is greater than a certain angle or the trolley deviates from the center of the slide rail at a certain distance, the game is considered to be over. The longer the game, the more rewards the game gives, and the higher the level of control of the agent.

深度学习之强化学习(1)强化学习案例

为了简化环境状态的表示,我们这里直接取高层的环境特征向量s作为智能体的输入,它一共包含了四个高层特征,分别为:小车位置、小车速度、杆角度和杆的速度。智能体的输出动作a a a为向左移动或者向右移动,动作施加在平衡杆系统上会产生一个新的状态,同时系统也会返回一个奖励值,这个奖励值可以简单的记为1,即时长加1。在每个时间戳t t t上面,智能体通过观察环境状态s t s_t s t ​而产生动作a t a_t a t ​,环境接收动作后状态改变为s t + 1 s_{t+1}s t +1 ​,并返回奖励r t r_t r t ​。

; 2. 策略网络

下面我们来探讨强化学习中最为关键的环节:如何判断和决策?我们把判断和决策叫做策略(Policy)。策略的输入是状态s s s,输出为某具体的动作a a a或动作的分布π θ ( a ∣ s ) π_θ (a|s)πθ​(a ∣s ),其中θ θθ为策略函数π ππ的参数,可以利用神经网络来参数化π θ π_θπθ​函数,如图2所示:

深度学习之强化学习(1)强化学习案例

图中神经网络π θ π_θπθ​的输入为平衡杆系统的状态s s s,即长度为4的向量,输出为所有动作的概率π θ ( a ∣ s ) π_θ (a|s)πθ​(a ∣s ):向左的概率P ( 向 左 ∣ s ) P(向左|s)P (向左∣s )和向右的概率P ( 向 右 ∣ s ) P(向右|s)P (向右∣s ),并满足所有动作概率之和为1的关系:
∑ a ∈ A π θ ( a ∣ s ) = 1 ∑{a∈A}π_θ (a|s)=1 a ∈A ∑​πθ​(a ∣s )=1
其中A A A为所有动作的集合。π θ π_θπθ​网络代表了智能体的策略,称为策略网络。很自然地,我们可以将策略函数具体化为输入节点为4个,中间多个全连接隐藏层,输出层的输出节点数为2的神经网络,代表了这两个动作的概率分布。在交互时,选择概率最大的动作
a t = argmax a ⁡ π θ ( a ∣ s t ) a_t=\underset{a}{\text{argmax}}⁡\ π_θ (a|s_t)a t ​=a argmax ​⁡πθ​(a ∣s t ​)
作为决策结果,作用与环境中,并得到新的状态s t + 1 s
{t+1}s t +1 ​和奖励r t r_t r t ​,如此循环往复,直至游戏回合结束。

我们将策略网络实施为两层完全连接的网络。第一层将长度为4的向量转换为长度为128的向量,第二层将长度为128的向量转换为向量2,即动作的概率分布。与创建正常神经网络的过程一样,代码如下:

[En]

We implement the policy network as a two-layer fully connected network. The first layer converts a vector of length 4 into a vector of length 128, and the second layer converts a vector of 128 into a vector of 2, that is, the probability distribution of the action. As in the process of creating a normal neural network, the code is as follows:

class Policy(keras.Model):
    # 策略网络,生成动作的概率分布
    def __init__(self):
        super(Policy, self).__init__()
        self.data = [] # 存储轨迹
        # 输入为长度为4的向量,输出为左、右2个动作,指定W张量的初始化方案
        self.fc1 = layers.Dense(128, kernel_initializer='he_normal')
        self.fc2 = layers.Dense(2, kernel_initializer='he_normal')
        # 网络优化器
        self.optimizer = optimizers.Adam(lr=learning_rate)

    def call(self, inputs, training=None):
        # 状态输入s的shape为向量:[4]
        x = tf.nn.relu(self.fc1(inputs))
        x = tf.nn.softmax(self.fc2(x), axis=1)  # 获得动作的概率分布
        return x

在交互时,我们将每个时间戳上的状态输入s t s_t s t ​,动作分布输出a t a_t a t ​,环境奖励r t r_t r t ​和新状态s t + 1 s_{t+1}s t +1 ​作为一个4元组item记录下来,用于策略网络的训练。代码如下:

def put_data(self, item):
    # 记录r,log_P(a|s)
    self.data.append(item)

3. 梯度更新

如果需要利用梯度下降算法来优化网络,需要知道每个输入s t s_t s t ​的标注信息a t a_t a t ​,并且确保从输入到损失值是连续可导的。但是强化学习与传统的有监督学习并不相同,主要体现为强化学习在每一个时间戳t t t上面的动作a t a_t a t ​并没有一个明确的好与坏的标准,奖励r t r_t r t ​可以在一定程度上反映动作的好坏,但不能直接决定动作的好坏,甚至有些游戏交互过程只有一个最终的代表游戏结果的奖励r t r_t r t ​信号,如围棋。那么给每个状态定义一个最优动作a t ∗ a_t^*a t ∗​作为神经网络输入s t s_t s t ​的标注可行吗?首先是游戏中的状态总数通常是巨大的,如围棋的状态数共有约1 0 170 10^{170}1 0 1 7 0之多。再者每个状态很难定义一个最优动作,有些动作虽然短期回报不高,但是长期回报却是较好的,而且有时候甚至连人类自己都不知道哪个动作才是最优的。

因此,策略的优化目标不应该是让输入s t s_t s t ​的输出尽可能地逼近标注动作,而是要最大化总回报的期望值。总回报可以定义为从游戏会和开始到游戏结束前的激励之和∑ r t ∑r_t ∑r t ​ 。一个好的策略,应能够在环境上面取得的总的回报的期望值J ( π θ ) J(π_θ)J (πθ​)最高。根据梯度上升算法的原理,我们如果能够求出∂ J ( θ ) ∂ θ \frac{∂J(θ)}{∂θ}∂θ∂J (θ)​,那么策略网络只需要按照
θ ′ = θ + η ⋅ ∂ J ( θ ) ∂ θ θ’=θ+η\cdot\frac{∂J(θ)}{∂θ}θ′=θ+η⋅∂θ∂J (θ)​
然后,可以迭代地优化策略网络,从而产生更大的预期总回报。

[En]

Then the strategy network can be iteratively optimized, resulting in a larger expected total return.

很遗憾的是,总回报期望J ( θ ) J(θ)J (θ)是由游戏环境给出的,如果无法得知环境模型,那么∂ J ( θ ) ∂ θ \frac{∂J(θ)}{∂θ}∂θ∂J (θ)​是不能通过自动微分计算的。那么即使J ( θ ) J(θ)J (θ)表达式未知,能不能直接求解偏导数∂ J ( θ ) ∂ θ \frac{∂J(θ)}{∂θ}∂θ∂J (θ)​呢?

答案是肯定的。我们这里直接给出∂ J ( θ ) ∂ θ \frac{∂J(θ)}{∂θ}∂θ∂J (θ)​的推导结果,具体的推导过程会在梯度推导的小节里详细介绍:
∂ J ( θ ) ∂ θ = E τ ∼ p θ ( τ ) [ ( ∑ t = 1 T ∂ ∂ θ log ⁡ π θ ( a t │ s t ) ) R ( τ ) ] \frac{∂J(θ)}{∂θ}=\mathbb E_{τ\sim p_θ (τ) }\bigg [\Big(∑_{t=1}^T\frac{∂}{∂θ} \text{log}⁡π_θ (a_t│s_t )\Big)R(τ)\bigg]∂θ∂J (θ)​=E τ∼p θ​(τ)​[(t =1 ∑T ​∂θ∂​log ⁡πθ​(a t ​│s t ​))R (τ)]
利用上式,只需要计算出∂ ∂ θ log ⁡ π θ ( a t │ s t ) \frac{∂}{∂θ} \text{log}⁡π_θ (a_t│s_t )∂θ∂​log ⁡πθ​(a t ​│s t ​),并乘以R ( τ ) R(τ)R (τ)即可更新出∂ J ( θ ) ∂ θ \frac{∂J(θ)}{∂θ}∂θ∂J (θ)​,按照θ ′ = θ − η ⋅ ∂ L ( θ ) ∂ θ θ’=θ-η\cdot\frac{∂\mathcal L(θ)}{∂θ}θ′=θ−η⋅∂θ∂L (θ)​方式更新策略网络,即可最大化J ( θ ) J(θ)J (θ)函数。其中R ( τ ) R(τ)R (τ)为某次交互的总回报,τ ττ为交互轨迹s 1 , a 1 , r 1 , s 2 , a 2 , r 2 , … , s T s_1,a_1,r_1,s_2,a_2,r_2,…,s_T s 1 ​,a 1 ​,r 1 ​,s 2 ​,a 2 ​,r 2 ​,…,s T ​,T T T是交互的时间戳数量或步数,log ⁡ π θ ( a t │ s t ) \text{log}⁡π_θ (a_t│s_t )log ⁡πθ​(a t ​│s t ​)为策略网络的输出中a t a_t a t ​动作的概率值取log \text{log}log函数,log ⁡ π θ ( a t │ s t ) \text{log}⁡π_θ (a_t│s_t )log ⁡πθ​(a t ​│s t ​)可以通过TensorFlow自动微分求解出网络的梯度,这一部分是连续可导的。

损失函数的代码实现为:

for r, log_prob in self.data[::-1]:  # 逆序取轨迹数据
    R = r + gamma * R  # 累加计算每个时间戳上的回报
    # 每个时间戳都计算一次梯度
    # grad_R=-log_P*R*grad_theta
    loss = -log_prob * R
完整的训练及更新代码如下:
def train_net(self, tape):
    # 计算梯度并更新策略网络参数。tape为梯度记录器
    R = 0  # 终结状态的初始回报为0
    for r, log_prob in self.data[::-1]:  # 逆序取轨迹数据
        R = r + gamma * R  # 累加计算每个时间戳上的回报
        # 每个时间戳都计算一次梯度
        # grad_R=-log_P*R*grad_theta
        loss = -log_prob * R
        with tape.stop_recording():
            # 优化策略网络
            grads = tape.gradient(loss, self.trainable_variables)
            # print(grads)
            self.optimizer.apply_gradients(zip(grads, self.trainable_variables))
    self.data = []  # 清空轨迹

4. 平衡杆游戏实战

我 们一共训练400个回合,在回合的开始,复位游戏状态,通过送入输入状态来采样动作,从而与环境进行交互,并记录每一个时间戳的信息,直至游戏回合结束。

互动和培训代码如下:

[En]

The interaction and training code is as follows:

for n_epi in range(400):
    s = env.reset()  # 回到游戏初始状态,返回s0
    with tf.GradientTape(persistent=True) as tape:
        for t in range(501):  # CartPole-v1 forced to terminates at 500 step.

            # 送入状态向量,获取策略
            s = tf.constant(s, dtype=tf.float32)
            # s: [4] => [1,4]
            s = tf.expand_dims(s, axis=0)
            prob = pi(s)  # 动作分布:[1,2]
            # 从类别分布中采样1个动作, shape: [1]
            a = tf.random.categorical(tf.math.log(prob), 1)[0]
            a = int(a)  # Tensor转数字
            s_prime, r, done, info = env.step(a)
            # 记录动作a和动作产生的奖励r
            # prob shape:[1,2]
            pi.put_data((r, tf.math.log(prob[0][a])))
            s = s_prime  # 刷新状态
            score += r  # 累积奖励

            if n_epi >1000:
                env.render()
                # im = Image.fromarray(s)
                # im.save("res/%d.jpg" % info['frames'][0])

            if done:  # 当前episode终止
                break
        # episode终止后,训练一次网络
        pi.train_net(tape)
    del tape

模型的训练过程如图3所示,横轴为训练回合数量,纵轴为回合的平均回报值。可以看到随着训练的进行,网络获得的平均回报越来越高,策略越来越好。实际上,强化学习算法对参数及其敏感,甚至修改随机种子都会导致截然不同的性能表现,在实现的过程中需要精调参数才能发挥出算法的潜力。

深度学习之强化学习(1)强化学习案例
图3. 平衡杆游戏训练过程

通过这个例子,我们对强化学习算法和强化学习的交互过程有了一个初步的印象和了解,进而对强化学习问题进行了形式化的描述。

[En]

Through this example, we have a preliminary impression and understanding of the interaction process between reinforcement learning algorithm and reinforcement learning, and then we will formally describe the reinforcement learning problem.

完整代码

import gym
import os
import numpy as np
import matplotlib
from matplotlib import pyplot as plt
Default parameters for plots
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, optimizers, losses
from PIL import Image

matplotlib.rcParams['font.size'] = 18
matplotlib.rcParams['figure.titlesize'] = 18
matplotlib.rcParams['figure.figsize'] = [9, 7]
matplotlib.rcParams['font.family'] = ['KaiTi']
matplotlib.rcParams['axes.unicode_minus'] = False

env = gym.make('CartPole-v1')  # 创建游戏环境
env.seed(2333)
tf.random.set_seed(2333)
np.random.seed(2333)
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
assert tf.__version__.startswith('2.')

learning_rate = 0.0002
gamma = 0.98

class Policy(keras.Model):
    # 策略网络,生成动作的概率分布
    def __init__(self):
        super(Policy, self).__init__()
        self.data = [] # 存储轨迹
        # 输入为长度为4的向量,输出为左、右2个动作,指定W张量的初始化方案
        self.fc1 = layers.Dense(128, kernel_initializer='he_normal')
        self.fc2 = layers.Dense(2, kernel_initializer='he_normal')
        # 网络优化器
        self.optimizer = optimizers.Adam(lr=learning_rate)

    def call(self, inputs, training=None):
        # 状态输入s的shape为向量:[4]
        x = tf.nn.relu(self.fc1(inputs))
        x = tf.nn.softmax(self.fc2(x), axis=1)  # 获得动作的概率分布
        return x

    def put_data(self, item):
        # 记录r,log_P(a|s)
        self.data.append(item)

    def train_net(self, tape):
        # 计算梯度并更新策略网络参数。tape为梯度记录器
        R = 0  # 终结状态的初始回报为0
        for r, log_prob in self.data[::-1]:  # 逆序取轨迹数据
            R = r + gamma * R  # 累加计算每个时间戳上的回报
            # 每个时间戳都计算一次梯度
            # grad_R=-log_P*R*grad_theta
            loss = -log_prob * R
            with tape.stop_recording():
                # 优化策略网络
                grads = tape.gradient(loss, self.trainable_variables)
                # print(grads)
                self.optimizer.apply_gradients(zip(grads, self.trainable_variables))
        self.data = []  # 清空轨迹

def main():
    pi = Policy()  # 创建策略网络
    pi(tf.random.normal((4, 4)))
    pi.summary()
    score = 0.0  # 计分
    print_interval = 20  # 打印间隔
    returns = []

    for n_epi in range(400):
        s = env.reset()  # 回到游戏初始状态,返回s0
        with tf.GradientTape(persistent=True) as tape:
            for t in range(501):  # CartPole-v1 forced to terminates at 500 step.

                # 送入状态向量,获取策略
                s = tf.constant(s, dtype=tf.float32)
                # s: [4] => [1,4]
                s = tf.expand_dims(s, axis=0)
                prob = pi(s)  # 动作分布:[1,2]
                # 从类别分布中采样1个动作, shape: [1]
                a = tf.random.categorical(tf.math.log(prob), 1)[0]
                a = int(a)  # Tensor转数字
                s_prime, r, done, info = env.step(a)
                # 记录动作a和动作产生的奖励r
                # prob shape:[1,2]
                pi.put_data((r, tf.math.log(prob[0][a])))
                s = s_prime  # 刷新状态
                score += r  # 累积奖励

                if n_epi > 1000:
                    env.render()
                    # im = Image.fromarray(s)
                    # im.save("res/%d.jpg" % info['frames'][0])

                if done:  # 当前episode终止
                    break
            # episode终止后,训练一次网络
            pi.train_net(tape)
        del tape

        if n_epi % print_interval == 0 and n_epi != 0:
            returns.append(score/print_interval)
            print(f"# of episode :{n_epi}, avg score : {score/print_interval}")
            score = 0.0
    env.close()  # 关闭环境

    plt.plot(np.arange(len(returns))*print_interval, returns)
    plt.plot(np.arange(len(returns))*print_interval, returns, 's')
    plt.xlabel('Number of Rounds')  # 回合数
    plt.ylabel('Total Return')  # 总回报
    plt.savefig('reinforce-tf-cartpole.svg')
    plt.show()

if __name__ == '__main__':
    main()

Original: https://blog.csdn.net/weixin_43360025/article/details/120772582
Author: 炎武丶航
Title: 深度学习之强化学习(1)强化学习案例

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/496743/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球