Files
paddlepaddle--paddle/python/paddle/nn/layer/layers.py
T
2026-07-13 12:40:42 +08:00

3970 lines
148 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copyright (c) 2018 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
import copy
import inspect
import re
import typing
import warnings
import weakref
from collections import OrderedDict, namedtuple
from collections.abc import Callable
from typing import TYPE_CHECKING, Any
import numpy as np
from typing_extensions import Self, overload
import paddle
from paddle import Tensor, dtype, nn, profiler
from paddle.autograd import PyLayer
from paddle.autograd.backward_utils import ValueSet
from paddle.base import core, framework, unique_name
from paddle.base.core import VarDesc
from paddle.base.dygraph import no_grad
from paddle.base.dygraph.base import (
_convert_into_variable,
in_declarative_mode, # noqa: F401
in_sot_simulation_mode,
in_to_static_mode,
)
from paddle.base.dygraph_utils import _append_activation_in_dygraph
from paddle.base.executor import Executor, global_scope
from paddle.base.framework import (
Parameter,
Program,
_current_expected_place as _get_device,
convert_nptype_to_datatype_or_vartype,
datatype_to_vartype,
default_main_program,
in_dygraph_mode,
in_pir_mode,
name_struct,
)
from paddle.base.layer_helper_base import LayerHelperBase
from paddle.distributed.flex_checkpoint.dcp.sharded_weight import (
ShardedStateDict,
build_sharded_state_dict,
)
from paddle.framework import ParamAttr
from paddle.profiler.utils import in_profiler_mode
from paddle.utils import deprecated
from paddle.utils.decorator_utils import (
param_one_alias,
)
if TYPE_CHECKING:
from collections.abc import Iterable, Iterator, Mapping, Sequence
from paddle._typing import DTypeLike, ParamAttrLike, PlaceLike, ShapeLike
from paddle.nn.initializer import Initializer
__all__ = []
_ForwardPreHook = (
Callable[["Layer", tuple[Any, ...]], Any | None]
| Callable[
["Layer", tuple[Any, ...], dict[str, Any]],
tuple[tuple[Any, ...], dict[str, Any]] | None,
]
)
_ForwardPostHook = (
Callable[["Layer", tuple[Any, ...], Any], Any | None]
| Callable[
["Layer", tuple[Any, ...], dict[str, Any], Any],
Any | None,
]
)
_StateDict = dict[str, Any] | typing.OrderedDict[str, Any]
_StateDictPreHook = Callable[["Layer", str, bool], None]
_StateDictHook = Callable[[_StateDict], None]
_EXTRA_STATE_KEY_SUFFIX = "_extra_state"
_first_cap_re = re.compile('(.)([A-Z][a-z]+)')
_all_cap_re = re.compile('([a-z])([A-Z])')
def record_program_ops_pre_hook(layer, inputs):
"""
A pre-hook to mark op numbers before enter layer.forward.
"""
if not in_dygraph_mode():
if layer._op_recorder.start < 0:
layer._op_recorder.start = len(
default_main_program().current_block().ops
)
layer._op_recorder.is_valid = True
else:
layer._op_recorder.is_valid = False
warnings.warn(
f"{layer._full_name} has recorded the op information before. Please check whether you call this layer twice."
)
def set_op_customized_attrs_post_hook(layer, inputs, outputs):
"""
A post-hook to append customized attributes into all operators generated in current layer.
"""
if not in_dygraph_mode() and layer._op_recorder.is_valid:
start = layer._op_recorder.start
end = len(default_main_program().current_block().ops)
assert start >= 0 and end >= start
ops = default_main_program().current_block().ops[start:end]
layer._op_recorder.end = end
layer._op_recorder.ops = ops
for op in ops:
for attr_name, val in layer._customized_attrs.items():
op._set_attr(attr_name, val)
# remove pre-hook and post-hook
for hook_helper in layer._op_recorder.hooks:
hook_helper.remove()
class _LayerBackwardInputHook(PyLayer):
@staticmethod
def forward(ctx, layer, *flat_inputs):
ctx.layer = layer
return tuple(inp.clone() for inp in flat_inputs)
@staticmethod
def backward(ctx, *grad_inputs):
layer = ctx.layer
grad_inputs = tuple(grad_inputs)
grad_outputs = getattr(layer, "_current_grad_outputs", ())
for hook in layer._get_backward_hooks():
hook_result = hook(layer, grad_inputs, grad_outputs)
if hook_result is not None:
if not isinstance(hook_result, tuple):
hook_result = (hook_result,)
grad_inputs = hook_result
if hasattr(layer, "_current_grad_outputs"):
delattr(layer, "_current_grad_outputs")
if hasattr(layer, "_has_backward_input_hook"):
delattr(layer, "_has_backward_input_hook")
return grad_inputs
class _LayerBackwardOutputHook(PyLayer):
@staticmethod
def forward(ctx, layer, *flat_outputs):
ctx.layer = layer
return tuple(out.clone() for out in flat_outputs)
@staticmethod
def backward(ctx, *grad_outputs):
layer = ctx.layer
grad_outputs = tuple(grad_outputs)
for hook in layer._get_backward_pre_hooks():
hook_result = hook(layer, grad_outputs)
if hook_result is not None:
if not isinstance(hook_result, tuple):
hook_result = (hook_result,)
grad_outputs = hook_result
if not getattr(layer, "_has_backward_input_hook", False):
grad_inputs = ()
for hook in layer._get_backward_hooks():
hook_result = hook(layer, grad_inputs, grad_outputs)
if hook_result is not None:
if not isinstance(hook_result, tuple):
hook_result = (hook_result,)
grad_inputs = hook_result
if hasattr(layer, "_has_backward_input_hook"):
delattr(layer, "_has_backward_input_hook")
layer._current_grad_outputs = grad_outputs
return grad_outputs
def _scope_dist2single(dist_scope):
mapping = {
"row_parallel_linear": "linear",
"column_parallel_linear": "linear",
"vocab_parallel_embedding": "embedding",
# "parallel_cross_entropy": "cross_entropy", while mp_layer has parallel_cross_entropy,
# but there is no parameters so the mapping of parallel_cross_entropy is not necessary.
}
return mapping.get(dist_scope, dist_scope)
def _convert_camel_to_snake(name):
s1 = _first_cap_re.sub(r'\1_\2', name)
return _all_cap_re.sub(r'\1_\2', s1).lower()
def _addindent(string, indent):
s1 = string.split('\n')
if len(s1) == 1:
return string
s2 = []
for idx, line in enumerate(s1):
if idx > 0:
s2.append(str((indent * ' ') + line))
return s1[0] + '\n' + '\n'.join(s2)
def _parse_to_args(*args, **kwargs):
"""Parse arguments for .to(), shared by Tensor.to and Layer.to.
Calling conventions::
to(device=None, dtype=None, blocking=True, copy=False, *, non_blocking=False)
to(dtype, blocking=True, copy=False, *, non_blocking=False)
to(tensor, blocking=True, copy=False, *, non_blocking=False)
Returns:
tuple: (device, dtype, blocking, copy)
"""
valid_dtypes = {
'bfloat16',
'float16',
'float32',
'float64',
'int8',
'int16',
'int32',
'int64',
'uint8',
'complex64',
'complex128',
'bool',
}
valid_keys = {
'device',
'dtype',
'blocking',
'copy',
'non_blocking',
'other',
'tensor',
}
invalid_keys = set(kwargs.keys()) - valid_keys
if invalid_keys:
raise TypeError(
"to() got an unexpected keyword argument '"
+ next(iter(invalid_keys))
+ "'"
)
device = kwargs.get('device', None)
dtype = kwargs.get('dtype', None)
blocking = kwargs.get('blocking', None)
copy = kwargs.get('copy', False)
non_blocking = kwargs.pop('non_blocking', None)
size_args = len(args)
size_kwargs = len(kwargs)
if size_args + size_kwargs > 4:
raise TypeError(
"to() received too many arguments - expected one of:\n"
" to(device=None, dtype=None, blocking=True, *, non_blocking=False)\n"
" to(dtype, blocking=True, *, non_blocking=False)\n"
" to(tensor, blocking=True, copy=False, *, non_blocking=False)"
)
if size_args > 0:
first = args[0]
if isinstance(first, paddle.Tensor):
# to(tensor, blocking=True, copy=False)
device = first.place
dtype = first.dtype
if size_args >= 2:
blocking = args[1]
if size_args >= 3:
copy = args[2]
elif isinstance(first, (core.DataType, VarDesc.VarType, np.dtype)) or (
isinstance(first, str) and first.lower() in valid_dtypes
):
# to(dtype, blocking=True, copy=False)
dtype = first
if size_args >= 2:
blocking = args[1]
if size_args >= 3:
copy = args[2]
elif first is None or isinstance(first, (str, core.Place)):
# to(device, dtype=None, blocking=True, copy=False)
device = first
if size_args >= 2:
dtype = args[1]
if size_args >= 3:
blocking = args[2]
if size_args >= 4:
copy = args[3]
else:
raise ValueError(
f"device should be type of str, paddle.CPUPlace, paddle.CUDAPlace, "
f"paddle.CUDAPinnedPlace, paddle.XPUPlace, or paddle.base.libpaddle.Place, "
f"but got {type(first).__name__}"
)
else:
tensor_arg = kwargs.get('other')
if tensor_arg is None:
tensor_arg = kwargs.get('tensor')
if tensor_arg is not None:
device = tensor_arg.place
dtype = tensor_arg.dtype
# Validate and resolve blocking / non_blocking
if blocking is not None and non_blocking is not None:
raise TypeError(
"to() received both 'blocking' and 'non_blocking' arguments. "
"These are mutually exclusive, please use only one of them."
)
if non_blocking is not None:
if not isinstance(non_blocking, bool):
raise TypeError("non_blocking value error, must be True or False")
blocking = not non_blocking
elif blocking is not None:
if not isinstance(blocking, bool):
raise TypeError("blocking value error, must be True, False or None")
else:
blocking = True
if copy is None:
copy = False
elif not isinstance(copy, bool):
raise TypeError("copy value error, must be True or False")
return device, dtype, blocking, copy
def _layer_trans_dtype(layer, dtype, excluded_layers):
if type(layer) in excluded_layers:
return
layer._to_impl(dtype=dtype, floating_only=True, include_sublayers=False)
class _IncompatibleKeys(
namedtuple("IncompatibleKeys", ["missing_keys", "unexpected_keys"]),
):
__slots__ = ()
def __repr__(self) -> str:
if not self.missing_keys and not self.unexpected_keys:
return "<All keys matched successfully>"
return super().__repr__()
__str__ = __repr__
class LayerObjectHelper(LayerHelperBase):
def __init__(self, name):
super().__init__(name, layer_type=name)
def append_op(
self,
type=None,
inputs=None,
outputs=None,
attrs=None,
stop_gradient=None,
):
"""append an operator for this layer object.
Args:
type: operator type
inputs: input variable of the operator
dtype: data type of this parameter
is_bias: if this is a bias parameter
default_initializer: set the default initializer for this parameter
Returns created parameter Variable.
"""
return self.main_program.current_block().append_op(
type=type,
inputs=inputs,
outputs=outputs,
attrs=attrs,
stop_gradient=stop_gradient,
)
def _multiple_input(self, inputs_in):
inputs = inputs_in
ret = []
if isinstance(inputs, (list, tuple)):
for inp in inputs:
ret.append(self.to_variable(inp))
else:
ret.append(self.to_variable(inputs))
return ret
# TODO: make it public when we need it
def _input(self, inputs_in):
inputs = self._multiple_input(inputs_in)
if len(inputs) != 1:
raise f"{self.layer_type} layer only takes one input in"
return inputs[0]
def _multiple_param_attr(self, length, param_attr_in=None):
param_attr = param_attr_in
if isinstance(param_attr, ParamAttr):
param_attr = [param_attr]
if len(param_attr) != 1 and len(param_attr) != length:
raise ValueError(f"parameter number mismatch in {self.name}")
elif len(param_attr) == 1 and length != 1:
tmp = [None] * length
for i in range(length):
tmp[i] = copy.deepcopy(param_attr[0])
param_attr = tmp
return param_attr
def iter_inputs_and_params(self, inputs_in, param_attr_in=None):
"""Access all inputs and params one by one
Args:
inputs_in: inputs to be iter
param_attr_in: param_attr to be iter
Returns input, param_attr
"""
param_attr_in = ParamAttr._to_attr(param_attr_in)
if isinstance(param_attr_in, bool):
raise ValueError(f'Param_attr should not be False in {self.name}')
inputs = inputs_in if (inputs_in is not None) else []
inputs = self._multiple_input(inputs)
param_attrs = self._multiple_param_attr(len(inputs), param_attr_in)
yield from zip(inputs, param_attrs)
def input_dtype(self, inputs_in):
"""Get input data type
Args:
inputs_in: inputs wanted know the data type
Returns dtype of the input
"""
inputs_in = inputs_in if (inputs_in is not None) else []
inputs = self._multiple_input(inputs_in)
dtype = None
for each in inputs:
if dtype is None:
dtype = each.dtype
elif dtype != each.dtype:
raise ValueError(
f"Data Type mismatch: {dtype} to {each.dtype} in {self.name}"
)
return dtype
def get_parameter(self, name):
"""Get parameter specifically
Args:
name: parameter's name
Returns target parameter
"""
param = self.main_program.global_block().var(name)
if not isinstance(param, Parameter):
raise ValueError(f"no Parameter name {name} found in {self.name}")
return param
# TODO: this should not be called anymore after all activation func move to Layers
def append_activation(self, input_var, act=None, use_cudnn=None):
"""Append activation
Args:
input_var: the input variable. The len(input_var.shape) is
larger or equal than 2.
act: activation type
use_cudnn: if use cudnn
Return the Variable of after append activation
"""
act = act
if act is None:
return input_var
if isinstance(act, str):
act = {'type': act}
else:
raise TypeError(f"{act} should be unicode or str in {self.name}")
if (use_cudnn is not None) and use_cudnn:
act['use_cudnn'] = use_cudnn
act_type = act.pop('type')
if in_dygraph_mode():
res = _append_activation_in_dygraph(input_var, act_type, use_cudnn)
return res
else:
tmp = self.create_variable_for_type_inference(dtype=input_var.dtype)
self.append_op(
type=act_type,
inputs={"X": [input_var]},
outputs={"Out": [tmp]},
attrs=act,
)
return tmp
def is_instance(self, param, cls):
"""Check if the input parameter is instance of input class
Args:
param: parameter to be check
cls: class of the parameter
Return result of the check (True or False)
"""
param = param
if not isinstance(param, cls):
raise TypeError(
"The input {0} parameter of method {1} must be {2}, in layer {3}",
param,
self.layer_type,
cls.__name__,
self.name,
)
class LayerOpsRecorder:
"""
Record generated operators information in nn.Layer.
"""
def __init__(self, start=-1, end=-1, ops=None, is_valid=False, hooks=None):
self.start = start
self.end = end
self.ops = ops
self.is_valid = is_valid
self.hooks = hooks
class HookRemoveHelper:
"""A HookRemoveHelper that can be used to remove hook."""
next_hook_id: int = 0
def __init__(
self,
hooks: typing.OrderedDict[int, Callable[..., Any]],
*,
extra_hook_dict: Any = None,
) -> None:
self._hooks_ref = weakref.ref(hooks)
self._hook_id = HookRemoveHelper.next_hook_id
HookRemoveHelper.next_hook_id += 1
self._extra_hooks_ref: tuple = ()
if extra_hook_dict is not None:
if isinstance(extra_hook_dict, list):
self._extra_hooks_ref = tuple(
weakref.ref(d) for d in extra_hook_dict
)
else:
self._extra_hooks_ref = (weakref.ref(extra_hook_dict),)
def remove(self) -> None:
hooks = self._hooks_ref()
if hooks is not None and self._hook_id in hooks:
del hooks[self._hook_id]
for ref in self._extra_hooks_ref:
extra_hooks = ref()
if extra_hooks is not None and self._hook_id in extra_hooks:
del extra_hooks[self._hook_id]
class Layer:
"""
Dynamic graph Layer based on OOD, includes the parameters of the layer, the structure of the forward graph and so on.
Parameters:
name_scope (str, optional): prefix name used by the layer to name parameters.
If prefix is "my_layer", parameter name in MyLayer
can be "my_layer_0.w_n", where "w" is the parameter
base name and "n" is an unique suffix auto-generated.
If None, prefix name will be snake cased class name. Default: None.
dtype(str, optional): data type of this parameter.
If set str, it can be "bool", "float16", "float32", "float64",
"int8", "int16", "int32", "int64", "uint8" or "uint16".
Default: "float32"
Returns:
None
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.seed(100)
>>> class MyLayer(paddle.nn.Layer):
... def __init__(self):
... super().__init__()
... self._linear = paddle.nn.Linear(1, 1)
... self._dropout = paddle.nn.Dropout(p=0.5)
...
... def forward(self, input):
... temp = self._linear(input)
... temp = self._dropout(temp)
... return temp
>>> x = paddle.randn([10, 1], 'float32')
>>> mylayer = MyLayer()
>>> mylayer.eval() # set mylayer._dropout to eval mode
>>> out = mylayer(x)
>>> mylayer.train() # set mylayer._dropout to train mode
>>> out = mylayer(x)
>>> print(out)
Tensor(shape=[10, 1], dtype=float32, place=Place(cpu), stop_gradient=False,
[[-3.44879317],
[ 0. ],
[ 0. ],
[-0.73825276],
[ 0. ],
[ 0. ],
[ 0.64444798],
[-3.22185946],
[ 0. ],
[-0.68077987]])
"""
training: bool
def __init__(
self, name_scope: str | None = None, dtype: DTypeLike = "float32"
) -> None:
self.training = True
if name_scope is None:
name_scope = _convert_camel_to_snake(self.__class__.__name__)
name_scope = _scope_dist2single(name_scope)
self._full_name = unique_name.generate(name_scope)
self._helper = LayerObjectHelper(self._full_name)
self._built = False
self._dtype = dtype
self._init_in_dynamic_mode = in_dygraph_mode()
self._parameters = OrderedDict()
# Buffers the variable (not parameter) created in layer
self._buffers = OrderedDict()
self._non_persistable_buffer_names_set = set()
self._sub_layers = OrderedDict()
self._loaddict_holder = OrderedDict()
# Record generated op_descs in this layer
self._op_recorder = LayerOpsRecorder(ops=[], hooks=[])
self._customized_attrs = {}
self._forward_pre_hooks: typing.OrderedDict[int, _ForwardPreHook] = (
OrderedDict()
)
self._forward_hooks: typing.OrderedDict[int, _ForwardPostHook] = (
OrderedDict()
)
self._forward_pre_hooks_with_kwargs: typing.OrderedDict[int, bool] = (
OrderedDict()
)
self._forward_hooks_with_kwargs: typing.OrderedDict[int, bool] = (
OrderedDict()
)
self._forward_hooks_always_called: typing.OrderedDict[int, bool] = (
OrderedDict()
)
self._backward_pre_hooks = OrderedDict()
self._backward_hooks = OrderedDict()
# only used in AMP Training
self._cast_to_low_precision = True
self._state_dict_hooks: typing.OrderedDict[int, _StateDictHook] = (
OrderedDict()
)
self._state_dict_pre_hooks: typing.OrderedDict[
int, _StateDictPreHook
] = OrderedDict()
self._load_state_dict_pre_hooks = OrderedDict()
self._load_state_dict_post_hooks = OrderedDict()
# Records original functions after @to_static to support to rollback
self._original_funcs = OrderedDict()
@property
def _modules(self):
return self._sub_layers
@_modules.setter
def _modules(self, value):
if not isinstance(value, dict):
raise TypeError(f"_modules must be dict-like, got {type(value)}")
self._sub_layers.clear()
self._sub_layers.update(value)
@property
def _forward_post_hooks(self):
return self._forward_hooks
@_forward_post_hooks.setter
def _forward_post_hooks(self, value):
self._forward_hooks = value
@property
def _forward_post_hooks_with_kwargs_flag(self):
return self._forward_hooks_with_kwargs
@_forward_post_hooks_with_kwargs_flag.setter
def _forward_post_hooks_with_kwargs_flag(self, value):
self._forward_hooks_with_kwargs = value
@property
def _forward_post_hooks_always_called(self):
return self._forward_hooks_always_called
@_forward_post_hooks_always_called.setter
def _forward_post_hooks_always_called(self, value):
self._forward_hooks_always_called = value
@property
def _forward_pre_hooks_with_kwargs_flag(self):
return self._forward_pre_hooks_with_kwargs
@_forward_pre_hooks_with_kwargs_flag.setter
def _forward_pre_hooks_with_kwargs_flag(self, value):
self._forward_pre_hooks_with_kwargs = value
@property
def _non_persistent_buffers_set(self):
return self._non_persistable_buffer_names_set
@_non_persistent_buffers_set.setter
def _non_persistent_buffers_set(self, value):
if not isinstance(value, set):
raise TypeError(
f"_non_persistent_buffers_set must be a set, got {type(value)}"
)
self._non_persistable_buffer_names_set.clear()
self._non_persistable_buffer_names_set.update(value)
def train(self, mode: bool = True) -> Self:
"""
Sets this Layer and all its sublayers to training mode.
This only effects certain modules like `Dropout` and `BatchNorm`.
Returns:
Layer: self
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.seed(100)
>>> class MyLayer(paddle.nn.Layer):
... def __init__(self):
... super().__init__()
... self._linear = paddle.nn.Linear(1, 1)
... self._dropout = paddle.nn.Dropout(p=0.5)
...
... def forward(self, input):
... temp = self._linear(input)
... temp = self._dropout(temp)
... return temp
>>> x = paddle.randn([10, 1], 'float32')
>>> mylayer = MyLayer()
>>> mylayer.eval() # set mylayer._dropout to eval mode
>>> out = mylayer(x)
>>> mylayer.train() # set mylayer._dropout to train mode
>>> out = mylayer(x)
>>> print(out)
Tensor(shape=[10, 1], dtype=float32, place=Place(cpu), stop_gradient=False,
[[-3.44879317],
[ 0. ],
[ 0. ],
[-0.73825276],
[ 0. ],
[ 0. ],
[ 0.64444798],
[-3.22185946],
[ 0. ],
[-0.68077987]])
"""
if not isinstance(mode, bool):
raise ValueError("training mode is expected to be boolean")
# global setting in dygraph
# NOTE(chenweihang): nn.Layer also can be used in static mode,
# but _dygraph_tracer() can not be called in static mode
if in_dygraph_mode():
if mode:
framework._dygraph_tracer().train_mode()
else:
framework._dygraph_tracer().eval_mode()
# Layer-level setting
self.training = mode
for layer in self.sublayers():
layer.training = mode
return self
def eval(self) -> Self:
"""
Sets this Layer and all its sublayers to evaluation mode.
This only effects certain modules like `Dropout` and `BatchNorm`.
Returns:
Layer: self
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.seed(100)
>>> class MyLayer(paddle.nn.Layer):
... def __init__(self):
... super().__init__()
... self._linear = paddle.nn.Linear(1, 1)
... self._dropout = paddle.nn.Dropout(p=0.5)
...
... def forward(self, input):
... temp = self._linear(input)
... temp = self._dropout(temp)
... return temp
>>> x = paddle.randn([10, 1], 'float32')
>>> mylayer = MyLayer()
>>> mylayer.eval() # set mylayer._dropout to eval mode
>>> out = mylayer(x)
>>> print(out)
Tensor(shape=[10, 1], dtype=float32, place=Place(cpu), stop_gradient=False,
[[-1.72439659],
[ 0.31532824],
[ 0.01192369],
[-0.36912638],
[-1.63426113],
[-0.93169814],
[ 0.32222399],
[-1.61092973],
[ 0.77209264],
[-0.34038994]])
"""
# global setting in dygraph
# NOTE(chenweihang): nn.Layer also can be used in static mode,
# but _dygraph_tracer() can not be called in static mode
if in_dygraph_mode():
framework._dygraph_tracer().eval_mode()
# Layer-level setting
self.training = False
for layer in self.sublayers():
layer.training = False
return self
def apply(self, fn: Callable[[Self], None]) -> Self:
"""
Applies ``fn`` recursively to every sublayer (as returned by ``.sublayers()``)
as well as self. Typical use includes initializing the parameters of a model.
Parameters:
fn (function): a function to be applied to each sublayer
Returns:
Layer, self
Examples:
.. code-block:: pycon
>>> import paddle
>>> import paddle.nn as nn
>>> paddle.seed(2023)
>>> net = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2))
>>> def init_weights(layer):
... if type(layer) == nn.Linear:
... print('before init weight:', layer.weight.numpy())
... new_weight = paddle.full(shape=layer.weight.shape, dtype=layer.weight.dtype, fill_value=0.9)
... layer.weight.set_value(new_weight)
... print('after init weight:', layer.weight.numpy())
>>> net.apply(init_weights)
>>> print(net.state_dict())
before init weight: [[ 0.89611185 0.04935038]
[-0.5888344 0.99266374]]
after init weight: [[0.9 0.9]
[0.9 0.9]]
before init weight: [[-0.18615901 -0.22924072]
[ 1.1517721 0.59859073]]
after init weight: [[0.9 0.9]
[0.9 0.9]]
OrderedDict([('0.weight', Parameter containing:
Tensor(shape=[2, 2], dtype=float32, place=Place(cpu), stop_gradient=False,
[[0.89999998, 0.89999998],
[0.89999998, 0.89999998]])), ('0.bias', Parameter containing:
Tensor(shape=[2], dtype=float32, place=Place(cpu), stop_gradient=False,
[0., 0.])), ('1.weight', Parameter containing:
Tensor(shape=[2, 2], dtype=float32, place=Place(cpu), stop_gradient=False,
[[0.89999998, 0.89999998],
[0.89999998, 0.89999998]])), ('1.bias', Parameter containing:
Tensor(shape=[2], dtype=float32, place=Place(cpu), stop_gradient=False,
[0., 0.]))])
"""
for layer in self.children():
layer.apply(fn)
fn(self)
return self
def full_name(self) -> str:
"""
Full name for this layer, composed by name_scope + "/" + MyLayer.__class__.__name__
Returns:
str, full name of this layer.
Examples:
.. code-block:: pycon
>>> import paddle
>>> class LinearNet(paddle.nn.Layer):
... def __init__(self):
... super().__init__(name_scope="demo_linear_net")
... self._linear = paddle.nn.Linear(1, 1)
...
... def forward(self, x):
... return self._linear(x)
>>> linear_net = LinearNet()
>>> print(linear_net.full_name())
demo_linear_net_0
"""
return self._full_name
def register_forward_post_hook(
self,
hook: _ForwardPostHook,
*,
prepend: bool = False,
with_kwargs: bool = False,
always_call: bool = False,
) -> HookRemoveHelper:
"""
Register a forward post-hook for Layer. The hook will be called after `forward` function has been computed.
It should have the following form, `input` and `output` of the `hook` is `input` and `output` of the `Layer` respectively.
User can use forward post-hook to change the output of the Layer or perform information statistics tasks on the Layer.
hook(Layer, input, output) -> None or modified output
Parameters:
hook(function): a function registered as a forward post-hook
prepend (bool): If ``True``, the provided ``hook`` will be fired
before all existing ``forward_post`` hooks on this
:class:`paddle.nn.Layer`.
Default: ``False``
with_kwargs (bool): If ``True``, the ``hook`` will be passed the
kwargs given to the forward function.
Default: ``False``
always_call (bool): If ``True`` the ``hook`` will be run regardless of
whether an exception is raised while calling the Module.
Default: ``False``
Returns:
HookRemoveHelper, a HookRemoveHelper object that can be used to remove the added hook by calling `hook_remove_helper.remove()` .
Examples:
.. code-block:: pycon
>>> import paddle
>>> import numpy as np
>>> # the forward_post_hook change the output of the layer: output = output * 2
>>> def forward_post_hook(layer, input, output):
... # user can use layer, input and output for information statistics tasks
...
... # change the output
... return output * 2
>>> linear = paddle.nn.Linear(13, 5)
>>> # register the hook
>>> forward_post_hook_handle = linear.register_forward_post_hook(forward_post_hook)
>>> value1 = np.arange(26).reshape(2, 13).astype("float32")
>>> in1 = paddle.to_tensor(value1)
>>> out0 = linear(in1)
>>> # remove the hook
>>> forward_post_hook_handle.remove()
>>> out1 = linear(in1)
>>> # hook change the linear's output to output * 2, so out0 is equal to out1 * 2.
>>> assert (out0.numpy() == (out1.numpy()) * 2).any()
"""
hook_remove_helper = HookRemoveHelper(
self._forward_post_hooks,
extra_hook_dict=[
self._forward_post_hooks_with_kwargs_flag,
self._forward_post_hooks_always_called,
],
)
self._forward_post_hooks[hook_remove_helper._hook_id] = hook
if with_kwargs:
self._forward_post_hooks_with_kwargs_flag[
hook_remove_helper._hook_id
] = True
if always_call:
self._forward_post_hooks_always_called[
hook_remove_helper._hook_id
] = True
if prepend:
self._forward_post_hooks.move_to_end(
hook_remove_helper._hook_id, last=False
)
return hook_remove_helper
# [aliases]
register_forward_hook = register_forward_post_hook
def register_forward_pre_hook(
self,
hook: _ForwardPreHook,
*,
prepend: bool = False,
with_kwargs: bool = False,
) -> HookRemoveHelper:
"""
Register a forward pre-hook for Layer. The hook will be called before `forward` function has been computed.
It should have the following form, `input` of the `hook` is `input` of the `Layer`,
hook can either return a tuple or a single modified value in the hook. We will wrap the value into a tuple if
a single value is returned(unless that value is already a tuple).
User can use forward pre-hook to change the input of the Layer or perform information statistics tasks on the Layer.
hook(Layer, input) -> None or modified input
Parameters:
hook(function): a function registered as a forward pre-hook
prepend (bool): If ``True``, the provided ``hook`` will be fired
before all existing ``forward_pre`` hooks on this
:class:`paddle.nn.Layer`.
Default: ``False``
with_kwargs (bool): If true, the ``hook`` will be passed the kwargs
given to the forward function.
Default: ``False``
Returns:
HookRemoveHelper, a HookRemoveHelper object that can be used to remove the added hook by calling `hook_remove_helper.remove()` .
Examples:
.. code-block:: pycon
>>> import paddle
>>> import numpy as np
>>> # the forward_pre_hook change the input of the layer: input = input * 2
>>> def forward_pre_hook(layer, input):
... # user can use layer and input for information statistics tasks
...
... # change the input
... input_return = input[0] * 2
... return input_return
>>> linear = paddle.nn.Linear(13, 5)
>>> # register the hook
>>> forward_pre_hook_handle = linear.register_forward_pre_hook(forward_pre_hook)
>>> value0 = np.arange(26).reshape(2, 13).astype("float32")
>>> in0 = paddle.to_tensor(value0)
>>> out0 = linear(in0)
>>> # remove the hook
>>> forward_pre_hook_handle.remove()
>>> value1 = value0 * 2
>>> in1 = paddle.to_tensor(value1)
>>> out1 = linear(in1)
>>> # hook change the linear's input to input * 2, so out0 is equal to out1.
>>> assert (out0.numpy() == out1.numpy()).any()
"""
hook_remove_helper = HookRemoveHelper(
self._forward_pre_hooks,
extra_hook_dict=self._forward_pre_hooks_with_kwargs_flag,
)
self._forward_pre_hooks[hook_remove_helper._hook_id] = hook
if with_kwargs:
self._forward_pre_hooks_with_kwargs_flag[
hook_remove_helper._hook_id
] = True
if prepend:
self._forward_pre_hooks.move_to_end(
hook_remove_helper._hook_id, last=False
)
return hook_remove_helper
def register_full_backward_pre_hook(
self, hook: Callable[..., Any], prepend: bool = False
) -> HookRemoveHelper:
hook_remove_helper = HookRemoveHelper(self._backward_pre_hooks)
self._backward_pre_hooks[hook_remove_helper._hook_id] = hook
if prepend:
self._backward_pre_hooks.move_to_end(
hook_remove_helper._hook_id, last=False
)
return hook_remove_helper
def register_backward_hook(
self, hook: Callable[..., Any]
) -> HookRemoveHelper:
raise NotImplementedError(
"register_backward_hook is not supported. "
"Please use register_full_backward_hook instead."
)
def register_full_backward_hook(
self, hook: Callable[..., Any], prepend: bool = False
) -> HookRemoveHelper:
hook_remove_helper = HookRemoveHelper(self._backward_hooks)
self._backward_hooks[hook_remove_helper._hook_id] = hook
if prepend:
self._backward_hooks.move_to_end(
hook_remove_helper._hook_id, last=False
)
return hook_remove_helper
def _get_backward_hooks(self):
return list(self._backward_hooks.values())
def _get_backward_pre_hooks(self):
return list(self._backward_pre_hooks.values())
def create_parameter(
self,
shape: ShapeLike,
attr: ParamAttrLike | None = None,
dtype: DTypeLike | None = None,
is_bias: bool = False,
default_initializer: Initializer | None = None,
device: PlaceLike | None = None,
) -> Tensor:
"""Create parameters for this layer.
Parameters:
shape(list): Shape of the parameter. The data type in the list must be int.
attr(ParamAttr, optional): Parameter attribute of weight. Please refer to :ref:`api_paddle_ParamAttr`. Default: None.
dtype(str, optional): Data type of this parameter.
If set str, it can be "bool", "float16", "float32", "float64",
"int8", "int16", "int32", "int64", "uint8" or "uint16". Default: "float32".
is_bias(bool, optional): if this is a bias parameter. Default: False.
default_initializer(Initializer, optional): the default initializer for this parameter.
If set None, default initializer will be set to paddle.nn.initializer.Xavier and paddle.nn.initializer.Constant
for non-bias and bias parameter, respectively. Default: None.
device(PlaceLike, optional): the device place for the parameter. Default: None.
Returns:
:Tensor, created parameter.
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.seed(2023)
>>> class MyLayer(paddle.nn.Layer):
... def __init__(self):
... super().__init__()
... self._linear = paddle.nn.Linear(1, 1)
... w_tmp = self.create_parameter([1, 1])
... self.add_parameter("w_tmp", w_tmp)
...
... def forward(self, input):
... return self._linear(input)
>>> mylayer = MyLayer()
>>> for name, param in mylayer.named_parameters():
... print(name, param) # will print w_tmp,_linear.weight,_linear.bias
w_tmp Parameter containing:
Tensor(shape=[1, 1], dtype=float32, place=Place(cpu), stop_gradient=False,
[[0.06979191]])
_linear.weight Parameter containing:
Tensor(shape=[1, 1], dtype=float32, place=Place(cpu), stop_gradient=False,
[[1.26729357]])
_linear.bias Parameter containing:
Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=False,
[0.])
"""
temp_attr = copy.deepcopy(attr)
if isinstance(temp_attr, str) and temp_attr == "":
temp_attr = None
return self._helper.create_parameter(
temp_attr, shape, dtype, is_bias, default_initializer, device=device
)
def get_parameter(self, target: str) -> Parameter:
"""
Return the parameter given by ``target`` if it exists, otherwise throw an error.
Parameters:
target(str): The fully-qualified string name of the Parameter to look for.
Returns:
Parameter: The Parameter referenced by ``target``.
"""
module_path, _, param_name = target.rpartition(".")
mod: paddle.nn.Layer = self.get_sublayer(module_path)
if not hasattr(mod, param_name):
raise AttributeError(
mod._get_name() + " has no attribute `" + param_name + "`"
)
param: paddle.nn.Parameter = getattr(mod, param_name)
if not isinstance(param, paddle.nn.Parameter):
raise AttributeError("`" + param_name + "` is not an nn.Parameter")
return param
@deprecated(
since="2.0.0",
update_to="paddle.nn.Layer.create_tensor",
reason="New api in create_tensor, easier to use.",
)
def create_variable(
self,
name: str | None = None,
persistable: bool | None = None,
dtype: DTypeLike | None = None,
) -> Tensor:
"""
Create Tensor for this layer.
Parameters:
name(str, optional): name of the tensor. Please refer to :ref:`api_guide_Name` . Default: None
persistable(bool, optional): if set this tensor persistable. Default: False
dtype(str, optional): data type of this parameter. If set str, it can be "bool", "float16", "float32", "float64","int8", "int16", "int32", "int64", "uint8" or "uint16". If set None, it will be "float32". Default: None
Returns:
Tensor, created Tensor.
Examples:
.. code-block:: pycon
>>> import paddle
>>> class MyLinear(paddle.nn.Layer):
... def __init__(self, in_features, out_features):
... super().__init__()
... self.linear = paddle.nn.Linear(10, 10)
...
... self.back_var = self.create_variable(name="linear_tmp_0", dtype=self._dtype)
...
... def forward(self, input):
... out = self.linear(input)
... paddle.assign(out, self.back_var)
...
... return out
"""
if name is not None:
var_name = ".".join([self._full_name, name])
else:
var_name = unique_name.generate(
".".join([self._full_name, "_generated_var"])
)
return self._helper.main_program.current_block().create_var(
name=var_name,
persistable=persistable,
dtype=dtype,
type=core.VarDesc.VarType.DENSE_TENSOR,
)
# TODO: Add more parameter list when we need them
def create_tensor(
self,
name: str | None = None,
persistable: bool | None = None,
dtype: DTypeLike | None = None,
) -> Tensor:
"""
Create Tensor for this layer.
Parameters:
name(str, optional): name of the tensor. Please refer to :ref:`api_guide_Name` . Default: None.
persistable(bool, optional): if set this tensor persistable. Default: False.
dtype(str, optional): data type of this parameter.
If set str, it can be "bool", "float16", "float32", "float64",
"int8", "int16", "int32", "int64", "uint8" or "uint16".
If set None, it will be "float32". Default: None.
Returns:
Tensor, created Tensor.
Examples:
.. code-block:: pycon
>>> import paddle
>>> class MyLinear(paddle.nn.Layer):
... def __init__(self, in_features, out_features):
... super().__init__()
... self.linear = paddle.nn.Linear(10, 10)
...
... self.back_var = self.create_tensor(name="linear_tmp_0", dtype=self._dtype)
...
... def forward(self, input):
... out = self.linear(input)
... paddle.assign(out, self.back_var)
...
... return out
"""
if name is not None:
var_name = ".".join([self._full_name, name])
else:
var_name = unique_name.generate(
".".join([self._full_name, "_generated_var"])
)
return self._helper.main_program.current_block().create_var(
name=var_name,
persistable=persistable,
dtype=dtype,
type=core.VarDesc.VarType.DENSE_TENSOR,
)
@param_one_alias(["include_sublayers", "recurse"])
def parameters(self, include_sublayers: bool = True) -> list[Tensor]:
"""
Returns a list of all Parameters from current layer and its sub-layers.
Parameters:
include_sublayers (bool, optional): Whether to return the parameters of the sublayer.
If True, the returned list contains the parameters of the sublayer.
Default: True.
Returns:
list, list of Tensor, a list of Parameters.
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.seed(100)
>>> linear = paddle.nn.Linear(1, 1)
>>> print(linear.parameters())
[Parameter containing:
Tensor(shape=[1, 1], dtype=float32, place=Place(cpu), stop_gradient=False,
[[0.18551230]]), Parameter containing:
Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=False,
[0.])]
"""
ret = [
param
for _, param in self.named_parameters(
include_sublayers=include_sublayers
)
]
return ret
def astype(self, dtype: DTypeLike | None = None) -> Self:
"""
Casts all parameters and buffers to dtype and then return the Layer.
Parameters:
dtype(str|paddle.dtype|numpy.dtype): target data type of layer.
If set str, it can be "bool", "bfloat16", "float16", "float32", "float64",
"int8", "int16", "int32", "int64", "uint8", "complex64", "complex128".
Default: None
Returns:
Layer, self
Examples:
.. code-block:: pycon
>>> import paddle
>>> import paddle.nn as nn
>>> weight_attr = paddle.ParamAttr(name="weight", initializer=paddle.nn.initializer.Constant(value=1.5))
>>> bias_attr = paddle.ParamAttr(name="bias", initializer=paddle.nn.initializer.Constant(value=2.5))
>>> linear = paddle.nn.Linear(2, 2, weight_attr=weight_attr, bias_attr=bias_attr).to(device="cpu", dtype="float32")
>>> print(linear)
Linear(in_features=2, out_features=2, dtype=float32)
>>> print(linear.parameters())
[Parameter containing:
Tensor(shape=[2, 2], dtype=float32, place=Place(cpu), stop_gradient=False,
[[1.50000000, 1.50000000],
[1.50000000, 1.50000000]]), Parameter containing:
Tensor(shape=[2], dtype=float32, place=Place(cpu), stop_gradient=False,
[2.50000000, 2.50000000])]
>>> linear = linear.astype("int8")
>>> print(linear)
Linear(in_features=2, out_features=2, dtype=paddle.int8)
>>> print(linear.parameters())
>>> # doctest: +SKIP("There are bugs in the `Layer.astype`. For details, refer to the following webpage: https://github.com/PaddlePaddle/Paddle/issues/76614")
[Parameter containing:
Tensor(shape=[2, 2], dtype=int8, place=Place(cpu), stop_gradient=False,
[[1, 1],
[1, 1]]), Parameter containing:
Tensor(shape=[2], dtype=int8, place=Place(cpu), stop_gradient=False,
[2, 2])]
>>> # doctest: -SKIP
"""
valid_dtypes = [
"bfloat16",
"float16",
"float32",
"float64",
"int8",
"int16",
"int32",
"int64",
"uint8",
"complex64",
"complex128",
"bool",
]
if (
isinstance(dtype, (paddle.dtype, np.dtype))
or type(dtype) is str
and dtype in valid_dtypes
):
if isinstance(dtype, (str, np.dtype)):
dtype = framework.convert_nptype_to_datatype_or_vartype(dtype)
self._dtype = dtype
for layer in self.sublayers():
layer._dtype = dtype
for _, param in self.named_parameters(include_sublayers=True):
param._to(None, dtype)
for _, buffer in self.named_buffers(include_sublayers=True):
buffer.to(None, dtype)
return self
else:
raise ValueError(
"dtype value error, must be 'bfloat16', 'float16', 'float32', 'float64', 'int8', 'int16', 'int32', 'int64', 'uint8', 'complex64', 'complex128', 'bool', or paddle.dtype, numpy.dtype, but receive "
+ str(dtype)
)
def children(self) -> Iterable[Layer]:
"""
Returns an iterator over immediate children layers.
Yields:
Layer: a child layer
Examples:
.. code-block:: pycon
>>> import paddle
>>> linear1 = paddle.nn.Linear(10, 3)
>>> linear2 = paddle.nn.Linear(3, 10, bias_attr=False)
>>> model = paddle.nn.Sequential(linear1, linear2)
>>> layer_list = list(model.children())
>>> print(layer_list)
[Linear(in_features=10, out_features=3, dtype=float32), Linear(in_features=3, out_features=10, dtype=float32)]
"""
for _, layer in self.named_children():
yield layer
def named_children(self) -> Iterable[tuple[str, Layer]]:
"""Returns an iterator over immediate children layers, yielding both
the name of the layer as well as the layer itself.
Yields:
(string, Layer): Tuple containing a name and child layer
Examples:
.. code-block:: pycon
>>> import paddle
>>> linear1 = paddle.nn.Linear(10, 3)
>>> linear2 = paddle.nn.Linear(3, 10, bias_attr=False)
>>> model = paddle.nn.Sequential(linear1, linear2)
>>> for prefix, layer in model.named_children():
... print(prefix, layer)
0 Linear(in_features=10, out_features=3, dtype=float32)
1 Linear(in_features=3, out_features=10, dtype=float32)
"""
memo = set()
for name, layer in self._sub_layers.items():
if layer is not None and layer not in memo:
memo.add(layer)
yield name, layer
def sublayers(self, include_self: bool = False) -> list[Layer]:
"""
Returns a list of sub layers.
Parameters:
include_self(bool, optional): Whether return self as sublayers. Default: False.
Returns:
list of Layer, a list of sub layers.
Examples:
.. code-block:: pycon
>>> import paddle
>>> class MyLayer(paddle.nn.Layer):
... def __init__(self):
... super().__init__()
... self._linear = paddle.nn.Linear(1, 1)
... self._dropout = paddle.nn.Dropout(p=0.5)
...
... def forward(self, input):
... temp = self._linear(input)
... temp = self._dropout(temp)
... return temp
>>> mylayer = MyLayer()
>>> print(mylayer.sublayers())
[Linear(in_features=1, out_features=1, dtype=float32), Dropout(p=0.5, axis=None, mode=upscale_in_train, inplace=False)]
"""
ret = [
layer
for _, layer in self.named_sublayers(include_self=include_self)
]
return ret
@param_one_alias(["include_sublayers", "recurse"])
def named_parameters(
self,
prefix: str = '',
include_sublayers: bool = True,
remove_duplicate: bool = True,
) -> Iterable[tuple[str, Tensor]]:
"""
Returns an iterator over all parameters in the Layer, yielding tuple of name and parameter.
Parameters:
prefix(str, optional): Prefix to prepend to all parameter names. Default: ''.
include_sublayers(bool, optional): Whether include the parameters of sublayers.
If True, also include the named parameters from sublayers. Default: True.
remove_duplicate(bool, optional): Whether to remove duplicated parameters in the result.
Default: True.
Yields:
(string, Parameter): Tuple of name and Parameter
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.seed(100)
>>> fc1 = paddle.nn.Linear(10, 3)
>>> fc2 = paddle.nn.Linear(3, 10, bias_attr=False)
>>> model = paddle.nn.Sequential(fc1, fc2)
>>> for name, param in model.named_parameters():
... print(name, param)
0.weight Parameter containing:
Tensor(shape=[10, 3], dtype=float32, place=Place(cpu), stop_gradient=False,
[[ 0.07276392, -0.39791510, -0.66356444],
[ 0.02143478, -0.18519843, -0.32485050],
[-0.42249614, 0.08450919, -0.66838276],
[ 0.38208580, -0.24303678, 0.55127048],
[ 0.47745085, 0.62117910, -0.08336520],
[-0.28653207, 0.47237599, -0.05868882],
[-0.14385653, 0.29945642, 0.12832761],
[-0.21237159, 0.38539791, -0.62760031],
[ 0.02637231, 0.20621127, 0.43255770],
[-0.19984481, -0.26259184, -0.29696006]])
0.bias Parameter containing:
Tensor(shape=[3], dtype=float32, place=Place(cpu), stop_gradient=False,
[0., 0., 0.])
1.weight Parameter containing:
Tensor(shape=[3, 10], dtype=float32, place=Place(cpu), stop_gradient=False,
[[ 0.01985580, -0.40268910, 0.41172385, -0.47249708, -0.09002256,
-0.00533628, -0.52048630, 0.62360322, 0.20848787, -0.02033746],
[ 0.58281910, 0.12841827, 0.12907702, 0.02325618, -0.07746267,
0.31950659, -0.37924835, -0.59209681, -0.11732036, -0.58378261],
[-0.62100595, 0.22293305, 0.28229684, -0.03687060, -0.59323978,
0.08411229, 0.53275704, 0.40431368, 0.03171402, -0.17922515]])
"""
params_set = (
ValueSet() if in_pir_mode() and not in_to_static_mode() else set()
)
named_sublayers = (
self.named_sublayers(
prefix=prefix,
include_self=True,
remove_duplicate=remove_duplicate,
)
if include_sublayers
else zip([prefix], [self])
)
for layer_prefix, sublayer in named_sublayers:
params = sublayer._parameters.items()
for key, param in params:
if param is None or param in params_set:
continue
if remove_duplicate:
params_set.add(param)
name = layer_prefix + ('.' if layer_prefix else '') + key
yield name, param
def named_sublayers(
self,
prefix: str = '',
include_self: bool = False,
layers_set: set[Layer] | None = None,
remove_duplicate: bool = True,
) -> Iterable[tuple[str, Layer]]:
"""
Returns an iterator over all sublayers in the Layer, yielding tuple of name and sublayer.
The duplicate sublayer will only be yielded once.
Parameters:
prefix(str, optional): Prefix to prepend to all parameter names. Default: ''.
include_self(bool, optional): Whether include the Layer itself. Default: False.
layers_set(set, optional): The set to record duplicate sublayers. Default: None.
remove_duplicate(bool, optional): Whether to remove duplicated sublayers in the result.
Default: True.
Yields:
(string, Layer): Tuple of name and Layer
Examples:
.. code-block:: pycon
>>> import paddle
>>> fc1 = paddle.nn.Linear(10, 3)
>>> fc2 = paddle.nn.Linear(3, 10, bias_attr=False)
>>> model = paddle.nn.Sequential(fc1, fc2)
>>> for prefix, layer in model.named_sublayers():
... print(prefix, layer)
0 Linear(in_features=10, out_features=3, dtype=float32)
1 Linear(in_features=3, out_features=10, dtype=float32)
>>> l = paddle.nn.Linear(10, 3)
>>> model = paddle.nn.Sequential(l, l)
>>> for prefix, layer in model.named_sublayers(include_self=True, remove_duplicate=True):
... print(prefix, layer)
Sequential(
(0): Linear(in_features=10, out_features=3, dtype=float32)
(1): Linear(in_features=10, out_features=3, dtype=float32)
)
0 Linear(in_features=10, out_features=3, dtype=float32)
>>> l = paddle.nn.Linear(10, 3)
>>> model = paddle.nn.Sequential(l, l)
>>> for prefix, layer in model.named_sublayers(include_self=True, remove_duplicate=False):
... print(prefix, layer)
Sequential(
(0): Linear(in_features=10, out_features=3, dtype=float32)
(1): Linear(in_features=10, out_features=3, dtype=float32)
)
0 Linear(in_features=10, out_features=3, dtype=float32)
1 Linear(in_features=10, out_features=3, dtype=float32)
"""
if layers_set is None:
layers_set = set()
if include_self and self not in layers_set:
if remove_duplicate:
layers_set.add(self)
yield prefix, self
for key, layer in self._sub_layers.items():
if layer is None:
continue
layer_prefix = prefix + ('.' if prefix else '') + key
yield from layer.named_sublayers(
prefix=layer_prefix,
include_self=True,
layers_set=layers_set,
remove_duplicate=remove_duplicate,
)
def modules(self) -> Iterator[Layer]:
"""
Return an iterator over all modules in the network.
Yields:
Layer: a layer in the network.
"""
for _, module in self.named_modules():
yield module
def named_modules(
self,
memo: set[Layer] | None = None,
prefix: str = "",
remove_duplicate: bool = True,
):
"""
Returns an iterator over all sublayers in the Layer, yielding tuple of name and sublayer.
The duplicate sublayer will only be yielded once.
Parameters:
memo(set, optional): The set to record duplicate sublayers. Default: None.
prefix(str, optional): Prefix to prepend to all parameter names. Default: ''.
remove_duplicate(bool, optional): Whether to remove duplicated sublayers in the result.
Default: True.
Yields:
(string, Layer): Tuple of name and Layer
"""
include_self = True
layers_set = memo
return self.named_sublayers(
prefix=prefix,
include_self=include_self,
layers_set=layers_set,
remove_duplicate=remove_duplicate,
)
@param_one_alias(["persistable", "persistent"])
def register_buffer(
self, name: str, tensor: Tensor, persistable: bool = True
) -> None:
"""
Registers a tensor as buffer into the layer.
`buffer` is a non-trainable tensor and will not be updated by optimizer,
but is necessary for evaluation and inference. For example, the mean and variance in BatchNorm layers.
The registered buffer is persistable by default, and will be saved into
`state_dict` alongside parameters. If set persistable=False, it registers
a non-persistable buffer, so that it will not be a part of `state_dict` .
Buffers can be accessed as attributes using given names.
Parameters:
name (string): name of the buffer. The buffer can be accessed
from this layer using the given name
tensor (Tensor): the tensor to be registered as buffer.
persistable (bool): whether the buffer is part of this layer's
state_dict.
Returns:
None
Examples:
.. code-block:: pycon
>>> import numpy as np
>>> import paddle
>>> linear = paddle.nn.Linear(10, 3)
>>> value = np.array([0]).astype("float32")
>>> buffer = paddle.to_tensor(value)
>>> linear.register_buffer("buf_name", buffer, persistable=True)
>>> # get the buffer by attribute.
>>> print(linear.buf_name)
Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,
[0.])
"""
if '_buffers' not in self.__dict__:
raise ValueError("super().__init__() should be called first")
elif not isinstance(name, str):
raise TypeError(
f"The name of buffer should be a string, but received {type(name).__name__}."
)
elif '.' in name:
raise KeyError(
"The name of buffer can not contain `.`, "
"because when you access the newly added buffer in the "
"form of `self.**.**`, it will cause AttributeError."
)
elif name == '':
raise KeyError("The name of buffer can not be empty.")
elif hasattr(self, name) and name not in self._buffers:
raise KeyError(f"attribute '{name}' already exists.")
elif tensor is not None and not (type(tensor) == core.eager.Tensor):
raise TypeError(
f"The registered buffer should be a Paddle.Tensor, but received {type(tensor).__name__}."
)
else:
self._buffers[name] = tensor
if persistable:
self._non_persistable_buffer_names_set.discard(name)
else:
self._non_persistable_buffer_names_set.add(name)
@param_one_alias(["include_sublayers", "recurse"])
def buffers(self, include_sublayers: bool = True) -> list[Tensor]:
"""
Returns a list of all buffers from current layer and its sub-layers.
Parameters:
include_sublayers(bool, optional): Whether include the buffers of sublayers. If True, also include the buffers from sublayers. Default: True.
Returns:
list of Tensor, a list of buffers.
Examples:
.. code-block:: pycon
>>> import numpy as np
>>> import paddle
>>> linear = paddle.nn.Linear(10, 3)
>>> value = np.array([0]).astype("float32")
>>> buffer = paddle.to_tensor(value)
>>> linear.register_buffer("buf_name", buffer, persistable=True)
>>> print(linear.buffers())
[Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,
[0.])]
"""
ret = [
buffer
for _, buffer in self.named_buffers(
include_sublayers=include_sublayers
)
]
return ret
def get_buffer(self, target: str) -> Tensor:
"""
Return the buffer given by ``target`` if it exists, otherwise throw an error.
See the docstring for ``get_sublayer`` for a more detailed
explanation of this method's functionality as well as how to
correctly specify ``target``.
Parameters:
target(str): The fully-qualified string name of the buffer to look for.
Returns:
Tensor: The buffer referenced by ``target``.
"""
module_path, _, buffer_name = target.rpartition(".")
mod = self.get_sublayer(module_path)
if not hasattr(mod, buffer_name):
raise AttributeError(
mod._get_name() + " has no attribute `" + buffer_name + "`"
)
buffer = getattr(mod, buffer_name)
if buffer_name not in mod._buffers:
raise AttributeError("`" + buffer_name + "` is not a buffer")
return buffer
@param_one_alias(["include_sublayers", "recurse"])
def named_buffers(
self,
prefix: str = '',
include_sublayers: bool = True,
remove_duplicate: bool = True,
) -> Iterable[tuple[str, Tensor]]:
"""
Returns an iterator over all buffers in the Layer, yielding tuple of name and Tensor.
Parameters:
prefix(str, optional): Prefix to prepend to all buffer names. Default: ''.
include_sublayers(bool, optional): Whether include the buffers of sublayers.
If True, also include the named buffers from sublayers. Default: True.
remove_duplicate(bool, optional): Whether to remove duplicated buffers in the result.
Default: True.
Yields:
(string, Tensor): Tuple of name and tensor
Examples:
.. code-block:: pycon
>>> import numpy as np
>>> import paddle
>>> fc1 = paddle.nn.Linear(10, 3)
>>> buffer1 = paddle.to_tensor(np.array([0]).astype("float32"))
>>> # register a tensor as buffer by specific `persistable`
>>> fc1.register_buffer("buf_name_1", buffer1, persistable=True)
>>> fc2 = paddle.nn.Linear(3, 10)
>>> buffer2 = paddle.to_tensor(np.array([1]).astype("float32"))
>>> # register a buffer by assigning an attribute with Tensor.
>>> # The `persistable` can only be False by this way.
>>> fc2.buf_name_2 = buffer2
>>> model = paddle.nn.Sequential(fc1, fc2)
>>> # get all named buffers
>>> for name, buffer in model.named_buffers():
... print(name, buffer)
0.buf_name_1 Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,
[0.])
1.buf_name_2 Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,
[1.])
"""
buffers_set = set()
named_sublayers = (
self.named_sublayers(
prefix=prefix,
include_self=True,
remove_duplicate=remove_duplicate,
)
if include_sublayers
else zip([prefix], [self])
)
for layer_prefix, sublayer in named_sublayers:
buffers = sublayer._buffers.items()
for key, buffer in buffers:
if buffer is None or buffer in buffers_set:
continue
if remove_duplicate:
buffers_set.add(buffer)
name = layer_prefix + ('.' if layer_prefix else '') + key
yield name, buffer
def clear_gradients(self, set_to_zero: bool = True) -> None:
"""
Clear the gradients of all parameters for this layer.
Args:
set_to_zero (bool, optional): Whether to set the trainable parameters'
gradients to zero or None. Default is True.
Returns:
None
Examples:
.. code-block:: pycon
>>> import paddle
>>> import numpy as np
>>> value = np.arange(26).reshape(2, 13).astype("float32")
>>> a = paddle.to_tensor(value)
>>> linear = paddle.nn.Linear(13, 5)
>>> adam = paddle.optimizer.Adam(
... learning_rate=0.01,
... parameters=linear.parameters(),
... )
>>> out = linear(a)
>>> out.backward()
>>> adam.step()
>>> linear.clear_gradients()
"""
for p in self.parameters():
if p.trainable:
p.clear_gradient(set_to_zero)
def _build_once(self, *args: Any, **kwargs: Any) -> None:
pass
def _dygraph_call_func(self, *inputs: Any, **kwargs: Any) -> Any:
outputs = None
called_always_called_hooks = set()
def inner():
nonlocal outputs, inputs, kwargs
backward_hooks = []
backward_pre_hooks = []
if self._backward_pre_hooks:
backward_pre_hooks = self._get_backward_pre_hooks()
if self._backward_hooks:
backward_hooks = self._get_backward_hooks()
for hook_id, forward_pre_hook in self._forward_pre_hooks.items():
if hook_id in self._forward_pre_hooks_with_kwargs_flag:
args_kwargs_result = forward_pre_hook(self, inputs, kwargs)
if args_kwargs_result is not None:
if (
isinstance(args_kwargs_result, tuple)
and len(args_kwargs_result) == 2
):
inputs, kwargs = args_kwargs_result
else:
raise RuntimeError(
"forward pre-hook must return None or a tuple "
f"of (new_args, new_kwargs), but got {args_kwargs_result}."
)
else:
hook_result = forward_pre_hook(self, inputs)
if hook_result is not None:
if not isinstance(hook_result, tuple):
hook_result = (hook_result,)
inputs = hook_result
if in_dygraph_mode() and (backward_hooks or backward_pre_hooks):
flat_inputs = paddle.utils.flatten(inputs)
tensor_inputs = [
inp
for inp in flat_inputs
if isinstance(inp, Tensor) and not inp.stop_gradient
]
if tensor_inputs:
self._has_backward_input_hook = True
hooked_inputs = _LayerBackwardInputHook.apply(
self, *tensor_inputs
)
if isinstance(hooked_inputs, Tensor):
hooked_inputs = (hooked_inputs,)
hooked_inputs = list(hooked_inputs)
def replace_input(inp):
if isinstance(inp, Tensor) and not inp.stop_gradient:
return hooked_inputs.pop(0)
return inp
inputs = paddle.utils.map_structure(replace_input, inputs)
if not self._built:
self._build_once(*inputs, **kwargs)
self._built = True
if in_profiler_mode():
with profiler.RecordEvent(
self.__class__.__name__, profiler.TracerEventType.Forward
):
outputs = self.forward(*inputs, **kwargs)
else:
with name_struct(self.__class__.__name__):
outputs = self.forward(*inputs, **kwargs)
for hook_id, forward_post_hook in self._forward_post_hooks.items():
# mark that always_called_hook to be run
if hook_id in self._forward_post_hooks_always_called:
called_always_called_hooks.add(hook_id)
if hook_id in self._forward_post_hooks_with_kwargs_flag:
hook_result = forward_post_hook(
self, inputs, kwargs, outputs
)
else:
hook_result = forward_post_hook(self, inputs, outputs)
if hook_result is not None:
outputs = hook_result
if in_dygraph_mode() and (backward_hooks or backward_pre_hooks):
flat_outputs = paddle.utils.flatten(outputs)
tensor_outputs = [
out
for out in flat_outputs
if isinstance(out, Tensor) and not out.stop_gradient
]
if tensor_outputs:
hooked_outputs = _LayerBackwardOutputHook.apply(
self, *tensor_outputs
)
if isinstance(hooked_outputs, Tensor):
hooked_outputs = (hooked_outputs,)
hooked_outputs = list(hooked_outputs)
def replace_output(out):
if isinstance(out, Tensor) and not out.stop_gradient:
return hooked_outputs.pop(0)
return out
outputs = paddle.utils.map_structure(
replace_output, outputs
)
return outputs
try:
return inner()
except Exception:
for hook_id, forward_post_hook in self._forward_post_hooks.items():
if (
hook_id in self._forward_post_hooks_always_called
) and hook_id not in called_always_called_hooks:
try:
if hook_id in self._forward_post_hooks_with_kwargs_flag:
hook_result = forward_post_hook(
self, inputs, kwargs, outputs
)
else:
hook_result = forward_post_hook(
self, inputs, outputs
)
if hook_result is not None:
outputs = hook_result
except Exception as e:
warnings.warn(
"forward hook with ``always_call=True`` raised an exception "
f"that was silenced as another error was raised in forward: {e!s}"
)
continue
# raise exception raised in try block
raise
def __call__(self, *inputs: Any, **kwargs: Any) -> Any:
if (
(not in_to_static_mode())
and (not self._forward_pre_hooks)
and (not self._forward_post_hooks)
and (not self._backward_pre_hooks)
and (not self._backward_hooks)
and (self.__class__._build_once is Layer._build_once or self._built)
and in_dygraph_mode()
and (not in_profiler_mode() or in_sot_simulation_mode())
):
return self.forward(*inputs, **kwargs)
else:
return self._dygraph_call_func(*inputs, **kwargs)
def forward(self, *inputs: Any, **kwargs: Any) -> Any:
"""
Defines the computation performed at every call.
Should be overridden by all subclasses.
Parameters:
*inputs(tuple): unpacked tuple arguments
**kwargs(dict): unpacked dict arguments
"""
raise NotImplementedError
def backward(self, *inputs: Any) -> Any:
raise ValueError("Layer shouldn't implement backward")
def add_sublayer(self, name: str, sublayer: Layer | None) -> Layer | None:
"""
Adds a sub Layer instance.
Added sublayer can be accessed by self.name
Parameters:
name(str): name of this sublayer.
sublayer(Layer): an instance of Layer.
Returns:
Layer, the sublayer passed in.
Examples:
.. code-block:: pycon
>>> import paddle
>>> class MySequential(paddle.nn.Layer):
... def __init__(self, *layers):
... super().__init__()
... if len(layers) > 0 and isinstance(layers[0], tuple):
... for name, layer in layers:
... self.add_sublayer(name, layer)
... else:
... for idx, layer in enumerate(layers):
... self.add_sublayer(str(idx), layer)
...
... def forward(self, input):
... for layer in self._sub_layers.values():
... input = layer(input)
... return input
>>> fc1 = paddle.nn.Linear(10, 3)
>>> fc2 = paddle.nn.Linear(3, 10, bias_attr=False)
>>> model = MySequential(fc1, fc2)
>>> for prefix, layer in model.named_sublayers():
... print(prefix, layer)
0 Linear(in_features=10, out_features=3, dtype=float32)
1 Linear(in_features=3, out_features=10, dtype=float32)
"""
assert isinstance(sublayer, Layer) or sublayer is None
self._sub_layers[name] = sublayer
return sublayer
def get_sublayer(self, target: str) -> Layer:
"""
Return the submodule given by ``target`` if it exists, otherwise throw an error.
Parameters:
target(str): The fully-qualified string name of the submodule to look for.
Returns:
Layer: The sublayer referenced by ``target``.
"""
if target == "":
return self
atoms: list[str] = target.split(".")
mod: paddle.nn.Layer = self
for item in atoms:
if not hasattr(mod, item):
raise AttributeError(
mod._get_name() + " has no attribute `" + item + "`"
)
mod = getattr(mod, item)
if not isinstance(mod, paddle.nn.Layer):
raise AttributeError("`" + item + "` is not an nn.Layer")
return mod
@param_one_alias(["layer", "module"])
def set_sublayer(
self, target: str, layer: Layer, strict: bool = False
) -> None:
"""
Set the sublayer given by ``target`` if it exists, otherwise throw an error.
Parameters:
target(str): The fully-qualified string name of the sublayer to look for.
layer(Layer): The layer to set the sublayer to.
strict(bool): If ``False``, the method will replace an existing sublayer
or create a new sublayer if the parent module exists. If ``True``,
the method will only attempt to replace an existing sublayer and throw an error
if the sublayer doesn't already exist.
"""
if target == "":
raise ValueError("Cannot set the sublayer without a target name!")
atoms: list[str] = target.split(".")
if not isinstance(layer, paddle.nn.Layer):
raise ValueError(
"`" + "module" + f"` is not an nn.Layer, found {type(layer)}"
)
if len(atoms) == 1:
parent: paddle.nn.Layer = self
else:
parent_key = ".".join(atoms[:-1])
parent = self.get_sublayer(parent_key)
if strict and not hasattr(parent, atoms[-1]):
raise AttributeError(
parent._get_name() + " has no attribute `" + atoms[-1] + "`"
)
if hasattr(parent, atoms[-1]):
mod = getattr(parent, atoms[-1])
if not isinstance(mod, paddle.nn.Layer):
raise AttributeError("`" + atoms[-1] + "` is not an nn.Layer")
setattr(parent, atoms[-1], layer)
get_submodule = get_sublayer
set_submodule = set_sublayer
def add_module(self, name: str, module: Layer | None) -> None:
"""
Adds a sub layer instance. Added layer can be accessed by self.name
Parameters:
name(str): name of this sublayer.
layer(Layer): an instance of Layer.
Returns:
None
"""
self.add_sublayer(name, module)
register_module = add_module
def add_parameter(self, name: str, parameter: Tensor) -> Tensor:
"""Adds a Parameter instance.
Added parameter can be accessed by self.name
Parameters:
name(str): name of this sublayer.
parameter(Parameter): an instance of Parameter.
Returns:
Parameter, the parameter passed in.
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.seed(100)
>>> class MyLayer(paddle.nn.Layer):
... def __init__(self):
... super().__init__()
... self._linear = paddle.nn.Linear(1, 1)
... w_tmp = self.create_parameter([1, 1])
... self.add_parameter("w_tmp", w_tmp)
...
... def forward(self, input):
... return self._linear(input)
>>> mylayer = MyLayer()
>>> for name, param in mylayer.named_parameters():
... print(name, param)
w_tmp Parameter containing:
Tensor(shape=[1, 1], dtype=float32, place=Place(cpu), stop_gradient=False,
[[-1.01448846]])
_linear.weight Parameter containing:
Tensor(shape=[1, 1], dtype=float32, place=Place(cpu), stop_gradient=False,
[[0.18551230]])
_linear.bias Parameter containing:
Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=False,
[0.])
"""
if '_parameters' not in self.__dict__:
raise RuntimeError("super().__init__() should be called firstly.")
elif not isinstance(name, str):
raise TypeError(
f"The name of parameter should be a string, but received {type(name).__name__}."
)
elif '.' in name:
raise KeyError(
"The name of parameter can not contain `.`, "
"because when you access the newly added parameter in the "
"form of `self.**.**`, it will cause AttributeError."
)
elif name == '':
raise KeyError("The name of parameter can not be empty.")
elif hasattr(self, name) and name not in self._parameters:
raise KeyError(f"The parameter '{name}' already exists.")
elif parameter is not None and not isinstance(
parameter, (framework.Parameter, paddle.pir.Value)
):
raise TypeError(
f"The parameter to be added should be a Parameter, but received {type(parameter).__name__}."
)
else:
if parameter is None:
self._parameters[name] = None
if len(self._loaddict_holder) > 0:
assert parameter.name in self._loaddict_holder, (
f"Parameter not found, Can't not find [ {parameter.name} ] in state_dict"
)
parameter.set_value(self._loaddict_holder[parameter.name])
self._parameters[name] = parameter
return parameter
def register_parameter(self, name: str, param: Parameter | None) -> None:
"""
Adds a Parameter instance. Added parameter can be accessed by self.name
Parameters:
name(str): name of this submodule.
parameter(Optional[Parameter]): an instance of Parameter.
Returns:
None
"""
self.add_parameter(name, param)
def _set_op_attrs(self, attrs):
"""
Add customized attribute while append_op. In case of quantization, we want to save
some attributes into op_desc while exporting inference model by @to_static.
Arguments:
attrs(dict): customized attributes that will be added into op_descs.
NOTE: The interface is only exposed to developers.
"""
def is_already_registered(is_pre_hook):
layers_hooks = (
self._forward_pre_hooks
if is_pre_hook
else self._forward_post_hooks
)
candidate_hook = (
record_program_ops_pre_hook
if is_pre_hook
else set_op_customized_attrs_post_hook
)
already_registered = False
if layers_hooks:
last_key = next(reversed(layers_hooks))
already_registered = layers_hooks[last_key] == candidate_hook
return already_registered
if not isinstance(attrs, dict):
raise TypeError(
f"attrs should be type(dict), but received {type(attrs).__name__}"
)
# NOTE: Overwrite behavior for same key.
self._customized_attrs.update(attrs)
if not is_already_registered(is_pre_hook=True):
pre_hook_helper = self.register_forward_pre_hook(
record_program_ops_pre_hook
)
assert len(self._op_recorder.hooks) == 0
self._op_recorder.hooks = [pre_hook_helper]
# manually register post_hook to ensure it is inserted into the head.
if not is_already_registered(is_pre_hook=False):
post_hook_helper = self.register_forward_post_hook(
set_op_customized_attrs_post_hook
)
if len(self._forward_post_hooks) > 1:
self._forward_post_hooks.move_to_end(
post_hook_helper._hook_id, last=False
)
assert len(self._op_recorder.hooks) == 1
# hooks that need to be removed once we finish executing them.
self._op_recorder.hooks.append(post_hook_helper)
def __getstate__(self) -> dict[str, Any]:
return self.__dict__
def __setstate__(self, state: dict[str, Any]) -> None:
self.__dict__.update(state)
def __getattr__(self, name: str) -> Any:
if '_parameters' in self.__dict__:
_parameters = self.__dict__['_parameters']
if name in self._parameters:
if in_to_static_mode():
return _convert_into_variable(self._parameters[name])
return self._parameters[name]
if '_sub_layers' in self.__dict__:
_sub_layers = self.__dict__['_sub_layers']
if name in self._sub_layers:
return self._sub_layers[name]
if '_buffers' in self.__dict__:
_buffers = self.__dict__['_buffers']
if name in _buffers:
if in_to_static_mode():
return _convert_into_variable(_buffers[name])
return _buffers[name]
return object.__getattribute__(self, name)
def __setattr__(self, name: str, value: Any) -> None:
def _remove_if_exist(*dicts):
for d in dicts:
if name in d:
del d[name]
if isinstance(
value, paddle.jit.dy2static.program_translator.StaticFunction
):
object.__setattr__(self, name, value)
value._patched_name = name
return
if isinstance(getattr(type(self), name, None), property):
object.__setattr__(self, name, value)
params = self.__dict__.get('_parameters', None)
if isinstance(value, framework.Parameter):
if params is None:
raise ValueError("super().__init__() should be called first")
if len(self._loaddict_holder) > 0:
assert value.name in self._loaddict_holder, (
f"Parameter not found, Can't not find [ {value.name} ] in state_dict"
)
value.set_value(self._loaddict_holder[value.name])
_remove_if_exist(self.__dict__, self._buffers, self._sub_layers)
params[name] = value
elif (
isinstance(value, paddle.pir.Value)
and value.get_defining_op().name() == 'builtin.parameter'
):
if params is None:
raise ValueError("super().__init__() should be called first")
_remove_if_exist(self.__dict__, self._buffers, self._sub_layers)
params[name] = value
elif params is not None and name in params:
if value is not None:
raise TypeError(
f"assignment to parameter '{name}' should be of type Parameter or None, but got '{type(value).__name__}'"
)
params[name] = None
else:
layers = self.__dict__.get('_sub_layers', None)
if isinstance(value, Layer):
if layers is None:
raise ValueError(
"super().__init__() should be called first"
)
_remove_if_exist(self.__dict__, self._parameters, self._buffers)
layers[name] = value
elif layers is not None and name in layers:
if value is not None:
raise TypeError(
f"assignment to sublayer '{name}' should be of type Layer or None, but got '{type(value).__name__}'"
)
layers[name] = None
else:
_buffers = self.__dict__.get('_buffers', None)
if isinstance(value, core.eager.Tensor):
if _buffers is None:
raise ValueError(
"super().__init__() should be called first"
)
_remove_if_exist(
self.__dict__, self._parameters, self._sub_layers
)
# Set persistable=False by default. Only `register_buffer` can
# add a persistable buffer.
if name not in self._buffers:
self._non_persistable_buffer_names_set.add(name)
if not value.name:
value.name = unique_name.generate('_buffers_' + name)
_buffers[name] = value
elif _buffers is not None and name in _buffers:
# Note(Aurelius84): In Dy2stat, the value of the Buffer may be modified in
# decorated function, such as `self.buffer = new_tensor`. So we update its
# value via `assign`.
if type(value) == framework.Variable or isinstance(
value, paddle.pir.Value
):
from paddle import assign
# Note(zhhsplendid): the condition below happens in PaddleGan model,
# but should all non-Variable _buffers[name] be re-assign? We
# should consider it in the future. I current wrote this as
# conservative code.
if in_to_static_mode() and _buffers[name] is None:
raise RuntimeError(
f'In Dy2stat, self.{name} is a buffer and self.{name} is '
f'not allowed to be set to Variable when self.{name} is None.'
)
elif (
_buffers[name] is None
or type(getattr(self, name)) == core.eager.Tensor
):
_buffers[name] = assign(value)
else:
assign(value, getattr(self, name))
elif value is not None:
raise TypeError(
f"assignment to buffers '{name}' should be of type core.DenseTensor or None, but got '{type(value).__name__}'"
)
else:
# Assigning None will remove the buffer, but if re-assign a new varBase to it,
# it will be remarked as a buffer with same `persistable` attribute.
_buffers[name] = None
else:
object.__setattr__(self, name, value)
def __delattr__(self, name: str) -> None:
if name in self._parameters:
del self._parameters[name]
elif name in self._sub_layers:
del self._sub_layers[name]
elif name in self._buffers:
del self._buffers[name]
self._non_persistable_buffer_names_set.discard(name)
else:
object.__delattr__(self, name)
def __dir__(self) -> list[str]:
"""
Return a list. Get all parameters, buffers(non-parameter tensors), sublayers, method and attr of Layer.
Examples:
.. code-block:: pycon
>>> import paddle
>>> import numpy as np
>>> class Mylayer(paddle.nn.Layer):
... def __init__(self):
... super().__init__()
... self.linear1 = paddle.nn.Linear(10, 10)
... self.linear2 = paddle.nn.Linear(5, 5)
... self.conv2d = paddle.nn.Conv2D(3, 2, 3)
... self.embedding = paddle.nn.Embedding(128, 16)
... self.h_0 = paddle.to_tensor(np.zeros([10, 10]).astype('float32'))
>>> mylayer = Mylayer()
>>> print(dir(mylayer))
['__call__', '__class__', '__delattr__', '__dict__', ..., 'training']
"""
method = dir(self.__class__)
attrs = list(self.__dict__.keys())
parameters = list(self._parameters.keys())
sublayers = list(self._sub_layers.keys())
buffers = list(self._buffers.keys())
keys = method + attrs + parameters + sublayers + buffers
return keys
def extra_repr(self) -> str:
"""
Extra representation of this layer, you can have custom implementation
of your own layer.
"""
return ''
def __repr__(self) -> str:
extra_lines = []
extra_repr = self.extra_repr()
extra_lines = extra_repr.split('\n')
sublayer_lines = []
for name, layer in self._sub_layers.items():
sublayer_str = repr(layer)
sublayer_str = _addindent(sublayer_str, 2)
sublayer_lines.append('(' + name + '): ' + sublayer_str)
final_str = self.__class__.__name__ + '('
if extra_lines:
if len(extra_lines) > 1:
final_str += '\n ' + '\n '.join(extra_lines) + '\n'
elif len(extra_lines) == 1:
final_str += extra_lines[0]
if sublayer_lines:
final_str += '\n ' + '\n '.join(sublayer_lines) + '\n'
final_str += ')'
return final_str
def register_state_dict_hook(
self, hook: _StateDictHook
) -> HookRemoveHelper:
hook_remove_helper = HookRemoveHelper(self._state_dict_hooks)
self._state_dict_hooks[hook_remove_helper._hook_id] = hook
return hook_remove_helper
def register_state_dict_post_hook(
self, hook: _StateDictHook
) -> HookRemoveHelper:
return self.register_state_dict_hook(hook)
def register_state_dict_pre_hook(self, hook: _StateDictPreHook):
hook_remove_helper = HookRemoveHelper(self._state_dict_pre_hooks)
self._state_dict_pre_hooks[hook_remove_helper._hook_id] = hook
return hook_remove_helper
def register_load_state_dict_pre_hook(self, hook: Callable[..., None]):
hook_remove_helper = HookRemoveHelper(self._load_state_dict_pre_hooks)
self._load_state_dict_pre_hooks[hook_remove_helper._hook_id] = hook
return hook_remove_helper
def register_load_state_dict_post_hook(self, hook: Callable[..., None]):
hook_remove_helper = HookRemoveHelper(self._load_state_dict_post_hooks)
self._load_state_dict_post_hooks[hook_remove_helper._hook_id] = hook
return hook_remove_helper
def _obtain_parameters_buffers(
self,
destination: _StateDict | None = None,
include_sublayers: bool = True,
structured_name_prefix: str = "",
) -> _StateDict:
"""
The difference from state_dict() is that state_dict_hook will not be called,
but the original types of parameters and buffers will be maintained.
"""
if destination is None:
destination = OrderedDict()
for name, data in self._parameters.items():
if data is not None:
destination[structured_name_prefix + name] = data
for name, buffer in self._buffers.items():
if (
buffer is not None
and name not in self._non_persistable_buffer_names_set
):
destination[structured_name_prefix + name] = buffer
if include_sublayers:
for layer_name, layer_item in self._sub_layers.items():
if layer_item is not None:
layer_item._obtain_parameters_buffers(
destination,
include_sublayers,
structured_name_prefix + layer_name + ".",
)
return destination
def _state_dict_impl(
self,
destination: _StateDict | None = None,
include_sublayers: bool = True,
structured_name_prefix: str = "",
include_non_persistable_buffer: bool = False,
use_hook: bool = True,
keep_vars: bool = True,
) -> _StateDict:
"""
Get all parameters and persistable buffers of current layer and its sub-layers. And set them into a dict
Parameters:
destination(dict, optional) : If provide, all the parameters and persistable buffers will be set to this dict . Default: None.
include_sublayers(bool, optional) : If true, also include the parameters and persistable buffers from sublayers. Default: True.
include_non_persistable_buffer(bool, optional): If true, include non persistable buffers of current layer and its sub-layers, it is used in pure fp16 and jit.save. Default: False.
use_hook(bool, optional) : If true, the operations contained in _state_dict_hooks will be appended to the destination. Default: True.
keep_vars(bool, optional) : If false, the returned tensors in the state dict are detached from autograd. Default: True.
"""
if destination is None:
destination = OrderedDict()
if use_hook:
for state_dict_pre_hook in self._state_dict_pre_hooks.values():
state_dict_pre_hook(self, structured_name_prefix, keep_vars)
for name, data in self._parameters.items():
if data is not None:
destination[structured_name_prefix + name] = (
data if keep_vars else data.detach()
)
for name, buffer in self._buffers.items():
if not include_non_persistable_buffer:
if (
buffer is not None
and name not in self._non_persistable_buffer_names_set
):
destination[structured_name_prefix + name] = (
buffer if keep_vars else buffer.detach()
)
else:
if buffer is not None:
destination[structured_name_prefix + name] = (
buffer if keep_vars else buffer.detach()
)
extra_state_key = structured_name_prefix + _EXTRA_STATE_KEY_SUFFIX
if (
getattr(self.__class__, "get_extra_state", Layer.get_extra_state)
is not Layer.get_extra_state
):
extra_state = self.get_extra_state()
if extra_state is not None:
destination[extra_state_key] = extra_state
if include_sublayers:
for layer_name, layer_item in self._sub_layers.items():
if layer_item is not None:
layer_item._state_dict_impl(
destination,
include_sublayers,
structured_name_prefix + layer_name + ".",
include_non_persistable_buffer,
use_hook,
keep_vars,
)
if use_hook:
local_metadata: dict[str, Any] = {}
for state_dict_hook in self._state_dict_hooks.values():
try:
hook_result = state_dict_hook(destination)
except TypeError:
hook_result = state_dict_hook(
self,
destination,
structured_name_prefix,
local_metadata,
)
if hook_result is not None:
destination = hook_result
return destination
def to_static_state_dict(
self,
destination: _StateDict | None = None,
include_sublayers: bool = True,
structured_name_prefix: str = "",
use_hook: bool = True,
keep_vars: bool = True,
) -> _StateDict:
'''
Get all parameters and buffers of current layer and its sub-layers. And set them into a dict
Parameters:
destination(dict, optional) : If provide, all the parameters and persistable buffers will be set to this dict . Default: None.
include_sublayers(bool, optional) : If true, also include the parameters and persistable buffers from sublayers. Default: True.
use_hook(bool, optional) : If true, the operations contained in _state_dict_hooks will be appended to the destination. Default: True.
keep_vars(bool, optional) : If false, the returned tensors in the state dict are detached from autograd. Default: True.
Returns:
dict, a dict contains all the parameters and persistable buffers.
Examples:
.. code-block:: pycon
>>> import paddle
>>> emb = paddle.nn.Embedding(10, 10)
>>> state_dict = emb.to_static_state_dict()
>>> paddle.save(state_dict, "paddle_dy.pdparams")
'''
return self._state_dict_impl(
destination=destination,
include_sublayers=include_sublayers,
structured_name_prefix=structured_name_prefix,
include_non_persistable_buffer=True,
use_hook=use_hook,
keep_vars=keep_vars,
)
@overload
def state_dict(
self,
destination: _StateDict | None = None,
include_sublayers: bool = True,
structured_name_prefix: str = "",
use_hook: bool = True,
keep_vars: bool = True,
) -> _StateDict: ...
@overload
def state_dict(
self,
*,
destination: _StateDict,
prefix: str = ...,
keep_vars: bool = ...,
) -> _StateDict: ...
@overload
def state_dict(
self,
destination: None,
*,
prefix: str = ...,
keep_vars: bool = ...,
) -> _StateDict: ...
@overload
def state_dict(
self, *args, destination=None, prefix="", keep_vars=False
) -> _StateDict: ...
def state_dict(self, *args: Any, **kwargs: Any) -> _StateDict:
'''
Get all parameters and persistable buffers of current layer and its sub-layers. And set them into a dict
Parameters:
destination(dict, optional) : If provide, all the parameters and persistable buffers will be set to this dict . Default: None.
include_sublayers(bool, optional) : If true, also include the parameters and persistable buffers from sublayers. Default: True.
use_hook(bool, optional) : If true, the operations contained in _state_dict_hooks will be appended to the destination. Default: True.
keep_vars(bool, optional) : If false, the returned tensors in the state dict are detached from autograd. Default: True.
Returns:
dict: a dict contains all the parameters and persistable buffers.
Examples:
.. code-block:: pycon
>>> import paddle
>>> emb = paddle.nn.Embedding(10, 10)
>>> state_dict = emb.state_dict()
>>> paddle.save(state_dict, "paddle_dy.pdparams")
'''
len_args = len(args)
def safe_set_param(key: str, value: Any):
if key in kwargs:
raise TypeError(f"got multiple values for argument '{key}'")
kwargs[key] = value
if len_args >= 2 and isinstance(args[1], bool):
return self._state_dict_impl(*args, **kwargs)
if any(
key in kwargs
for key in [
'include_sublayers',
'structured_name_prefix',
'use_hook',
]
):
return self._state_dict_impl(*args, **kwargs)
if (len_args >= 2 and isinstance(args[1], str)) or 'prefix' in kwargs:
base_param_keys = ["destination", "prefix", "keep_vars"]
for idx in range(min(len_args, len(base_param_keys))):
safe_set_param(base_param_keys[idx], args[idx])
return self._state_dict_impl(
destination=kwargs.get('destination', None),
include_sublayers=True,
structured_name_prefix=kwargs.get('prefix', ""),
include_non_persistable_buffer=False,
use_hook=True,
keep_vars=kwargs.get('keep_vars', False),
)
return self._state_dict_impl(*args, **kwargs)
def sharded_state_dict(
self,
structured_name_prefix: str = "",
) -> ShardedStateDict:
"""Recursively builds a sharded state dictionary for the model and its sub-layers.
Args:
structured_name_prefix: Prefix to prepend to all tensor names for hierarchical naming.
Returns:
Dictionary mapping tensor names to ShardedWeight.
The dictionary contains both the current layer's parameters and all sub-layer parameters.
"""
sharded_state_dict = {}
# Get current layer's state dict (without sub-layers)
state_dict = self.state_dict(
structured_name_prefix="", # We handle prefixing ourselves
include_sublayers=False,
)
# Convert to sharded state dict
current_sharded_dict = build_sharded_state_dict(
state_dict=state_dict,
shard_rules=None, # No tensor parallelism rules by default
prefix=structured_name_prefix,
)
sharded_state_dict.update(current_sharded_dict)
# Recursively process sub-layers
for layer_name, layer_item in self._sub_layers.items():
if layer_item is not None:
sub_sharded = layer_item.sharded_state_dict(
structured_name_prefix=f"{structured_name_prefix}{layer_name}.",
)
sharded_state_dict.update(sub_sharded)
return sharded_state_dict
def full(
self,
aoa_config: dict[str : list[str]] | None = None,
**kwargs,
):
"""
Returns an iterator over the full, unsharded model parameters.
The output parameters can be customized using the `aoa_config` argument.
Args:
sharded_state_dict (ShardedStateDict):
The state dict containing parameter shards local to the current process.
aoa_config (dict[str, list[str]] | None, optional):
AoA (Almost AllReduce) configuration. Default is None.
kwargs:
Optional keyword arguments:
- h_group: The horizontal communication group.
If using group communication, both h_group and v_group must be provided.
- v_group: The vertical communication group.
- process_group: The communication group in single-group setups (when h_group and v_group are not used).
- num_splits (int): The number of splits to divide the parameters.
- shard_idx (int): The index of the split handled by the current process. Default is 0.
- memory_growth_threshold (int): The memory threshold (in bytes) for controlling memory growth during parameter assembly.
Default is 8 * (2 ** 30), i.e., 8GB.
Returns:
Iterator:
An iterator over the full, unsharded model parameters, optionally filtered and customized according to `aoa_config`.
"""
from paddle.distributed.flex_checkpoint.dcp.full_param import (
full_param,
)
return full_param(self.sharded_state_dict(), aoa_config, **kwargs)
@framework.deprecate_stat_dict
def set_state_dict(
self,
state_dict: _StateDict,
use_structured_name: bool = True,
assign: bool = False,
) -> _IncompatibleKeys:
'''
Set parameters and persistable buffers from state_dict. All the parameters and buffers will be reset by the tensor in the state_dict
Parameters:
state_dict(dict) : Dict contains all the parameters and persistable buffers.
use_structured_name(bool, optional) : If true, use structured name as key, otherwise, use parameter or buffer name as key.
Default: True.
assign(bool, optional): When set to ``False``, the properties of the tensors
in the current layer are preserved whereas setting it to ``True`` preserves
properties of the tensors in the state dict. Default: ``False``.
Returns:
``NamedTuple`` with ``missing_keys`` and ``unexpected_keys`` fields:
* ``missing_keys`` is a list of str containing any keys that are expected
by this module but missing from the provided ``state_dict``.
* ``unexpected_keys`` is a list of str containing the keys that are not
expected by this module but present in the provided ``state_dict``.
Examples:
.. code-block:: pycon
>>> import paddle
>>> emb = paddle.nn.Embedding(10, 10)
>>> state_dict = emb.state_dict()
>>> paddle.save(state_dict, "paddle_dy.pdparams")
>>> para_state_dict = paddle.load("paddle_dy.pdparams")
>>> emb.set_state_dict(para_state_dict)
'''
missing_keys = []
match_keys = set()
unexpected_keys = []
def _check_match(key, param):
state = state_dict.get(key, None)
if state is None:
missing_keys.append(key)
raise ValueError(f"{key} is not found in the provided dict.")
if isinstance(state, (dict, list)):
if len(state) != len(param):
missing_keys.append(key)
raise ValueError(
f"{key} receives the length of {len(state)}, "
f"but the expected shape is {len(param)}"
)
else:
match_keys.add(key)
return param, state
else:
state_shape = (
state.shape()
if inspect.ismethod(state.shape)
else state.shape
)
if list(state_shape) != list(param.shape):
missing_keys.append(key)
raise ValueError(
f"{key} receives a shape {list(state_shape)}, but the expected shape is {list(param.shape)}."
)
match_keys.add(key)
return param, state
matched_param_state = []
for key, param in self._obtain_parameters_buffers().items():
key_name = key if use_structured_name else param.name
try:
match_res = _check_match(key_name, param)
matched_param_state.append((key, *match_res))
except ValueError as err:
warnings.warn(f"Skip loading for {key}. " + str(err))
for key in state_dict.keys():
if key not in match_keys:
unexpected_keys.append(key)
if in_dygraph_mode():
for key, param, state in matched_param_state:
if assign and use_structured_name:
module_path, _, param_name = key.rpartition(".")
mod = self.get_sublayer(module_path)
if isinstance(param, paddle.nn.Parameter):
if isinstance(state, paddle.nn.Parameter):
state.trainable = param.trainable
else:
state = paddle.nn.Parameter(
state, trainable=param.trainable
)
setattr(mod, param_name, state)
else:
param.set_value(state)
else:
def _set_var(var, ndarray):
t = global_scope().find_var(var.name).get_tensor()
p = t._place()
if p.is_cpu_place():
place = core.CPUPlace()
elif p.is_cuda_pinned_place():
place = core.CUDAPinnedPlace()
elif p.is_xpu_place():
p = core.Place()
p.set_place(t._place())
place = core.XPUPlace(p.xpu_device_id())
elif p.is_custom_place():
p = core.Place()
p.set_place(t._place())
place = core.CustomPlace(
paddle.device.get_device().split(':')[0],
p.custom_device_id(),
)
else:
p = core.Place()
p.set_place(t._place())
place = core.CUDAPlace(p.gpu_device_id())
t.set(ndarray, place)
try:
# restore parameter states
if in_pir_mode():
executor = Executor(
paddle.base.framework._current_expected_place_()
)._default_executor
paddle.base.libpaddle.pir.create_loaded_parameter(
[param for key, param, state in matched_param_state],
global_scope(),
executor,
)
else:
executor = Executor(_get_device())._default_executor
core._create_loaded_parameter(
[param for key, param, state in matched_param_state],
global_scope(),
executor,
)
for key, param, state in matched_param_state:
_set_var(param, state)
except ValueError as e:
raise ValueError(
"This error might happens in dy2static, while calling 'set_state_dict' dynamically in 'forward', which is not supported. If you only need call 'set_state_dict' once, move it to '__init__'."
)
except TypeError as e:
raise ValueError(
"This error might happens in dy2static, while calling 'set_state_dict' dynamically in 'forward', which is not supported. If you only need call 'set_state_dict' once, move it to '__init__'."
)
return _IncompatibleKeys(missing_keys, unexpected_keys)
def load_state_dict(
self,
state_dict: Mapping[str, Any],
strict: bool = True,
assign: bool = False,
) -> _IncompatibleKeys:
"""
Copy parameters and buffers from :attr:`state_dict` into this module and its descendants.
If :attr:`strict` is ``True``, then
the keys of :attr:`state_dict` must exactly match the keys returned
by this module's :meth:`~torch.nn.Module.state_dict` function.
Parameters:
state_dict (dict): a dict containing parameters and persistent buffers.
strict (bool, optional): whether to strictly enforce that the keys
in :attr:`state_dict` match the keys returned by this module's
:meth:`~torch.nn.Module.state_dict` function. Default: ``True``
assign (bool, optional): When set to ``False``, the properties of the tensors
in the current module are preserved whereas setting it to ``True`` preserves
properties of the Tensors in the state dict. The only
exception is the ``requires_grad`` field of :class:`~torch.nn.Parameter`
for which the value from the module is preserved. Default: ``False``
Returns:
``NamedTuple`` with ``missing_keys`` and ``unexpected_keys`` fields:
* ``missing_keys`` is a list of str containing any keys that are expected
by this module but missing from the provided ``state_dict``.
* ``unexpected_keys`` is a list of str containing the keys that are not
expected by this module but present in the provided ``state_dict``.
"""
error_msgs: list[str] = []
missing_keys: list[str] = []
unexpected_keys: list[str] = []
def visit_load_state_dict_hooks(layer, prefix, is_post_hook=False):
if is_post_hook:
incompatible_keys = _IncompatibleKeys(
missing_keys, unexpected_keys
)
for hook in layer._load_state_dict_post_hooks.values():
hook_result = hook(layer, incompatible_keys)
if hook_result is not None:
raise AssertionError(
"Hooks registered with ``register_load_state_dict_post_hook`` are not"
"expected to return new values, if incompatible_keys need to be modified,"
"it should be done inplace."
)
else:
local_metadata: dict[str, Any] = {}
for hook in layer._load_state_dict_pre_hooks.values():
hook(
layer,
state_dict,
prefix,
local_metadata,
strict,
missing_keys,
unexpected_keys,
error_msgs,
)
for layer_name, layer_item in layer._sub_layers.items():
if layer_item is not None:
visit_load_state_dict_hooks(
layer_item,
prefix + layer_name + ".",
is_post_hook,
)
visit_load_state_dict_hooks(self, "")
load_missing_keys, load_unexpected_keys = self.set_state_dict(
state_dict, use_structured_name=True, assign=assign
)
missing_keys.extend(load_missing_keys)
unexpected_keys.extend(load_unexpected_keys)
def load_extra_state(layer, prefix):
extra_state_key = prefix + _EXTRA_STATE_KEY_SUFFIX
if extra_state_key in unexpected_keys:
unexpected_keys.remove(extra_state_key)
if (
getattr(
layer.__class__, "set_extra_state", Layer.set_extra_state
)
is not Layer.set_extra_state
):
if extra_state_key in state_dict:
layer.set_extra_state(state_dict[extra_state_key])
elif strict:
missing_keys.append(extra_state_key)
elif strict and extra_state_key in state_dict:
unexpected_keys.append(extra_state_key)
for layer_name, layer_item in layer._sub_layers.items():
if layer_item is not None:
load_extra_state(layer_item, prefix + layer_name + ".")
load_extra_state(self, "")
visit_load_state_dict_hooks(self, "", is_post_hook=True)
if strict:
if len(unexpected_keys) > 0:
error_msgs.insert(
0,
"Unexpected key(s) in state_dict: {}. ".format(
", ".join(f'"{k}"' for k in unexpected_keys)
),
)
if len(missing_keys) > 0:
error_msgs.insert(
0,
"Missing key(s) in state_dict: {}. ".format(
", ".join(f'"{k}"' for k in missing_keys)
),
)
if len(error_msgs) > 0:
raise RuntimeError(
"Error(s) in loading state_dict for {}:\n\t{}".format(
self.__class__.__name__, "\n\t".join(error_msgs)
)
)
return _IncompatibleKeys(missing_keys, unexpected_keys)
@overload
def to(
self,
device: PlaceLike | None = ...,
dtype: DTypeLike | None = ...,
blocking: bool = ...,
*,
non_blocking: bool = ...,
) -> Self: ...
@overload
def to(
self,
dtype: DTypeLike,
blocking: bool = ...,
*,
non_blocking: bool = ...,
) -> Self: ...
@overload
def to(
self,
tensor: Tensor,
blocking: bool = ...,
*,
non_blocking: bool = ...,
) -> Self: ...
def to(self, *args, **kwargs) -> Self:
'''
Move and/or cast the parameters and buffers.
This API has three calling conventions:
1. ``to(device=None, dtype=None, blocking=True, *, non_blocking=False)``:
Moves and/or casts the parameters and buffers.
2. ``to(dtype, blocking=True, *, non_blocking=False)``:
Equivalent to ``self.to(device=None, dtype=dtype, ...)``.
3. ``to(tensor, blocking=True, *, non_blocking=False)``:
Equivalent to ``self.to(device=tensor.place, dtype=tensor.dtype, ...)``.
.. note::
This method modifies the layer in-place.
Args:
device (str|paddle.CPUPlace()|paddle.CUDAPlace()|paddle.CUDAPinnedPlace()|paddle.XPUPlace()|None, optional):
The device of the Layer which want to be stored.
If None, the device is the same with the original Tensor.
If device is string, it can be ``cpu``, ``gpu:x`` and ``xpu:x``,
where ``x`` is the index of the GPUs or XPUs. Default: ``None``.
dtype (str|numpy.dtype|paddle.dtype|None, optional):
The type of the data. If None, the dtype is the same with the
original Tensor. Default: ``None``.
blocking (bool, optional):
If ``False`` and the source is in pinned memory, the copy will be
asynchronous with respect to the host. Otherwise, the argument
has no effect. Default: ``True``.
Keyword args:
non_blocking (bool, optional):
If ``True`` and the source is in pinned memory, the copy will be
asynchronous with respect to the host. Default: ``False``.
``non_blocking`` and ``blocking`` are mutually exclusive
and cannot both be set at the same time.
Returns:
self
Examples:
.. code-block:: pycon
>>> import paddle
>>> paddle.seed(2023)
>>> linear = paddle.nn.Linear(2, 2)
>>> linear.weight
>>> print(linear.weight)
Parameter containing:
Tensor(shape=[2, 2], dtype=float32, place=Place(gpu:0), stop_gradient=False,
[[ 0.89611185, 0.04935038],
[-0.58883440, 0.99266374]])
>>> linear.to(dtype='float64')
>>> linear.weight
>>> print(linear.weight)
Parameter containing:
Tensor(shape=[2, 2], dtype=float64, place=Place(gpu:0), stop_gradient=False,
[[ 0.89611185, 0.04935038],
[-0.58883440, 0.99266374]])
>>> linear.to(device='cpu')
>>> linear.weight
>>> print(linear.weight)
Parameter containing:
Tensor(shape=[2, 2], dtype=float64, place=Place(cpu), stop_gradient=False,
[[ 0.89611185, 0.04935038],
[-0.58883440, 0.99266374]])
>>> # doctest: +REQUIRES(env:GPU)
>>> linear.to(device=paddle.CUDAPinnedPlace(), blocking=False)
>>> linear.weight
>>> print(linear.weight)
Parameter containing:
Tensor(shape=[2, 2], dtype=float64, place=Place(gpu_pinned), stop_gradient=False,
[[ 0.89611185, 0.04935038],
[-0.58883440, 0.99266374]])
'''
device, dtype, blocking, _ = _parse_to_args(*args, **kwargs)
return self._to_impl(
device=device,
dtype=dtype,
blocking=blocking,
include_sublayers=True,
floating_only=True,
)
def _apply(
self,
func: Callable[
[Tensor, PlaceLike | None, DTypeLike | None, bool | None], None
],
device: PlaceLike | None,
dtype: DTypeLike | None,
blocking: bool | None,
include_sublayers: bool = True,
) -> None:
if include_sublayers:
for layer in self.children():
layer._apply(func, device, dtype, blocking, include_sublayers)
for key, param in self._parameters.items():
if param is not None:
with no_grad():
param_applied = func(param, device, dtype, blocking)
if param.grad is not None:
with no_grad():
grad_applied = func(
param._grad_ivar(), device, dtype, blocking
)
for key, buf in self._buffers.items():
if buf is not None:
self._buffers[key] = func(buf, device, dtype, blocking)
self._dtype = dtype
def _transform(
self,
t: Tensor,
device: PlaceLike | None,
dtype: DTypeLike | None,
blocking: bool | None,
) -> Tensor:
if device is None:
device = t.place
if dtype is None:
dtype = t.dtype
if not isinstance(dtype, (VarDesc.VarType, core.DataType)):
dtype = convert_nptype_to_datatype_or_vartype(dtype)
# 1. gpu place need to determine whether the memory is sufficient for allocation:
if t.place.is_gpu_place():
# for gpu, minimum memory allocation unit is 256 bytes.
var_dtype = (
datatype_to_vartype[dtype]
if isinstance(dtype, core.DataType)
else dtype
)
size_dtype = core.size_of_dtype(var_dtype)
# Note(zhangbo): Paddle GPU minimum memory allocation unit is 256 bytes, waiting_alloc_memory will compute t occupied memory space.
# Coefficient 1.2 is used to avoid OOM that may occur in this critical state when the memory is just enough.
waiting_alloc_memory = (
((np.prod(t.shape) * size_dtype) / 256 + 1) * 256 * 1.2
)
gpu_memory_available = core.gpu_memory_available()
if gpu_memory_available < waiting_alloc_memory:
# Copy param / Tensor to cpu
t_used = t._copy_to(
paddle.CPUPlace(), blocking
) # k-v type will error
# Release mem of t
t.value().get_tensor()._clear()
else:
t_used = t
else:
t_used = t
# 2. cast param / Tensor to dtype
if dtype is not None and dtype != t_used.dtype:
with paddle.base.framework._dygraph_place_guard(place=t_used.place):
t_casted = t_used.cast(dtype=dtype)
else:
t_casted = t_used
# 3. Copy casted cpu param / Tensor to device
if device is not None and not t_casted.place._equals(device):
new_t = t_casted._copy_to(device, blocking)
else:
new_t = t_casted
# 4. share Tensor to origin param / Tensor
dst_tensor = t.value().get_tensor()
src_tensor = new_t.value().get_tensor()
if t._is_initialized():
dst_tensor._share_data_with(src_tensor)
else:
# If the tensor is not initialized, we can't check the memory size.
dst_tensor._share_data_nocheck_with(src_tensor)
return t
def _to_impl(
self,
device: PlaceLike | None = None,
dtype: DTypeLike | None = None,
blocking: bool | None = None,
include_sublayers: bool = True,
floating_only: bool = False,
):
'''
Cast the parameters and buffers of Layer by the give device, dtype and blocking.
Parameters:
device(str|paddle.CPUPlace()|paddle.CUDAPlace()|paddle.CUDAPinnedPlace()|paddle.XPUPlace()|None, optional):
The device of the Layer which want to be stored. Default: None.
dtype(str|numpy.dtype|paddle.dtype|None, optional):
The type of the data. Default: None.
blocking(bool|None, optional):
If False and the source is in pinned memory, the copy will be
asynchronous with respect to the host. Default: None.
include_sublayers(bool, optional):
If True, deal with self and all sublayers parameters and
buffers. Default: True.
floating_only(bool, optional):
If True, only cast floating point parameters and buffers.
Returns:
self
'''
if device is None and dtype is None and blocking is None:
return self
if device is not None:
if isinstance(device, str):
device = paddle.device._convert_to_place(device)
elif isinstance(
device,
core.Place,
):
pass
else:
raise ValueError(
f"device should be type of str, paddle.CPUPlace, paddle.CUDAPlace, paddle.CUDAPinnedPlace, paddle.XPUPlace, or paddle.base.libpaddle.Place, but got {type(device).__name__}"
)
if blocking is None:
blocking = True
else:
assert isinstance(blocking, bool), (
"blocking value error, must be the True, False or None"
)
def transform(t, device, dtype, blocking):
if floating_only and paddle.is_integer(t):
if device is None:
return t
return self._transform(t, device, None, blocking)
return self._transform(t, device, dtype, blocking)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=UserWarning)
self._apply(transform, device, dtype, blocking, include_sublayers)
self._dtype = dtype
return self
def _startup_program(self) -> Program:
"""
Return startup program containing initialization operations of all parameters.
NOTE(dev): This is a very low level API and only for inner developer.
"""
startup_program = paddle.base.Program()
main_program = paddle.base.Program()
with paddle.base.program_guard(main_program, startup_program):
for param in self.parameters():
param._create_init_op(startup_program.global_block())
if paddle.framework.use_pir_api():
return main_program
else:
return startup_program
# [aliases] Compatible with old method names
set_dict = set_state_dict
load_dict = set_state_dict
def type(self, dst_type: dtype | str) -> Self:
"""
Casts all parameters and buffers to :attr:`dst_type`.
Parameters:
dtype(str|paddle.dtype): target data type of layer.
If set str, it can be "bool", "bfloat16", "float16", "float32", "float64",
"int8", "int16", "int32", "int64", "uint8", "complex64", "complex128".
Default: None
Returns:
Layer: self
"""
valid_dtypes = [
"bfloat16",
"float16",
"float32",
"float64",
"int8",
"int16",
"int32",
"int64",
"uint8",
"complex64",
"complex128",
"bool",
]
if (
isinstance(dst_type, (paddle.dtype, np.dtype))
or type(dst_type) is str
and dst_type in valid_dtypes
):
if isinstance(dst_type, (str, np.dtype)):
dst_type = framework.convert_nptype_to_datatype_or_vartype(
dst_type
)
def layer_trans(layer):
layer._to_impl(
dtype=dst_type, floating_only=False, include_sublayers=True
)
return self.apply(layer_trans)
else:
raise ValueError(
"dtype value error, must be 'bfloat16', 'float16', 'float32', 'float64', 'int8', 'int16', 'int32', 'int64', 'uint8', 'complex64', 'complex128', 'bool', or paddle.dtype, numpy.dtype, but receive "
+ str(dtype)
)
def double(self) -> Self:
"""
Casts all floating point parameters and buffers to ``double`` datatype.
Returns:
Module: self
"""
return self.type(paddle.float64)
def half(self) -> Self:
"""
Casts all floating point parameters and buffers to ``half`` datatype.
Returns:
Module: self
"""
return self.type(paddle.float16)
def float(
self, excluded_layers: Layer | Sequence[Layer] | None = None
) -> Self:
'''
Casts all floating point parameters and buffers to ``float`` data type.
Parameters:
excluded_layers(nn.Layer|list|tuple|None, optional): Specify the layers that need to be kept original data type. if excluded_layers is None, casts all floating point parameters and buffers. Default: None.
Returns:
Layer: self
Examples:
.. code-block:: pycon
>>> import paddle
>>> class Model(paddle.nn.Layer):
... def __init__(self):
... super().__init__()
... self.linear = paddle.nn.Linear(1, 1)
... self.dropout = paddle.nn.Dropout(p=0.5)
...
... def forward(self, input):
... out = self.linear(input)
... out = self.dropout(out)
... return out
>>> model = Model()
>>> model.float()
Model(
(linear): Linear(in_features=1, out_features=1, dtype=paddle.float32)
(dropout): Dropout(p=0.5, axis=None, mode=upscale_in_train, inplace=False)
)
'''
excluded_layers = [] if excluded_layers is None else excluded_layers
if isinstance(excluded_layers, type):
excluded_layers = [excluded_layers]
elif isinstance(excluded_layers, (list, tuple)):
excluded_layers = list(excluded_layers)
else:
raise TypeError(
f"excluded_layers should be type nn.Layer or list, but got {type(excluded_layers).__name__}.",
)
def layer_trans(layer):
_layer_trans_dtype(layer, paddle.float32, excluded_layers)
return self.apply(layer_trans)
def float16(
self, excluded_layers: Layer | Sequence[Layer] | None = None
) -> Self:
'''
Casts all floating point parameters and buffers to ``float16`` data type.
.. note::
``nn.BatchNorm`` does not support ``bfloat16`` weights, so it would not be converted by default.
Parameters:
excluded_layers(nn.Layer|list|tuple|None, optional): Specify the layers that need to be kept original data type. if excluded_layers is None, casts all floating point parameters and buffers except ``nn.BatchNorm``. Default: None.
Returns:
Layer: self
Examples:
.. code-block:: pycon
>>> # doctest: +SKIP('Paddle compiled by the user does not support float16, so keep original data type.')
>>> import paddle
>>> class Model(paddle.nn.Layer):
... def __init__(self):
... super().__init__()
... self.linear = paddle.nn.Linear(1, 1)
... self.dropout = paddle.nn.Dropout(p=0.5)
...
... def forward(self, input):
... out = self.linear(input)
... out = self.dropout(out)
... return out
>>> model = Model()
>>> model.float16()
Model(
(linear): Linear(in_features=1, out_features=1, dtype=float32)
(dropout): Dropout(p=0.5, axis=None, mode=upscale_in_train)
)
'''
if paddle.amp.is_float16_supported() is False:
warnings.warn(
"Paddle compiled by the user does not support float16, so keep original data type."
)
return self
excluded_layers = (
[nn.BatchNorm] if excluded_layers is None else excluded_layers
)
if isinstance(excluded_layers, type):
excluded_layers = [excluded_layers]
elif isinstance(excluded_layers, (list, tuple)):
excluded_layers = list(excluded_layers)
else:
raise TypeError(
f"excluded_layers should be type nn.Layer or list, but got {type(excluded_layers).__name__}.",
)
def layer_trans(layer):
_layer_trans_dtype(layer, paddle.float16, excluded_layers)
return self.apply(layer_trans)
def bfloat16(
self, excluded_layers: Layer | Sequence[Layer] | None = None
) -> Self:
'''
Casts all floating point parameters and buffers to ``bfloat16`` data type.
.. note::
``nn.BatchNorm`` does not support ``bfloat16`` weights, so it would not be converted by default.
Parameters:
excluded_layers(nn.Layer|list|tuple|None, optional): Specify the layers that need to be kept original data type. if excluded_layers is None, casts all floating point parameters and buffers except ``nn.BatchNorm``. Default: None.
Returns:
Layer: self
Examples:
.. code-block:: pycon
>>> # doctest: +SKIP('bfloat need V100 compile')
>>> import paddle
>>> class Model(paddle.nn.Layer):
... def __init__(self):
... super().__init__()
... self.linear = paddle.nn.Linear(1, 1)
... self.dropout = paddle.nn.Dropout(p=0.5)
...
... def forward(self, input):
... out = self.linear(input)
... out = self.dropout(out)
... return out
>>> model = Model()
>>> model.bfloat16()
>>> # UserWarning: Paddle compiled by the user does not support bfloat16, so keep original data type.
Model(
(linear): Linear(in_features=1, out_features=1, dtype=float32)
(dropout): Dropout(p=0.5, axis=None, mode=upscale_in_train)
)
'''
if paddle.amp.is_bfloat16_supported() is False:
warnings.warn(
"Paddle compiled by the user does not support bfloat16, so keep original data type."
)
return self
excluded_layers = (
[nn.BatchNorm] if excluded_layers is None else excluded_layers
)
if isinstance(excluded_layers, type):
excluded_layers = [excluded_layers]
elif isinstance(excluded_layers, (list, tuple)):
excluded_layers = list(excluded_layers)
else:
raise TypeError(
f"excluded_layers should be type nn.Layer or list, but got {type(excluded_layers).__name__}.",
)
def layer_trans(layer):
_layer_trans_dtype(layer, paddle.bfloat16, excluded_layers)
return self.apply(layer_trans)
def cuda(self, device: int | PlaceLike | None = None) -> Self:
"""
Move all model parameters and buffers to the GPU.
This also makes associated parameters and buffers different objects. So
it should be called before constructing the optimizer if the layer will
live on GPU while being optimized.
Parameters:
device(int, optional): if specified, all parameters will be copied to that device.
Returns:
Layer: self
"""
if device is None:
device = paddle.CUDAPlace(paddle.cuda.current_device())
elif isinstance(device, int):
device = paddle.CUDAPlace(device)
elif isinstance(device, paddle.CUDAPlace):
pass
else:
raise TypeError(
f"device must be int, paddle.CUDAPlace or None, got {type(device)}"
)
return self._to_impl(device=device)
def xpu(self, device: int | PlaceLike | None = None) -> Self:
"""
Move all model parameters and buffers to the XPU.
This also makes associated parameters and buffers different objects. So
it should be called before constructing optimizer if the layer will
live on XPU while being optimized.
Parameters:
device(int, optional): if specified, all parameters will be copied to that device.
Returns:
Layer: self
"""
if device is None:
device = paddle.XPUPlace(0)
elif isinstance(device, int):
device = paddle.XPUPlace(device)
elif isinstance(device, paddle.XPUPlace):
pass
else:
raise TypeError(
f"device must be int, paddle.XPUPlace or None, got {type(device)}"
)
return self._to_impl(device=device)
def cpu(self) -> Self:
"""
Move all model parameters and buffers to the CPU.
Returns:
Layer: self
"""
return self._to_impl(device=paddle.CPUPlace())
def get_extra_state(self) -> Any:
raise RuntimeError(
"Reached a code path in Module.get_extra_state() that should never be called. "
)
def set_extra_state(self, state: Any) -> None:
raise RuntimeError(
"Reached a code path in Module.set_extra_state() that should never be called. "
)
def requires_grad_(self, requires_grad: bool = True) -> Self:
"""
Change if autograd should record operations on parameters in this layer.
Parameters:
requires_grad (bool): whether autograd should record operations on
parameters in this layer. Default: ``True``.
Returns:
Layer: self
"""
for p in self.parameters():
p.stop_gradient = not requires_grad
return self
def zero_grad(self, set_to_none: bool = True) -> None:
"""
Reset gradients of all model parameters.
Parameters:
set_to_none (bool): instead of setting to zero, set the grads to None. Currently, set_to_none=True
is not fully supported.
"""
for p in self.parameters():
if p.grad is not None:
p.clear_gradient(not set_to_none)
def to_empty(
self, device: PlaceLike | None = None, recurse: bool = True
) -> Self:
"""
Move the parameters and buffers to the specified device without copying storage.
Re-creates the parameters and buffers as empty tensors on the target device.
Args:
device (PlaceLike, optional): The device to move parameters and buffers to.
If None, the current device is used. Default: None.
recurse (bool, optional): Whether to recursively process sublayers.
Default: True.
Returns:
Layer: self
"""
if recurse:
for layer in self.children():
layer.to_empty(device, recurse=True)
for key, param in self._parameters.items():
if param is not None:
with no_grad():
empty_param = paddle.empty_like(param, device=device)
param._set_impl(empty_param)
for key, buf in self._buffers.items():
if buf is not None:
self._buffers[key] = paddle.empty_like(buf, device=device)
return self
def _get_name(self):
return self.__class__.__name__