如何处理强化学习中不同状态 space 大小?

How to deal with different state space size in reinforcement learning?

我在 A2C 强化学习中工作,我的环境中代理的数量在增加和减少。由于代理数量的增加和减少,状态 space 也会发生变化。我试过这样解决改变状态的问题space:

有效,但结果不如预期,我不知道这是否正确。

我的问题

是否有任何参考文献处理此类问题以及如何处理状态变化space?

对于本文,我将提供与 other post 中相同的参考资料: Benchmarks for reinforcement learning minmixed-autonomy traffic.

实际上,在这种方法中,预期的代理人数量(预期会在任何时刻出现在模拟中)是预先确定的。在运行时,模拟中存在的代理的观察结果被检索并压缩到一个固定大小的容器(张量)中(我们称之为 整体观察容器 ),它可以包含尽可能多的观察结果(来自单个代理),因为在模拟中的任何时刻都期望有代理出现。明确一点:size(overall observation container) = expected number of agents * individual observation size。由于模拟中存在的实际代理数量可能因时间步长而异,因此适用以下内容:

  • 如果环境中的主体数量少于预期,因此提供的观察结果少于整个观察容器所能容纳的数量,然后 zero-padding 用于填充空的观察槽。
  • 如果代理人的数量超过预期的代理人数量,则只会使用所提供的观察结果的一个子集。因此,仅从可用代理的随机选择子集中将观察结果放入固定大小的整体观察容器中。仅对于选定的代理,控制器将计算要执行的操作,而“多余的代理”必须在模拟中被视为 non-controlled 个代理。

回到您的示例代码,我会做一些不同的事情。

首先,我想知道为什么您同时拥有变量 state(传递给函数 get_state_new)和调用 get_state(env),因为我希望 get_state(env) 与已存储在变量 state 中的相同。作为提示,如果您可以尝试仅使用 state 变量(如果变量和函数调用确实提供相同的信息),它会使代码更易读。

我要做的第二件事是处理状态的方式:p = np.exp(p)p = p * (1. / p.sum())。这通过所有单独观察中存在的所有指数值的总和来规范化整个观察容器。相比之下,我会孤立地对每个单独的观察进行归一化。

这有以下原因:如果您提供少量的观测值,那么所有单个观测值中包含的取幂值的总和可能会小于对较大的观测值中包含的取幂值求和时的总和个人观察的数量。然后用于归一化的总和的这些差异将导致归一化值的不同大小(粗略地说,作为个体观察数量的函数)。考虑以下示例:

import numpy as np

# Less state representations
state = np.array([1,1,1])
state = state/state.sum()
state
# Output: array([0.33333333, 0.33333333, 0.33333333])

# More state representations
state = np.array([1,1,1,1,1])
state = state/state.sum()
state
# Output: array([0.2, 0.2, 0.2, 0.2, 0.2])

实际上,单个代理获得的相同输入状态表示在归一化后始终会产生相同的输出状态表示,而不管模拟中当前存在的代理数量如何。因此,请确保自己对所有观察结果进行归一化。下面我举个例子。

此外,请确保跟踪哪些代理的观察结果(以及顺序)已被压缩到您的变量中 statappend。这很重要,原因如下。

如果有代理 A1A5,但整个观察容器只能进行三个观察,将随机选择五个状态表示中的三个。假设随机选择要压缩到整体观察容器中的观察来自以下代理,顺序如下:A2, A5, A1。然后,这些代理的观察结果将按照这个顺序被压缩到整个观察容器中。首先是 A2 的观察,然后是 A5 的观察,最后是 A1 的观察。相应地,给定上述整体观察容器,你的强化学习控制器预测的三个动作将分别对应代理A2A5A1(顺序!)。换句话说,输入端代理的顺序也决定了输出端预测的动作对应于哪些代理。

我建议如下:

import numpy as np

