TensorFlow代码运行报错:conv2d_transpose形状不兼容求助
我帮你排查了代码里的几个问题,这个Shapes (64, 48, 48, 128) and (64, 24, 24, 128) are not compatible错误主要是反卷积层的输出形状硬编码错误,还有几个其他潜在问题共同导致的,下面是具体的解决思路和修正后的代码:
核心问题分析
错误出在梯度计算阶段,根源是反卷积层的输出形状手动固定,没有和输入张量的实际形状动态对齐,再加上网络通道数不匹配,导致正向传播的张量形状和反向传播时的梯度形状无法兼容。
具体修正点
修复字符串格式化错误
原代码中"conv_d_w" % (i + 1)的写法不符合Python语法,需要用f-string或者str.format()生成带编号的变量名,否则会触发TypeError。修正残差拼接后的通道数
在18层循环里,每次将卷积结果和初始的low_tensor(64通道)拼接后,通道数变为128,但后续卷积层的输入通道参数还是64,这会导致卷积操作形状不兼容,需要把后续卷积权重的输入通道改为128。动态设置反卷积的输出形状
原代码硬编码了反卷积的output_shape为[64,48*(j+1),48*(j+1),128],应该基于输入张量的形状动态计算,比如用tf.shape(tensor)[0]获取当前batch_size,用输入尺寸乘以stride得到输出尺寸,避免硬编码带来的形状不匹配。移除重复的权重列表拼接
原代码最后weights = weights + biases是多余的,因为前面已经将biases添加到weights列表中了,这会导致变量重复,干扰梯度计算。
修正后的完整代码
import tensorflow as tf import numpy as np def model(input_tensor): with tf.device("/gpu:0"): weights = [] biases = [] tensor = None # 初始卷积层 conv_00_w = tf.get_variable("conv_00_w", [3, 3, 1, 64], initializer=tf.random_normal_initializer(stddev=np.sqrt(2.0 / 9))) conv_00_b = tf.get_variable("conv_00_b", [64], initializer=tf.constant_initializer(0)) weights.append(conv_00_w) biases.append(conv_00_b) low_tensor = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(input_tensor, conv_00_w, strides=[1, 1, 1, 1], padding='SAME'), conv_00_b)) # 18层残差卷积(修正通道数) tensor = low_tensor for i in range(18): # 修正变量名格式化,同时根据循环次数调整输入通道数 in_channels = 128 if i > 0 else 64 conv_w = tf.get_variable(f"conv_d_w{i+1}", [3, 3, in_channels, 64], initializer=tf.random_normal_initializer(stddev=np.sqrt(2.0 / 9 / in_channels))) conv_b = tf.get_variable(f"conv_d_b{i+1}", [64], initializer=tf.constant_initializer(0)) weights.append(conv_w) biases.append(conv_b) # 卷积操作 conv_out = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(tensor, conv_w, strides=[1, 1, 1, 1], padding='SAME'), conv_b)) # 拼接残差,第一次后通道变为128 tensor = tf.concat([conv_out, low_tensor], axis=3) # 反卷积上采样层(修正输出形状) for j in range(2): # 获取当前输入的动态形状 batch_size = tf.shape(tensor)[0] curr_height = tf.shape(tensor)[1] curr_width = tf.shape(tensor)[2] # 反卷积权重:[kernel_h, kernel_w, output_channels, input_channels] deconv_w = tf.get_variable(f"deconv_d_w{j+1}", [2, 2, 128, 128], initializer=tf.random_normal_initializer(stddev=np.sqrt(2.0 / 4 / 128))) deconv_b = tf.get_variable(f"deconv_d_b{j+1}", [128], initializer=tf.constant_initializer(0)) weights.append(deconv_w) biases.append(deconv_b) # 动态计算输出形状:上采样2倍 output_shape = tf.stack([batch_size, curr_height * 2, curr_width * 2, 128]) deconv_tensor = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d_transpose(tensor, deconv_w, output_shape, strides=[1, 2, 2, 1], padding='SAME'), deconv_b)) # 输出卷积层:为每次反卷积后的结果生成1通道特征 conv_w = tf.get_variable(f"conv_20_w{j+1}", [3, 3, 128, 1], initializer=tf.random_normal_initializer(stddev=np.sqrt(2.0 / 9 / 128))) conv_b = tf.get_variable(f"conv_20_b{j+1}", [1], initializer=tf.constant_initializer(0)) weights.append(conv_w) biases.append(conv_b) tensor = tf.nn.bias_add(tf.nn.conv2d(deconv_tensor, conv_w, strides=[1, 1, 1, 1], padding='SAME'), conv_b) # 移除重复的权重拼接 return tensor, weights if __name__ == '__main__': INPUT_SIZE = (24, 24) OUTPUT_SIZE = (96, 96) BATCH_SIZE = 64 train_input = tf.placeholder(tf.float32, shape=(BATCH_SIZE, INPUT_SIZE[0], INPUT_SIZE[1], 1)) train_gt = tf.placeholder(tf.float32, shape=(BATCH_SIZE, OUTPUT_SIZE[0], OUTPUT_SIZE[1], 1)) shared_model = tf.make_template('shared_model', model) train_output, weights = shared_model(train_input) loss = tf.reduce_sum(tf.nn.l2_loss(tf.subtract(train_output,train_gt))) learning_rate = 0.001 params = tf.trainable_variables() grads = tf.gradients(loss, weights) print("finished")
额外说明
- 原代码中在两次反卷积后都接了一个1通道的卷积,最终输出的
train_output形状会是(64,96,96,1),和train_gt的形状完美匹配,这个逻辑我保留了,如果你的需求是两次上采样后再统一生成最终输出,可以调整这部分逻辑。 - 动态计算
output_shape可以避免因为输入尺寸变化导致的形状不兼容问题,比硬编码更灵活。
内容的提问来源于stack exchange,提问作者Hung

