PaddlePaddle / Paddle

PArallel Distributed Deep LEarning: Machine Learning Framework from Industrial Practice (『飞桨』核心框架,深度学习&机器学习高性能单机、分布式训练和跨平台部署)
http://www.paddlepaddle.org/
Apache License 2.0
22.09k stars 5.55k forks source link

【与PyTorch对比,模型前向在train模式下有较大差异】 #67770

Open 9zdq opened 3 weeks ago

9zdq commented 3 weeks ago

bug描述 Describe the Bug

BUG:

PyTorch和Paddle相同的模型结构:一层Conv2D + 一层BatchNorm2D。 输入相同数据,PyTorch模型的权重复制给Paddle,分别设置了model.eval()和model.train(),测试两个框架的模型前向误差。 在model.eval()下,最大误差:1.8775463104248047e-06 在model.train()下,最大误差:2.5391578674316406e-05 train模式下的误差大于1e-5,是eval模式的10倍多。

测试环境

测试代码

import torch
import paddle
import numpy as np

def weight_copy_torch2paddle(torch_model, paddle_model):
    torch_dict = torch_model.state_dict()
    paddle_dict = paddle_model.state_dict()

    filtered_torch_items = [(name, param) for name, param in torch_dict.items() if 'num_batches_tracked' not in name]

    for (_, torch_param), (_, paddle_param) in zip(filtered_torch_items, paddle_dict.items()):
        numpy_param = torch_param.detach().cpu().numpy()
        paddle_tensor = paddle.to_tensor(numpy_param)
        paddle_param.set_value(paddle_tensor)

class TorchModel(torch.nn.Module):
    def __init__(self):
        super(TorchModel, self).__init__()
        self.conv1 = torch.nn.Conv2d(in_channels=256,out_channels=512, kernel_size=7, stride=1, padding=0)
        self.bn1 = torch.nn.BatchNorm2d(num_features=512)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        return x

class PaddleModel(paddle.nn.Layer):
    def __init__(self):
        super(PaddleModel, self).__init__()
        self.conv1 = paddle.nn.Conv2D(in_channels=256,out_channels=512, kernel_size=7, stride=1, padding=0)
        self.bn1 = paddle.nn.BatchNorm2D(num_features=512)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        return x

if __name__ == "__main__":

    torch.manual_seed(42)
    np.random.seed(42)

    data = np.random.rand(4, 256, 7, 7)

    paddle_input = paddle.to_tensor(data, dtype='float32')
    torch_input = torch.tensor(data, dtype=torch.float32)

    torch_model = TorchModel()
    paddle_model = PaddleModel()

    weight_copy_torch2paddle(torch_model, paddle_model)

    paddle_model.eval()
    torch_model.eval()

    paddle_out = paddle_model(paddle_input)
    torch_out = torch_model(torch_input)

    print(f"model.eval():" )
    print(f"Max absolute difference: {np.max(paddle_out.numpy() - torch_out.cpu().detach().numpy())}")

    paddle_model.train()
    torch_model.train()

    paddle_out = paddle_model(paddle_input)
    torch_out = torch_model(torch_input)

    print(f"model.train():" )
    print(f"Max absolute difference: {np.max(paddle_out.numpy() - torch_out.cpu().detach().numpy())}")

测试结果

image

补充

去掉BatchNorm,单独测试一层卷积,误差是可以接受的。加上BatchNorm后,train模式下的误差就大于1e-5了。

其他补充信息 Additional Supplementary Information

No response

xiaoguoguo626807 commented 3 weeks ago

这个是训练模型发现精度问题还是构造单测发现问题?这几个算子模型常用,我们有做过精度对齐,不应该出现较大的误差。 建议关注模型收敛的效果,因为两个框架算子实现逻辑不一定完全对齐。

9zdq commented 3 weeks ago

这个是训练模型发现精度问题还是构造单测发现问题?这几个算子模型常用,我们有做过精度对齐,不应该出现较大的误差。 建议关注模型收敛的效果,因为两个框架算子实现逻辑不一定完全对齐。

感谢您的回复。 我在模型迁移的时候,paddle的指标结果差了好多,然后发现模型前向在train模式下会有1e-4的误差,于是就单独测试了一层卷积一层BN的情况,最后发现了这个问题。

xiaoguoguo626807 commented 3 weeks ago

可以试一下在cpu 情况下的误差,如果cpu 误差正常,可以关掉cudnn 的随机性export FLAGS_cudnn_deterministic=1 看下,如果还有问题我们内部查看kernel情况。建议把两个算子分开测试,减少变量

9zdq commented 3 weeks ago

可以试一下在cpu 情况下的误差,如果cpu 误差正常,可以关掉cudnn 的随机性export FLAGS_cudnn_deterministic=1 看下,如果还有问题我们内部查看kernel情况。建议把两个算子分开测试,减少变量

感谢您的回复。 cpu上的误差不正常: model.eval(): Max absolute difference: 4.470348358154297e-06 model.train(): Max absolute difference: 5.906820297241211e-05

cpu上单测卷积: model.eval(): Max absolute difference: 4.470348358154297e-06 model.train(): Max absolute difference: 4.470348358154297e-06

cpu上单测BN: model.eval(): Max absolute difference: 0.0 model.train(): Max absolute difference: 3.2186508178710938e-06

这个issue的测试代码已经包含了下面几个flags:

import os
os.environ['FLAGS_embedding_deterministic'] = '1'
os.environ['FLAGS_cudnn_deterministic'] = '1'
os.environ['NVIDIA_TF32_OVERRIDE'] = '0'
os.environ['NCCL_ALGO'] = 'Tree'

不加的话,误差会更大: model.eval(): Max absolute difference: 0.0003285259008407593 model.train(): Max absolute difference: 0.00496247410774231

xiaoguoguo626807 commented 2 weeks ago

好的,我们内部检查一下,感谢反馈

9zdq commented 2 weeks ago

好的,我们内部检查一下,感谢反馈

感谢您的回复。 请问内部检查的结果怎么样呢?

9zdq commented 1 week ago

好的,我们内部检查一下,感谢反馈

您好,请问内部检查有结果了吗?