您的位置:首页 > 其它

DQN——深度强化学习的理解以及keras实现

2020-06-06 07:31 211 查看

起源

Q-learing与SARSA算法

Q-learing是一种经典的时序差分离线控制算法,与之相对的SARSA算法是时序差分在线控制算法的代表。

所谓的在线,是一直使用一个策略来更新价值函数和选择新的动作。而离线是使用两个控制策略,一个策略用于选择新的动作,另一个策略用于更新价值函数。

SARSA算法流程为:

  1. 起初,我们使用ϵ−\epsilon-ϵ−贪婪法在当前状态S选择一个动作A,这样系统会转到一个新的状态S′S^\primeS′, 同时给我们一个即时奖励RRR, 在新的状态S′S^\primeS′,
  2. 然后,我们使用ϵ−\epsilon-ϵ−贪婪法在状态S′S^\primeS′选择一个动作A′A^\primeA′,但是注意这时候我们并不执行这个动作A′,只是用来更新的我们的价值函数:
    Q(S,A)=Q(S,A)+α(R+γQ(S′,A′)−Q(S,A)) Q(S,A)=Q(S,A)+\alpha(R+\gamma Q(S^\prime,A^\prime)-Q(S,A)) Q(S,A)=Q(S,A)+α(R+γQ(S′,A′)−Q(S,A))
  3. S=S′S=S^\primeS=S′,A=A′A=A^\primeA=A′

注释:

  1. Q(S,A)Q(S,A)Q(S,A)更新时使用的A′A^\primeA′将会作为下一阶段开始时候的执行动作AAA。
  2. 选择的动作A′A^\primeA′只会参与价值函数的更新,不会真正的执行。

Q-learing算法流程为:

  1. 起初,我们使用ϵ−\epsilon-ϵ−贪婪法在当前状态S选择一个动作A,这样系统会转到一个新的状态S′S^\primeS′, 同时给我们一个即时奖励RRR, 在新的状态S′S^\primeS′,
  2. 然后,我们使用贪婪法在状态S′S^\primeS′选择一个动作A′A^\primeA′,但是注意这时候我们并不执行这个动作A′,只是用来更新的我们的价值函数:
    Q(S,A)=Q(S,A)+α(R+γmaxaQ(S′,A′)−Q(S,A)) Q(S,A)=Q(S,A)+\alpha(R+\gamma \mathop{max}\limits_{a}Q(S^\prime,A^\prime)-Q(S,A)) Q(S,A)=Q(S,A)+α(R+γamax​Q(S′,A′)−Q(S,A))
  3. S=S′S=S^\primeS=S′

注释:
1.选择的动作A′A^\primeA′只会参与价值函数的更新,不会真正的执行。
2. Q(S,A)Q(S,A)Q(S,A)更新后,下一轮迭代新的执行动作AAA需要基于状态S′,用ϵ−贪婪法重新选择得到。

DQN算法解读

Q-learning相比,DQN的Q(S,A)Q(S,A)Q(S,A)不是直接通过状态值SSS和动作AAA来计算,而是通过一个神经网络QQQ网络来计算。
如果想用神经网络取而代之,就需要一个技巧取代先前用来存放QQQ值表格,这里用到了经验回放(experience replay)。其作用是将每次和环境交互得到的奖励RRR与状态SSS得更新情况都保存起来,用于后面目标QQQ值的更新。

DQN算法流程为:

  1. 基于状态SSS,可得到对应得特征向量h(S)h(S)h(S)。
  2. 将h(S)h(S)h(S)作为神经网络的输入。神经网络输出得到所有动作对应的QQQ值输出。用ϵ−\epsilon-ϵ−贪婪法在当前QQQ值输出中选择对应的动作AAA。
  3. 在状态SSS执行当前动作AAA,得到新状态S′S^\primeS′对应的特征向量h(S′)h(S^\prime)h(S′)和奖励RRR。
  4. 将[h(S),A,R,h(S′),done][h(S),A,R,h(S^\prime),done][h(S),A,R,h(S′),done]这个5元组存入经验回放集合DDD。
  5. S=S′S=S\primeS=S′
  6. 从经验回放集合DDD中采样mmm个样本[h(Sj),Aj,Rj,h(Sj′),done][h(S_j),A_j,R_j,h(S_j^\prime),done][h(Sj​),Aj​,Rj​,h(Sj′​),done],j=1,2.,,,mj=1,2.,,,mj=1,2.,,,m,计算当前目标QQQ值:
    yj=Rj+Q(h(Sj′),Aj′,w) y_j=R_j+Q(h(S_j^\prime),A^\prime_j,w) yj​=Rj​+Q(h(Sj′​),Aj′​,w)
  7. 使用mse损失函数更新网络参数:
    Loss=1m(yj−Q(h(Sj),Aj,w)) Loss=\frac{1}{m}(y_j-Q(h(S_j),A_j,w)) Loss=m1​(yj​−Q(h(Sj​),Aj​,w))
    注:使用神经网络计算Q(h(Sj),Aj,w)Q(h(S_j),A_j,w)Q(h(Sj​),Aj​,w)和Q(h(Sj′),Aj′,w)Q(h(S_j^\prime),A^\prime_j,w)Q(h(Sj′​),Aj′​,w)的值。

