oneAPI Deep Neural Network Library (oneDNN)  1.4.0
Performance library for Deep Learning
Loading...
Searching...
No Matches
Layer Normalization

API Reference

The layer normalization primitive performs a forward or backward layer normalization operation on a 2-5D data tensor.

The layer normalization operation performs normalization over the last logical axis of the data tensor and is defined by the following formulas. We show formulas only for 3D data, which are straightforward to generalize to cases of higher dimensions. Variable names follow the standard Naming Conventions.

Forward

\[ \dst(t, n, c) = \gamma(c) \cdot \frac{\src(t, n, c) - \mu(t, n)} {\sqrt{\sigma^2(t, n) + \varepsilon}} + \beta(c), \]

where

  • \(\gamma(c), \beta(c)\) are optional scale and shift for a channel (see dnnl_use_scaleshift flag),
  • \(\mu(t, n), \sigma^2(t, n)\) are mean and variance (see dnnl_use_global_stats flag), and
  • \(\varepsilon\) is a constant to improve numerical stability.

Mean and variance are computed at runtime or provided by a user. When mean and variance are computed at runtime, the following formulas are used:

  • \(\mu(t, n) = \frac{1}{C} \sum\limits_{c} \)\src\f$(t, n, c)_{} \(, - \)\sigma^2(t, n) = \frac{1}{C} \sum\limits_{c} {}_{} ( \(\src\)(t, n, c) - \mu(t, n))^2 \(. The \)\gamma(c) \( and \)\beta(c) \( tensors are considered learnable. @subsubsection autotoc_md183 Difference Between Forward Training and Forward Inference * If mean and variance are computed at runtime (i.e., #dnnl_use_global_stats is not set), they become outputs for the propagation kind #dnnl_forward_training (because they would be required during the backward propagation). Data layout for mean and variance must be specified during initialization of the layer normalization descriptor by passing the memory descriptor for statistics (e.g., by passing stat_desc in dnnl::layer_normalization_forward::desc::desc()). Mean and variance are not exposed for the propagation kind #dnnl_forward_inference. @subsection autotoc_md184 Backward The backward propagation computes \) \(\diffsrc\)(t, n, c) \(, \) \(\diffgamma\)(c)^* \(, and \) \(\diffbeta\)(c)^* \(based on \) \(\diffdst\)(t, n, c) \(, \)src(t, n, c) \(, \)\mu(t, n) \(, \)\sigma^2(t, n) \(, \)\gamma(c) ^* \(, and \)\beta(c) ^* \(. The tensors marked with an asterisk are used only when the primitive is configured to use \)\gamma(c) \(, and \)\beta(c) \( (i.e., #dnnl_use_scaleshift is set). @section autotoc_md185 Execution Arguments Depending on the @ref dnnl_normalization_flags_t "flags" and @ref dnnl_prop_kind_t "propagation kind", the layer normalization primitive requires different inputs and outputs. For clarity, a summary is shown below. <table class="markdownTable"> <tr class="markdownTableHead"> <th class="markdownTableHeadLeft"> \ilinebr </th> <th class="markdownTableHeadLeft"> #dnnl_forward_inference \ilinebr </th> <th class="markdownTableHeadLeft"> #dnnl_forward_training \ilinebr </th> <th class="markdownTableHeadLeft"> #dnnl_backward \ilinebr </th> <th class="markdownTableHeadLeft"> #dnnl_backward_data \ilinebr </th> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyLeft"> #dnnl_normalization_flags_none \ilinebr </td> <td class="markdownTableBodyLeft"> <em>Inputs</em>: \)\src\f$

    Outputs: \(\dst\)

Inputs: \(\src\)

Outputs: \(\dst\), \(\mu\), \(\sigma^2\)

Inputs: \(\diffdst\), \(\src\), \(\mu\), \(\sigma^2\)

Outputs: \(\diffsrc\)

Inputs: \(\diffdst\), \(\src\), \(\mu\), \(\sigma^2\)

Outputs: \(\diffsrc\)

dnnl_use_global_stats

Inputs: \(\src\), \(\mu\), \(\sigma^2\)

Outputs: \(\dst\)

Inputs: \(\src\), \(\mu\), \(\sigma^2\)

Outputs: \(\dst\)

Inputs: \(\diffdst\), \(\src\), \(\mu\), \(\sigma^2\)

Outputs: \(\diffsrc\)

Inputs: \(\diffdst\), \(\src\), \(\mu\), \(\sigma^2\)

Outputs: \(\diffsrc\)

dnnl_use_scaleshift

Inputs: \(\src\), \(\gamma\), \(\beta\)

Outputs: \(\dst\)

Inputs: \(\src\), \(\gamma\), \(\beta\)

Outputs: \(\dst\), \(\mu\), \(\sigma^2\)

Inputs: \(\diffdst\), \(\src\), \(\mu\), \(\sigma^2\), \(\gamma\), \(\beta\)

Outputs: \(\diffsrc\), \(\diffgamma\), \(\diffbeta\)

Inputs: \(\diffdst\), \(\src\), \(\mu\), \(\sigma^2\), \(\gamma\), \(\beta\)

Outputs: \(\diffsrc\)

dnnl_use_global_stats | dnnl_use_scaleshift

Inputs: \(\src\), \(\mu\), \(\sigma^2\), \(\gamma\), \(\beta\)

Outputs: \(\dst\)

Inputs: \(\src\), \(\mu\), \(\sigma^2\), \(\gamma\), \(\beta\)

Outputs: \(\dst\)

Inputs: \(\diffdst\), \(\src\), \(\mu\), \(\sigma^2\), \(\gamma\), \(\beta\)

Outputs: \(\diffsrc\), \(\diffgamma\), \(\diffbeta\)

Inputs: \(\diffdst\), \(\src\), \(\mu\), \(\sigma^2\), \(\gamma\), \(\beta\)

Outputs: \(\diffsrc\)

When executed, the inputs and outputs should be mapped to an execution argument index as specified by the following table.

Primitive input/output Execution argument index
\(\src\) DNNL_ARG_SRC
\(\gamma, \beta\) DNNL_ARG_SCALE_SHIFT
mean ( \(\mu\)) DNNL_ARG_MEAN
variance ( \(\sigma\)) DNNL_ARG_VARIANCE
\(\dst\) DNNL_ARG_DST
\(\diffdst\) DNNL_ARG_DIFF_DST
\(\diffsrc\) DNNL_ARG_DIFF_SRC
\(\diffgamma\), \(\diffbeta\) DNNL_ARG_DIFF_SCALE_SHIFT

Implementation Details

General Notes

  1. The different flavors of the primitive are partially controlled by the flags parameter that is passed to the operation descriptor initialization function (e.g., dnnl::layer_normalization_forward::desc::desc()). Multiple flags can be set using the bitwise OR operator (|).
  2. For forward propagation, the mean and variance might be either computed at runtime (in which case they are outputs of the primitive) or provided by a user (in which case they are inputs). In the latter case, a user must set the dnnl_use_global_stats flag. For the backward propagation, the mean and variance are always input parameters.
  3. The memory format and data type for src and dst are assumed to be the same, and in the API they are typically referred to as data (e.g., see data_desc in dnnl::layer_normalization_forward::desc::desc()). The same is true for diff_src and diff_dst. The corresponding memory descriptors are referred to as diff_data_desc.
  4. Both forward and backward propagation support in-place operations, meaning that src can be used as input and output for forward propagation, and diff_dst can be used as input and output for backward propagation. In case of an in-place operation, the original data will be overwritten.

Data Type Support

The operation supports the following combinations of data types:

Propagation Source / Destination Mean / Variance / ScaleShift
forward / backward f32 f32
forward f16 f32

Data Representation

Mean and Variance

The mean ( \(\mu\)) and variance ( \(\sigma^2\)) are separate tensors with number of dimensions equal to ( \(data\_ndims - 1\)) and size \((data\_dim[0], data\_dim[1], ..., data\_dim[ndims - 2])\).

The corresponding memory object can have an arbitrary memory format. Unless mean and variance are computed at runtime and not exposed (i.e., propagation kind is dnnl_forward_inference and dnnl_use_global_stats is not set), the user should provide a memory descriptor for statistics when initializing the layer normalization descriptor. For best performance, it is advised to use the memory format that follows the data memory format; i.e., if the data format is dnnl_tnc, the best performance can be expected for statistics with the dnnl_tn format and suboptimal for statistics with the dnnl_nt format.

Scale and Shift

If used, the scale ( \(\gamma\)) and shift ( \(\beta\)) are combined in a single 2D tensor of shape \(2 \times C\).

The format of the corresponding memory object must be dnnl_nc (dnnl_ab).

Source, Destination, and Their Gradients

The layer normalization primitive works with an arbitrary data tensor; however, it was designed for RNN data tensors (i.e., dnnl_nc, dnnl_tnc, dnnl_ldnc). Unlike CNN data tensors, RNN data tensors have a single feature dimension. Layer normalization performs normalization over the last logical dimension (feature dimension for RNN tensors) across non-feature dimensions.

The layer normalization primitive is optimized for the following memory formats:

Logical tensor Implementations optimized for memory formats
NC dnnl_nc (dnnl_ab)
TNC dnnl_tnc (dnnl_abc), dnnl_ntc (dnnl_bac)
LDNC dnnl_ldnc (dnnl_abcd)

Performance Tips

  1. For data tensors (src, dst, diff_src, diff_dst), use memory formats for which the last logical axis is the last in the physical memory layout.
  2. For mean/variance, use the memory format that follows the data memory format; i.e., if the data format is dnnl_tnc, the best performance can be expected for statistics with dnnl_tn and suboptimal for statistics with the dnnl_nt format.
  3. For backward propagation, use the same memory format for src, diff_dst, and diff_src (the format of diff_dst and diff_src are always the same because of the API). Different formats are functionally supported but lead to highly suboptimal performance.
  4. Use in-place operations whenever possible.

Examples

Engine Name Comments
CPU/GPU Layer Normalization Primitive Example

This C++ API example demonstrates how to create and execute a Layer normalization primitive in forward propagation mode.

Key optimizations included in this example:

  • In-place primitive execution;
  • Creation of memory objects using the primitive descriptor.