Deep Neural Network Library (DNNL)  1.3.0
Performance library for Deep Learning
Convolution

API Reference

The convolution primitive computes forward, backward, or weight update for a batched convolution operation on 1D, 2D, or 3D spatial data with bias.

The convolution operation is defined by the following formulas. We show formulas only for 2D spatial data which are straightforward to generalize to cases of higher and lower dimensions. Variable names follow the standard Naming Conventions.

Let \(\src\), \(\weights\) and \(\dst\) be \(N \times IC \times IH \times IW\), \(OC \times IC \times KH \times KW\), and \(N \times OC \times OH \times OW\) tensors respectively. Let \(\bias\) be a 1D tensor with \(OC\) elements.

Furthermore, let the remaining convolution parameters be:

Parameter Depth Height Width Comment
Padding:
Front, top, and left
\(PD_L\) \(PH_L\) \(PW_L\) In the API we use padding_l to indicate the corresponding vector of paddings (_l in the name stands for left)
Padding:
Back, bottom, and right
\(PD_R\) \(PH_R\) \(PW_R\) In the API we use padding_r to indicate the corresponding vector of paddings (_r in the name stands for right)
Stride \(SD\) \(SH\) \(SW\) Non-strided convolution should have the stride parameters equal 1
Dilation \(DD\) \(DH\) \(DW\) Dilation starts with 0, so non-dilated convolution should have the dilation parameters equal 0

The following formulas show how DNNL computes convolutions. They are broken down into several types to simplify the exposition, but in reality the convolution types can be combined.

To further simplify the formulas, we assume that \(\src(n, ic, ih, iw) = 0\) if \(ih < 0\), or \(ih \geq IH\), or \(iw < 0\), or \(iw \geq IW\).

Forward

Regular Convolution

\[\dst(n, oc, oh, ow) = \bias(oc) + \\ + \sum_{ic=0}^{IC-1}\sum_{kh=0}^{KH-1}\sum_{kw=0}^{KW-1} \src(n, ic, oh \cdot SH + kh - PH_L, ow \cdot SW + kw - PW_L) \cdot \weights(oc, ic, kh, kw).\]

Here:

Convolution with Groups

In the API, DNNL adds a separate groups dimension to memory objects representing \(\weights\) tensors and represents weights as \(G \times OC_G \times IC_G \times KH \times KW \) 5D tensors for 2D convolutions with groups.

\[ \dst(n, g \cdot OC_G + oc_g, oh, ow) = \bias(g \cdot OC_G + oc_g) + \\ + \sum_{ic_g=0}^{IC_G-1}\sum_{kh=0}^{KH-1}\sum_{kw=0}^{KW-1} \src(n, g \cdot IC_G + ic_g, oh + kh - PH_L, ow + kw - PW_L) \cdot \weights(g, oc_g, ic_g, kh, kw), \]

where

The case when \(OC_G = IC_G = 1\) is also known as a depthwise convolution.

Convolution with Dilation

\[ \dst(n, oc, oh, ow) = \bias(oc) + \\ + \sum_{ic=0}^{IC-1}\sum_{kh=0}^{KH-1}\sum_{kw=0}^{KW-1} \src(n, ic, oh + kh \cdot (DH + 1) - PH_L, ow + kw \cdot (DW + 1) - PW_L) \cdot \weights(oc, ic, kh, kw). \]

Here:

Note
In DNNL dilation parameter equals 0 corresponds to non-dilated, i.e. regular, convolution. Other libraries might use another convention, where dilation parameter equals 1 corresponds to regular convolution.

Deconvolution (Transposed Convolution)

Deconvolutions (also called fractionally strided convolutions or transposed convolutions) work by swapping the forward and backward passes of a convolution. One way to put it is to note that the weights define a convolution, but whether it is a direct convolution or a transposed convolution is determined by how the forward and backward passes are computed.

Difference Between Forward Training and Forward Inference

There is no difference between the dnnl_forward_training and dnnl_forward_inference propagation kinds.

Backward

The backward propagation computes \(\diffsrc\) based on \(\diffdst\) and \(\weights\).

The weights update computes \(\diffweights\) and \(\diffbias\) based on \(\diffdst\) and \(\src\).

Note
The optimized memory formats \(\src\) and \(\weights\) might be different on forward propagation, backward propagation, and weights update.

Execution Arguments

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
\(\weights\) DNNL_ARG_WEIGHTS
\(\bias\) DNNL_ARG_BIAS
\(\dst\) DNNL_ARG_DST
\(\diffsrc\) DNNL_ARG_DIFF_SRC
\(\diffweights\) DNNL_ARG_DIFF_WEIGHTS
\(\diffbias\) DNNL_ARG_DIFF_BIAS
\(\diffdst\) DNNL_ARG_DIFF_DST
\(depthwise\) DNNL_ARG_ATTR_POST_OP_DW

Implementation Details

General Notes

N/A.

Data Types

Convolution primitive supports the following combination of data types for source, destination, and weights memory objects:

Propagation Source Weights Destination Bias
forward / backward f32 f32 f32 f32
forward f16 f16 f16 f16
forward u8, s8 s8 u8, s8, s32, f32 u8, s8, s32, f32
forward bf16 bf16 f32, bf16 f32, bf16
backward f32, bf16 bf16 bf16
weights update bf16 f32, bf16 bf16 f32, bf16
Warning
There might be hardware and/or implementation specific restrictions. Check Implementation Limitations section below.

Data Representation

Like other CNN primitives, the convolution primitive expects the following tensors:

Spatial Source / Destination Wei
1D \(N \times C \times W\) \([G \times ] OC \times IC \times KW\)
2D \(N \times C \times H \times W\) \([G \times ] OC \times IC \times KH \times KW\)
3D \(N \times C \times D \times H \times W\) \([G \times ] OC \times IC \times KD \times KH \times KW\)