def get_overall_observation(observations, expected_observations=5):
    # Return value:
    #   order_agents: The returned observations stem from this ordered set of agents (in sequence)

    # Get some info
    n_observations = observations.shape[0]  # Actual nr of observations
    observation_size = list(observations.shape[1:])  # Shape of an agent's individual observation

    # Normalitze individual observations
    for i in range(n_observations):
        # TODO: handle possible 0-divisions
        observations[i,:] = observations[i,:] / observations[i,:].max()

    if n_observations == expected_observations:
        # Return (normalized) observations as they are & sequence of agents in order (i.e. no randomization)
        order_agents = np.arange(n_observations)
        return observations, order_agents
    if n_observations < expected_observations:
        # Return padded observations as they are & padded sequence of agents in order (i.e. no randomization)
        padded_observations = np.zeros([expected_observations]+observation_size)
        padded_observations[0:n_observations,:] = observations
        order_agents = list(range(n_observations))+[-1]*(expected_observations-n_observations) # -1 == agent absent
        return padded_observations, order_agents
    if n_observations > expected_observations:
        # Return random selection of observations in random order
        order_agents = np.random.choice(range(n_observations), size=expected_observations, replace=False)
        selected_observations = np.zeros([expected_observations] + observation_size)
        for i_selected, i_given_observations in enumerate(order_agents):
            selected_observations[i_selected,:] = observations[i_given_observations,:]
        return selected_observations, order_agents


# Example usage
n_observations = 5      # Number of actual observations
width = height =  2     # Observation dimension
state = np.random.random(size=[n_observations,height,width])  # Random state
print(state)
print(get_overall_observation(state))

我使用不同的解决方案解决了问题,但我发现编码是解决我的问题的最佳方案

  • Select 具有 pre-estimate 最大状态 space 的模型,如果 state space 小于最大状态,我们填充了状态 space 带零
  • 只考虑代理本身的状态,没有任何共享 另一个州。
  • 正如论文[1]提到的那样,额外连接的自主 车辆(CAV)不包括在该州内,如果它们较少 比最大 CAVs,状态用零填充。我们可以 select 如何 许多代理,我们可以共享他们的状态添加到代理的 状态。
  • 编码状态 它将帮助我们处理输入并将信息压缩成固定长度。在编码器中,每个 LSTM 层中的单元或具有门控循环单元 (GRU) 的 RNN returns 隐藏状态 (Ht) 和细胞状态 (E’)。

对于编码器,我使用 Neural machine translation with attention code

class Encoder(tf.keras.Model):
  def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):
    super(Encoder, self).__init__()
    self.batch_sz = batch_sz
    self.enc_units = enc_units
    self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
    self.gru = tf.keras.layers.GRU(self.enc_units,
                                   return_sequences=True,
                                   return_state=True,
                                   recurrent_initializer='glorot_uniform')

  def call(self, x, hidden):
    x = self.embedding(x)
    output, state = self.gru(x, initial_state = hidden)
    return output, state

  def initialize_hidden_state(self):
    return tf.zeros((self.batch_sz, self.enc_units))
  • LSTM 零填充和掩码 我们用特殊值填充状态,以便稍后屏蔽(跳过)。如果我们在没有掩蔽的情况下进行填充,则 填充值将被视为实际值,因此成为噪声 在状态 [2-4].

1- Vinitsky, E., Kreidieh, A., Le Flem, L., Kheterpal, N., Jang, K., Wu, C., ... & Bayen, A. M.(2018 年 10 月) . mixed-autonomy 流量中强化学习的基准。在机器人学习会议中(第 399-409 页)

2- Kochkina, E., Liakata, M., & Augenstein, I. (2017)。 Turing 在 semeval-2017 任务 8:使用 branch-lstm 进行谣言立场分类的顺序方法。 arXiv 预印本 arXiv:1704.07221.

3- Ma, L., & Liang, L. (2020)。增强 CNN 对可变长度 12 导联心电图分类噪声的鲁棒性。 arXiv 预印本 arXiv:2008.03609.

4- How to feed LSTM with different input array sizes?

5- Zhao, X., Xia, L., Zhang, L., Ding, Z., Yin, D., & Tang, J.(2018 年 9 月)。 page-wise 推荐的深度强化学习。在第 12 届 ACM 推荐系统会议论文集(第 95-103 页)中。