zoukankan      html  css  js  c++  java
  • 强化学习入门必读 | 跨过DQN !

    本文首发于:行者AI

    DRL(Deep Reinforcement Learning)的首次惊艳亮相,应该是 DeepMind 在2013年首次将其应用于 Atari 游戏中提出的 DQN(Deep Q Network)算法。时至今日,DRL 已经从玩 Atari,进化为下围棋(Alphago)、玩电竞(Dota AI、StarCraft AI),一次次刷新大家的三观。

    1. 什么是Q-Learning

    Q-Learning算法是一种使用时序差分求解强化学习控制问题的方法。通过当前的状态(S),动作(A),即时奖励(R),衰减因子(γ),探索率(ϵ),就最最优的动作价值函数(Q)和最有策略(π)

    • (S):表示环境的状态,在(t)时刻环境的状态(S_t)

    • (A):agent的动作,在(t)时刻采取的动作(A_t)

    • (R):环境的奖励,在(t)时刻agent在状态St采取动作(A_t)对应的奖励(R_{t+1}) 会在 (t+1) 时刻得到

    • (gamma):折扣因子, 当前延时奖励的权重

    • (epsilon):探索率,在Q-learning我们会选取当前轮迭代价值最大的动作,可能会导致有的动作重来都没被执行过,在agent选择动作时,有小概率不是选取当前迭代价值最大的动作。

    1.1 Q-Learning算法简介

    首先我们基于状态(S),用(ϵ−greed)(贪心)选择到动作(A), 然后执行动作(A),得到奖励(R),并进入状态(S')(Q)值的更新公式如下:​
    (Q(S,A)=Q(S,A)+alpha(R+gamma maxQ(S',a)-Q(S,A)))

    1.2 Q-learning的算法流程

    • 随机初始化状态和动作价值对应的价值。(初始化(Q)表格)

    • for i from 1 to TT:迭代的总轮数)
              a)初始化(S)为当前状态的序列的第一个状态
              b)用(ϵ)−贪婪法在当前状态(S)选择出动作(A)
              c)在状态(S)执行当前动作(A),得到新状态(S′)和奖励(R)
              d)更新价值函数(Q(S,A))
                                    (Q(S,A)=Q(S,A)+alpha(R+gamma maxQ(S',a)-Q(S,A)))

              e)(S=S')
              f) if (done) 完成当前迭代

    1.3 关于Q_table举个例子

    (1)游戏地图

    • 黑色的框为陷阱

    • 黄色的框为出口(奖励点)

    (2) 这是一个训练模型之后的Q表格

    (3) 举个简单的例子

    • 如果agent在“1”的位置进入迷宫,会更具Q表格,向下走的Q值最大为0.59,那么agent就会走到“5”的位置。
    • agent在“5”的位置之后,更具Q表格,向下走的Q值最大为0.66,依然是向下走,那么agent就走到了“9”的位置。
    • agent在“9”的位置之后,更具Q表格,向右走的Q值最大为0.73,依然是向下走,那么agent就走到了“10”的位置。
    • agent在“10”的位置之后,更具Q表格,向下走的Q值最大为0.81,依然是向下走,那么agent就走到了“14”的位置。
    • agent在“14”的位置之后,更具Q表格,向右走的Q值最大为0.9,依然是向下走,那么agent就走到了“15”的位置。
    • agent在“15”的位置之后,更具Q表格,向右走的Q值最大为1,依然是向下走,那么agent就走到了“16”的位置,到达终点。

    最后agent的动作路线为1-->5-->9-->10-->14-->15-->16

    每跑一次,(Q)表格的值都会有所改变,但是原理不变。

    如果想看到更加直观的视觉戳这里

    2. DQN(Deep Q Network)

    前面讲过Q-Learning的决策是根据Q表格的值,执行那个动作后得到的奖励更多,就选取那个动作执行。前面所讲的状态空间和动作空间都很小,如果状态空间和动作空间变得很大很大,那我们还能用一个Q表格来表示吗?显然不可以,就引入了价值函数近似

    2.1 价值函数近似

    由于在实际问题中,一个问题的状态规模很大,一个可行的解决办法就是使用价值函数近似。我们引入一个状态价值函数(hat v),由权重(omega)描述,以状态(s)作为输入,计算得到状态(s)的价值:
    (hat v(s,w)approx v_pi(s))

    上面我们提到的(omega)就相当于我们神经网络中的参数,通过输入的状态(s),采用MC(蒙特卡洛)/TD(时序差分)计算出价值函数作为输出,然后对权重(omega)进行训练,直到收敛。事实上,所谓的DQN就是将神经网络和Q-Learning结合,将Q表格变成了Q网络。

    2.2 Deep Q-Learning算法思路

    DQN是一种Off-Policy算法,用李宏毅老师的话讲,可以看着别人学习,那么DQN为什么能够看着别人学习呢?DQN采用了一种经验回放的方式进行学习。每次agent和环境交互得到的奖励,当前状态和下一个状态等数据保存起来,用于后面Q网络的更新。

    下面我们看下Nature DQN,其实Nature DQN为DQN第二代了,DQN NIPS为最原始的DQN,在这之上的还有很多的DQN的版本,比如Double DQN、Dueling DQN等等。之所以在这儿给大家介绍Nature DQN呢!个人觉得这个版本的DQN,应该是最经典的了。接下来我们看看DQN是如何进行强化学习的吧。

    2.3 算法流程图

    输入:总迭代轮数(T),状态特征维度(n),动作维度(A), 步长(a),衰减因子(gamma), 探索率(epsilon), 当前Q网络(Q),目标Q网络(Q'), 批量梯度下降的样本数(m),目标Q网络参数更新频率(P)

    输出:Q网络参数

    • 随机初始化所有的状态和动作对应的价值(Q),随机初始化当前(Q)网络的所有参数(omega),初始化目标Q网络(Q')的参数(omega)'=(omega),清空经验池(D)
    • for i from 1 to T(不断地迭代)
                a)初始化环境,获取第一个状态(s),获取特征向量(phi)((S))
                b)在(Q)网络中使用(phi)((S))作为输入,得到(Q)网络所有动作的Q值,使用(epsilon)-贪婪法在当前的Q值输出中选择对应的动作(A)
                c)在状态(S)下执行动作(A),得到新的状态(S'),以及其对应的(phi)((S'))和奖励(R),是否为结束状态(isdone)
                d)将{(phi(S)),   (A),   (R),  (phi(S')),   (isdone) }将这5个元素放入经验池(D)
                e) (S=S')
                f)从经验池(D)中采取(m)个样本,{(phi(S_j)),   (A_j),   (R_j),  (phi(S'_j)(isdone_j)),(j=1,2,3,4....m),计算当前(Q)(y_j):

              g)使用均方差损失函数(left(frac{1}{m} ight))(sum_{j=1}^m)((y_j-Q(phi (S_j),A_j,omega))^2)通过神经网络梯度下降反向传播更新参数(omega)
              h)如果i%P=0,跟新目标(Q)网络的参数(omega'=omega)
              i)如果(S')为终止状态,则当前迭代完毕,否则跳转到步骤(2)

    2.4 DQN实现代码

    (1) 网络结构

    class Net(nn.Module):
        def __init__(self, ):
            super(Net, self).__init__()
            self.fc1 = nn.Linear(N_STATES, 50)
            self.fc1.weight.data.normal_(0, 0.1)   # initialization
            self.out = nn.Linear(50, N_ACTIONS)
            self.out.weight.data.normal_(0, 0.1)   # initialization
        def forward(self, x):
            x = self.fc1(x)
            x = F.relu(x)
            actions_value = self.out(x)
            return actions_value
    
    • 两个网络的结构一样
    • 参数权重有区别,一个是实时更新,一个是隔一段时间在更新。

    (2) 动作的选取

        def choose_action(self, x):  #x为当前状态的4个值
            x = torch.unsqueeze(torch.FloatTensor(x), 0)  #在数据的第0维处增加一维
            # input only one sample
            if np.random.uniform() < EPSILON:   # greedy #贪婪取法
                actions_value = self.eval_net.forward(x)  ##传入eval_net获取下一个的动作
                action = torch.max(actions_value, 1)[1].data.numpy()  ##返回这一行中最大值的索引
                action = action[0] if ENV_A_SHAPE == 0 else action.reshape(ENV_A_SHAPE)  # return the argmax index
            else:   # random
                action = np.random.randint(0, N_ACTIONS)
                # action = random.sample(N_ACTIONS)
                action = action if ENV_A_SHAPE == 0 else action.reshape(ENV_A_SHAPE)
            return action
    

    加入了一个探索值((epsilon)),在即小的可能性是随机选择动作。

    (3) 经验池

        def store_transition(self, s, a, r, s_):  #s和s_都为4个值,分别为  位置 移动速度  角度  移动角度
            transition = np.hstack((s, [a, r], s_))
            # replace the old memory with new memory #更新经验
            index = self.memory_counter % MEMORY_CAPACITY
            self.memory[index, :] = transition  #将第index经验替换为transition
            self.memory_counter += 1
    

    (4) 更新网络参数

     def learn(self):
            # target parameter update 目标参数更新
            if self.learn_step_counter % TARGET_REPLACE_ITER == 0:
                self.target_net.load_state_dict(self.eval_net.state_dict())  ## 每学习200步将eval_net的参数赋值给target_net
            self.learn_step_counter += 1
            # sample batch transitions  #选取过渡
            sample_index = np.random.choice(MEMORY_CAPACITY, BATCH_SIZE) #从MEMORY_CAPACITY随机选取BATCH_SIZE个
            b_memory = self.memory[sample_index, :]
            b_s = torch.FloatTensor(b_memory[:, :N_STATES])  #第一个状态
            b_a = torch.LongTensor(b_memory[:, N_STATES:N_STATES+1].astype(int)) #动作
            print("--------")
            print(b_a)
            print("-----")
            b_r = torch.FloatTensor(b_memory[:, N_STATES+1:N_STATES+2]) #得分
            b_s_ = torch.FloatTensor(b_memory[:, -N_STATES:]) #下一个状态
            # q_eval w.r.t the action in experience
            q_eval = self.eval_net(b_s).gather(1, b_a)   # shape (batch, 1) 当前状态的Q值使用eval_net计算
            # print("++++++")
            # print(self.eval_net(b_s))
            # print(self.eval_net(b_s).gather(1,b_a))
            # print("+++++++")
    
            q_next = self.target_net(b_s_).detach()   #使用target_net计算下一步Q值  # detach from graph, don't backpropagate detach防止targent——net反向传播
            q_target = b_r + GAMMA * q_next.max(1)[0].view(BATCH_SIZE, 1)   # shape (batch, 1)
            loss = self.loss_func(q_eval, q_target)
            self.optimizer.zero_grad()   #zer——grad设置所有优化器的梯度为0
            loss.backward()   #反向传播
            self.optimizer.step()    #执行下个优化
    

    DQN是深度强化学习的门槛,只要踏进了大门,后面的学习就会很轻松。


    PS:更多技术干货,快关注【公众号 | xingzhe_ai】,与行者一起讨论吧!

  • 相关阅读:
    linux 查看硬盘使用情况
    linux 用户操作命令
    Win10系列:C#应用控件进阶2
    Win10系列:C#应用控件进阶3
    Win10系列:C#应用控件进阶1
    Win10系列:C#应用控件基础23
    Win10系列:C#应用控件基础20
    Win10系列:C#应用控件基础21
    Win10系列:C#应用控件基础19
    Win10系列:C#应用控件基础17
  • 原文地址:https://www.cnblogs.com/xingzheai/p/14324288.html
Copyright © 2011-2022 走看看