强化学习-DDQN(三)

强化学习-DDQN(四)

DDQN的算法建模

DDQN和Nature DQN一样,也有一样的两个Q网络结构。在Nature DQN的基础上,通过解耦目标Q值动作的选择和目标Q值的计算这两步,来消除过度估计的问题。

在上一节里,Nature DQN对于非终止状态,其目标Q值的计算式子是:
yj=Rj+γmaxa′Q′(ϕ(S′j),A′j,w′)
    在DDQN这里,不再是直接在目标Q网络里面找各个动作中最大Q值,而是先在当前Q网络中先找出最大Q值对应的动作,即
amax(S′j,w)=argmaxa′Q(ϕ(S′j),a,w)
    然后利用这个选择出来的动作amax(S′j,w)在目标网络里面去计算目标Q值。即:
yj=Rj+γQ′(ϕ(S′j),amax(S′j,w),w′)
    综合起来写就是:
yj=Rj+γQ′(ϕ(S′j),argmaxa′Q(ϕ(S′j),a,w),w′)
    除了目标Q值的计算方式以外,DDQN算法和Nature DQN的算法流程完全相同。

DDQN算法流程

这里我们总结下DDQN的算法流程,和Nature DQN的区别仅仅在步骤2.f中目标Q值的计算。

算法输入:迭代轮数T,状态特征维度n, 动作集A, 步长α,衰减因子γ, 探索率ϵ, 当前Q网络Q,目标Q网络Q′, 批量梯度下降的样本数m,目标Q网络参数更新频率C。

输出:Q网络参数

1. 随机初始化所有的状态和动作对应的价值Q. 随机初始化当前Q网络的所有参数w,初始化目标Q网络Q′的参数w′=w。清空经验回放的集合D。

2. for i from 1 to T,进行迭代。

a) 初始化S为当前状态序列的第一个状态, 拿到其特征向量ϕ(S)
      b) 在Q网络中使用ϕ(S)作为输入,得到Q网络的所有动作对应的Q值输出。用ϵ−贪婪法在当前Q值输出中选择对应的动作A
      c) 在状态S执行当前动作A,得到新状态S′对应的特征向量ϕ(S′)和奖励R$,是否终止状态is_end

d) 将{ϕ(S),A,R,ϕ(S′),is_end}这个五元组存入经验回放集合D
      e) S=S′
      f) 从经验回放集合D中采样m个样本{ϕ(Sj),Aj,Rj,ϕ(S′j),is_endj},j=1,2.,m,计算当前目标Q值yj:
yj={RjRj+γQ′(ϕ(S′j),argmaxa′Q(ϕ(S′j),a,w),w′)is_endjistrueis_endjisfalse
      g) 使用均方差损失函数1m∑j=1m(yj−Q(ϕ(Sj),Aj,w))2,通过神经网络的梯度反向传播来更新Q网络的所有参数w
      h) 如果T%C=1,则更新目标Q网络参数w′=w
      i) 如果S′是终止状态,当前轮迭代完毕,否则转到步骤b)

注意,上述第二步的f步和g步的Q值计算也都需要通过Q网络计算得到。另外,实际应用中,为了算法较好的收敛,探索率ϵ需要随着迭代的进行而变小。

网络结构

class AtariNet(nn.Module):

    def __init__(self, num_actions):
        super(AtariNet, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(1, 32, kernel_size=8, stride=4),
            nn.ReLU()
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(32, 64, kernel_size=4, stride=2),
            nn.ReLU()
        )
        self.conv3 = nn.Sequential(
            nn.Conv2d(64, 64, kernel_size=3, stride=1),
            nn.ReLU()
        )
        self.hidden = nn.Sequential(
            nn.Linear(64 * 7 * 7, 512, bias=True),
            nn.ReLU()
        )
        self.out = nn.Sequential(
            nn.Linear(512, num_actions, bias=True)
        )
        self.apply(self.init_weights)

    def init_weights(self, m):
        if type(m) == nn.Conv2d:
            m.weight.data.normal_(0.0, 0.02)
        if type(m) == nn.Linear:
            torch.nn.init.xavier_uniform_(m.weight)
            m.bias.data.fill_(0.01)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = x.view(x.size(0), -1)
        x = self.hidden(x)
        x = self.out(x)
        return x


class CnnDQN(nn.Module):
    def __init__(self, inputs_shape, num_actions):
        super(CnnDQN, self).__init__()

        self.inut_shape = inputs_shape
        self.num_actions = num_actions

        self.features = nn.Sequential(
            nn.Conv2d(inputs_shape[0], 32, kernel_size=8, stride=4),
            nn.ReLU(),
            nn.Conv2d(32, 64, kernel_size=4, stride=2),
            nn.ReLU(),
            nn.Conv2d(64, 64, kernel_size=3, stride=1),
            nn.ReLU()
        )

        self.fc = nn.Sequential(
            nn.Linear(self.features_size(), 512),
            nn.ReLU(),
            nn.Linear(512, self.num_actions)
        )

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

    def features_size(self):
        return self.features(torch.zeros(1, *self.inut_shape)).view(1, -1).size(1)

动作选择

    def action(self, state, israndom):
        if israndom and random.random() < EPSILON:
            return np.random.randint(0, ACTIONS_SIZE)
        state = torch.unsqueeze(torch.FloatTensor(state), 0)
        actions_value = self.network.forward(state)
        return torch.max(actions_value, 1)[1].data.numpy()[0]

更新网络

 def learn(self, state, action, reward, next_state, done):
        if done:
            self.memory.append((state, action, reward, next_state, 0))
        else:
            self.memory.append((state, action, reward, next_state, 1))
        if len(self.memory) > MEMORY_SIZE:
            self.memory.popleft()
        if len(self.memory) < MEMORY_THRESHOLD:
            return

        if self.learning_count % UPDATE_TIME == 0:
            self.target_network.load_state_dict(self.network.state_dict())
        self.learning_count += 1

        batch = random.sample(self.memory, BATCH_SIZE)
        state = torch.FloatTensor([x[0] for x in batch])
        action = torch.LongTensor([[x[1]] for x in batch])
        reward = torch.FloatTensor([[x[2]] for x in batch])
        next_state = torch.FloatTensor([x[3] for x in batch])
        done = torch.FloatTensor([[x[4]] for x in batch])

        actions_value = self.network.forward(next_state)
        next_action = torch.unsqueeze(torch.max(actions_value, 1)[1], 1)
        eval_q = self.network.forward(state).gather(1, action)
        next_q = self.target_network.forward(next_state).gather(1, next_action)
        target_q = reward + GAMMA * next_q * done
        loss = self.loss_func(eval_q, target_q)

        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

需要源代码请上我的github

你可能感兴趣的:(强化学习)