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

YOLOv3 TensorFlow训练调用mAP计算时出现InaccessibleTensorError报错

问题根因

你的代码独立运行时处于Eager模式,允许Python原生的列表、for循环操作张量,但model.fit的验证逻辑会被自动封装进tf.function图模式执行,图模式下Python的控制流(普通for、if、列表append张量)会产生作用域隔离,具体问题点如下:

  • filter_predictions函数的if max_prediction < len(det_probs_this_inst):分支只写了if内的返回逻辑,没有写else分支的返回值,图模式下cond两个分支的返回结构必须完全一致,否则张量会被创建在if分支的子图里,外部访问不到
  • 两层Python原生for循环(遍历类别、遍历nms结果的keep)+ Python列表存张量的写法,在图模式下会被转换为tf.while_loop,每个循环迭代都是子图,append到Python列表里的张量属于子图作用域,外层代码无法访问
  • 外层遍历batch的for i in tf.range(num_images)用的是Python for套tf.range,也会触发图模式下的循环作用域隔离问题
解决方案

方案1:全量改造为图模式兼容写法(推荐)

第一步:改造filter_predictions函数,全用TensorFlow原生API替换Python控制流

def filter_predictions(localizations , det_probs_this_inst , det_class_this_inst , num_classes, max_prediction=100, iou_threshold=0.5, score_threshold=0.001):
    # 所有输入都是单张图的张量,无batch维度
    det_probs_this_inst = tf.squeeze(det_probs_this_inst)
    det_class_this_inst = tf.squeeze(det_class_this_inst)
    
    # 先取分数topk,统一分支逻辑,避免if分支返回结构不一致
    k = tf.minimum(max_prediction, tf.shape(det_probs_this_inst)[0])
    pred_order = tf.argsort(det_probs_this_inst, direction='DESCENDING')[:k]
    locs = tf.gather(localizations, pred_order, axis=0)
    probs = tf.gather(det_probs_this_inst, pred_order)
    cls_idx = tf.gather(det_class_this_inst, pred_order)

    # 按类别做NMS,用tf.map_fn替换Python for循环
    def per_class_nms(c):
        # 取当前类的所有框和分数
        mask = tf.equal(cls_idx, c)
        class_locs = tf.boolean_mask(locs, mask)
        class_probs = tf.boolean_mask(probs, mask)
        # 调用tf原生NMS
        keep = tf.image.non_max_suppression(class_locs, class_probs, max_output_size=k, iou_threshold=iou_threshold, score_threshold=score_threshold)
        # 返回当前类保留的框、分数、类别
        return tf.gather(class_locs, keep), tf.gather(class_probs, keep), tf.fill((tf.shape(keep)[0],), c)
    
    # 遍历所有类别
    all_boxes, all_probs, all_classes = tf.map_fn(
        per_class_nms, 
        tf.range(num_classes),
        fn_output_signature=(
            tf.TensorSpec(shape=(None,4), dtype=tf.float32),
            tf.TensorSpec(shape=(None,), dtype=tf.float32),
            tf.TensorSpec(shape=(None,), dtype=tf.int64)
        )
    )
    
    # 合并所有类的结果,拍平维度
    final_boxes = tf.reshape(all_boxes, (-1,4))
    final_probs = tf.reshape(all_probs, (-1,1))
    final_class = tf.reshape(all_classes, (-1,1))
    
    return final_boxes, final_probs, final_class

第二步:改造外层batch遍历逻辑

不要用Python for遍历tf.range(num_images),改用tf.map_fn批量处理整个batch的所有样本,避免循环作用域问题:

# 定义单张图的后处理逻辑
def process_single_image(args):
    adjusted_bbox, det_prob, det_cls = args
    final_boxes, final_probs, final_class = filter_predictions(adjusted_bbox, det_prob, det_cls, num_classes)
    # 过滤低分数框
    mask = tf.squeeze(final_probs > filter_threshold)
    final_boxes = tf.boolean_mask(final_boxes, mask)
    final_probs = tf.boolean_mask(final_probs, mask)
    final_class = tf.boolean_mask(final_class, mask)
    return gen_box_objects(final_boxes, final_probs, final_class)

# 批量处理整个batch,代替Python for循环
pred_box_objects = tf.map_fn(
    process_single_image,
    elems=(adjusted_bbox_delta, det_probs, det_class),
    fn_output_signature=tf.TensorSpec(shape=(None, 6), dtype=tf.float32) # 此处要和gen_box_objects的输出类型、维度匹配
)

方案2:临时快速验证方案

如果只是验证阶段算mAP,不需要图模式加速的话,可以在自定义mAP指标的update_state方法上加@tf.autograph.experimental.do_not_convert装饰器,强制这部分逻辑用Eager模式执行,就不会有图作用域问题,不需要修改原有后处理逻辑:

class mAP(tf.keras.metrics.Metric):
    # 其他原有代码
    @tf.autograph.experimental.do_not_convert
    def update_state(self, y_true, y_pred, sample_weight=None):
        # 你原来的后处理逻辑直接放这里即可
后续排查注意事项
  • 所有在model.fit里执行的自定义逻辑,优先用TensorFlow原生API,不要混用Python的控制流和列表存张量
  • 图模式下if/else的两个分支返回的张量数量、维度、类型必须完全一致
  • 出现InaccessibleTensorError首先排查是不是有张量在子图(if分支、循环迭代)里创建,被外层作用域访问

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 08:24:04