基于PyTorch的黑箱二维函数寻优深度强化学习实现问询
无Gym依赖的Q-Learning智能体实现(PyTorch)
完全可以用PyTorch或TensorFlow实现这个问题,不需要依赖Gym/Gymnasium类库——这个环境逻辑简单,我们可以手动实现状态转换、奖励计算的核心逻辑,直接训练Q网络智能体。
原代码的问题修正
你提供的ChatGPT生成代码存在几个影响训练效果的问题,修正点如下:
- 学习率设置过高(原代码0.9),Adam优化器适合用
1e-3量级的学习率 - 缺少ε-greedy探索机制,智能体初期需要随机探索,避免陷入局部最优
- 未对状态(x,y)做归一化,[-100,100]的数值范围会增加神经网络的训练难度
- 终止状态的目标Q值计算错误:找到最大值后没有后续动作,目标Q值应直接等于当前奖励,无需加上折扣后的未来Q值
- 多余的评估模式切换,无需在找到最大值时调用
eval()
修正后的完整代码
import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F import random # 设备配置 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # Q网络定义:输入坐标(x,y),输出4个动作的Q值 class QNetwork(nn.Module): def __init__(self): super(QNetwork, self).__init__() self.fc1 = nn.Linear(2, 128) self.fc2 = nn.Linear(128, 64) self.fc3 = nn.Linear(64, 4) def forward(self, x): x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) return self.fc3(x) # Q学习智能体 class Agent: def __init__(self): self.q_network = QNetwork().to(device) self.optimizer = optim.Adam(self.q_network.parameters(), lr=1e-3) # 修正学习率 self.gamma = 0.95 # 折扣因子 self.epsilon = 0.9 # 初始探索率 self.epsilon_min = 0.01 # 最小探索率 self.epsilon_decay = 0.995 # 探索率衰减系数 def select_action(self, state): # ε-greedy策略选择动作 if random.random() < self.epsilon: # 随机探索:选0-3的随机动作 return random.randint(0, 3) else: # 利用当前最优动作 with torch.no_grad(): q_values = self.q_network(state) return torch.argmax(q_values).item() def update_epsilon(self): # 衰减探索率 if self.epsilon > self.epsilon_min: self.epsilon *= self.epsilon_decay # 黑箱函数:模拟未知地形,这里用示例函数,实际可替换为任意黑箱逻辑 def calculate_function_value(x, y): # 示例函数最大值在x=-1, y=-1处,z=4,对应MAX_VALUE设为4 return 2 - x**2 - y**2 - 2*y - 2*x # 训练参数设置 agent = Agent() num_episodes = 1000 MAX_VALUE = 4 # 已知的函数最大值 x_range = (-100, 100) y_range = (-100, 100) # 状态归一化函数:把[-100,100]映射到[-1,1] def normalize_state(x, y): norm_x = (x - x_range[0]) / (x_range[1] - x_range[0]) * 2 - 1 norm_y = (y - y_range[0]) / (y_range[1] - y_range[0]) * 2 - 1 return torch.tensor([norm_x, norm_y], dtype=torch.float32).to(device) # 开始训练 for episode in range(num_episodes): # 随机初始化起始坐标 x, y = random.randint(*x_range), random.randint(*y_range) state = normalize_state(x, y) total_reward = 0 done = False while not done: # 选择动作 action = agent.select_action(state) # 执行动作,更新坐标 if action == 0: # 左移 x -= 1 x = max(x, x_range[0]) elif action == 1: # 右移 x += 1 x = min(x, x_range[1]) elif action == 2: # 下移 y -= 1 y = max(y, y_range[0]) elif action == 3: # 上移 y += 1 y = min(y, y_range[1]) # 计算新状态和奖励 new_state = normalize_state(x, y) z = calculate_function_value(x, y) reward = -1 if abs(z - MAX_VALUE) < 1e-6: # 浮点误差容忍 reward = 100 done = True total_reward += reward # 计算目标Q值 with torch.no_grad(): if done: target_q = torch.tensor(reward, dtype=torch.float32).to(device) else: max_future_q = torch.max(agent.q_network(new_state)) target_q = reward + agent.gamma * max_future_q # 计算当前Q值和损失 current_q = agent.q_network(state)[action] loss = F.mse_loss(current_q, target_q) # 优化网络 agent.optimizer.zero_grad() loss.backward() agent.optimizer.step() # 更新状态 state = new_state # 衰减探索率 agent.update_epsilon() print(f"第 {episode+1} 轮训练,总奖励: {total_reward},当前探索率: {agent.epsilon:.3f}") # 测试智能体:从随机位置出发,验证是否能快速找到最大值 print("\n=== 测试智能体 ===") x_test, y_test = random.randint(*x_range), random.randint(*y_range) print(f"起始位置: ({x_test}, {y_test})") state_test = normalize_state(x_test, y_test) steps = 0 while True: action = agent.select_action(state_test) if action == 0: x_test -= 1 elif action == 1: x_test += 1 elif action == 2: y_test -= 1 elif action == 3: y_test += 1 steps += 1 z_test = calculate_function_value(x_test, y_test) if abs(z_test - MAX_VALUE) < 1e-6: print(f"找到最大值!位置: ({x_test}, {y_test}),步数: {steps}") break state_test = normalize_state(x_test, y_test)
关键说明
- 状态归一化:将[-100,100]的坐标映射到[-1,1],让神经网络更容易学习到状态和动作的关联
- ε-greedy探索:初期高探索率让智能体遍历更多状态,后期逐步降低探索率,转为利用已学习的最优策略
- 终止状态处理:当找到最大值时,目标Q值直接等于奖励,因为没有后续动作的收益
- 黑箱函数替换:只需修改
calculate_function_value函数,即可适配任意未知黑箱函数,无需改动其他逻辑
内容的提问来源于stack exchange,提问作者Bubble
相关产品推荐
相关产品推荐

