Keras模型访问Input层元素创建序列掩码报错解决方案
我基于Keras TensorFlow编译训练用于回归任务的RNN模型,采用函数式API完成模型定义。
模型共设置2个不同输入:
- 第一个输入原代码命名为
input,为训练数据,数组形状为(TOTAL_TRAIN_DATA, SEQUENCE_LENGTH, NUM_OF_FEATURES) = (15000,1564,2),对应15000个视频每帧的2项特征;由于原始视频帧长不统一,所有视频通过重复最后一行的方式填充至SEQUENCE_LENGTH=1564帧。 - 第二个输入
lengths为形状(15000,)的向量,存储每个视频的原始有效帧长,示例为:lengths = [317 215 576 ... 1245 213 654]。
原实现逻辑为:拼接GRU层输出的所有特征,再乘以对应掩码,仅保留原始视频有效长度对应的特征值。具体来说,GRU层输出形状为(batch_size, SEQUENCE_LENGTH, GRU_UNITS) = (50,1564,256),经Flatten()层重塑为(50, 1564*256),需要创建形状为(50,1564*256)的掩码数组,数组每一行对应当前batch内一个样本的掩码规则。
原实现的掩码函数代码:
def mask_creator(lengths,number_of_GRU_features=256,max_pad_len=1564): masks = np.zeros((lengths.shape[0],number_of_GRU_features*max_pad_len)) for i, length in enumerate(lengths): masks[i,:] = np.concatenate((np.ones([length * number_of_GRU_features, ]), np.zeros([(max_pad_len - length) * number_of_GRU_features, ])), axis=0) return masks
原模型搭建代码:
#tf.compat.v1.enable_eager_execution() #tf.data.experimental.enable_debug_mode() #tf.config.run_functions_eagerly(True) GRU_UNITS = 256 SEQUENCE_LENGTH = 1564 NUM_OF_FEATURES = 2 input = tf.keras.layers.Input(shape=(SEQUENCE_LENGTH,NUM_OF_FEATURES)) lengths = tf.keras.layers.Input(shape=()) masks = tf.keras.layers.Lambda(mask_creator, name="mask_function")(lengths) gru = tf.keras.layers.GRU(GRU_UNITS , return_sequences=True)(input) flat = tf.keras.layers.Flatten()(gru) multiplied = tf.keras.layers.Multiply()([flat, masks]) outputs = tf.keras.layers.Dense(7, name="pred")(multiplied ) # Compile model = tf.keras.Model([input, lengths], outputs, name="RNN") # optimizer = tf.keras.optimizers.Adam(learning_rate=1e-2) #Compile keras model model.compile(optimizer='adam', loss='mean_squared_error', metrics=['MeanSquaredError', 'MeanAbsoluteError']), #run_eagerly=True) model.summary()
为生成掩码,需要访问作为模型输入传入的lengths向量(定义语句为lengths = tf.keras.layers.Input(shape=())),因此定义Lambda层(masks=tf.keras.layers.Lambda(mask_creator, name="mask_function")(lengths))调用mask_creator函数生成掩码。按照预期,lengths应为形状(batch_size,)=(50,)的张量,但始终无法正常访问lengths的元素,抛出如下类型错误:
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-30-8e31522694ee> in <module>() 9 input = tf.keras.layers.Input(shape=(SEQUENCE_LENGTH,FEATURES)) 10 lengths = tf.keras.layers.Input(shape=()) ---> 11 masks = tf.keras.layers.Lambda(mask_creator, name="mask_function")(lengths) 12 gru = tf.keras.layers.GRU(GRU_UNITS , return_sequences=True)(input) 13 flat = tf.keras.layers.Flatten()(gru) 1 frames <ipython-input-19-9490084e8336> in mask_creator(lengths, number_of_GRU_features, max_pad_len) 1 def mask_creator(lengths,number_of_GRU_features=256,max_pad_len=1564): 2 ----> 3 masks = np.zeros((lengths.shape[0],number_of_GRU_features*max_pad_len)) 4 5 for i, length in enumerate(lengths): TypeError: Exception encountered when calling layer "mask_function" (type Lambda). 'NoneType' object cannot be interpreted as an integer Call arguments received: • inputs=tf.Tensor(shape=(None,), dtype=float32) • mask=None • training=None
- 模型构建阶段,Keras所有输入张量的batch维度都是动态值
None,代表batch大小不固定,不会提前绑定固定值。原代码直接取lengths.shape[0]传给np.zeros作为维度参数,拿到的是None,自然无法被识别为整数维度,触发类型错误。 - Lambda层内混用numpy操作与TensorFlow张量:传入Lambda层的
lengths是TensorFlow张量,不是numpy数组,直接用enumerate遍历、调用numpy的数组初始化/拼接接口,即便解决batch维度问题,在TensorFlow默认的图执行模式下也无法正常运行。
补充:原代码将输入层命名为input,会和Python内置的input()函数重名,存在潜在冲突风险。
将掩码生成逻辑全部替换为TensorFlow原生操作,支持动态batch维度,可正常在图模式下运行,无需开启eager执行模式。
修复后的掩码函数:
import tensorflow as tf import numpy as np def mask_creator(lengths, number_of_GRU_features=256, max_pad_len=1564): # 读取动态batch大小 batch_size = tf.shape(lengths)[0] # 生成帧位置索引,形状为(1, max_pad_len) frame_pos = tf.range(max_pad_len, dtype=tf.float32)[None, :] # 生成帧级掩码:位置索引小于有效帧长的位置为1,否则为0,形状(batch_size, max_pad_len) frame_mask = tf.cast(frame_pos < lengths[:, None], dtype=tf.float32) # 将掩码扩展到GRU输出的特征维度,再展平为与Flatten层输出一致的形状 feature_mask = tf.tile(frame_mask[:, :, None], [1, 1, number_of_GRU_features]) flat_mask = tf.reshape(feature_mask, [batch_size, max_pad_len * number_of_GRU_features]) return flat_mask
修复后的模型构建代码(修正输入层命名问题,指定帧长输入的类型为整数):
GRU_UNITS = 256 SEQUENCE_LENGTH = 1564 NUM_OF_FEATURES = 2 # 修改输入层命名,避免与内置函数冲突 seq_input = tf.keras.layers.Input(shape=(SEQUENCE_LENGTH,NUM_OF_FEATURES)) len_input = tf.keras.layers.Input(shape=(), dtype=tf.int32) masks = tf.keras.layers.Lambda(mask_creator, name="mask_function")(len_input) gru = tf.keras.layers.GRU(GRU_UNITS , return_sequences=True)(seq_input) flat = tf.keras.layers.Flatten()(gru) multiplied = tf.keras.layers.Multiply()([flat, masks]) outputs = tf.keras.layers.Dense(7, name="pred")(multiplied ) # 编译模型 model = tf.keras.Model([seq_input, len_input], outputs, name="RNN") model.compile(optimizer='adam', loss='mean_squared_error', metrics=['MeanSquaredError', 'MeanAbsoluteError']) model.summary()
优化提示:GRU层原生支持mask输入,可直接传入帧级掩码让GRU自动跳过填充帧的计算,相比当前先计算所有填充帧输出再乘0清零的写法,能大幅降低计算量和内存占用。
内容的提问来源于stack exchange,提问作者MJ13

