策略梯度(Policy Gradient)的基本思想,就是直接根據狀態輸出動做或者動做的機率。注意這裏和DQN的區別就是DQN輸出動做獲取的Q值,而Policy Gradient輸出的是動做的機率,二者的輸出維度是同樣的,可是含義不一樣。git
咱們使用神經網絡輸入當前的狀態,網絡就能夠輸出咱們在這個狀態下采起每一個動做的機率,那麼網絡應該如何訓練來實現最終的收斂呢?咱們以前在訓練神經網絡時,使用最多的方法就是反向傳播算法,咱們須要一個偏差函數,經過梯度降低來使咱們的損失最小。但對於強化學習來講,咱們不知道動做的正確與否,只能經過獎勵值來判斷這個動做的相對好壞。基於上面的想法,咱們有個很是簡單的想法:算法
若是一個動做獲得的reward多,那麼咱們就使其出現的機率增長,若是一個動做獲得的reward少,咱們就使其出現的機率減少。緩存
根據這個思想,咱們構造以下的損失函數:loss= -log(prob)*vt網絡
上式中log(prob)表示在狀態 s 時所選動做爲a的機率值,prob<=1,所以log(prob)<0。顯然prob這個值越大越好,此時-log(prob)<0而且越小越好。app
而vt表明的是當前狀態s下采起動做a所能獲得的獎勵,這是當前的獎勵和將來獎勵的貼現值的求和。若是在prob很小的狀況下, 獲得了一個大的結果,也就是大的vt, 那麼-log(prob)*vt就更大, 表示更吃驚, (我選了一個不常選的動做, 卻發現原來它能獲得了一個好的 reward, 那我就得對我此次的參數進行一個大幅修改)。dom
換句話說,咱們訓練的目標就是使得被選擇的動做的機率很大,同時使得它能得到的獎勵貼現最小,說明此時已經不適合再選取其餘動做了。函數
這就是 -log(prob)*vt的物理意義。下面咱們看看策略梯度的代碼應該怎麼寫。學習
一、定義參數優化
首先,咱們定義了一些模型的參數:spa
#self.ep_obs,self.ep_as,self.ep_rs分別存儲了當前episode的狀態,動做和獎勵。 self.n_actions = n_actions #動做維度 self.n_features = n_features #狀態維度 self.lr = learning_rate #學習速率 self.gamma = reward_decay #貼現率 self.ep_obs,self.ep_as,self.ep_rs = [],[],[] #經驗回放緩存
二、定義模型輸入
模型的輸入包括三部分,分別是觀察值,動做和獎勵值。
with tf.name_scope('inputs'): self.tf_obs = tf.placeholder(tf.float32,[None,self.n_features],name='observation') self.tf_acts = tf.placeholder(tf.int32,[None,],name='actions_num') self.tf_vt = tf.placeholder(tf.float32,[None,],name='actions_value')
三、構建模型
咱們的模型定義了兩層的神經網絡,網絡的輸入是每次的狀態值,而輸出是該狀態下采起每一個動做的機率,這些機率在最後會通過一個softmax獲得歸一化以後的各個動做的機率值向量。
layer = tf.layers.dense( inputs = self.tf_obs, units = 10, activation= tf.nn.tanh, kernel_initializer=tf.random_normal_initializer(mean=0,stddev=0.3), bias_initializer= tf.constant_initializer(0.1), name='fc1' ) all_act = tf.layers.dense( inputs = layer, units = self.n_actions, activation = None, kernel_initializer=tf.random_normal_initializer(mean=0,stddev=0.3), bias_initializer = tf.constant_initializer(0.1), name='fc2' ) self.all_act_prob = tf.nn.softmax(all_act,name='act_prob')
四、模型的損失
咱們以前介紹過了,模型的損失函數計算公式爲:loss= -log(prob)*vt,咱們能夠直接使用tf.nn.sparse_softmax_cross_entropy_with_logits 來計算前面一部分,即-log(prob),不過爲了更清楚的顯示咱們的計算過程,咱們使用了以下的方式:
with tf.name_scope('loss'): neg_log_prob = tf.reduce_sum(-tf.log(self.all_act_prob) * tf.one_hot(indices=self.tf_acts,depth=self.n_actions),axis=1) loss = tf.reduce_mean(neg_log_prob * self.tf_vt)
而咱們選擇AdamOptimizer優化器進行參數的更新:
with tf.name_scope('train'): self.train_op = tf.train.AdamOptimizer(self.lr).minimize(loss)
五、動做選擇
咱們這裏動做的選擇再也不根據貪心的策略來選擇了,而是根據輸出動做機率的大小來選擇不一樣的可能性選擇對應的動做:
def choose_action(self,observation): prob_weights = self.sess.run(self.all_act_prob,feed_dict={self.tf_obs:observation[np.newaxis,:]}) action = np.random.choice(range(prob_weights.shape[1]),p=prob_weights.ravel()) return action
六、存儲經驗
以前說過,policy gradient是在一個完整的episode結束後纔開始訓練的,所以,在一個episode結束前,咱們要存儲這個episode全部的經驗,即狀態,動做和獎勵。
def store_transition(self,s,a,r): self.ep_obs.append(s) self.ep_as.append(a) self.ep_rs.append(r)
七、計算獎勵的貼現值
咱們以前存儲的獎勵是當前狀態s採起動做a得到的即時獎勵,而當前狀態s採起動做a所得到的真實獎勵應該是即時獎勵加上將來直到episode結束的獎勵貼現和。
def _discount_and_norm_rewards(self): discounted_ep_rs = np.zeros_like(self.ep_rs) running_add = 0 # reserved 返回的是列表的反序,這樣就獲得了貼現求和值。 for t in reversed(range(0,len(self.ep_rs))): running_add = running_add * self.gamma + self.ep_rs[t] discounted_ep_rs[t] = running_add discounted_ep_rs -= np.mean(discounted_ep_rs) discounted_ep_rs /= np.std(discounted_ep_rs) return discounted_ep_rs
八、模型訓練
在定義好上面全部的部件以後,咱們就能夠編寫模型訓練函數了,這裏須要注意的是,咱們餵給模型的並非咱們存儲的獎勵值,而是在通過上一步計算的獎勵貼現和。另外,咱們須要在每一次訓練以後清空咱們的經驗池。
def learn(self): discounted_ep_rs_norm = self._discount_and_norm_rewards() self.sess.run(self.train_op,feed_dict={ self.tf_obs:np.vstack(self.ep_obs), self.tf_acts:np.array(self.ep_as), self.tf_vt:discounted_ep_rs_norm, }) self.ep_obs,self.ep_as,self.ep_rs = [],[],[] return discounted_ep_rs_norm
好了,模型相關的代碼咱們就介紹完了,如何調用這個模型的代碼相信你們一看便明白,咱們就再也不介紹啦。