強化學習DQN

強化學習中有兩種重要的方法:Policy GradientsQ-learning。其中Policy Gradients方法直接預測在某個環境下應該採起的Action,而Q-learning方法預測某個環境下全部Action的指望值(Q值)。通常來講,Q-learning方法只適合有少許離散取值的Action環境,而Policy Gradients方法適合有連續取值的Action環境。在與深度學習方法結合後,這兩種算法就變成了DPG(Deterministic Policy Gradient)DQN(Deep Q-learning Network),他們都是由DeepMind提出來的。DDPGDeep Deterministic Policy Gradient)則是利用 DQN 擴展 Q 學習算法的思路對DPG方法進行改造獲得的(Actor-CriticAC)框架的算法,該算法可用於解決連續動做空間上的 DRL 問題。git

這裏推薦莫煩大神的關於強化學習的代碼總結,git地址:github

https://github.com/MorvanZhou/Reinforcement-learning-with-tensorflow/tree/master/contents算法

你們也能夠搜索他的博客,裏面有不少關於強化的系列文章,頗有價值。網絡

一、gym環境框架

先安裝openaigym環境,gym是一個模擬遊戲環境的小引擎,openai開發gym就是爲了方便廣大機器學習愛好者作一些遊戲交互方面的機器學習嘗試。dom

pip install gym安裝完成後,試試gym能不能正常運行。機器學習

運行下面代碼:函數

import gym
# 建立一個小車倒立擺模型
env = gym.make('CartPole-v0')
# 初始化環境
env.reset()
# 刷新當前環境,並顯示
env.render()

CartPole-v0’是小車遊戲名稱。出現上面這個窗口就表示gym環境已經能夠正常運行了!學習

第二步,讓小車動起來!優化

count = 0
for t in range(100):
	action = env.action_space.sample() #隨機採樣動做
	print("action:%s"%(action))
	observation, reward, done, info = env.step(action)  #與環境交互,得到下一步的時刻
	if done:             
		break
	env.render()         #繪製場景
	count+=1
	time.sleep(0.2)      #每次等待0.2s
print(count)

env.step(action)是讓小車按照action的動做動起來,返回動起來以後的小車狀態,回報值,是否結束等信息。

done=True表示本次遊戲結束;observation是小車狀態,reward是本次遊戲動做的回報值。

​​​​​​​二、Q-learning

Q-learning是一種基於Q值計算的強化學習,還有一種是基於策略梯度(Policy Gradient)的強化學習。

Q-learning中,咱們維護一張Q值表,表的維數爲:狀態數S * 動做數A,表中每一個數表明在當前狀態S下能夠採用動做A能夠得到的將來收益的折現和。咱們不斷的迭代咱們的Q值表使其最終收斂,而後根據Q值表咱們就能夠在每一個狀態下選取一個最優策略。

