Short answer
TL;DR: Technically Theoretically, convolutional neural networks (CNNs) can either perform the cross-correlation or convolution: it does not really matter whether youthey perform the cross-correlation or convolution because the kernels are learnable, so they can adapt to the cross-correlation or convolution given the data, although, in the typical diagrams, CNNs are shownare shown to perform the cross-correlation because (probably because it is conceptually simpler and it is possible thatin libraries like TensorFlow) they are implementedtypically implemented with cross-correlations (and cross-correlations are conceptually simpler than convolutions)!. Moreover, in general, the kernels can or not be symmetric (although they typically won't be symmetric). In the case they are symmetric, the cross-correlation is equal to the convolution.
Long answer
To understand the answer to this question, I will provide two examples that show the similarities and differences between the convolution and cross-correlation operations. I will focus on the convolution and cross-correlation applied to 1-dimensional discrete and finite signals (which is the simplest case to which these operations can be applied). I will only talk about convolutions because, essentially, CNNs process finite and cross-correlations for discrete and finite signals because that's the case of CNNs(although typically higher-dimensional ones, but this answer applies to higher-dimensional signals too). Moreover, in this answer, I will assume that you are at least familiar with how the convolution (or cross-correlation?) in a CNN is performed, so that I do not have to explain these operations in detail (otherwise this answer would be even longer).
What is the convolution and cross-correlation?
Anyway, let me first remind you of the intuitive definition ofBoth the convolution and the cross-correlation. Both of these operations can be thought ofare defined as the dot product between a small vectormatrix and different parts of another typically bigger vectormatrix (in the case of CNNs, it is an image or a feature map). Here's the usual illustration (which is applicable to both the convolution and cross-correlation).
Example 1
To be more concrete, let's suppose that we have the output of a function (or signal) $f$ grouped in a vectormatrix $$f = [2, 1, 3, 5, 4] \in \mathbb{R}^{1 \times 5},$$ and the output of a kernel function also grouped in another vectormatrix $$h=[1, -1] \in \mathbb{R}^{1 \times 2}.$$ For simplicity, let's assume that we do not pad the input signal and we perform the convolution and cross-correlation with a stride of 1 (I assume that you are familiar with the concepts of padding and stride).
Convolution
\begin{align}
f \circledast h = g_1
&=\\
[(-1)*2 + 1*1, (-1)*1 + 1*3, (-1)*3 + 1*5, (-1)*5+1*4]
&=\\
[-2 + 1, -2 + 3, -3 + 5, -5 + 4]
&=\\
[-1, 1, 2, -1] \in \mathbb{R}^{1 \times 4}
\end{align}\begin{align}
f \circledast h = g_1
&=\\
[(-1)*2 + 1*1, \\
(-1)*1 + 1*3, \\
(-1)*3 + 1*5, \\
(-1)*5+1*4]
&=\\
[-2 + 1, -1 + 3, -3 + 5, -5 + 4]
&=\\
[-1, 2, 2, -1] \in \mathbb{R}^{1 \times 4}
\end{align}
Cross-correlation
In the case ofSimilarly, the cross-correlation of $f$ with $h$, denoted as $f \otimes h = g_2$, where $\otimes$ is the cross-correlation operator, the only difference is that we doalso defined as a dot product between not flip$h$ and different parts of $f$, but without flipping the elements of the kernel before applying the element-wise multiplications, that is
\begin{align}
f \otimes h = g_2
&=\\
[1*2 + (-1)*1, 1*1 + (-1)*3, 1*3 + (-1)*5, 1*5 + (-1)*4]
&=\\
[2 - 1, 1 - 3, 3 - 5, 5 - 4]
&=\\
[1, -2, -2, 1] \in \mathbb{R}^{1 \times 4}
\end{align}\begin{align}
f \otimes h = g_2
&=\\
[1*2 + (-1)*1, \\
1*1 + (-1)*3, \\
1*3 + (-1)*5, \\
1*5 + (-1)*4]
&=\\
[2 - 1, 1 - 3, 3 - 5, 5 - 4]
&=\\
[1, -2, -2, 1] \in \mathbb{R}^{1 \times 4}
\end{align}
The first thing to note is that in both cases the result is a $1 \times 4$ vector. If we had convolved $f$ with a $1 \times 1$ vector, the result would have been a $1 \times 5$ vector. Recall that we assumed no padding (i.e. we don't add dummy elements to the left or right borders of $f$) and stride 1 (i.e. we shift the kernel to the right one element at a time). Similarly, if we had convolved $f$ with a $1 \times 3$, the result would have been a $1 \times 3$ vector.
Notes
The second thing to note is that $g_1$ and $g_2$ are different, so the result of the convolution is generally different than the result of the cross-correlation, given the same signals and kernels (as you probably have suspected?).
The only difference between the convolution and cross-correlation operations is that, in the first case, the kernel is flipped (along all spatial dimensions) before being applied.
In both cases, the result is a $1 \times 4$ vector. If we had convolved $f$ with a $1 \times 1$ vector, the result would have been a $1 \times 5$ vector. Recall that we assumed no padding (i.e. we don't add dummy elements to the left or right borders of $f$) and stride 1 (i.e. we shift the kernel to the right one element at a time). Similarly, if we had convolved $f$ with a $1 \times 3$, the result would have been a $1 \times 3$ vector (as you will see from the next example).
The results of the convolution and cross-correlation, $g_1$ and $g_2$, are different. Specifically, one is the negated version of the other. So, the result of the convolution is generally different than the result of the cross-correlation, given the same signals and kernels (as you might have suspected).
The third thing to note is that the only difference between the convolution and cross-correlation operations is that, in the first case, the kernel is flipped (along all spatial dimensions) before being applied.
Example 2: symmetric kernel
CNNs have learnable kernels
Do libraries implement the convolution or correlation?
In practice, certain libraries provide functions to compute both convolution and cross-correlation. For example, NumPy provides both the functions convolve
and correlate
to compute both the convolution and cross-correlation, respectively. If you execute the following piece of code (AfterPython 3.7), you will get results that are consistent with my explanations above.
import numpy as np
f = np.array([2., 1., 3., 5., 4.])
h = np.array([1., -1.])
h2 = np.array([-1., 2., 1.])
g1 = np.convolve(f, h, mode="valid")
g2 = np.correlate(f, h, mode="valid")
print("g1 =", g1) # g1 = [-1. 2. 2. -1.]
print("g2 =", g2) # g2 = [ 1. -2. -2. 1.]
However, NumPy is not really a library that provides out-of-the-box functionality to build CNNs.
On the other hand, TensorFlow's and PyTorch's functions to build the convolutional layers actually perform cross-correlations. As I said above, although it does not really matter whether CNNs perform the convolution or cross-correlation, this naming is misleading. Here's a proof that TensorFlow's tf.nn.conv1d
actually implements the cross-correlation.
import tensorflow as tf # TensorFlow 2.2
f = tf.constant([2., 1., 3., 5., 4.], dtype=tf.float32)
h = tf.constant([1., -1.], dtype=tf.float32)
# Reshaping the inputs because conv1d accepts only certain shapes.
f = tf.reshape(f, [1, int(f.shape[0]), 1])
h = tf.reshape(h, [int(h.shape[0]), 1, 1])
g = tf.nn.conv1d(f, h, stride=1, padding="VALID")
print("g =", g) # [1, -2, -2, 1]
Further reading
After having written this answer, I found the article Convolution vs. Cross-Correlation (2019) by Rachel Draelos, which essentially says the same thing that I am saying here, but provides more details and examples).