-
class DQN: ''' DQN算法 ''' def __init__(self, state_dim, hidden_dim, action_dim, learning_rate, gamma, epsilon, target_update, device): self.action_dim = action_dim self.q_net = Qnet(state_dim, hidden_dim, self.action_dim).to(device) # Q网络 # 目标网络 self.target_q_net = Qnet(state_dim, hidden_dim, self.action_dim).to(device) # 使用Adam优化器 self.optimizer = torch.optim.Adam(self.q_net.parameters(), lr=learning_rate) self.gamma = gamma # 折扣因子 self.epsilon = epsilon # epsilon-贪婪策略 self.target_update = target_update # 目标网络更新频率 self.count = 0 # 计数器,记录更新次数 self.device = device def take_action(self, state): # epsilon-贪婪策略采取动作 if np.random.random() < self.epsilon: action = np.random.randint(self.action_dim) else: state = torch.tensor([state], dtype=torch.float).to(self.device)、 # 输入到网络当中的假如是n个状态,输出的是m个动作对应的不同状态下的q-value值,先找到最大项的索引,然后取值 action = self.q_net(state).argmax().item() return action def update(self, transition_dict): states = torch.tensor(transition_dict['states'], dtype=torch.float).to(self.device) actions = torch.tensor(transition_dict['actions']).view(-1, 1).to( self.device) rewards = torch.tensor(transition_dict['rewards'], dtype=torch.float).view(-1, 1).to(self.device) next_states = torch.tensor(transition_dict['next_states'], dtype=torch.float).to(self.device) dones = torch.tensor(transition_dict['dones'], dtype=torch.float).view(-1, 1).to(self.device) # gather函数第一个参数是1就代表gather的索引列表都是针对列说的,要是0就代表针对行说的 # 此时的actions已经是指定好了的,因为这些值都是从replay buffer当中获取的 # gather函数 还有一个特点 就是 结果的形状和索引列表的shape是相同的。 q_values = self.q_net(states).gather(1, actions) # Q值 # 下个状态的最大Q值 ''' self.target_q_net(next_states)输出的是一个包含n个状态,m个动作的张量 .max(1)操作是在上边的n*m大小的张量中的每一行选取最大值,返回来的是一个元组(其中包括最大值和他们对应的索引两个列表 ) [0]这个操作就是只选取最大值那个列表 ''' max_next_q_values = self.target_q_net(next_states).max(1)[0].view( -1, 1) q_targets = rewards + self.gamma * max_next_q_values * (1 - dones ) # TD误差目标 dqn_loss = torch.mean(F.mse_loss(q_values, q_targets)) # 均方误差损失函数 self.optimizer.zero_grad() # PyTorch中默认梯度会累积,这里需要显式将梯度置为0 dqn_loss.backward() # 反向传播更新参数 self.optimizer.step() if self.count % self.target_update == 0: self.target_q_net.load_state_dict(self.q_net.state_dict()) # 更新目标网络 self.count += 1
-
我们也可以看到,在 DQN 的性能得到提升后,它会持续出现一定程度的震荡,这主要是神经网络过拟合到一些局部经验数据后由argmax运算带来的影响。对这句话的理解
-
神经网络过拟合到一些局部经验数据:在DQN中,深度神经网络负责逼近Q函数。如果网络过度学习(过拟合)了某些特定的经验数据(可能是由于这些数据在训练集中出现得过于频繁),它可能会在这些数据上表现得特别好,但在未见过的或不常见的数据上表现得较差。
-
由运算带来的影响:这里指的是由于神经网络在学习过程中的这种过拟合现象,导致了算法性能的震荡。即算法在某些方面学得很好,但这可能限制了其在更广泛、更一般的情况下的适应性和灵活性。
-
-
DQN的主要思想 就是 用一个神经网络来表示最优策略函数Q,然后用Q-learning的思想进行参数更新。
-
两个小trick : Replay Buffer 和 目标网络 两个模块
-
class ConvolutionalQnet(torch.nn.Module): ''' 加入卷积层的Q网络 ''' def __init__(self, action_dim, in_channels=4): super(ConvolutionalQnet, self).__init__() # in_channels 对于一般的输入图像而言指的是RGB三个通道,但是这里边默认值设置成了4代表着将连续4帧图像叠加在一起 # 32就是32个卷积核 核大小是8*8 步长是4 self.conv1 = torch.nn.Conv2d(in_channels, 32, kernel_size=8, stride=4) # 32就是上一层的输入通道 64就是这一层的输出通道数量 self.conv2 = torch.nn.Conv2d(32, 64, kernel_size=4, stride=2) self.conv3 = torch.nn.Conv2d(64, 64, kernel_size=3, stride=1) # 7 * 7 * 64 代表着 特征图的宽度 * 高度 * 通道数 512 代表着这一层网络的学习能力 如此设计只是出于经验 self.fc4 = torch.nn.Linear(7 * 7 * 64, 512) self.head = torch.nn.Linear(512, action_dim) def forward(self, x): x = x / 255 x = F.relu(self.conv1(x)) x = F.relu(self.conv2(x)) x = F.relu(self.conv3(x)) x = F.relu(self.fc4(x)) return self.head(x)
-
input output stride之间的关系: o u t p u t = i n p u t ∗ 2 s t r i d e − 1 output = input * 2^{stride - 1} output=input∗2stride−1
动手学强化学习之强化学习进阶篇:DQN算法总结
最新推荐文章于 2024-07-23 22:06:37 发布