百度强化学习7日训练营-小结

一直想找个强化学习的入门课,刚好看到了百度这个训练营,果断报名。7天下来,收获满满,再次感谢人美心善代码6的科老师。

本文做个简单的小结,方便以后复习。

1. 初识强化学习

1.1 基本概念

强化学习(Reinforcement Learning)是机器学习的一个领域,强调智能体如何基于环境而行动,已取得最大化的预期收益。

核心思想:智能体agent在环境environment中学习,根据环境的状态state(或观测到的observation),执行动作action,并根据环境的反馈 reward(奖励)来指导更好的动作。

1.2 对比其他机器学习方法

强化学习、监督学习、非监督学习是机器学习里的三个不同的领域,都跟深度学习有交集。

监督学习寻找输入到输出之间的映射,比如分类和回归问题。

非监督学习主要寻找数据之间的隐藏关系,比如聚类问题。

强化学习则需要在与环境的交互中学习和寻找最佳决策方案。

监督学习处理认知问题,强化学习处理决策问题。

1.3 代码环境

强化学习经典环境库GYM将环境交互接口规范化为:重置环境reset()、交互step()、渲染render()

强化学习框架库PARL将强化学习框架抽象为Model、Algorithm、Agent三层,使得强化学习算法的实现和调试更方便和灵活。

课程中使用的包有PaddlePaddle1.6.3,parl1.3.1和gym。

2. 基于表格的方法

2.1 Sarsa

Sarsa全称是state-action-reward-state'-action',目标是学习特定state下,特定action的价值,最终建立和优化一个Q表格。根据agent与环境交互所获得的reward来更新Q表格,更新公式为:

Q(s_{t},a_{t}) \leftarrow Q(s_{t},a_{t}) + \alpha\left[R_{t+1} + \gamma Q(s_{t+1},a_{t+1}) - Q(s_{t},a_{t})\right]

使用中可以将Q(s_{t},a_{t})理解为predict\_Q,将R_{t+1} + \gamma Q(s_{t+1},a_{t+1})理解为target\_Q,则公式变为:
Q(s_{t},a_{t}) \leftarrow predict\_Q + \alpha \left(target\_Q - predict\_Q\right)
通过不断迭代,就可以得到优化后的Q表格。
训练时为了更好的探索环境,采用\epsilon-greed方式采样action。

2.1.1 关键代码

\epsilon-greed算法采样动作

# 根据输入观察值,采样输出的动作值,带探索 
def sample(self, obs): 
    if np.random.uniform(0, 1) < (1.0 - self.epsilon): #根据table的Q值选动作 
        action = self.predict(obs)
    else:
        action = np.random.choice(self.act_n) #有一定概率随机探索选取一个动作
        return action

根据公式更新Q-table的方法

def learn(self, obs, action, reward, next_obs, next_action, done):
    predict_Q = self.Q[obs, action]
    target_Q = reward + (1-done) * self.gamma * self.Q[next_obs, next_action] #Sarsa
    self.Q[obs, action] += self.lr * (target_Q - predict_Q) # 按学习率更新Q值

2.1.2 运行效果

在“CliffWalking-v0”环境上训练500个episode后的效果如下:


sarsa效果.gif

2.2 Q-Learning

Q-Learning和Sarsa一样都是采用Q表格存储Q值(状态动作价值),决策部分也是采用\epsilon-greed方式增加探索。
Q-Learning和Sarsa不一样的地方在于更新Q表格的方式。

Sarsa是on-policy的更新方式,先做出动作再更新。
Q-Learning是off-policy的更新方式,更新时无需获取下一步动作,而是假设下一步动作是Q值最大的动作。

Q-Learning的更新公式为:
Q(s_{t},a_{t}) \leftarrow Q(s_{t},a_{t}) + \alpha\left[R_{t+1} + \gamma \max_{a}Q(s_{t+1},a) - Q(s_{t},a_{t})\right]

2.2.1 关键代码

根据公式更新Q-table的方法

def learn(self, obs, action, reward, next_obs, done):
    predict_Q = self.Q[obs, action] 
    target_Q = reward + (1-done) * self.gamma * np.max(self.Q[next_obx, :]) # Q-Learning
    self.Q[obs, action] += self.lr * (target_Q - predict_Q) # 修正Q值

其它代码和Sarsa基本一致。

2.2.2 运行效果

同样在“CliffWalking-v0”环境上训练500个episode后的效果如下:


q-learning效果.gif

和Sarsa比较发现,Q-Learning训练后的可以沿着最短路径走到终点,而Sarsa选择的路线比较“保守”,没有紧贴着悬崖走。这是因为Sarsa在更新Q时,会考虑到下一步动作的结果,如果下一步动作Q值较低,就会拉低当前的Q值;而Q-Learning默认下一步动作总是选择Q值最大的动作,因此不会因为下一步可能掉到悬崖就降低当前Q值,最后表现就是比较“大胆”。

