Files
2026-07-13 12:40:42 +08:00

551 lines
17 KiB
Python

# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import unittest
import numpy as np
from op_test import (
OpTest,
convert_float_to_uint16,
get_device_place,
get_places,
is_custom_device,
)
from utils import static_guard
import paddle
from paddle import base
from paddle.base import core
def instance_norm_wrapper(
input, weight, bias, epsilon=1e-5, momentum=0.9, data_format='NCHW'
):
if data_format == "AnyLayout":
data_format = "NCDHW"
return paddle.nn.functional.instance_norm(
input, None, None, weight, bias, True, momentum, epsilon, data_format
)
def _reference_instance_norm(x, scale, bias, epsilon):
prev_x_shape = x.shape
if len(x.shape) < 4:
x = np.reshape(x, (x.shape[0], x.shape[1], -1, 1))
N, C, H, W = x.shape
mean = np.mean(x, axis=(2, 3), keepdims=True)
variance = np.var(x, axis=(2, 3), keepdims=True)
std = np.sqrt(variance + epsilon)
x_norm = (x - mean) / std
scale = scale.reshape([1, C, 1, 1])
bias = bias.reshape([1, C, 1, 1])
x_norm = scale * x_norm + bias
return x_norm.reshape(prev_x_shape), mean.reshape(N * C), std.reshape(N * C)
def _reference_instance_norm_grad(x, scale, mean, var):
prev_x_shape = x.shape
if len(x.shape) < 4:
N, C = x.shape[0], x.shape[1]
x = np.reshape(x, (N, C, -1, 1))
n, c, h, w = x.shape
d_y = np.ones(x.shape) / (np.prod(x.shape))
d_bias = np.ones((c,)) / c
mean_tile = np.reshape(mean, (n, c, 1, 1))
mean_tile = np.tile(mean_tile, (1, 1, h, w))
var_tile = np.reshape(var, (n, c, 1, 1))
var_tile = np.tile(var_tile, (1, 1, h, w))
d_scale = np.sum(d_y * (x - mean_tile) * var_tile, axis=(0, 2, 3))
var_inv = var_tile
scale_tile = np.reshape(scale, (1, c, 1, 1))
scale_tile = np.tile(scale_tile, (n, 1, h, w))
d_x = (
scale_tile
* var_inv
* (
d_y
- np.mean(d_y, axis=(2, 3), keepdims=True)
- (x - mean_tile)
* var_inv
* np.mean(
d_y * (x - mean_tile) * var_inv, axis=(2, 3), keepdims=True
)
)
)
return d_x.reshape(prev_x_shape), d_scale, d_bias
class TestInstanceNorm(unittest.TestCase):
def test_error(self):
for p in get_places():
def error1d():
x_data_4 = np.random.random(size=(2, 1, 3, 3)).astype('float32')
instance_norm1d = paddle.nn.InstanceNorm1D(1)
instance_norm1d(paddle.to_tensor(x_data_4))
def error2d():
x_data_3 = np.random.random(size=(2, 1, 3)).astype('float32')
instance_norm2d = paddle.nn.InstanceNorm2D(1)
instance_norm2d(paddle.to_tensor(x_data_3))
def error3d():
x_data_4 = np.random.random(size=(2, 1, 3, 3)).astype('float32')
instance_norm3d = paddle.nn.InstanceNorm3D(1)
instance_norm3d(paddle.to_tensor(x_data_4))
def weight_bias_false():
x_data_4 = np.random.random(size=(2, 1, 3, 3)).astype('float32')
instance_norm3d = paddle.nn.InstanceNorm3D(
1, weight_attr=False, bias_attr=False
)
with base.dygraph.guard(p):
weight_bias_false()
self.assertRaises(ValueError, error1d)
self.assertRaises(ValueError, error2d)
self.assertRaises(ValueError, error3d)
def test_dygraph(self):
for p in get_places():
shape = [4, 10, 4, 4]
def compute_v1(x):
with base.dygraph.guard(p):
bn = paddle.nn.InstanceNorm2D(shape[1])
y = bn(paddle.to_tensor(x))
return y.numpy()
def compute_v2(x):
with base.dygraph.guard(p):
bn = paddle.nn.InstanceNorm2D(shape[1])
y = bn(paddle.to_tensor(x))
return y.numpy()
x = np.random.randn(*shape).astype("float32")
y1 = compute_v1(x)
y2 = compute_v2(x)
np.testing.assert_allclose(y1, y2, rtol=1e-05)
def test_static(self):
with static_guard():
for p in get_places():
exe = base.Executor(p)
shape = [4, 10, 16, 16]
def compute_v1(x_np):
with paddle.static.program_guard(
paddle.static.Program(), paddle.static.Program()
):
ins = paddle.nn.InstanceNorm2D(shape[1])
x = paddle.static.data(
name='x', shape=x_np.shape, dtype=x_np.dtype
)
y = ins(x)
exe.run(paddle.static.default_startup_program())
r = exe.run(feed={'x': x_np}, fetch_list=[y])[0]
return r
def compute_v2(x_np):
with paddle.static.program_guard(
paddle.static.Program(), paddle.static.Program()
):
ins = paddle.nn.InstanceNorm2D(shape[1])
x = paddle.static.data(
name='x', shape=x_np.shape, dtype=x_np.dtype
)
y = ins(x)
exe.run(paddle.static.default_startup_program())
r = exe.run(feed={'x': x_np}, fetch_list=[y])[0]
return r
x = np.random.randn(*shape).astype("float32")
y1 = compute_v1(x)
y2 = compute_v2(x)
np.testing.assert_allclose(y1, y2, rtol=1e-05)
class TestInstanceNormFP32OP(OpTest):
def setUp(self):
'''Test instance_norm op with default value'''
self.op_type = "instance_norm"
self.__class__.op_type = self.op_type
self.data_format = "NCHW"
self.eps = 1e-5
self.init_dtype()
self.init_shape()
self.init_value()
self.set_err_threshold()
self.inputs = {'X': self.value, 'Scale': self.scale, 'Bias': self.bias}
self.attrs = {
'epsilon': self.eps,
'momentum': 0.9,
'data_format': self.data_format,
}
y, mean, variance = _reference_instance_norm(
self.value, self.scale, self.bias, self.eps
)
self.python_out_sig = ['Y']
self.outputs = {
'Y': y,
'SavedMean': mean,
'SavedVariance': 1.0 / variance,
}
self.prim_op_type = "comp"
self.python_api = instance_norm_wrapper
self.public_python_api = instance_norm_wrapper
self.check_prim = False
def test_check_output(self):
self.check_output(
atol=self.atol,
check_prim=self.check_prim,
check_pir=True,
check_prim_pir=(
False if os.getenv("FLAGS_enable_pir_in_executor") else True
),
)
def test_check_grad(self):
self.check_grad(
['X', 'Scale', 'Bias'],
'Y',
check_prim=self.check_prim,
check_pir=True,
check_prim_pir=(
False if os.getenv("FLAGS_enable_pir_in_executor") else True
),
)
def init_dtype(self):
self.dtype = np.float32
def init_shape(self):
self.shape = [4, 100, 4, 4]
def init_value(self):
np.random.seed(0)
self.value = np.random.random(self.shape).astype(self.dtype)
self.scale = np.random.random([self.shape[1]]).astype(np.float32)
self.bias = np.random.random([self.shape[1]]).astype(np.float32)
def set_err_threshold(self):
self.atol = 1e-3
self.fw_comp_rtol = 1e-6
self.fw_comp_atol = 1e-6
self.rev_comp_rtol = 1e-4
self.rev_comp_atol = 1e-4
self.cinn_rtol = 1e-4
self.cinn_atol = 1e-4
class TestInstanceNormWithNCL(TestInstanceNormFP32OP):
def init_shape(self):
self.shape = [4, 100, 16]
def test_check_output(self):
self.check_output(
atol=self.atol,
check_pir=True,
check_prim_pir=(
False if os.getenv("FLAGS_enable_pir_in_executor") else True
),
)
def test_check_grad(self):
self.check_grad(
['X', 'Scale', 'Bias'],
'Y',
check_pir=True,
check_prim_pir=(
False if os.getenv("FLAGS_enable_pir_in_executor") else True
),
)
class TestInstanceNormWithNC(TestInstanceNormFP32OP):
def init_shape(self):
self.shape = [4, 100]
def set_err_threshold(self):
super().set_err_threshold()
self.fw_comp_atol = 3e-5
def test_check_output(self):
self.check_output(
atol=self.atol,
check_pir=True,
check_prim_pir=(
False if os.getenv("FLAGS_enable_pir_in_executor") else True
),
)
def test_check_grad(self):
self.check_grad(
['X', 'Scale', 'Bias'],
'Y',
check_pir=True,
check_prim_pir=(
False if os.getenv("FLAGS_enable_pir_in_executor") else True
),
)
@unittest.skipIf(
not (core.is_compiled_with_cuda() or is_custom_device())
or not core.is_float16_supported(get_device_place()),
"core is not compiled with CUDA or not support the float16",
)
class TestInstanceNormFP16OP(TestInstanceNormFP32OP):
def setUp(self):
super().setUp()
def init_dtype(self):
self.dtype = np.float16
def set_err_threshold(self):
self.atol = 0.03125
self.max_relative_error = 8e-3
def test_check_output(self):
place = get_device_place()
self.check_output_with_place(
place,
atol=self.atol,
check_prim=self.check_prim,
check_pir=True,
check_prim_pir=(
False if os.getenv("FLAGS_enable_pir_in_executor") else True
),
)
def test_check_grad(self):
place = get_device_place()
self.check_grad_with_place(
place,
['X', 'Scale', 'Bias'],
'Y',
max_relative_error=self.max_relative_error,
check_prim=self.check_prim,
check_pir=True,
check_prim_pir=(
False if os.getenv("FLAGS_enable_pir_in_executor") else True
),
)
@unittest.skipIf(
not (core.is_compiled_with_cuda() or is_custom_device())
or not core.is_bfloat16_supported(get_device_place()),
"core is not compiled with CUDA or not support the bfloat16",
)
class TestInstanceNormBF16OP(OpTest):
def setUp(self):
self.op_type = "instance_norm"
self.prim_op_type = "comp"
self.__class__.op_type = self.op_type
self.python_api = instance_norm_wrapper
self.public_python_api = instance_norm_wrapper
self.eps = 1e-5
self.data_format = "NCHW"
self.dtype = np.uint16
self.init_shape()
self.init_value()
y, mean, variance = _reference_instance_norm(
self.value, self.scale, self.bias, self.eps
)
var_inv = 1.0 / variance
self.user_defined_grads = _reference_instance_norm_grad(
self.value, self.scale, mean, var_inv
)
self.python_out_sig = ['Y']
self.outputs = {
'Y': convert_float_to_uint16(y),
'SavedMean': mean,
'SavedVariance': var_inv,
}
self.inputs = {
'X': convert_float_to_uint16(self.value),
'Scale': self.scale,
'Bias': self.bias,
}
self.attrs = {
'epsilon': self.eps,
'momentum': 0.9,
'data_format': self.data_format,
}
self.check_prim = False
def init_value(self):
np.random.seed(0)
self.value = np.random.random(self.shape).astype(np.float32)
self.scale = np.random.random([self.shape[1]]).astype(np.float32)
self.bias = np.random.random([self.shape[1]]).astype(np.float32)
def init_shape(self):
self.shape = [4, 100, 4, 4]
def test_check_output(self):
place = get_device_place()
self.check_output_with_place(
place,
check_prim=self.check_prim,
check_pir=True,
check_prim_pir=(
False if os.getenv("FLAGS_enable_pir_in_executor") else True
),
)
def test_check_grad(self):
place = get_device_place()
self.check_grad_with_place(
place,
['X', 'Scale', 'Bias'],
'Y',
user_defined_grads=self.user_defined_grads,
check_prim=self.check_prim,
check_pir=True,
check_prim_pir=(
False if os.getenv("FLAGS_enable_pir_in_executor") else True
),
)
class PrimNet(paddle.nn.Layer):
def __init__(self):
super().__init__()
self.conv = paddle.nn.Conv2D(2, 4, (3, 3), bias_attr=False)
self.instance_norm = paddle.nn.InstanceNorm2D(4)
def forward(self, x):
y = self.conv(x)
out = self.instance_norm(y)
res = paddle.nn.functional.max_pool2d(
out, kernel_size=2, stride=2, padding=0
)
return res
def apply_to_static(net, use_cinn):
return paddle.jit.to_static(net, backend=None, full_graph=True)
class TestPrimForwardAndBackward(unittest.TestCase):
"""
Test PrimNet with @to_static + amp O2(with fp32)
"""
def setUp(self):
paddle.seed(2022)
paddle.disable_static()
self.x = paddle.randn([4, 2, 6, 6], dtype="float32")
self.x.stop_gradient = False
def train(self, use_amp, data_layout="NCHW"):
paddle.seed(2022)
net = PrimNet()
sgd = paddle.optimizer.SGD(
learning_rate=0.1, parameters=net.parameters()
)
net = apply_to_static(net, False)
if use_amp:
net = paddle.amp.decorate(models=net, level='O2')
with paddle.amp.auto_cast(enable=use_amp, level='O2'):
out = net(self.x)
loss = paddle.mean(out)
loss.backward()
sgd.step()
sgd.clear_grad()
return loss
def test_amp_nchw(self):
if not isinstance(
paddle.base.framework._current_expected_place(), core.CPUPlace
):
expected = self.train(False)
actual = self.train(True)
np.testing.assert_allclose(
expected,
actual,
rtol=1e-3,
atol=1e-3,
)
class TestInstanceNormOp_ZeroSize(OpTest):
def setUp(self):
paddle.disable_static()
self.op_type = "instance_norm"
self.__class__.op_type = self.op_type
self.data_format = "NCHW"
self.eps = 1e-5
self.init_dtype()
self.init_shape()
self.init_value()
self.inputs = {'X': self.value, 'Scale': self.scale, 'Bias': self.bias}
self.attrs = {
'epsilon': self.eps,
'momentum': 0.9,
'data_format': self.data_format,
}
self.python_out_sig = ['Y']
self.python_api = instance_norm_wrapper
self.public_python_api = instance_norm_wrapper
def test_check_output(self):
self.check_output(
atol=1e-3,
check_pir=True,
)
def test_check_grad(self):
self.check_grad(
['X', 'Scale', 'Bias'],
'Y',
check_pir=True,
)
def init_dtype(self):
self.dtype = np.float32
def init_shape(self):
self.shape = [2, 0, 4, 5]
self.scale_shape = [100]
y = np.random.random([2, 0, 4, 5]).astype(self.dtype)
mean = np.random.random(0).astype(self.dtype)
variance_1 = np.random.random(0).astype(self.dtype)
self.outputs = {
'Y': y,
'SavedMean': mean,
'SavedVariance': variance_1,
}
def init_value(self):
np.random.seed(0)
self.value = np.random.random(self.shape).astype(self.dtype)
self.scale = np.random.random(self.scale_shape).astype(np.float32)
self.bias = np.random.random(self.scale_shape).astype(np.float32)
if __name__ == '__main__':
paddle.enable_static()
unittest.main()