训练 DQN 时 Q 值爆炸

Ome*_*ick 6 python machine-learning neural-network keras tensorflow

我正在训练一个 DQN 来玩 OpenAI 的 Atari 环境,但是我的网络的 Q 值很快就远远超出了实际值。

这是代码的相关部分:

for state, action, reward, next_state, done in minibatch:
        if not done:
            # To save on memory, next_state is just one frame
            # So we have to add it to the current state to get the actual input for the network
            next_4_states = np.array(state)
            next_4_states = np.roll(next_4_states, 1, axis=3)
            next_4_states[:, :, :, 0] = next_state
            target = reward + self.gamma * \
                np.amax(self.target_model.predict(next_4_states))
        else:
            target = reward
        target_f = self.target_model.predict(state)
        target_f[0][action] = target

        self.target_model.fit(state, target_f, epochs=1, verbose=0)
Run Code Online (Sandbox Code Playgroud)

折扣因子为 0.99(折扣因子为 0.9 时不会发生,但也不会收敛,因为它无法提前考虑得足够远)。

逐步执行代码,它发生的原因是所有不打算更新的 Q 值(我们没有采取的行动)略有增加。我的理解是,在训练期间将网络自己的输出传递给网络应该保持输出不变,而不是增加或减少它。我的模型有问题吗?有什么方法可以屏蔽更新,以便它只更新相关的 Q 值?

编辑:我的模型创建代码在这里:

def create_model(self, input_shape, num_actions, learning_rate):
        model = Sequential()
        model.add(Convolution2D(32, 8, strides=(4, 4),
                                activation='relu', input_shape=(input_shape)))
        model.add(Convolution2D(64, 4, strides=(2, 2), activation='relu'))
        model.add(Convolution2D(64, 3, strides=(1, 1), activation='relu'))
        model.add(Flatten())
        model.add(Dense(512, activation='relu'))
        model.add(Dense(num_actions))

        model.compile(loss='mse', optimizer=Adam(lr=learning_rate))

        return model
Run Code Online (Sandbox Code Playgroud)

我创建了其中的两个。一种用于在线网络,一种用于目标。

Den*_*ers 5

哪些预测会更新?

逐步执行代码,它发生的原因是所有不打算更新的 Q 值(我们没有采取的行动)略有增加。我的理解是,在训练期间将网络自己的输出传递给网络应该保持输出不变,而不是增加或减少它。

下面我画了一个非常简单的神经网络,有 3 个输入节点、3 个隐藏节点和 3 个输出节点。假设您只为第一个动作设置了一个新目标,并简单地将现有预测再次用作其他动作的目标。这仅导致delta第一个动作/输出的非零(为简单起见,我假设大于零)错误(在图像中表示),以及0其他。

我已经绘制了这个错误将从输出传播到隐藏层的粗体连接。请注意隐藏层中的每个节点如何仍然出错。当这些节点然后将它们的错误传播回输入层时,它们将通过输入层和隐藏层之间的所有连接来执行此操作,因此可以修改所有这些权重。

所以,想象一下所有这些权重都被更新了,现在想象一下用原始输入做一个新的前向传递。您是否希望输出节点 2 和 3 具有与以前完全相同的输出?不,可能不是;从隐藏节点到最后两个输出的连接可能仍然具有相同的权重,但所有三个隐藏节点将具有不同的激活级别。所以不,其他输出不能保证保持不变。

示例神经网络

有什么方法可以屏蔽更新,以便它只更新相关的 Q 值?

不容易,如果有的话。问题在于,除了最后一对层之间的连接之外,层对之间的连接不是特定于动作的,我认为您也不希望它们如此。

目标网络

我的模型有问题吗?

我看到的一件事是,您似乎正在更新用于生成目标的同一个网络:

target_f = self.target_model.predict(state)
Run Code Online (Sandbox Code Playgroud)

self.target_model.fit(state, target_f, epochs=1, verbose=0)
Run Code Online (Sandbox Code Playgroud)

两者都使用self.target_model. 您应该为这两行使用单独的网络副本,并且只有在较长时间后才能将更新后的网络权重复制到用于计算目标的网络中。对于多一点关于这个,看到加3在这个岗位

双DQN

除此之外,众所周知,DQN 仍然有高估 Q 值的趋势(尽管它通常不应该完全爆炸)。这可以通过使用双 DQN来解决(注意:这是后来在 DQN 之上添加的改进)。