3. 基于神经网络的方法

3.1 DQN简介

基于表格型的方法,在面对状态数量巨大的环境时(如围棋,机器人控制等),存储和查找效率都受限,DQN的提出解决这一局限,通过神经网络来近视替代Q表格。

本质上DQN还是一个Q-Learning算法,更新方式一致,也同样采用\epsilon-greed方式训练。

在Q-Learning的基础上,DQN提出两个技巧使得Q网络的训练更稳定:
1、经验回放Experience Replay:使用一个经验池存储多条(s,a,r,s')经验,再随机抽取一部分数据送去训练。主要解决样本关联性和利用效率的问题。
2、固定Q目标Fixed-Q-target:复制一个和Q网络一个的Target Q网络,用于计算Q目标值。主要解决网络训练不稳定的问题。

3.2 关键代码

使用PARL框架,定义并创建model,algorithm,agent对象。agent负责和环境交互,并将产生的数据传给algorithm,algorithm根据收到的数据,结合model的预测值计算loss值,再使用SGD或其它优化器不断优化。

Model

用来定义计算Q值的网络。

class Model(parl.Model):
    def __init__(self, act_dim):
        self.fc1 = layers.fc(128, act='relu')
        self.fc2 = layers.fc(128, act='relu')
        self.fc3 = layers.fc(act_dim, act=None)

    # 输入state,输出所有action对应的Q值(Q[s,a1], Q[s,a2], Q[s,a3]...)
    def value(self, obs):
        Q = self.fc3(self.fc2(self.fc1(obs)))
        return Q
Algorithm

定义了具体的算法来更新Model,也就是通过定义损失函数来更新Model,和算法相关的计算都放在Algorithm。

class Algorithm(parl.Algorithm):
    def __init__(self, model, act_dim, gamma, lr):
        self.model = model
        self.target_model = copy.deepcopy(model) # 复制Q网络
        self.act_dim = act_dim
        self.gamma = gamma
        self.lr = lr

    def predict(self, obs):
        return self.model.value(obs)

    def sysn_target(self):
        # 同步model模型参数到target_model
        self.model.sync_weights_to(self.target_model)

    def learn(self, obs, action, reward, next_obs, terminal):
        # 使用DQN算法(类似Q-Learning)更新model参数
        # 从target_model中获取Q值,用于计算target_Q
        target_pred_value = self.target_model.value(obs)
        best_v = layers.max(target_pred_value, dim=1)
        best_v.stop_gradient = True # 阻止梯度传递,防止更新target_model模型参数
        terminal = layers.cast(terminal, dtype='float32')
        target_Q = reward + (1.0-terminal) * self.gamma * best_v

        # 预测当前obs下所有动作的Q值
        pred_value = self.model.value(obs)
        action_onehot = layers.one_hot(action, self.act_dim)
        action_onehot = layers.cast(action_onehot, dtype='float32')
        predict_Q = layers.reduce_sum(
            layers.elementwise_mul(action_onehot, pred_value), dim=1)

        # 使用MSE计算得到loss
        cost = layers.square_error_loss(predict_Q, target_Q)
        cost = layers.reduce_mean(cost)

        # 使用Adam优化器
        optimizer = fluid.optimizer.Adam(learning_rate=self.lr)
        optimizer.minimize(cost)
        return cost
Agent

用于和环境交互,并把生成的数据传给Algorithm。数据的处理流程一般也在这定义。

class Agent(parl.Agent):
    def __init__(self, algorithm,  obs_dim, act_dim, e_greed=0.1, e_greed_decrement=0):
        略...

    # 搭建计算图
    def build_program(self):
        略...

    # 使用ϵ-greed方式探索环境
    def sample(self, obs):
        略...
        return act

    # 选择最优动作
    def predict(self, obs):
        略...
        return act

    def learn(self, obs, act, reward, next_obs, terminal):
        # 每隔200个training steps同步一次model和target_model的参数
        if self.global_step % self.update_target_steps == 0:
            self.alg.sync_target()
        self.global_step += 1
        ...略
        cost = self.fluid_executor.run(
            self.learn_program, feed=feed, fetch_list=[self.cost])[0]  # 训练一次网络
        return cost

另外在训练时,并非每个step后都调用learn,而是每5个step后learn一次。

3.3 运行效果

在"CartPole-v0"环境上,运行2000个episode后,reward能稳定在200。


4. 基于策略梯度的方法

4.1 Policy Gradient简介

在强化学习中,有两大类方法:一种基于值(Value-based),一种基于策略(Policy-based)。
Value-based算法的典型代表为Q-Learning和Sarsa,讲Q函数优化到最优,再根据Q函数选取最优策略。
Policy-based算法的典型代表为Policy Gradient,直接优化策略函数。

Policy Gradient采用神经网络拟合策略函数,需要计算策略梯度用于优化策略网络。
优化的目标是在策略\pi(s,a)的期望回报:所有轨迹获得的回报R和对应轨迹发生的概率P的加权和。当N足够大时,可以采样N个Episode的回报然求平均来近似。

优化目标:\bar {R}_{\theta}=\sum_{\tau} R(\tau) p_{\theta}(\tau) \approx \frac{1}{N} \sum_{n=1}^{N} R(\tau)
策略梯度:\nabla \bar{R}_{\theta} \approx \frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} R\left(\tau^{n}\right) \nabla \log \pi_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right)

