You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ResNet34首次调用训练触发tf.Variable创建ValueError求助

问题根因

这个报错是因为部分持有tf.Variable的对象(最常见的是Keras的Conv2D、BatchNormalization、Dense等层)没有在模型初始化阶段创建,而是在tf.function追踪完计算图后的前向传播过程中被重复实例化。
结合你给出的代码片段和报错中输入张量shape为(None, 56, 56, 64)(正好是你定义的layer0的输出尺寸),问题基本集中在3个点:

  • 残差块的堆叠逻辑写在了call方法内部:如果你是在ResNet的call函数里通过循环创建ResBlock实例来搭建后续网络,每次前向传播都会生成全新的残差块对象,连带创建新的卷积、BN层权重,直接触发该错误。
  • 层定义位置错误:你贴出的ResBlock代码不完整,如果BN、ReLU这类层是写在ResBlock的call方法里而非__init__构造方法中,每次前向传播都会生成新的层实例,同样会重复创建变量触发报错。
  • 语法错误:你贴出的ResNet类代码存在缩进问题,def __init__行没有在类定义下做正确缩进,会导致构造方法不生效,层初始化逻辑完全失效,间接引发变量创建异常。
修复方案

所有会持有权重变量的Keras层,必须在模型/模块的__init__方法中完成实例化,call方法中仅允许做张量计算逻辑,禁止实例化新的层或创建新的tf.Variable:

  1. 修复类方法缩进:确保ResNet、ResBlock的__init__、call方法都有正确的4空格缩进,属于类的内部方法。
  2. 把残差块堆叠逻辑移到__init__中:不要在call里循环生成ResBlock,可以在__init__里写一个辅助方法提前组装好所有残差阶段的层,存在类属性中。
  3. 补全ResBlock的层定义:把主路径、shortcut路径上所有的卷积、BN、ReLU层都放到ResBlock的__init__里实例化,call里只写前向计算流程。

参考修正代码

补全后的ResBlock实现

class ResBlock(keras.Model):
    def __init__(self, filters, downsample):
        super().__init__()
        # 主路径所有层提前初始化
        self.conv1 = layers.Conv2D(filters, 3, strides=2 if downsample else 1, padding='same')
        self.bn1 = layers.BatchNormalization()
        self.relu1 = layers.ReLU()
        self.conv2 = layers.Conv2D(filters, 3, strides=1, padding='same')
        self.bn2 = layers.BatchNormalization()
        self.relu2 = layers.ReLU()

        # shortcut路径层提前初始化
        if downsample:
            self.shortcut = keras.Sequential([
                layers.Conv2D(filters, 1, strides=2),
                layers.BatchNormalization()
            ])
        else:
            self.shortcut = layers.Identity()

    def call(self, x, training=None):
        # 仅做张量计算,不创建任何新层/变量
        residual = self.shortcut(x)
        x = self.conv1(x)
        x = self.bn1(x, training=training)
        x = self.relu1(x)
        x = self.conv2(x)
        x = self.bn2(x, training=training)
        x = x + residual
        x = self.relu2(x)
        return x

修正后的ResNet初始化逻辑

class ResNet(keras.Model):
    def __init__(self, resblock, repeat, outputs=1000):
        super().__init__()
        # stem层
        self.layer0 = keras.Sequential([
            layers.Conv2D(64, 7, strides=2, padding='same'),
            layers.BatchNormalization(),
            layers.ReLU(),
            layers.MaxPool2D(pool_size=3, strides=2, padding='same'),
        ], name='layer0')

        # 所有残差块在初始化阶段就创建完成
        self.layer1 = self._build_res_stage(resblock, 64, repeat[0], first_stage=True)
        self.layer2 = self._build_res_stage(resblock, 128, repeat[1], first_stage=False)
        self.layer3 = self._build_res_stage(resblock, 256, repeat[2], first_stage=False)
        self.layer4 = self._build_res_stage(resblock, 512, repeat[3], first_stage=False)

        # 分类头
        self.avg_pool = layers.GlobalAveragePooling2D()
        self.fc = layers.Dense(outputs)

    def _build_res_stage(self, resblock, filters, block_count, first_stage):
        blocks = []
        # 第一个stage不需要下采样,后续stage第一个block做下采样
        blocks.append(resblock(filters, downsample=not first_stage))
        for _ in range(1, block_count):
            blocks.append(resblock(filters, downsample=False))
        return keras.Sequential(blocks)

    def call(self, x, training=None):
        x = self.layer0(x, training=training)
        x = self.layer1(x, training=training)
        x = self.layer2(x, training=training)
        x = self.layer3(x, training=training)
        x = self.layer4(x, training=training)
        x = self.avg_pool(x)
        x = self.fc(x)
        return x

初始化ResNet34时传入repeat=[3,4,6,3]即可,原有训练代码不需要改动,调整后即可解决该报错。


内容的提问来源于stack exchange,提问作者Anak Cerdas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 11:06:29