其中更新的方法是用 Bellman Equation

    Q(S,a)=r+γ*max(Q(S',a')

其中,

S 表明當前的狀態,a 表明當前狀態所採起的行動,

S’ 表明這個行動所引發的下一個狀態,a’ 是這個新狀態時採起的行動,

r 表明採起這個行動所獲得的獎勵 rewardγ discount 因子,

Q(S,a)稱爲Q-target,即咱們使用貝爾曼方程加貪心策略認爲實際應該獲得的獎勵,咱們的目標就是使咱們的Q值不斷的接近Q-target值。

Q-learning只要明白它的原理就能夠了,具體算法的代碼咱們不用太過於深究,由於如今直接用Q-learning的場景很是很是的少,重點能夠看下DQN的具體實現。

​​​​​​​三、深度Q網絡(DQN)

爲何會出現DQN

在普通的Q-learning中,當狀態和動做空間是離散且維數不高時可以使用Q-Table儲存每一個狀態動做對的Q值,而當狀態和動做空間是高維連續時,使用Q-Table不現實。

如何將原始的Q-learning轉換成深度學習問題

Q-Table的更新問題變成一個函數擬合問題,相近的狀態獲得相近的輸出動做。以下式,經過更新參數 θ 使Q函數逼近最優Q 。所以,DQN就是要設計一個神經網絡結構,經過函數來擬合Q值。DQN網絡輸入狀態s,輸出動做action

這裏的Deep即一樣使用DQN中的經驗池和雙網絡結構來促進神經網絡可以有效學習。DQN的主要特色:

1、經過Q-Learning使用reward來構造標籤。

2、經過experience replay(經驗池)的方法來解決相關性及非靜態分佈問題。

3、使用一個神經網絡產生當前Q值,使用另一個神經網絡產生Target Q值。

經驗回放

經驗池的功能主要是解決相關性及非靜態分佈問題。具體作法是把每一個時間步agent與環境交互獲得的轉移樣本 (st,at,rt,st+1) 儲存到回放記憶單元,要訓練時就隨機拿出一些(minibatch)來訓練。(其實就是將遊戲的過程打成碎片存儲,訓練時隨機抽取就避免了相關性問題)。

s是當前狀態,a是當前動做,r是回報值,s_是下階段狀態。經驗存儲的代碼以下。

    def store_transition(self,s,a,r,s_):
        if not hasattr(self, 'memory_counter'):
            self.memory_counter = 0
        # hstack:Stack arrays in sequence horizontally
        transition = np.hstack((s,[a,r],s_))
        index = self.memory_counter % self.memory_size
        self.memory[index,:] = transition
        self.memory_counter += 1

 

雙網絡結構

DQN使用單個網絡來進行選擇動做和計算目標Q值;Nature DQN使用了兩個網絡,一個當前主網絡用來選擇動做,更新模型參數,另外一個目標網絡用於計算目標Q值,兩個網絡的結構是如出一轍的。目標網絡的網絡參數不須要迭代更新,而是每隔一段時間從當前主網絡複製過來,即延時更新,這樣能夠減小目標Q值和當前的Q值相關性。Nature DQNDQN相比,除了用一個新的相同結構的目標網絡來計算目標Q值之外,其他部分基本是徹底相同的。

Nature DQN的實現流程以下:

1)首先構建神經網絡,一個主網絡,一個目標網絡,他們的輸入都爲obervation,輸出爲不一樣action對應的Q值。

2)在一個episode結束時(遊戲勝利或死亡),將env重置,即observation恢復到了初始狀態observation,經過貪婪選擇法ε-greedy選擇action。根據選擇的action,獲取到新的next_observationreward和遊戲狀態。將[observation, action, reward, next_observation, done]放入到經驗池中。經驗池有必定的容量,會將舊的數據刪除。

3)從經驗池中隨機選取batch個大小的數據,計算出observationQ值做爲Q_target。對於doneFalse的數據,使用rewardnext_observation計算discount_reward。而後將discount_reward更新到Q_traget中。

4)每個action進行一次梯度降低更新,使用MSE做爲損失函數。注意參數更新不是發生在每次遊戲結束,而是發生在遊戲進行中的每一步。

5)每一個batch咱們更新參數epsilonegreedyepsilon是不斷變小的,也就是隨機性不斷變小。

6)每隔固定的步數,從主網絡中複製參數到目標網絡。

引入target_net後,再一段時間裏目標Q值使保持不變的,必定程度下降了當前Q值和目標Q值的相關性,提升了算法穩定性。

接下來,咱們重點看一下咱們DQN相關的代碼。

定義相關輸入

這了,咱們用s表明當前狀態,用a表明當前狀態下采起的動做,r表明得到的獎勵,s_表明轉移後的狀態。

self.s = tf.placeholder(tf.float32,[None,self.n_features],name='s')
self.s_ = tf.placeholder(tf.float32,[None,self.n_features],name='s_')
self.r = tf.placeholder(tf.float32,[None,],name='r')
self.a = tf.placeholder(tf.int32,[None,],name='a')

