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

369 lines
11 KiB
Python

# Copyright (c) 2021 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.
from __future__ import annotations
from typing import TYPE_CHECKING
import numpy as np
import paddle
from paddle import base
from paddle.base.layer_helper import LayerHelper
from paddle.base.param_attr import ParamAttr
from paddle.nn import (
Layer,
initializer as I,
)
if TYPE_CHECKING:
from paddle import Tensor
from paddle._typing import DataLayout2D, ParamAttrLike
def resnet_unit(
x: Tensor,
filter_x: Tensor,
scale_x: Tensor,
bias_x: Tensor,
mean_x: Tensor,
var_x: Tensor,
z: Tensor | None,
filter_z: Tensor | None,
scale_z: Tensor | None,
bias_z: Tensor | None,
mean_z: Tensor | None,
var_z: Tensor | None,
stride: int,
stride_z: int,
padding: int,
dilation: int,
groups: int,
momentum: float,
eps: float,
data_format: DataLayout2D,
fuse_add: bool,
has_shortcut: bool,
use_global_stats: bool,
is_test: bool,
act: str,
) -> Tensor:
helper = LayerHelper('resnet_unit', **locals())
bn_param_dtype = base.core.VarDesc.VarType.FP32
bit_mask_dtype = base.core.VarDesc.VarType.INT32
out = helper.create_variable_for_type_inference(x.dtype)
bit_mask = helper.create_variable_for_type_inference(
dtype=bit_mask_dtype, stop_gradient=True
)
# intermediate_out for x
conv_x = helper.create_variable_for_type_inference(
dtype=x.dtype, stop_gradient=True
)
saved_mean_x = helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
saved_invstd_x = helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
running_mean_x = mean_x
running_var_x = var_x
# intermediate_out for z
conv_z = helper.create_variable_for_type_inference(
dtype=x.dtype, stop_gradient=True
)
saved_mean_z = helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
saved_invstd_z = helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
running_mean_z = (
helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
if mean_z is None
else mean_z
)
running_var_z = (
helper.create_variable_for_type_inference(
dtype=bn_param_dtype, stop_gradient=True
)
if var_z is None
else var_z
)
inputs = {
'X': x,
'FilterX': filter_x,
'ScaleX': scale_x,
'BiasX': bias_x,
'MeanX': mean_x,
'VarX': var_x,
'Z': z,
'FilterZ': filter_z,
'ScaleZ': scale_z,
'BiasZ': bias_z,
'MeanZ': mean_z,
'VarZ': var_z,
}
attrs = {
'stride': stride,
'stride_z': stride_z,
'padding': padding,
'dilation': dilation,
'group': groups,
'momentum': momentum,
'epsilon': eps,
'data_format': data_format,
'fuse_add': fuse_add,
'has_shortcut': has_shortcut,
'use_global_stats': use_global_stats,
'is_test': is_test,
'act_type': act,
}
outputs = {
'Y': out,
'BitMask': bit_mask,
'ConvX': conv_x,
'SavedMeanX': saved_mean_x,
'SavedInvstdX': saved_invstd_x,
'RunningMeanX': running_mean_x,
'RunningVarX': running_var_x,
'ConvZ': conv_z,
'SavedMeanZ': saved_mean_z,
'SavedInvstdZ': saved_invstd_z,
'RunningMeanZ': running_mean_z,
'RunningVarZ': running_var_z,
}
helper.append_op(
type='resnet_unit', inputs=inputs, outputs=outputs, attrs=attrs
)
return out
class ResNetUnit(Layer):
r"""
******Temporary version******.
ResNetUnit is designed for optimize the performance by using cudnnv8 API.
"""
def __init__(
self,
num_channels_x: int,
num_filters: int,
filter_size: int,
stride: int = 1,
momentum: float = 0.9,
eps: float = 1e-5,
data_format: DataLayout2D = 'NHWC',
act: str = 'relu',
fuse_add: bool = False,
has_shortcut: bool = False,
use_global_stats: bool = False,
is_test: bool = False,
filter_x_attr: ParamAttrLike | None = None,
scale_x_attr: ParamAttrLike | None = None,
bias_x_attr: ParamAttrLike | None = None,
moving_mean_x_name: str | None = None,
moving_var_x_name: str | None = None,
num_channels_z: int = 1,
stride_z: int = 1,
filter_z_attr: ParamAttrLike | None = None,
scale_z_attr: ParamAttrLike | None = None,
bias_z_attr: ParamAttrLike | None = None,
moving_mean_z_name: str | None = None,
moving_var_z_name: str | None = None,
) -> None:
super().__init__()
self._stride = stride
self._stride_z = stride_z
self._dilation = 1
self._kernel_size = paddle.utils.convert_to_list(
filter_size, 2, 'kernel_size'
)
self._padding = (filter_size - 1) // 2
self._groups = 1
self._momentum = momentum
self._eps = eps
self._data_format = data_format
self._act = act
self._fuse_add = fuse_add
self._has_shortcut = has_shortcut
self._use_global_stats = use_global_stats
self._is_test = is_test
# check format
valid_format = {'NHWC', 'NCHW'}
if data_format not in valid_format:
raise ValueError(
f"conv_format must be one of {valid_format}, but got conv_format='{data_format}'"
)
def _get_default_param_initializer(channels):
filter_elem_num = np.prod(self._kernel_size) * channels
std = (2.0 / filter_elem_num) ** 0.5
return I.Normal(0.0, std)
is_nchw = data_format == 'NCHW'
# initial filter
bn_param_dtype = base.core.VarDesc.VarType.FP32
if not is_nchw:
bn_param_shape = [1, 1, 1, num_filters]
filter_x_shape = [
num_filters,
filter_size,
filter_size,
num_channels_x,
]
filter_z_shape = [
num_filters,
filter_size,
filter_size,
num_channels_z,
]
else:
bn_param_shape = [1, num_filters, 1, 1]
filter_x_shape = [
num_filters,
num_channels_x,
filter_size,
filter_size,
]
filter_z_shape = [
num_filters,
num_channels_z,
filter_size,
filter_size,
]
self.filter_x = self.create_parameter(
shape=filter_x_shape,
attr=filter_x_attr,
default_initializer=_get_default_param_initializer(num_channels_x),
)
self.scale_x = self.create_parameter(
shape=bn_param_shape,
attr=scale_x_attr,
dtype=bn_param_dtype,
default_initializer=I.Constant(1.0),
)
self.bias_x = self.create_parameter(
shape=bn_param_shape,
attr=bias_x_attr,
dtype=bn_param_dtype,
is_bias=True,
)
self.mean_x = self.create_parameter(
attr=ParamAttr(
name=moving_mean_x_name,
initializer=I.Constant(0.0),
trainable=False,
),
shape=bn_param_shape,
dtype=bn_param_dtype,
)
self.mean_x.stop_gradient = True
self.var_x = self.create_parameter(
attr=ParamAttr(
name=moving_var_x_name,
initializer=I.Constant(1.0),
trainable=False,
),
shape=bn_param_shape,
dtype=bn_param_dtype,
)
self.var_x.stop_gradient = True
if has_shortcut:
self.filter_z = self.create_parameter(
shape=filter_z_shape,
attr=filter_z_attr,
default_initializer=_get_default_param_initializer(
num_channels_z
),
)
self.scale_z = self.create_parameter(
shape=bn_param_shape,
attr=scale_z_attr,
dtype=bn_param_dtype,
default_initializer=I.Constant(1.0),
)
self.bias_z = self.create_parameter(
shape=bn_param_shape,
attr=bias_z_attr,
dtype=bn_param_dtype,
is_bias=True,
)
self.mean_z = self.create_parameter(
attr=ParamAttr(
name=moving_mean_z_name,
initializer=I.Constant(0.0),
trainable=False,
),
shape=bn_param_shape,
dtype=bn_param_dtype,
)
self.mean_z.stop_gradient = True
self.var_z = self.create_parameter(
attr=ParamAttr(
name=moving_var_z_name,
initializer=I.Constant(1.0),
trainable=False,
),
shape=bn_param_shape,
dtype=bn_param_dtype,
)
self.var_z.stop_gradient = True
else:
self.filter_z = None
self.scale_z = None
self.bias_z = None
self.mean_z = None
self.var_z = None
def forward(self, x: Tensor, z: Tensor | None = None) -> Tensor:
if self._fuse_add and z is None:
raise ValueError("z can not be None")
out = resnet_unit(
x,
self.filter_x,
self.scale_x,
self.bias_x,
self.mean_x,
self.var_x,
z,
self.filter_z,
self.scale_z,
self.bias_z,
self.mean_z,
self.var_z,
self._stride,
self._stride_z,
self._padding,
self._dilation,
self._groups,
self._momentum,
self._eps,
self._data_format,
self._fuse_add,
self._has_shortcut,
self._use_global_stats,
self._is_test,
self._act,
)
return out