理解和实现 Keras 架构下的 Dueling DQN

Dueling DQN(双重深度Q网络)是一种增强学习算法,它在学习的过程中将动作价值(Q-value)分解为状态价值和优势函数。这种结构能够有效提高智能体在复杂环境中的决策能力。

本文将指导你如何在Keras环境下实现Dueling DQN。我们将使用Python及Keras库来构建和训练我们的模型。

实现流程

在开始编写代码之前,首先要了解整个过程的步骤。以下是Dueling DQN实现的主要步骤:

步骤 描述
1 导入必要的库
2 创建环境
3 定义神经网络架构
4 设定超参数
5 初始化经验回放存储器
6 训练DQN算法
7 测试模型

接下来,我们将逐步深入每一个细节。

第一步:导入必要的库

在开始之前,我们需要导入将要使用的库。

import numpy as np
import gym
from keras.models import Sequential
from keras.layers import Dense, Input
from keras.optimizers import Adam
from collections import deque
import random

代码说明

  • numpy: 用于数值计算。
  • gym: OpenAI的强化学习环境库。
  • keras.modelskeras.layers: 用于构建深度学习模型。
  • deque: 提供一个双端队列,用于存储经验回放。
  • random: 用于实现随机选择。

第二步:创建环境

选择我们要使用的环境。这是通过gym创建的。

env = gym.make('CartPole-v1')

代码说明

  • gym.make('CartPole-v1'): 创建一个CartPole环境,目标是平衡一个杆子在移动的底座上。

第三步:定义神经网络架构

构建Dueling DQN的核心在于创建适当的神经网络架构。

def build_dueling_dqn(state_size, action_size):
    # 创建模型
    model = Sequential()
    model.add(Input(shape=(state_size,)))
    model.add(Dense(24, activation='relu'))
    model.add(Dense(24, activation='relu'))
    
    # 状态价值网络
    value = Dense(1, activation='linear')(model.output)
    
    # 优势网络
    advantage = Dense(action_size, activation='linear')(model.output)
    
    # 将值和优势结合
    from keras.layers import Lambda
    from keras import backend as K

    def dueling_output(inputs):
        advantage_output = K.expand_dims(inputs[1], axis=1)  # 状态价值
        value_output = inputs[0]  # 优势
        return value_output + (advantage_output - K.mean(advantage_output, axis=1, keepdims=True))

    output = Lambda(dueling_output)([value, advantage])
    
    model = Model(inputs=model.input, outputs=output)
    model.compile(loss='mse', optimizer=Adam(lr=0.001))
    return model

代码说明

  • build_dueling_dqn: 创建Dueling DQN模型。
  • Dense: 全连接层。
  • Lambda: 构建自定义输出层以实现Dueling结构。
  • K.mean(): 用于计算优势函数的平均值,以便在输出中进行减法操作。

第四步:设定超参数

定义超参数是模型训练成功的关键。

state_size = env.observation_space.shape[0]
action_size = env.action_space.n
memory = deque(maxlen=2000)  # 经验回放存储器
batch_size = 32
gamma = 0.95  # 折扣因子
epsilon = 1.0  # 初始epsilon值
epsilon_decay = 0.995
epsilon_min = 0.01

代码说明

  • state_sizeaction_size: 状态空间和动作空间的大小。
  • memory: 存储经验的队列。
  • gamma: 折扣因子,用于未来奖励的估计。
  • epsilon: 用于ε-greedy策略的阈值。
  • epsilon_decay: 每次训练后降低的epsilon值。
  • epsilon_min: epsilon的最小值。

第五步:初始化经验回放存储器

添加经验回放逻辑。

def remember(state, action, reward, next_state, done):
    memory.append((state, action, reward, next_state, done))

代码说明

  • remember: 将 (状态,动作,奖励,下一个状态,终止标志) 存储到经验回放中。

第六步:训练DQN算法

训练过程包括选择动作、存储经验、采样以及训练模型。

def train_dqn():
    for e in range(EPISODES):
        state = env.reset()
        state = np.reshape(state, [1, state_size])
        
        for time in range(500):
            if np.random.rand() <= epsilon:
                action = random.randrange(action_size)
            else:
                q_values = model.predict(state)
                action = np.argmax(q_values[0])
            
            next_state, reward, done, _ = env.step(action)
            reward = reward if not done else -10
            next_state = np.reshape(next_state, [1, state_size])
            remember(state, action, reward, next_state, done)
            state = next_state
            
            if len(memory) > batch_size:
                minibatch = random.sample(memory, batch_size)
                for sample in minibatch:
                    s, a, r, s_next, d = sample
                    target = r
                    if not d:
                        target += gamma * np.amax(model.predict(s_next)[0])
                    target_f = model.predict(s)
                    target_f[0][a] = target
                    model.fit(s, target_f, epochs=1, verbose=0)
            
            if done:
                print(f"Episode: {e}/{EPISODES}, Score: {time}, Epsilon: {epsilon}")
                break
        
        if epsilon > epsilon_min:
            epsilon *= epsilon_decay

代码说明

  • train_dqn: 训练DQN的主函数。
  • 通过ε-greedy策略选择动作,存储经验,并进行训练。

第七步:测试模型

测试训练成功的模型表现。

for e in range(5):
    state = env.reset()
    state = np.reshape(state, [1, state_size])
    for time in range(500):
        env.render()  # 可视化环境
        q_values = model.predict(state)
        action = np.argmax(q_values[0])
        next_state, reward, done, _ = env.step(action)
        next_state = np.reshape(next_state, [1, state_size])
        state = next_state
        if done:
            print(f"Test Episode: {e}, Score: {time}")
            break

代码说明

  • 在测试过程中,我们通过可视化展示模型在环境中的表现。

结尾

通过以上步骤,你已经学会了如何实现Keras架构下的Dueling DQN。你可以根据需求进一步优化超参数和网络架构,以适应其他Gym环境或更多的复杂场景。希望你在强化学习的道路上越走越远!