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

float32转float16对np.isclose的atol影响及模型层输出对比问题

问题描述

我正在对比TensorFlow和自定义轻量库训练的两个机器学习模型的各层输出,二者在单元测试中表现一致,但float32与float16训练模式下的测试差异显著——尽管最终预测性能相近:

  • 使用float32权重训练时,用np.isclose(atol=1e-08)对比层输出,误差极小;
  • 切换到float16权重训练后,即便将atol调至1e-04,误差仍大幅增加。

我想确认:仅调大atol是否为正确的解决思路?

复现代码

以Dense层为例对比Keras与自定义实现的输出:float16模式下,即便按位宽比例调整容差,错误率仍达60%以上,而float32时仅约2%。代码如下:

import keras.backend
import numpy as np
import keras

def custom_dense_layer(_input_data, weights, biases) -> np.ndarray:
    _layer_output = np.matmul(_input_data, weights) + biases  # weight matrix = [n1_weights, n2_weights, ..]. Thus computing (X.W + B) instead of (transpose(W).transpose(X) + transpose(B))
    return _layer_output


if __name__ == '__main__' :
    bit_precision=16  # used to set the computation and weights / biases dtypes, here the goal is to compare between 16 and 32
    keras.backend.set_floatx(f'float{bit_precision}') # set computation dtype
    weights=np.random.randn(16,16).astype(f'float{bit_precision}') # generate random weights
    biases=np.random.randn(16).astype(f'float{bit_precision}') # generate random biases
    input = keras.Input(shape=(16, ),dtype=f'float{bit_precision}') # set input shape
    input_data = np.random.randn(1000,16).astype(f'float{bit_precision}') # generate random data
    keras_dense = keras.layers.Dense(units=16) # generate keras dense layer
    keras_dense(input)
    keras_dense.set_weights([weights,biases]) # set the weights and biases
    custom_op = custom_dense_layer(_input_data=input_data, weights=weights, biases=biases) # custom dense layer prediction calculation
    keras_op = np.array(keras_dense(input_data)) # keras prediction calculation
    isclose = np.isclose(keras_op, custom_op,atol=10**-(bit_precision/4),rtol=(2**-(bit_precision/2))).all(axis=1) # checking results similarity levels
    failed_points = np.where(~np.isclose(keras_op, custom_op,atol=10**-(bit_precision/4),rtol=(2**-(bit_precision/2))).all(axis=1))[0]# checking results that are not close enough
    perc_failed = round(100 * failed_points.size / isclose.size, 3) # "failed" results percentage
    print(perc_failed)
分析与解决方案

1. 仅调大atol不是最优方案

float16下误差激增的根本原因是计算路径的数值差异,而非单纯的精度不足。Keras/TensorFlow的float16计算包含大量优化逻辑:

  • 内部可能启用混合精度(部分计算转float32执行)避免溢出;
  • 矩阵乘法、加法依赖硬件加速指令(如CUDA Tensor Core),数值舍入方式和纯Numpy的np.matmul不同;
  • 部分操作会做精度补偿(比如bias加法前的类型转换逻辑)。

只调大容差相当于掩盖差异,无法确认自定义实现的数值正确性,后续复杂模型(如带激活、归一化的层)可能出现更严重的偏移。

2. 正确的排查与对齐步骤

  • 对齐计算路径的精度逻辑:
    检查Keras是否开启混合精度(默认可能自动启用),自定义实现要同步该逻辑——比如矩阵乘法后先转float32加bias再转回float16,或全程保持float16但匹配Keras的舍入模式。
  • 排除硬件加速干扰:
    若在GPU上运行,可强制切换到CPU计算(tf.config.set_visible_devices([], 'GPU')),对比Numpy和Keras的CPU计算结果,定位是否由硬件指令差异导致。
  • 以float32为基准逐步对齐:
    先确保float32下两者输出完全一致(atol=1e-8通过),再逐步切换到float16,每一步对比中间结果,定位差异出现的具体环节(是矩阵乘法还是加法?)。
  • 基于float16固有精度调整容差:
    float16的最小可表示相对误差约为2^-10 ≈ 9.7e-4,合理的rtol应设为1e-3,`atol`设为1e-4(针对绝对值较小的数值),但前提是计算路径已经对齐,否则再大的容差也可能覆盖错误。

3. 单元测试的优化建议

单元测试的核心是验证模型行为一致,而非数值完全相等:

  • 对于float16,可采用“相对误差在合理范围内”+“预测结果top-k一致”的组合测试;
  • 若自定义库用于部署,可直接对比最终推理的准确率、召回率等业务指标,只要指标一致,层输出的微小差异可接受。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:03:19