Python中CNN的ResNet34编码器出现IndexError的原因及修复方案
问题原因与修复方案
为什么会出现IndexError?
你遇到的IndexError: list index out of range本质是ResNet34的残差块结构和你复用的ResNet50版res_blk函数不兼容:
- 原
res_blk是为ResNet50的Bottleneck瓶颈结构写的:它需要3层卷积(1x1、3x3、1x1),所以调用时要传长度为3的ch(通道数列表)和ksize(卷积核尺寸列表),比如[64,64,256]和[1,3,1]。 - 但ResNet34用的是BasicBlock基础残差块,只有2层3x3卷积,你传入的
ch是[64,64](长度为2),但函数里依然尝试访问ch[2](第三层卷积的通道数),自然就触发索引越界了。
修复方法:适配ResNet34的BasicBlock修改res_blk
既然目标是实现ResNet34编码器,我们需要把res_blk改成两层卷积的基础残差块结构,同时修正通道数的获取逻辑(原代码ch_in = l.get_shape().as_list()拿到的是整个张量形状,应该只取通道维度)。
修改后的res_blk代码:
def res_blk(name, l, ch, ksize, count, strides=1, freeze=False): # TensorPack默认NCHW格式,通道数在shape的第1位 ch_in = l.get_shape().as_list()[1] with tf.variable_scope(name): for i in range(count): with tf.variable_scope('block' + str(i)): x = l if i == 0 else BNReLU('preact', l) # ResNet34 BasicBlock:两层3x3卷积 x = Conv2D('conv1', x, ch[0], ksize[0], strides=strides if i == 0 else 1, activation=BNReLU) x = Conv2D('conv2', x, ch[1], ksize[1], activation=tf.identity) # Shortcut逻辑:步长≠1或输入输出通道不同时,用1x1卷积调整 if (strides != 1 or ch_in != ch[1]) and i == 0: l = Conv2D('convshortcut', l, ch[1], 1, strides=strides) x = tf.stop_gradient(x) if freeze else x l = l + x l = BNReLU('bnlast', l) return l
然后你的encoder函数可以保持原调用参数不变(因为现在res_blk已经支持两层残差块的参数了):
def encoder(i, freeze): d1 = Conv2D('conv0', i, 64, 7, padding='valid', strides=1, activation=BNReLU) d1 = res_blk('group0', d1, [64, 64], [3, 3], 3, strides=1, freeze=freeze) d2 = res_blk('group1', d1, [128, 128], [3, 3], 4, strides=2, freeze=freeze) d2 = tf.stop_gradient(d2) if freeze else d2 d3 = res_blk('group2', d2, [256, 256], [3, 3], 6, strides=2, freeze=freeze) d3 = tf.stop_gradient(d3) if freeze else d3 d4 = res_blk('group3', d3, [512, 512], [3, 3], 3, strides=2, freeze=freeze) d4 = tf.stop_gradient(d4) if freeze else d4 d4 = Conv2D('conv_bot', d4, 1024, 1, padding='same') return [d1, d2, d3, d4]
额外提示
- ResNet34和ResNet50的核心差异就是残差块:
- ResNet34:BasicBlock(2个3x3卷积,通道数一致)
- ResNet50+:Bottleneck(1x1压缩通道→3x3卷积→1x1恢复通道,减少计算量)
- 如果你坚持不想修改
res_blk,也可以强行按Bottleneck格式传参数(比如[64,64,64]),但这会违背ResNet34的设计,增加不必要的计算,不推荐这么做。
内容的提问来源于stack exchange,提问作者CCZ23
相关产品推荐
相关产品推荐