策略梯度的推导过程:
\nabla \bar{R}_{\theta}=\sum_{\tau} R(\tau) \nabla p_{\theta}(\tau) =\sum_{\tau} R(\tau) p_{\theta}(\tau) \frac{\nabla p_{\theta}(\tau)}{p_{\theta}(\tau)}
由于\nabla f(x)=f(x) \nabla \log f(x),因此

\qquad\nabla \bar{R}_{\theta}=\sum_{\tau} R(\tau) p_{\theta}(\tau) \nabla \log p_{\theta}(\tau)=\frac{1}{N} \sum_{n=1}^{N} R(\tau) \nabla \log p_{\theta}(\tau) \quad
又由于
p_{\theta}(\tau)=p\left(s_{1}\right) \pi_{\theta}\left(a_{1} | s_{1}\right) \cdot p\left(s_{2} | s_{1}, a_{1}\right) \pi_{\theta}\left(a_{2} | s_{2}\right) \cdot p\left(s_{3} | s_{2}, a_{2}\right)\pi_{\theta}\left(a_{3} | s_{3}\right) ...
因此
\qquad\nabla\bar{R}_{\theta} =\frac{1}{N} \sum_{n=1}^{N} R(\tau) \nabla \log \left[p(s1) \prod_{t=1}^{T} \pi_{\theta}\left(a_{t} | s_{t}\right) p\left({s}_{t+1} | s_{t}, a_{t}\right)\right]
因为p(s1),p\left({s}_{t+1} | s_{t}, a_{t}\right)是由环境决定的,与\theta无关,即对\theta的导数为零,因此:
\begin{array}{l} \nabla\bar {R}_{\theta} =\frac{1}{N} \sum_{n=1}^{N} R(\tau) \sum_{t=1}^{T} \nabla \log \pi_{\theta}\left(a_{t} | s_{t}\right) \\ \qquad\,\,=\frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T} R(\tau) \nabla \log \pi_{\theta}\left(a_{t} | s_{t}\right) \end{array}

4.2 关键代码

同DQN一样,搭建Model,Algorithm,Agent。还是用来解决CartPole问题。

Model
class Model(parl.Model):
    def __init__(self, act_dim):
        act_dim = act_dim
        hid1_size = act_dim * 10

        self.fc1 = layers.fc(size=hid1_size, act='tanh')
        self.fc2 = layers.fc(size=act_dim, act='softmax')

    def forward(self, obs): 
        out = self.fc1(obs)
        out = self.fc2(out)
        return out
Algorithm
class PolicyGradient(parl.Algorithm):
    
    # 其它代码略...

    def learn(self, obs, action, reward):
        """ 用policy gradient 算法更新policy model
        """
        act_prob = self.model(obs)  # 获取输出动作概率
        # log_prob = layers.cross_entropy(act_prob, action) # 交叉熵
        log_prob = layers.reduce_sum(
            -1.0 * layers.log(act_prob) * layers.one_hot(
                action, act_prob.shape[1]),
            dim=1)
        cost = log_prob * reward # 策略梯度公式
        cost = layers.reduce_mean(cost)

        optimizer = fluid.optimizer.Adam(self.lr)
        optimizer.minimize(cost)
        return cost
Agent

代码基本和DQN的Agent类似。

其它

在计算Reward时,需要累积当前步之后的所有Reward带衰减因子的累加和。

# 根据一个episode的每个step的reward列表,计算每一个Step的Gt
def calc_reward_to_go(reward_list, gamma=1.0):
    for i in range(len(reward_list) - 2, -1, -1):
        # G_t = r_t + γ·r_t+1 + ... = r_t + γ·G_t+1
        reward_list[i] += gamma * reward_list[i + 1]  # Gt
    return np.array(reward_list)

4.3 运行效果

在"CartPole-v0"环境上,运行1000个episode后,reward能稳定在200。


policy gradient效果.gif

5. 连续动作空间

待续...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352