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")
原因分析
- 输出为原始Logits:KerasCV的RetinaNet训练时用的是Focal Loss,该损失支持直接基于原始logits(未经过激活函数的输出)计算,因此模型分类头默认不会输出经过sigmoid/softmax激活的置信度。Python端推理时,KerasCV内置流程会自动对logits做激活处理,所以能看到正常的0-1区间置信度;但直接导出模型时,输出的是未激活的原始logits,这些值本身可能为负。
- 输入预处理不一致:如果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
相关产品推荐
相关产品推荐

