You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DQN模型训练失效或异常缓慢问题求助(附实现代码)

DQN训练失效/缓慢问题排查与修复

问题描述

我正在为博士研究构建DQN模型,在应用真实数据前先用模拟数据(dummy data)做测试。之前用简单Q Learning方法时效果不错,但切换到DQN后,训练要么失效要么异常缓慢,已经开启GPU加速但无济于事,怀疑是模拟数据集规模或其他未知因素导致,寻求专业指导。

修复后的实现代码

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import random
import pandas as pd
from collections import deque

# 设置计算设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("运行设备:", device)

# 生成模拟数据
data = {
    'message_size': np.random.randint(1000, 70000, size=1000),
    'cpu_usage': np.random.uniform(40, 100, size=1000),
    'submission_time': np.random.uniform(0, 300, size=1000)
}
dummy_data = pd.DataFrame(data)

# 超参数设置
MAX_BLOCK_SIZE = 32768
GAMMA = 0.9
EPSILON = 1.0
EPSILON_MIN = 0.01
EPSILON_DECAY = 0.99
BATCH_SIZE = 32
EPISODES = 1000
MAX_STEPS_PER_EPISODE = 20
TARGET_UPDATE_FREQ = 10

# DQN模型定义
class DQN(nn.Module):
    def __init__(self, input_dim, num_actions):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(input_dim, 64)
        self.fc2 = nn.Linear(64, 64)
        self.fc3 = nn.Linear(64, num_actions)
    
    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)

# 计算最大可能动作数
max_possible_action = int(dummy_data['submission_time'].max() // MAX_BLOCK_SIZE) + 1

# 初始化模型、优化器与经验回放池
dqn = DQN(input_dim=2, num_actions=max_possible_action).to(device)
target_model = DQN(input_dim=2, num_actions=max_possible_action).to(device)
target_model.load_state_dict(dqn.state_dict())
optimizer = optim.Adam(dqn.parameters(), lr=0.001)
memory = deque(maxlen=2000)

# 动作选择函数
def block_choice(state):
    max_action = int(state[0] // MAX_BLOCK_SIZE) + 1
    if random.random() < EPSILON:
        return random.randint(1, max_action)
    else:
        state_tensor = torch.FloatTensor(state).unsqueeze(0).to(device)
        q_values = dqn(state_tensor)
        valid_q_values = q_values[0, :max_action]
        return torch.argmax(valid_q_values).item() + 1

# 奖励函数
def utility_function_rewarding(total_latency, cpu_per_block, max_latency=300, max_cpu=100):
    latency_reward = max(0, 1 - (total_latency / max_latency))
    cpu_reward = max(0, 1 - (cpu_per_block / max_cpu))
    return latency_reward + cpu_reward

# DQN训练函数
def dqn_training(batch_size):
    if len(memory) < batch_size:
        return
    batch = random.sample(memory, batch_size)
    states, actions, rewards, next_states, dones = zip(*batch)
    
    states = torch.FloatTensor(states).to(device)
    rewards = torch.FloatTensor(rewards).unsqueeze(1).to(device)
    next_states = torch.FloatTensor(next_states).to(device)
    dones = torch.FloatTensor(dones).unsqueeze(1).to(device)

    # 获取当前状态下所选动作的Q值
    state_action_values = dqn(states).gather(1, torch.LongTensor(actions).unsqueeze(1).to(device) - 1)
    # 获取下一状态的最大Q值(来自目标网络)
    next_state_values = target_model(next_states).max(1)[0].unsqueeze(1)
    expected_values = rewards + (GAMMA * next_state_values * (1 - dones))

    loss = nn.functional.mse_loss(state_action_values, expected_values)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

# 存储经验到回放池
def store_transition(state, action, reward, next_state, done):
    memory.append((state, action, reward, next_state, done))

# 主训练循环
for episode in range(EPISODES):
    print(f"开始第 {episode + 1}/{EPISODES} 轮训练")
    row = dummy_data.sample().iloc[0]
    state = [row['submission_time'], row['cpu_usage']]
    total_reward = 0
    done = False
    steps = 0

    while not done and steps < MAX_STEPS_PER_EPISODE:
        action = block_choice(state)
        # 基于当前动作生成下一状态(替代原随机采样逻辑)
        next_latency = state[0] * (1 - 0.05 * action)  # 示例:块数量越多,延迟降低
        next_cpu = state[1] / action  # 块数量越多,单块CPU占用越低
        next_state = [next_latency, next_cpu]

        reward = utility_function_rewarding(next_latency, next_cpu)
        total_reward += reward
        done = (steps == MAX_STEPS_PER_EPISODE - 1)
        store_transition(state, action, reward, next_state, done)
        
        state = next_state
        dqn_training(BATCH_SIZE)
        steps += 1
    
    # 衰减探索率
    if EPSILON > EPSILON_MIN:
        EPSILON *= EPSILON_DECAY

    # 定期更新目标网络
    if episode % TARGET_UPDATE_FREQ == 0:
        target_model.load_state_dict(dqn.state_dict())

    print(f"第 {episode + 1}/{EPISODES} 轮训练完成 - 总奖励: {total_reward:.2f}")

核心问题与修复说明

  • 动作空间与模型输出不匹配:原模型仅输出1维值,无法对应离散动作空间。修改输出层维度为最大可能动作数,确保每个动作有独立Q值。
  • 训练循环死循环:原代码仅最后一轮训练才终止循环,导致前序轮次无限运行。改为每轮训练固定步数后终止。
  • 状态转移无因果关联:原逻辑随机采样下一状态,动作与状态变化完全无关,模型无法学习有效策略。调整为基于当前动作生成下一状态,建立动作-状态的逻辑关联。
  • 学习率过高:原Adam优化器学习率0.1过大,导致参数震荡无法收敛,调整为DQN常用的0.001。
  • 缺失目标网络更新:新增定期同步目标网络参数的逻辑,避免Q值估计波动,提升训练稳定性。
  • 动作索引逻辑错误:修正动作的0/1基转换问题,确保模型输出的索引与实际可选动作范围匹配。

内容的提问来源于stack exchange,提问作者Konstantinos Voulgaridis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 15:28:11