TensorFlow2中使用预训练YOLOv4 Keras模型时出现AttributeError问题的解决咨询
第一个错误:AttributeError: 'Tensor' object has no attribute 'items'
你最初直接调用new_model(image_data)的写法有问题——在TensorFlow2中,直接调用模型返回的是Eager张量(单输出模型)或Eager张量列表(多输出模型),而非字典。所以当你尝试用.items()遍历的时候,自然会触发属性错误。
你后续改成用model.predict(image_data)是正确的方向:predict()方法会返回模型输出的numpy数组列表,再和model.output_names配对成字典,就能正常遍历处理了。
第二个错误:AttributeError: 'Tensor' object has no attribute 'numpy'
你的推测完全正确:这个错误确实源于代码处于图执行模式。TensorFlow2默认是Eager执行模式,但如果你的代码在@tf.function装饰的函数内部、禁用了Eager执行,或者处于兼容TF1的静态图上下文里,此时得到的是图张量,这类张量无法直接调用.numpy()方法。
下面给你几种可行的解决方案:
方案1:确保代码在Eager模式下执行
先检查当前执行模式:
print(tf.executing_eagerly())
如果输出是False,可以在代码最开头手动开启Eager模式(必须在任何TensorFlow操作前调用):
import tensorflow as tf tf.compat.v1.enable_eager_execution()
如果你的后处理代码在@tf.function装饰的函数中,可以把.numpy()的调用移到函数外面——@tf.function的函数在Eager模式下调用时,返回的是Eager张量,支持.numpy():
@tf.function def run_detection(image_data): prediction_list = new_model(image_data) prediction_dict = {name: pred for name, pred in zip(new_model.output_names, prediction_list)} value = prediction_dict[new_model.output_names[0]] # 根据你的模型输出调整索引 boxes = value[:, :, 0:4] pred_conf = value[:, :, 4:] return tf.image.combined_non_max_suppression( boxes=tf.reshape(boxes, (tf.shape(boxes)[0], -1, 1, 4)), scores=tf.reshape(pred_conf, (tf.shape(pred_conf)[0], -1, tf.shape(pred_conf)[-1])), max_output_size_per_class=50, max_total_size=50, iou_threshold=FLAGS.iou, score_threshold=FLAGS.score ) # 在Eager模式下调用并转换为numpy boxes, scores, classes, valid_detections = run_detection(image_data) pred_bbox = [boxes.numpy(), scores.numpy(), classes.numpy(), valid_detections.numpy()] image = utils.draw_bbox(original_image, pred_bbox)
方案2:在图模式下用Session获取numpy值
如果你必须在图模式下运行(比如兼容旧代码),可以用TensorFlow会话来运行张量,获取numpy数组:
# 执行非极大值抑制得到图张量 boxes, scores, classes, valid_detections = tf.image.combined_non_max_suppression( boxes=tf.reshape(boxes, (tf.shape(boxes)[0], -1, 1, 4)), scores=tf.reshape(pred_conf, (tf.shape(pred_conf)[0], -1, tf.shape(pred_conf)[-1])), max_output_size_per_class=50, max_total_size=50, iou_threshold=FLAGS.iou, score_threshold=FLAGS.score ) # 用Session运行张量得到numpy数组 with tf.compat.v1.Session() as sess: sess.run(tf.compat.v1.global_variables_initializer()) boxes_np, scores_np, classes_np, valid_detections_np = sess.run([boxes, scores, classes, valid_detections]) pred_bbox = [boxes_np, scores_np, classes_np, valid_detections_np] image = utils.draw_bbox(original_image, pred_bbox)
方案3:用TensorFlow操作替代numpy转换(推荐部署场景)
如果你的代码需要完全在图模式下运行(比如部署到TensorRT或导出SavedModel),可以避免使用.numpy(),把绘制逻辑包装成tf.py_function来兼容Eager操作:
def draw_bbox_tensor(original_image, boxes, scores, classes, valid_detections): # 转换为numpy数组后调用原绘制函数 pred_bbox = [boxes.numpy(), scores.numpy(), classes.numpy(), valid_detections.numpy()] return utils.draw_bbox(original_image.numpy(), pred_bbox) # 用tf.py_function包装,让它能在图模式中执行Eager操作 image_tensor = tf.py_function( func=draw_bbox_tensor, inp=[original_image, boxes, scores, classes, valid_detections], Tout=tf.uint8 )
内容的提问来源于stack exchange,提问作者jimmy12487

