Как изменить модель Pytorch для работы с 3D-вводом вместо 2d-ввода?

#pytorch #reinforcement-learning #conv-neural-network #stable-baselines

#pytorch #подкрепление-обучение #conv-нейронная сеть #стабильные базовые показатели

Вопрос:

Я пытаюсь обучить агента играть в игру Connect4. Я нашел пример того, как его можно обучить. Представление платы — массив размером 1x6x7:

 [[[0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 2]
  [0 0 0 0 0 0 1]]]
 

Используется эта архитектура нейронной сети:

 class Net(BaseFeaturesExtractor):
    def __init__(self, observation_space: gym.spaces.Box, features_dim: int = 256):
        super(Net, self).__init__(observation_space, features_dim)
        # We assume CxHxW images (channels first)
        # Re-ordering will be done by pre-preprocessing or wrapper
        n_input_channels = observation_space.shape[0]
        self.cnn = nn.Sequential(
            nn.Conv2d(n_input_channels, 32, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=0),
            nn.ReLU(),
            nn.Flatten(),
        )

        # Compute shape by doing one forward pass
        with th.no_grad():
            n_flatten = self.cnn(th.as_tensor(observation_space.sample()[None]).float()).shape[1]

        self.linear = nn.Sequential(nn.Linear(n_flatten, features_dim), nn.ReLU())

    def forward(self, observations: th.Tensor) -> th.Tensor:
        return self.linear(self.cnn(observations))
 

И он показал неплохие результаты в игре с агентом 2, который перемещается случайным образом:

 Agent 1 Win Percentage: 0.59 
Agent 2 Win Percentage: 0.38 
Number of Invalid Plays by Agent 1: 3 
Number of Invalid Plays by Agent 2: 0
Number of Draws (in 100 game rounds): 0
 

Здесь было предложено 3-уровневое представление в качестве одного из способов улучшения агента:

введите описание изображения здесь

Я попытался реализовать это, и это пример нового 3-слойного представления платы:

 [[[0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 1]]

 [[0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 1]
  [0 0 0 0 0 0 0]]

 [[0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0]
  [0 0 0 0 0 0 1]
  [0 0 0 0 0 0 0]
  [1 1 1 1 1 1 0]]]
 

Когда я запускаю это с текущей архитектурой нейронной сети, агент не может обучаться соответствующим образом:

 Agent 1 Win Percentage: 0.0
Agent 2 Win Percentage: 0.0
Number of Invalid Plays by Agent 1: 100
Number of Invalid Plays by Agent 2: 0
Number of Draws (in 100 game rounds): 0
 

Здесь вы можете увидеть мой код.

Как вы можете видеть, теперь у меня 3 слоя вместо одного. Вот почему я попытался использовать Conv3d:

 class Net(BaseFeaturesExtractor):
    def __init__(self, observation_space: gym.spaces.Box, features_dim: int = 256):
        super(Net, self).__init__(observation_space, features_dim)
        # We assume CxHxW images (channels first)
        # Re-ordering will be done by pre-preprocessing or wrapper
        n_input_channels = observation_space.shape[0]
        self.cnn = nn.Sequential(
            nn.Conv3d(n_input_channels, 32, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv3d(32, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv3d(64, 128, kernel_size=3, stride=1, padding=0),
            nn.ReLU(),
            nn.Flatten(),
        )

        # Compute shape by doing one forward pass
        with th.no_grad():
            n_flatten = self.cnn(th.as_tensor(observation_space.sample()[None]).float()).shape[1]

        self.linear = nn.Sequential(nn.Linear(n_flatten, features_dim), nn.ReLU())
 

Когда я пытаюсь запустить этот код, он показывает эту ошибку:

Ошибка времени выполнения: ожидаемый 5-мерный ввод для 5-мерного веса [32, 1, 3, 3, 3], но вместо этого получил 4-мерный ввод размера [1, 3, 6, 7]

Мой вопрос: как я могу использовать слой Conv3D с вводом в форме 3x6x7?

Комментарии:

1. Я не думаю, что вам нужен conv3d здесь. Это все еще 2d с большим количеством входных каналов.

Ответ №1:

Комментарий от Shai верен. Здесь вам не нужно использовать слой Conv3D. Форма ваших фильтров Conv3D будет нарушать вычисление размера после применения сверточного фильтра, уменьшая по крайней мере 1 измерение до менее 1, поэтому вы получаете свою ошибку (вы не можете умножить на значение, которое не существует).

Простое использование исходной реализации модели должно работать для вас.

Подобно изображениям с 3 цветовыми полосами, они обычно не обрабатываются с помощью Conv3d (возможно, другой случай с гиперспектральными изображениями, но здесь это не актуально). Существует некоторое обсуждение того, как обрабатывать каждую из цветовых полос, и вы можете повлиять на это различными способами.

Например, настройка groups аргумента слоя Conv2D при создании экземпляра изменит связи между in_channels и out_channels слоя, и которые свернуты с которым, согласно их документации: https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html .

Возможно, вы сможете оптимизировать это в своей модели или иным образом поэкспериментировать с ней.

В любом случае, вам подойдет простое использование существующей реализации с Conv2D. Conv3D обычно используется в случае 3 пространственных измерений, или иногда 2 пространственных и 1 временное измерение. Хотя ваш случай похож на ограниченную версию из 3 пространственных измерений, это не обязательно то же самое, что, скажем, 3D векторное поле потока жидкости в отношении того, как каждый «пиксель» имеет некоторую пространственную релевантность / корреляцию с соседними «пикселями». Ваши «пространственные пиксели» имеют несколько иной вид отображения релевантности или корреляции, чем этот.

Комментарии:

1. Да, я пытался обучить агента с помощью conv2d, однако он не может обучаться должным образом. Также я должен отметить, что 3-слойный ввод представляет собой представление доски 6×7 в игре Connect4. Первый слой предназначен для первого игрока, второй слой предназначен для второго игрока, а третий слой показывает возможные ходы для игрока с текущими ходами.

2. Спасибо за информацию о том, из чего состоит каждое представление платы. Не имея возможности обучаться должным образом, вы просто имеете в виду, что она не сходится или работает плохо? Если это так, вы можете попробовать изменить groups аргумент, чтобы он был эквивалентен in_channels аргументу, или иным образом напрямую реализовать отдельные слои conv для обработки каждого из состояний платы (и последующего их объединения), чтобы гарантировать, что временное несоответствие можно разделить, а его актуальность может быть изучена моделью.

3. Если вы решите сделать последнее, упомянутое в моем последнем комментарии, я бы перешел с использования self.cnn = nn.Sequential(...) на « self.conv1_p1 = nn.Conv2d(…) self.conv2_p1 = nn.Conv2d(…) self.conv1_p2 = nn.Conv2d(…) self.conv1_moves = nn.Conv2d(…)) « и т. Д. По сути, создание нескольких cnn. Вы также можете просто использовать несколько nn.Sequential моделей, а затем объединить их выходные данные в forward() функции.