Объединение двух тензоров разной размерности в Keras

Пытаюсь переделать сеть для игры в крестики-нолики, добавив на вход еще четыре числа. Т.е., получается, что на вход идет поле и четыре дополнительных числа. При вызове метода Concatenate получаю исключение:

A Concatenate layer requires inputs with matching shapes except for the concatenation axis. Received: input_shape=[(None, 5, 5), (None, 4)]

Подскажите, как исправить эту ошибку?

Сеть:

class TicTacToeNNet():
    def __init__(self, game, args):
        # game params
        self.board_x, self.board_y = game.getBoardSize()
        self.action_size = game.getActionSize()
        self.args = args

        # Neural Net

        input1 = Input(shape=(self.board_x, self.board_y))    # s: batch_size x board_x x board_y
        input2 = Input(shape=(4,))
        merged = Concatenate(axis=1)([input1, input2])

        h_conv1 = Activation('relu')(BatchNormalization(axis=3)(Conv2D(args.num_channels, 3, padding='same')(merged)))         # batch_size  x board_x x board_y x num_channels
        h_conv2 = Activation('relu')(BatchNormalization(axis=3)(Conv2D(args.num_channels, 3, padding='same')(h_conv1)))         # batch_size  x board_x x board_y x num_channels
        h_conv3 = Activation('relu')(BatchNormalization(axis=3)(Conv2D(args.num_channels, 3, padding='same')(h_conv2)))        # batch_size  x (board_x) x (board_y) x num_channels
        h_conv4 = Activation('relu')(BatchNormalization(axis=3)(Conv2D(args.num_channels, 3, padding='valid')(h_conv3)))        # batch_size  x (board_x-2) x (board_y-2) x num_channels
        h_conv4_flat = Flatten()(h_conv4)       
        s_fc1 = Dropout(args.dropout)(Activation('relu')(BatchNormalization(axis=1)(Dense(1024)(h_conv4_flat))))  # batch_size x 1024
        s_fc2 = Dropout(args.dropout)(Activation('relu')(BatchNormalization(axis=1)(Dense(512)(s_fc1))))          # batch_size x 1024
        self.pi = Dense(self.action_size, activation='softmax', name='pi')(s_fc2)   # batch_size x self.action_size
        self.v = Dense(1, activation='tanh', name='v')(s_fc2)                    # batch_size x 1

        self.model = Model(inputs=self.input_boards, outputs=[self.pi, self.v])
        self.model.compile(loss=['categorical_crossentropy','mean_squared_error'], optimizer=Adam(args.lr))

Оригинал


Ответы (1 шт):

Автор решения: CrazyElf
input_shape=[(None, 5, 5), (None, 4)]

Ну вот есть у вас матрица 5x5 и вектор длины 4. Как вы их хотите объединить?

xxxxx
xxxxx
xxxxx + xxxx = ???
xxxxx
xxxxx

Варианты, конечно, есть. Можно сразу сделать Flatten вашей матрице и присоединить вектор к вектору.

xxxxxxxxxxxxxxxxxxxxxxxxx + xxxx = xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Но тогда все слои Conv2D остаются не у дел сразу, потому что у вас получится вектор длины 29, а слои конволюции Conv2D работают с матрицей, ищут двумерные шаблоны в данных. При превращении матрицы в вектор часть такой информации просто потеряется. Можно сделать Conv1D на векторе, но качество модели будет хуже, чем если работать с матрицей.

Я бы предложил лучше сделать объединение с вектором где-то там, где у вас уже после слоёв Conv2D будет превращение матрицы в вектор, то есть где-то после слоя Flatten или даже после дальнейших Dense и Dropout, это вы сами смотрите, как лучше будет. Если эти 4 числа - совсем другая история, чем матрица, там нет каких-то общих паттернов, то я бы предложил где-то поближе к выходному слою конкатенацию делать. В общем, в любом случае, возьмите любой слой, который уже не матрица, но вектор, то есть начиная со слоя h_conv4_flat и далее, и где-то там сделайте конкатенацию.

# конкатенацию можно сделать после любого из этих слоёв
h_conv4_flat = Flatten()(h_conv4)       
s_fc1 = Dropout(args.dropout)(Activation('relu')(BatchNormalization(axis=1)(Dense(1024)(h_conv4_flat))))  # batch_size x 1024
s_fc2 = Dropout(args.dropout)(Activation('relu')(BatchNormalization(axis=1)(Dense(512)(s_fc1))))          # batch_size x 1024
# но лучше всего где-то здесь, мне кажется
→ Ссылка