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

KerasCV RetinaNet转CoreML后置信度全为负值的问题求助

问题

用KerasCV的RetinaNet(基于mobilenet_v3_large_imagenet预训练模型)训练了10类自定义目标检测器,训练过程正常,Python端识别结果准确。但通过CoreML转换模型后,输出的boxes维度为1×76725×4、confidence维度为1×76725×10,且confidence数组内所有值均为负值(尚未经过NMS处理),求异常原因及解决办法。

相关代码

模型创建代码

model = keras_cv.models.RetinaNet.from_preset(
    "mobilenet_v3_large_imagenet",
    num_classes=len(class_mapping),
    bounding_box_format="xyxy",
)
model.compile(
    classification_loss="focal",
    box_loss="smoothl1",
    optimizer=optimizer,
    metrics=None,
)
model.fit(
    train_ds.ragged_batch(4),
    validation_data=eval_ds.ragged_batch(4),
    epochs=40,
    callbacks=[tensorboard_callback, VisualizeDetections(), model_checkpoint_callback],
)

CoreML转换代码

outputss = [ct.TensorType(name="Identity", dtype=np.float32), ct.TensorType(name="Identity_1", dtype=np.float32)]
converted_model = ct.convert(model, inputs=[ct.ImageType(shape=(1, 640, 640, 3))], outputs=outputss, convert_to="mlprogram")
print(converted_model.output_description)
# save converted model
converted_model.save("converted.mlpackage")

原因分析

  1. 输出为原始Logits:KerasCV的RetinaNet训练时用的是Focal Loss,该损失支持直接基于原始logits(未经过激活函数的输出)计算,因此模型分类头默认不会输出经过sigmoid/softmax激活的置信度。Python端推理时,KerasCV内置流程会自动对logits做激活处理,所以能看到正常的0-1区间置信度;但直接导出模型时,输出的是未激活的原始logits,这些值本身可能为负。
  2. 输入预处理不一致:如果CoreML转换时的图像预处理规则和Python训练/推理时不一致(比如像素值归一化范围不同),也会导致模型输出异常,包括置信度全为负的情况。

解决办法

方法1:修改模型,添加激活层后再导出

在导出CoreML前,给模型的分类输出添加sigmoid激活(RetinaNet是单类别独立检测,适合用sigmoid而非softmax),确保输出的是0-1区间的置信度:

import tensorflow as tf

# 获取模型骨干网络输出
backbone_output = model.backbone.output
# 保留原始框回归输出
box_output = model.box_head(backbone_output)
# 对分类输出添加sigmoid激活
class_output = model.classification_head(backbone_output)
activated_class_output = tf.keras.layers.Activation("sigmoid")(class_output)

# 构建新模型
modified_model = tf.keras.Model(
    inputs=model.input,
    outputs=[box_output, activated_class_output]
)

# 用修改后的模型进行CoreML转换
outputss = [ct.TensorType(name="boxes", dtype=np.float32), ct.TensorType(name="confidence", dtype=np.float32)]
converted_model = ct.convert(modified_model, inputs=[ct.ImageType(shape=(1, 640, 640, 3))], outputs=outputss, convert_to="mlprogram")
converted_model.save("converted_with_activation.mlpackage")

方法2:在移动端手动处理Logits

如果不想修改原模型,可以在iOS/Android端拿到confidence的logits后,手动计算sigmoid转换为正常置信度:

  • 公式:confidence = 1 / (1 + exp(-logit))
  • iOS Swift示例:
    func sigmoid(_ x: Float) -> Float {
        return 1.0 / (1.0 + exp(-x))
    }
    // 遍历confidence数组,对每个值应用sigmoid
    let convertedConfidence = confidenceTensor.map { sigmoid($0) }
    

方法3:统一输入预处理规则

确认Python端训练时的图像预处理方式,同步到CoreML转换配置中:

  • 若Python端将像素值从0-255归一化到0-1:
    inputs=[ct.ImageType(shape=(1, 640, 640, 3), scale=1/255.0)]
    
  • 若归一化到-1-1:
    inputs=[ct.ImageType(shape=(1, 640, 640, 3), scale=1/127.5, bias=-1)]
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 11:13:26