多输出Q网络的深度Q学习损失计算Keras实现疑问
在Keras中实现DQN的动作对应损失计算方案
核心思路
DQN的损失只需要针对智能体实际执行的动作a计算Q值误差,其他动作的Q值在当前更新步骤中不需要调整。常规的简单实现不需要复杂的Keras后端操作,核心是构造合适的y_true数组。
方法一:构造带动作掩码的y_true(最常用)
这是DQN实现的标准做法,步骤如下:
- 从经验回放池中取出一批样本:
states, actions, rewards, next_states, donesactions是每个样本对应的动作索引(比如贪吃蛇的上/下/左/右对应0-3)
- 计算当前Q网络对
states的预测Q值:current_q_values = q_model.predict(states, verbose=0) - 计算目标网络对
next_states的最大Q值:next_q_values = target_q_model.predict(next_states, verbose=0) next_q_max = next_q_values.max(axis=1) - 用贝尔曼方程计算每个样本的目标Q值:
target_q_values = rewards + gamma * next_q_max * (1 - dones)- 注意:
dones是布尔数组,终止状态下next_q_max的权重为0,因为没有后续状态
- 注意:
- 构造
y_true:复制current_q_values,然后将每个样本中动作a对应的索引位置替换为target_q_values:y_true = current_q_values.copy() for i in range(len(states)): y_true[i, actions[i]] = target_q_values[i] - 用MSE损失训练模型:
这样,损失函数只会计算动作a对应位置的Q值误差,其他位置因为q_model.fit(states, y_true, batch_size=batch_size, epochs=1, verbose=0)y_true和y_pred(current_q_values)完全一致,误差为0,不会影响权重更新。
方法二:将动作作为模型输入(更直观)
如果觉得构造y_true不够直观,可以修改模型结构,把动作作为输入的一部分:
- 模型输入分为两部分:状态特征(比如贪吃蛇的游戏状态矩阵)和动作的one-hot编码
- 将状态特征和动作编码拼接后输入网络,输出该动作对应的Q值:
from keras.layers import Input, Dense, Concatenate state_input = Input(shape=state_shape) action_input = Input(shape=(num_actions,)) # one-hot编码的动作 x = Dense(64, activation='relu')(state_input) x = Concatenate()([x, action_input]) x = Dense(64, activation='relu')(x) q_value_output = Dense(1, activation='linear')(x) q_model = Model(inputs=[state_input, action_input], outputs=q_value_output) q_model.compile(optimizer='adam', loss='mse') - 训练时,输入是
[states, one_hot_actions],y_true就是贝尔曼计算的target_q_values:
这种方法直接针对每个[状态,动作]对计算Q值,损失就是该动作Q值与目标值的误差,逻辑更清晰,但需要调整模型输入结构。q_model.fit([states, one_hot_actions], target_q_values, batch_size=batch_size, epochs=1, verbose=0)
关键要点
你之前的误区在于认为损失函数需要直接获取动作信息,但实际上通过构造y_true的方式,已经间接实现了只计算目标动作的误差。第一种方法不需要修改模型结构,是DQN最常用的实现方式,大多数教程可能省略了构造y_true的细节,但这是核心步骤。
内容的提问来源于stack exchange,提问作者gacsta
相关产品推荐
相关产品推荐

