Ошибка в пользовательском RNN/LSTM с несколькими входными данными

Вопрос или проблема

Я хочу реализовать собственную модель RNN/LSTM, подобную этой. Модель должна принимать два отдельных вектора в качестве входных данных и обрабатывать их. Я следовал учебнику Keras по созданию пользовательского слоя Keras и подавал два вектора a и b в виде списка [a,b] на слой, как показано ниже.

import keras
from keras.layers.recurrent import RNN
import keras.backend as K

class MinimalRNNCell(keras.layers.Layer):
    def __init__(self, units, **kwargs):
        self.units = units
        self.state_size = units
        super(MinimalRNNCell, self).__init__(**kwargs)
    def build(self, input_shape):
        print(type(input_shape))
        self.kernel = self.add_weight(shape=(input_shape[0][-1], self.units),
                                      initializer="uniform",
                                      name="kernel")
        self.recurrent_kernel = self.add_weight(
            shape=(self.units, self.units),
            initializer="uniform",
            name="recurrent_kernel")
        self.built = True
    def call(self, inputs, states):
        prev_output = states[0]
        h = K.dot(inputs[0], self.kernel)
        output = h + K.dot(prev_output, self.recurrent_kernel)
        return output, [output]

# Давайте используем эту ячейку в слое RNN:
cell = MinimalRNNCell(32)
a = keras.Input((None, 5))
b = keras.Input((None, 5))
layer = RNN(cell)
y = layer([a,b])

Но я получаю ошибку TypeError: 'NoneType' object has no attribute '__getitem__' в строке

self.kernel = self.add_weight(shape=(input_shape[0][-1], self.units),
                                          initializer="uniform",
                                          name="kernel")

Кроме того, тип input_shape показывает <type 'tuple'>, а не список. Что я делаю не так и как преодолеть эту ошибку. Пожалуйста, помогите.

Я думаю, что input_shape[0] равен None. Судя по моему пониманию, вы передадите матрицу размером (размер_партии, временные шаги, размер данных каждого временного шага) в ячейку RNN. И при создании вашей архитектуры input_shape[0] даст размер партии (если вы указали размер партии в входном слое, если нет, вы получите None). Даже если вы укажете размер партии, он будет целым числом, и вы не можете индексировать целые числа, поэтому вы получаете эту ошибку.

Ответ или решение

Ошибка, с которой вы сталкиваетесь, связана с тем, как Keras обрабатывает входные данные для вашего пользовательского RNN-клетки. Давайте разберемся с тем, что происходит и как это исправить.

  1. Проблема с input_shape: Когда вы создаете MinimalRNNCell и вызываете метод build, input_shape передается как кортеж, и первый элемент этого кортежа может быть None в силу того, что размер партии (batch size) еще не определен. Таким образом, вы не можете индексировать его, как это делаете в строке:

    self.kernel = self.add_weight(shape=(input_shape[0][-1], self.units),
                                  initializer="uniform",
                                  name="kernel")

    Вместо этого вам нужно использовать input_shape корректно, проверив его на наличие None.

  2. Корректировка метода build: Чтобы избежать обращения к элементам кортежа напрямую, можно изменить определение build следующим образом:

    def build(self, input_shape):
        input_dim_a, input_dim_b = input_shape  # unpacking tuple
        self.kernel = self.add_weight(shape=(input_dim_a[-1], self.units),
                                      initializer="uniform",
                                      name="kernel")
        self.recurrent_kernel = self.add_weight(
            shape=(self.units, self.units),
            initializer="uniform",
            name="recurrent_kernel")
        self.built = True

    Здесь мы распаковываем input_shape в input_dim_a и input_dim_b, которые будут представлять ваши два входа, а затем используем input_dim_a[-1] для определения размерности входных данных.

  3. Переопределение метода call: Необходимо также убедиться, что метод call корректно обрабатывает входные данные:

    def call(self, inputs, states):
        prev_output = states[0]
        h_a = K.dot(inputs[0], self.kernel)  # обработка входа a
        h_b = K.dot(inputs[1], self.kernel)  # обработка входа b (если нужно)
    
        output = h_a + K.dot(prev_output, self.recurrent_kernel)
        return output, [output]

    В этом случае h_b тоже может быть добавлен, если необходимо комбинировать данные из обоих входов, в зависимости от вашей логики RNN.

  4. Инициализация RNN: Если вы хотите использовать вашу RNN-клетку, вам нужно убедиться, что вы используете правильный подход для работы с разными типами входов. Ваша текущая реализация сработает, если оба входа имеют одинаковую размерность для обработки.

Вот как будет выглядеть полная реализация модифицированного кода:

import keras
from keras.layers import RNN
import keras.backend as K

class MinimalRNNCell(keras.layers.Layer):
    def __init__(self, units, **kwargs):
        self.units = units
        self.state_size = units
        super(MinimalRNNCell, self).__init__(**kwargs)

    def build(self, input_shape):
        input_dim_a = input_shape[0]  # Вход a
        # Нет необходимости проверять на None, если размерность известна
        self.kernel = self.add_weight(shape=(input_dim_a[-1], self.units),
                                      initializer="uniform",
                                      name="kernel")
        self.recurrent_kernel = self.add_weight(
            shape=(self.units, self.units),
            initializer="uniform",
            name="recurrent_kernel")
        self.built = True

    def call(self, inputs, states):
        prev_output = states[0]
        h_a = K.dot(inputs[0], self.kernel)  # Обработка входа a
        h_b = K.dot(inputs[1], self.kernel)  # Обработка входа b (если нужно)

        output = h_a + K.dot(prev_output, self.recurrent_kernel)  # Суммируем результаты
        return output, [output]

# Используем нашу клетку в RNN слое:
cell = MinimalRNNCell(32)
a = keras.Input((None, 5))
b = keras.Input((None, 5))
layer = RNN(cell)
y = layer([a, b])

После переломления кода, тестируйте вашу модель, чтобы убедиться, что все работает корректно. Эта структура должна помочь избежать ошибки, связанной с обращением к NoneType. Удачи в вашей разработке на Keras!

Оцените материал
Добавить комментарий

Капча загружается...