TensorFlow 2.x 自定义 metric 必须继承 tf.keras.metrics.Metric 类并实现 __init__、update_state、result 和 reset_state 方法,状态变量须用 self.add_weight 创建并在 reset_state 中手动清零,且必须传实例而非类名注册。
TensorFlow 2.x 中自定义 metric 类必须继承 tf.keras.metrics.Metric
直接写函数或用 @tf.function 包裹的纯计算逻辑,无法被 model.evaluate() 或训练循环自动调用——Keras 的评估流程只认 Metric 子类实例。继承后需至少实现三个方法:__init__(初始化状态变量)、update_state(逐 batch 累积中间值)、result(返回最终标量)。注意:所有状态变量(如 self.total、self.count)必须用 self.add_weight 创建,否则无法跨 GPU/TPU 正确聚合。
避免在 update_state 中做 argmax 或硬阈值化
常见错误是把预测转成 one-hot 后再算 accuracy 式指标,比如手动 tf.argmax(y_pred, axis=-1) 再比对标签。这会切断梯度(虽评估时不反向传播,但影响分布式训练下多设备同步逻辑),更严重的是破坏了 tf.distribute.Strategy 的 all-reduce 行为——因为 argmax 结果是非连续张量,跨设备聚合时可能出错。正确做法是保持 logits 或概率输出,在 result 中统一处理,或用 tf.nn.softmax + tf.math.top_k 等可导近似(如果真需要 top-k 类逻辑)。
- 推荐:在
update_state中只累积原始预测(y_pred)和真实标签(y_true),延迟到result计算最终值 - 若必须二分类阈值(如 F1),把阈值设为
self.threshold = threshold并用tf.cast(y_pred > self.threshold, tf.float32),而非 Pythonif判断 - 注意
y_true和y_pred的 shape 是否匹配:多分类中y_true常为整数索引(shape(batch_size,)),而y_pred是 logits((batch_size, num_classes)),需用tf.one_hot对齐或改用sparse_版本指标
reset_state 必须重写且清空所有 add_weight 变量
默认父类的 reset_state 不会自动重置你用 self.add_weight 定义的状态,导致多次 evaluate() 调用结果累加。例如实现 precision 时定义了 self.true_positives = self.add_weight(name='tp', initializer='zeros'),就必须在 reset_state 中显式写 self.true_positives.assign(0)。漏掉这一步,第一次 eval 得 0.8,第二次接着算可能变成 1.5——尤其在 Jupyter 中反复运行 cell 时极易复现。
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
- 不要依赖
super().reset_state()自动处理;它只重置父类内部变量 - 所有
add_weight创建的变量都得手动 assign 零值,包括tf.Variable类型的中间缓存 - 如果用了
tf.TensorArray等动态结构,也要在reset_state中重建新实例
用 model.compile(..., metrics=[CustomMetric()]) 注册时别传类名
写成 metrics=[MyF1Score](没加括号)会导致实例未创建,Keras 在内部调用 __call__ 时报 TypeError: 'type' object is not callable。必须传实例:metrics=[MyF1Score(threshold=0.5)]。另外,若指标依赖训练模式(如 dropout 影响预测分布),需在 update_state 中显式检查 tf.keras.backend.learning_phase(),但通常评估阶段 learning_phase=0,无需额外分支。
复杂点在于多输出模型:每个输出要单独配一个 metric 实例,Keras 不会自动广播;容易被忽略的是,自定义 metric 的 name 属性会直接出现在日志键名里,比如 self.name = 'f1_macro',那训练输出里就显示 f1_macro: 0.723——拼错名字会导致你盯着 logs 找不到指标。
就爱读