此外,平时阅读各类文献出现的名词:目标QQQ值(target Q−Q-Q−value)为Rt+1+Q(St+1,At+1)R_{t+1}+Q(S_{t+1},A_{t+1})Rt+1​+Q(St+1​,At+1​)。再根据贝尔曼方程(Bellman Equation)可知:
Vπ(S)=E(Rt+1+γRt+2+γ2Rt+3+...∣St=s)=uA⋅nB=E(Rt+1+γ(Rt+2+γRt+3+...)∣St=s)=E(Rt+1+γGt+1∣St=s)=E(Rt+1+Q(St+1,At+1)∣St=s) \begin{aligned} V_\pi(S) & = E(R_{t+1}+\gamma R_{t+2}+\gamma ^2R_{t+3}+...|S_t=s) & =uA\cdot nB \\ & =E(R_{t+1}+\gamma (R_{t+2}+\gamma R_{t+3}+...)|S_t=s)\\ & = E(R_{t+1}+\gamma G_{t+1}|S_t=s) \\ & = E(R_{t+1}+Q(S_{t+1},A_{t+1})|S_t=s) \end{aligned} Vπ​(S)​=E(Rt+1​+γRt+2​+γ2Rt+3​+...∣St​=s)=E(Rt+1​+γ(Rt+2​+γRt+3​+...)∣St​=s)=E(Rt+1​+γGt+1​∣St​=s)=E(Rt+1​+Q(St+1​,At+1​)∣St​=s)​=uA⋅nB
将VVV换成QQQ的话,我们可以发现(target Q−Q-Q−value)是Q(St,At)Q(S_{t},A_{t})Q(St​,At​)的估计值,这也就解释了神经网络损失函数LossLossLoss为什么是那样。

keras实现DQN

这里使用Gym进行模拟。

Gym 是 OpenAI 发布的用于开发和比较强化学习算法的工具包。使用它我们可以让 AI 智能体做很多事情,比如行走、跑动,以及进行多种游戏。在这个Demo中,我们使用的是车杆游戏(Cart-Pole)这个小游戏。

游戏规则:游戏里面有一个小车,上有竖着一根杆子。小车需要左右移动来保持杆子竖直。如果杆子倾斜的角度大于15°,那么游戏结束。小车也不能移动出一个范围(中间到两边各2.4个单位长度)。

环境

  • Python 3.6
  • Tensorflow-gpu 1.1.0
  • Keras 2.1.1
  • Gym 0.10.8
from DRL import DRL
from collections import deque
import os
import random
from keras.layers import Dense,Input
from keras import Model
from keras.optimizers import Adam
import numpy as np

class DQN(DRL):
def __init__(self):
super(DQN, self).__init__()

self.model = self.build_model()
self.memory_buffer = deque(maxlen=200)
self.gamma = 0.95
self.epsilon = 1.0
self.epsilon_decay = 0.995
self.epsilon_min = 0.01

def load(self):
if os.path.exists("model/dqn.h5"):
self.model.load_weights("model/dqn.h5")

def build_model(self):
inputs = Input(shape=(4,))
x = Dense(16, activation='relu')(inputs)
x = Dense(16, activation= 'relu')(x)
x = Dense(2, activation='linear')(x)

model = Model(inputs=inputs, outputs=x)

model.compile(loss='mse', optimizer=Adam(1e-3))

return model

def egreedy_action(self, state):
if np.random.rand() >= self.epsilon:
return random.randint(0,1)
else:
q_values = self.model.predict(state)
return np.argmax(q_values)

def remember(self, state, action, reward, next_state, done):
item = (state, action, reward, next_state, done)
self.memory_buffer.append(item)

def update_epsilon(self):
if self.epsilon >= self.epsilon_min:
self.epsilon *= self.epsilon_decay

def process_batch(self,batch):
data = random.sample(self.memory_buffer, batch)

# 这里取元组的第1个值,即当前state
states = np.array([d[0] for d in data])

# 这里取元组的第4个值,即下一个state
next_states = np.array([d[3] for d in data])

y = self.model.predict(states)
q = self.model.predict(next_states)
print("y value {}, and q value {}".format(y, q))
for i, (_, action, reward, _, done) in enumerate(data):
target = reward
if not done:
target += self.gamma * np.amax(q[i])
y[i][action] = target

return states, y

def train(self, episode, batch):
history = {'episode': [], 'Episode_reward': [], 'Loss': []}

count = 0
for i in range(episode):
observation = self.env.reset()
reward_sum = 0
loss = np.infty
done = False

while not done:
# 一个参数为-1时,reshape函数会根据另一个参数的维度计算出数组的另外一个shape属性值
x = observation.reshape(-1, 4)
action = self.egreedy_action(x)
observation, reward, done, _ = self.env.step(action)
reward_sum += reward
self.remember(x[0], action, reward, observation, done)

if len(self.memory_buffer) > batch:
X, y = self.process_batch(batch)
loss = self.model.train_on_batch(X, y)

count += 1
self.update_epsilon()

if i % 5 == 0:
history['episode'].append(i)
history['Episode_reward'].append(reward_sum)
history['Loss'].append(loss)

print('Episode: {} | Episode reward: {} | loss: {:.3f} | e:{:.2f}'.format(i, reward_sum, loss, self.epsilon))

self.model.save_weights('model/dqn.h5')

return history

if __name__ == '__main__':
model = DQN()

history = model.train(600, 32)
model.save_history(history, 'dqn.csv')
model.load()
model.play()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