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

子类化tf.keras.Model构建UNet时model.fit报Keras符号输入转numpy数组错误

问题定位与解决方案

核心错误原因

    1. 子类化tf.keras.Model的call方法中错误访问预训练骨干网络的静态.output属性:你在call方法里直接拿encoder[idx].output和encoder.pop(-1).output,这些是构建Functional模型时用的静态符号张量,在子类化模型的动态前向传播过程中,这些符号张量无法参与实时计算,传入后续层时就会触发符号张量转numpy的报错。
    1. 损失函数与输出层不匹配:你的输出层是Conv2D(1, activation='sigmoid'),属于二分类输出,但你用了SparseCategoricalCrossentropy多分类损失,维度不匹配也会间接导致张量计算异常。
    1. 前向传播逻辑错误:call方法中你先将self.backbone(input_layer)赋值给x,但后续完全没有用到这个动态计算的结果,反而直接用了静态的bridge张量,逻辑完全错位。

修复方案

第一步:调整骨干网络特征提取逻辑

不要在call方法里遍历层拿静态output,改为在__init__阶段就构建一个多输出的Functional骨干模型,直接返回所有需要的编码器特征层的动态计算结果:

class UNet(tf.keras.Model):
    def __init__(self,
                 model_size="base",
                 num_classes=3,
                 img_size=224,
                 filters_root=32,
                 kernel_size=(3, 3),
                 backbone=None,
                 dropout_rate=.0,
                 dropout_decay=None,
                 local_pooling_layer=layers.MaxPool2D((2, 2)),
                 **kwargs
                 ):
        super().__init__(name="UNet", **kwargs)
        if model_size not in ["small", "base"]:
            model_size = "base"
            print("Model size input is not amongst {}. Model size is defaulted to base.".format(["small", "base"]))
        self.model_size = model_size
        self.num_classes = num_classes
        self.img_size = img_size
        self.filters_root = filters_root
        self.kernel_size = kernel_size
        self.backbone = backbone if backbone else get_unet_backbone(
            backbone_name="Efficient_net", pretrained_weights="imagenet")
        self.backbone.trainable = False  # 冻结编码器骨干
        self.dropout_rate = dropout_rate
        
        # 初始化阶段就构建多输出骨干模型,提前拿到需要的特征层
        encoder_output_layer_names = [l.name for l in self.backbone.layers if "a_activation" in l.name]
        if self.model_size == "small":
            encoder_output_layer_names = encoder_output_layer_names[:3] + encoder_output_layer_names[-2:]
        # 构建返回所有编码器特征的功能模型
        self.encoder_model = tf.keras.Model(
            inputs=self.backbone.input,
            outputs=[self.backbone.get_layer(name).output for name in encoder_output_layer_names]
        )

    def call(self, input_layer, training=False):
        # 直接拿到所有编码器层的动态计算结果
        encoder_features = self.encoder_model(input_layer, training=training)
        # 最后一个特征作为桥接层
        x = encoder_features[-1]
        # 倒序遍历前面的特征层做跳跃连接
        for idx in range(len(encoder_features)-2, 0, -1):
            x = decoder_block(x,
                              encoder_features[idx],
                              filters=self.filters_root * 2 ** idx,
                              kernel_size=self.kernel_size,
                              dropout_rate=self.dropout_rate)

        # 单类分割输出用1通道+sigmoid,多类分割可改为num_classes通道+softmax
        outputs = layers.Conv2D(1, kernel_size=(1, 1), padding="same",
                                activation="sigmoid",
                                kernel_initializer=LecunNormal(seed=global_seed),
                                bias_initializer=LecunNormal(seed=global_seed))(x)

        return outputs

第二步:修正损失函数配置

如果是二分类/单类分割,改为二进制交叉熵:

model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(from_logits=False) ,
    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4), 
    metrics=['accuracy']
)

如果是多类分割,输出层改为Conv2D(num_classes, activation='softmax'),保留SparseCategoricalCrossentropy即可。

可选兼容处理

如果还有类似报错,可以在代码开头加入全局配置禁用严格的张量类型检查:

import tensorflow as tf
tf.compat.v1.disable_eager_execution()

内容的提问来源于stack exchange,提问作者Phan Nguyen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 14:45:03