Вопрос или проблема
Я хочу реализовать собственную модель 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-клетки. Давайте разберемся с тем, что происходит и как это исправить.
-
Проблема с 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
. -
Корректировка метода 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]
для определения размерности входных данных. -
Переопределение метода 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. -
Инициализация 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!