雙網絡DQN代碼

w_initializer, b_initializer = tf.random_normal_initializer(0., 0.3), tf.constant_initializer(0.1)
# ------------------ build evaluate_net ------------------
with tf.variable_scope('eval_net'):
    e1 = tf.layers.dense(self.s,20,tf.nn.relu,kernel_initializer=w_initializer,
                         bias_initializer=b_initializer,name='e1'
                         )
    self.q_eval = tf.layers.dense(e1,self.n_actions,kernel_initializer=w_initializer,
                                  bias_initializer=b_initializer,name='q')

# ------------------ build target_net ------------------
with tf.variable_scope('target_net'):
    t1 = tf.layers.dense(self.s_, 20, tf.nn.relu, kernel_initializer=w_initializer,
                         bias_initializer=b_initializer, name='t1')
    self.q_next = tf.layers.dense(t1, self.n_actions, kernel_initializer=w_initializer,
                                  bias_initializer=b_initializer, name='t2')

網絡結構也能夠本身定義,總的原則就是輸入是狀態,輸出是Naction的可能性。

每隔必定的步數,咱們就要將target_net中的參數複製到eval_net中:

t_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,scope='target_net')
e_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,scope='eval_net')
with tf.variable_scope('soft_replacement'):
      self.target_replace_op = [tf.assign(t,e) for t,e in zip(t_params,e_params)]

計算損失並優化

首先,對於eval_net來講,咱們只要獲得當前的網絡輸出便可,可是咱們定義的網絡輸出是N個動做對應的q-eval值,咱們要根據實際的a來選擇對應的q-eval值,這一部分的代碼以下:

with tf.variable_scope('q_eval'):
    a_indices = tf.stack([tf.range(tf.shape(self.a)[0], dtype=tf.int32), self.a], axis=1)
    # indices從張量params獲得新張量
    # 這裏self.q_evalbatch * action_number,a_indicesbatch * 1,也就是說選擇當前估計每一個動做的Q
    self.q_eval_wrt_a = tf.gather_nd(params=self.q_eval, indices=a_indices)

第一部分的R咱們是已經獲得了的,剩下的就是根據貪心策略選擇N個輸出中最大的一個便可:

with tf.variable_scope('q_target'):
    q_target = self.r + self.gamma * tf.reduce_max(self.q_next,axis=1,name='Qmax_s_')
    # 一個節點被 stop以後,這個節點上的梯度,就沒法再向前BP
    self.q_target = tf.stop_gradient(q_target)

接下來,咱們就能夠定義咱們的損失函數並選擇優化器進行優化:

with tf.variable_scope('loss'):
    self.loss = tf.reduce_mean(tf.squared_difference(self.q_target,self.q_eval_wrt_a,name='TD_error'))
with tf.variable_scope('train'):
    self._train_op = tf.train.RMSPropOptimizer(self.lr).minimize(self.loss)

網絡的訓練

每隔必定的步數,咱們就要將eval_net中的參數複製到target_net中,同時咱們要從經驗池中選擇batch大小的數據輸入到網絡中進行訓練。

def learn(self):
    if self.learn_step_counter % self.replace_target_iter == 0:
        self.sess.run(self.target_replace_op)
        print('\ntarget_params_replaced\n')

    if self.memory_counter > self.memory_size:
        sample_index = np.random.choice(self.memory_size,size=self.batch_size)
    else:
        sample_index = np.random.choice(self.memory_counter,size = self.batch_size)

    batch_memory = self.memory[sample_index,:]

    _,cost = self.sess.run(
        [self._train_op,self.loss],
        feed_dict={
            self.s:batch_memory[:,:self.n_features],
            self.a:batch_memory[:,self.n_features],
            self.r:batch_memory[:,self.n_features+1],
            self.s_:batch_memory[:,-self.n_features:]
        }
    )
相關文章
相關標籤/搜索