Physical format of data and weights memory objects is critical for convolution primitive performance. In the DNNL programming model, convolution is one of the few primitives that support the placeholder memory format tag dnnl::memory::format_tag::any (shortened to any from now on) and can define data and weight memory objects format based on the primitive parameters. When using any it is necessary to first create a convolution primitive descriptor and then query it for the actual data and weight memory objects formats.

While convolution primitives can be created with memory formats specified explicitly, the performance is likely to be suboptimal.

The table below shows the combinations for which plain memory formats the convolution primitive is optimized for.

Spatial Convolution Type Data / Weights logical tensor Imp
1D, 2D, 3D any optimized
1D f32, bf16 NCW / OIW, GOIW dnnl_ncw (dnnl_abc) / dnnl_oiw (dnnl_abc), dnnl_goiw (dnnl_abcd)
1D int8 NCW / OIW dnnl_nwc (dnnl_acb) / dnnl_wio (dnnl_cba)
2D f32, bf16 NCHW / OIHW, GOIHW dnnl_nchw (dnnl_abcd) / dnnl_oihw (dnnl_abcd), dnnl_goihw (dnnl_abcde)
2D int8 NCHW / OIHW, GOIHW dnnl_nhwc (dnnl_acdb) / dnnl_hwio (dnnl_cdba), dnnl_hwigo (dnnl_decab)
3D f32, bf16 NCDHW / OIDHW, GOIDHW dnnl_ncdhw (dnnl_abcde) / dnnl_oidhw (dnnl_abcde), dnnl_goidhw (dnnl_abcdef)
3D int8 NCDHW / OIDHW dnnl_ndhwc (dnnl_acdeb) / dnnl_dhwio (dnnl_cdeba)

Post-ops and Attributes

Post-ops and attributes enable you to modify the behavior of the convolution primitive by applying the output scale to the result of the primitive and by chaining certain operations after the primitive. The following attributes and post-ops are supported:

Propagation Type Operation Description Restrictions
forward attribute Output scale Scales the result of convolution by given scale factor(s) int8 convolutions only
forward post-op eltwise Applies an Eltwise operation to the result
forward post-op sum Adds the operation result to the destination tensor instead of overwriting it
Note
The library doesn't prevent using post-ops in training, but note that not all post-ops are feasible for training usage. For instance, using ReLU with non-zero negative slope parameter as a post-op would not produce an additional output workspace that is required to compute backward propagation correctly. Hence, in this particular case one should use separate convolution and eltwise primitives for training.

The following post-ops chaining is supported by the library:

Type of convolutions Pos
f32 and bf16 convolution eltwise, sum, sum -> eltwise
int8 convolution eltwise, sum, sum -> eltwise, eltwise -> sum

The attributes and post-ops take effect in the following sequence:

The operations during attributes and post-ops applying are done in single precision floating point data type. The conversion to the actual destination data type happens just before the actual storing.

Example 1

Consider the following pseudo code:

attribute attr;
attr.set_output_scale(alpha);
attr.set_post_ops({
{ sum={scale=beta} },
{ eltwise={scale=gamma, type=tanh, alpha=ignore, beta=ignored }
});
convolution_forward(src, weights, dst, attr)

The would lead to the following:

\[ \dst(\overline{x}) = \gamma \cdot \tanh \left( \alpha \cdot conv(\src, \weights) + \beta \cdot \dst(\overline{x}) \right) \]

Example 2

The following pseudo code:

attribute attr;
attr.set_output_scale(alpha);
attr.set_post_ops({
{ eltwise={scale=gamma, type=relu, alpha=eta, beta=ignored }
{ sum={scale=beta} },
});
convolution_forward(src, weights, dst, attr)

That would lead to the following:

\[ \dst(\overline{x}) = \beta \cdot \dst(\overline{x}) + \gamma \cdot ReLU \left( \alpha \cdot conv(\src, \weights), \eta \right) \]

Algorithms

DNNL implements convolution primitives using several different algorithms:

Direct Algorithm

DNNL supports the direct convolution algorithm on all supported platforms for the following conditions:

In case any of these constraints are not met, the implementation will silently fall back to an explicit GEMM algorithm.

Winograd Convolution

DNNL supports the Winograd convolution algorithm on systems with Intel(R) AVX-512 support and above under the following conditions:

In case any of these constraints is not met, the implementation will silently fall back to the direct algorithm.

The Winograd convolution algorithm implementation additionally chooses tile size based on the problem shape and propagation kind:

The following side effects should be weighed against the (potential) performance boost achieved from using the Winograd algorithm:

Create a Winograd convolution by simply creating a convolution descriptor (step 6 in simple network example specifying the Winograd algorithm. The rest of the steps are exactly the same.

auto conv1_desc = convolution_forward::desc(
prop_kind::forward_inference, algorithm::convolution_winograd,
conv1_src_md, conv1_weights_md, conv1_bias_md, conv1_dst_md,
conv1_strides, conv1_padding_l, conv1_padding_r);

Automatic Algorithm Selection

DNNL supports dnnl::algorithm::convolution_auto algorithm that instructs the library to automatically select the best algorithm based on the heuristics that take into account tensor shapes and the number of logical processors available. (For automatic selection to work as intended, use the same thread affinity settings when creating the convolution as when executing the convolution.)

Implementation Limitations

  1. Refer to Data Types for limitations related to data types support.
  2. CPU
    • Winograd are implemented only for Intel(R) AVX-512 or Intel(R) AVX512-DL Boost instruction sets
  3. GPU
    • No support for Winograd algorithm

Performance Tips