TensorFlow强化学习2D卷积Actor模型使用PReLU时出现维度不匹配错误的问题咨询
你遇到的这个维度不匹配错误,核心原因是重复使用了同一个PReLU层实例,而TensorFlow的PReLU层默认会为输入的每个空间位置+通道创建独立的可学习alpha参数。
当你第一次在Conv1中使用这个PReLU实例时,输入的形状是(None, 1000, 4000, 32),此时PReLU会初始化一个形状为(1000, 4000, 32)的alpha参数张量。但后续经过Conv_pool_1的池化操作后,Batchnorm_1的输出形状变成了(None, 500, 2000, 32),此时再用同一个PReLU实例处理这个输入,就会出现alpha参数的空间维度(1000×4000)和输入的空间维度(500×2000)不匹配的问题——这就是报错里提到的"Dimensions must be equal, but are 1000 and 500"的根源。
而LeakyReLU没有这个问题,因为它的alpha是固定的标量,不需要为每个输入元素/通道学习参数,所以复用实例完全没问题。
方案1:每次使用时创建新的PReLU实例
不要提前定义一个全局的activation变量,而是在每个Conv2D层的activation参数中直接创建新的PReLU层,这样每个层都会有自己独立的alpha参数,适配对应输入的形状:
from tensorflow.keras.layers import Conv2D, Input, MaxPool1D, concatenate, Lambda, Dense, Flatten import tensorflow as tf def ActorNetwork(input_shape_A,input_shape_B, n_actions): input_layer_A = Input(shape=input_shape_A[1:], name="input_layer_A") input_layer_B = Input(shape=input_shape_B[1:], name="input_layer_B") Rescale = Lambda(lambda x: tf.divide(tf.subtract(x, tf.reduce_max(x)), tf.subtract(tf.reduce_max(x), tf.reduce_min(x))))(input_layer_A) # 每个Conv层都创建新的PReLU实例 Conv1 = Conv2D(32, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv1")(Rescale) Conv2 = Conv2D(32, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv2")(Conv1) Conv_pool_1 = Conv2D(32, 2, strides=2, activation='relu', padding='same', name="Conv_pool_1")(Conv2) Batchnorm_1 = tf.keras.layers.BatchNormalization(name='Batchnorm_1')(Conv_pool_1) Conv3 = Conv2D(32, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv3")(Batchnorm_1) Conv4 = Conv2D(32, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv4")(Conv3) Conv_pool_2 = Conv2D(32, 2, strides=2, activation='relu', padding='same', name="Conv_pool_2")(Conv4) Batchnorm_2 = tf.keras.layers.BatchNormalization(name='Batchnorm_2')(Conv_pool_2) Conv5 = Conv2D(64, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv5")(Batchnorm_2) Conv6 = Conv2D(64, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv6")(Conv5) Conv_pool_3 = Conv2D(64, 2, strides=2, activation='relu', padding='same', name="Conv_pool_3")(Conv6) Batchnorm_3 = tf.keras.layers.BatchNormalization(name='Batchnorm_3')(Conv_pool_3) Conv7 = Conv2D(64, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv7")(Batchnorm_3) Conv8 = Conv2D(64, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv8")(Conv7) Conv_pool_4 = Conv2D(64, 2, strides=2, activation='relu', padding='same', name="Conv_pool_4")(Conv8) Batchnorm_4 = tf.keras.layers.BatchNormalization(name='Batchnorm_4')(Conv_pool_4) Conv9 = Conv2D(128, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv9")(Batchnorm_4) Conv10 = Conv2D(128, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv10")(Conv9) Conv_pool_5 = Conv2D(128, 2, strides=2, activation='relu', padding='same', name="Conv_pool_5")(Conv10) Batchnorm_5 = tf.keras.layers.BatchNormalization(name='Batchnorm_5')(Conv_pool_5) Conv11 = Conv2D(128, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv11")(Batchnorm_5) Conv12 = Conv2D(128, 3, activation=tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25)), padding='same', name="Conv12")(Conv11) Conv_pool_6 = Conv2D(128, 2, strides=2, activation='relu', padding='same', name="Conv_pool_6")(Conv12) Batchnorm_6 = tf.keras.layers.BatchNormalization(name='Batchnorm_6')(Conv_pool_6) Conv_pool_7 = Conv2D(128, 1, strides=1, activation='relu', padding='same', name="Conv_pool_7")(Batchnorm_6) Conv_pool_8 = Conv2D(64, 1, strides=1, activation='relu', padding='same', name="Conv_pool_8")(Conv_pool_7) Conv_pool_9 = Conv2D(32, 1, strides=1, activation='relu', padding='same', name="Conv_pool_9")(Conv_pool_8) flatten = Flatten()(Conv_pool_9) Concat_2 = tf.keras.layers.concatenate([flatten, input_layer_B], axis=-1,name='Concat_2') fc1 = Dense(8194, activation='relu', name="fc1")(Concat_2) fc2 = Dense(4096, activation='relu', name="fc2")(fc1) fc3 = Dense(n_actions, activation='softmax', name="fc3")(fc2) return tf.keras.models.Model(inputs=[input_layer_A,input_layer_B], outputs = fc3, name="actor_model")
方案2:设置PReLU的shared_axes参数共享空间维度的alpha
如果你希望复用同一个PReLU实例(减少参数数量),可以设置shared_axes=[1,2],这样alpha参数会在空间维度(高度和宽度)上共享,只针对每个通道保留一个可学习的alpha值,不管输入的空间形状怎么变化都能适配:
from tensorflow.keras.layers import Conv2D, Input, MaxPool1D, concatenate, Lambda, Dense, Flatten import tensorflow as tf # 设置shared_axes=[1,2],共享空间维度的alpha参数 activation = tf.keras.layers.PReLU(alpha_initializer=tf.initializers.constant(0.25), shared_axes=[1,2]) def ActorNetwork(input_shape_A,input_shape_B, n_actions): # 后面的代码和你原来的完全一样即可 input_layer_A = Input(shape=input_shape_A[1:], name="input_layer_A") input_layer_B = Input(shape=input_shape_B[1:], name="input_layer_B") Rescale = Lambda(lambda x: tf.divide(tf.subtract(x, tf.reduce_max(x)), tf.subtract(tf.reduce_max(x), tf.reduce_min(x))))(input_layer_A) Conv1 = Conv2D(32, 3, activation= activation, padding='same', name="Conv1")(Rescale) Conv2 = Conv2D(32, 3, activation=activation, padding='same', name="Conv2")(Conv1) Conv_pool_1 = Conv2D(32, 2, strides=2, activation='relu', padding='same', name="Conv_pool_1")(Conv2) Batchnorm_1 = tf.keras.layers.BatchNormalization(name='Batchnorm_1')(Conv_pool_1) Conv3 = Conv2D(32, 3, activation= activation, padding='same', name="Conv3")(Batchnorm_1) # ... 后续代码保持不变
这个方案的优势是减少了模型的参数总量,因为原本每个空间位置+通道都有一个alpha,现在每个通道只有一个alpha,更轻量化。
修改后,你可以重新运行代码,模型应该能正常初始化、编译并运行,不会再出现维度不匹配的错误。
内容的提问来源于stack exchange,提问作者AB Music Box

