<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>divergences</title>
<link>https://divergences.xyz/tag/pytorch.html</link>
<atom:link href="https://divergences.xyz/tag/pytorch.xml" rel="self" type="application/rss+xml"/>
<description>A blog by Sigrid Keydana. Opinions are exclusively mine.</description>
<generator>quarto-1.10.19</generator>
<lastBuildDate>Tue, 09 May 2023 00:00:00 GMT</lastBuildDate>
<item>
  <title>Group-equivariant neural networks with escnn</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/group-equivariant-cnn-3/</link>
  <description><![CDATA[ 






<p>Today, we resume our exploration of group equivariance. This is the third post in the series. The <a href="https://blogs.rstudio.com/ai/posts/2023-03-09-group-equivariant-cnn-1/">first</a> was a high-level introduction: what this is all about; how equivariance is operationalized; and why it is of relevance to many deep-learning applications. The <a href="https://blogs.rstudio.com/ai/posts/2023-03-27-group-equivariant-cnn-2/">second</a> sought to concretize the key ideas by developing a group-equivariant CNN from scratch. That being instructive, but too tedious for practical use, today we look at a carefully designed, highly-performant <a href="https://github.com/QUVA-Lab/escnn">library</a> that hides the technicalities and enables a convenient workflow.</p>
<p>First though, let me again set the context. In physics, an all-important concept is that of symmetry<sup>1</sup>, a symmetry being present whenever some quantity is being conserved. But we don’t even need to look to science. Examples arise in daily life, and – otherwise why write about it - in the tasks we apply deep learning to.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;This is nicely explained in, for example, Jakob Schwichtenberg’s <a href="http://physicsfromsymmetry.com/">Physics from symmetry</a>.</p></div></div><p>In daily life: Think about speech – me stating “it is cold”, for example. Formally, or denotation-wise, the sentence will have the same meaning now as in five hours. (Connotations, on the other hand, can and will probably be different!). This is a form of translation symmetry, translation in time.</p>
<p>In deep learning: Take image classification. For the usual convolutional neural network, a cat in the center of the image is just that, a cat; a cat on the bottom is, too. But one sleeping, comfortably curled like a half-moon “open to the right”, will not be “the same” as one in a mirrored position. Of course, we can train the network to treat both as equivalent by providing training images of cats in both positions, but that is not a scaleable approach. Instead, we’d like to make the network aware of these symmetries, so they are automatically preserved throughout the network architecture.</p>
<section id="purpose-and-scope-of-this-post" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="purpose-and-scope-of-this-post">Purpose and scope of this post</h2>
<p>Here, I introduce <a href="https://github.com/QUVA-Lab/escnn"><code>escnn</code></a>, a PyTorch extension that implements forms of group equivariance for CNNs operating on the plane or in (3d) space. The library is used in various, amply illustrated research papers; it is appropriately documented; and it comes with introductory notebooks both relating the math and exercising the code. Why, then, not just refer to the <a href="https://github.com/QUVA-Lab/escnn/blob/master/examples/introduction.ipynb">first notebook</a>, and immediately start using it for some experiment?</p>
<p>In fact, this post should – as quite a few texts I’ve written – be regarded as an introduction to an introduction. To me, this topic seems anything but easy, for various reasons. Of course, there’s the math. But as so often in machine learning, you don’t need to go to great depths to be able to apply an algorithm correctly. So if not the math itself, what generates the difficulty? For me, it’s two things.</p>
<p>First, to map my understanding of the mathematical concepts to the terminology used in the library, and from there, to correct use and application. Expressed schematically: We have a concept A, which figures (among other concepts) in technical term (or object class) B. What does my understanding of A tell me about how object class B is to be used correctly? More importantly: How do I use it to best attain my goal C? This first difficulty I’ll address in a very pragmatic way. I’ll neither dwell on mathematical details, nor try to establish the links between A, B, and C in detail. Instead, I’ll present the characters<sup>2</sup> in this story by asking what they’re good for.</p>
<div class="no-row-height column-margin column-container"><div id="fn2"><p><sup>2</sup>&nbsp;If you have some background on representations: No, not <em>those</em> characters …</p></div><div id="fn3"><p><sup>3</sup>&nbsp;Yes, that word <a href="https://www.merriam-webster.com/dictionary/quaternity">exists</a>, although I must admit I didn’t know before typing it into a search engine.</p></div><div id="fn4"><p><sup>4</sup>&nbsp;One paper particularly stood out to me: <a href="https://arxiv.org/abs/2106.06020">Coordinate Independent Convolutional Networks – Isometry and Gauge Equivariant Convolutions on Riemannian Manifolds</a>.</p></div><div id="fn5"><p><sup>5</sup>&nbsp;Directly pertinent to today’s topic, thinking of the materials produced for University of Amsterdam’s <a href="https://uvagedl.github.io/">course on group-equivariant deep learning</a>.</p></div></div><p>Second – and this will be of relevance to just a subset of readers – the topic of group equivariance, particularly as applied to image processing, is one where visualizations can be of tremendous help. The quaternity<sup>3</sup> of conceptual explanation, math, code, and visualization can, together, produce an understanding of emergent-seeming quality… if, and only if, all of these explanation modes “work” for you. (Or if, in an area, a mode that does not wouldn’t contribute that much anyway.) Here, it so happens that from what I saw, several papers have excellent visualizations<sup>4</sup>, and the same holds for some lecture slides and accompanying notebooks<sup>5</sup>. But for those among us with limited spatial-imagination capabilities – e.g., people with <a href="https://en.wikipedia.org/wiki/Aphantasia">Aphantasia</a> – these illustrations, intended to help, can be very hard to make sense of themselves. If you’re not one of these, I totally recommend checking out the resources linked in the above footnotes. This text, though, will try to make the best possible use of verbal explanation to introduce the concepts involved, the library, and how to use it.</p>
<p>That said, let’s start with the software.</p>
</section>
<section id="using-escnn" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="using-escnn">Using <em>escnn</em></h2>
<p><code>Escnn</code> depends on PyTorch. Yes, PyTorch, not <code>torch</code>; unfortunately, the library hasn’t been ported to R yet.<sup>6</sup> For now, thus, we’ll employ <a href="https://rstudio.github.io/reticulate/"><code>reticulate</code></a><sup>7</sup> to access the Python objects directly.</p>
<div class="no-row-height column-margin column-container"><div id="fn6"><p><sup>6</sup>&nbsp;If, after reading this post, you feel that maybe <em>you</em> would be interested in porting it – I can definitely say I think that’s a great idea!</p></div><div id="fn7"><p><sup>7</sup>&nbsp;If you’re new to <code>reticulate</code>, please consult its excellent documentation on topics like <a href="https://rstudio.github.io/reticulate/articles/calling_python.html">calling Python from R</a>, <a href="https://rstudio.github.io/reticulate/articles/versions.html">determining the Python version used</a>, <a href="https://rstudio.github.io/reticulate/articles/python_packages.html">installing Python packages</a>, as well as an outstanding <a href="https://rstudio.github.io/reticulate/articles/python_primer.html">introduction to Python for R users</a>.</p></div></div><p>The way I’m doing this is install <code>escnn</code> in a virtual environment, with PyTorch version 1.13.1. As of this writing, Python 3.11 is not yet supported by one of <code>escnn</code>’s dependencies; the virtual environment thus builds on Python 3.10. As to the library itself, I am using the development version from GitHub, running <code>pip install git+https://github.com/QUVA-Lab/escnn</code>.</p>
<p>Once you’re ready, issue</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(reticulate)</span>
<span id="cb1-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Verify correct environment is used.</span></span>
<span id="cb1-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Different ways exist to ensure this; I've found most convenient to configure this on</span></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># a per-project basis in RStudio's project file (&lt;myproj&gt;.Rproj)</span></span>
<span id="cb1-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">py_config</span>()</span>
<span id="cb1-6"></span>
<span id="cb1-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># bind to required libraries and get handles to their namespaces</span></span>
<span id="cb1-8">torch <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">import</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"torch"</span>)</span>
<span id="cb1-9">escnn <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">import</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"escnn"</span>)</span></code></pre></div></div>
<p><code>Escnn</code> loaded, let me introduce its main objects and their roles in the play.</p>
</section>
<section id="spaces-groups-and-representations-escnngspaces" class="level2">
<h2 class="anchored" data-anchor-id="spaces-groups-and-representations-escnngspaces">Spaces, groups, and representations: <code>escnn$gspaces</code></h2>
<p>We start by peeking into <code>gspaces</code>, one of the two sub-modules we are going to make direct use of.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1">gspaces <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> escnn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>gspaces</span>
<span id="cb2-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">py_list_attributes</span>(gspaces) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> (\(vec) <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">grep</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"On"</span>, vec, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>))() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sort</span>()</span></code></pre></div></div>
<pre><code>[1] "conicalOnR3" "cylindricalOnR3" "dihedralOnR3" "flip2dOnR2" "flipRot2dOnR2" "flipRot3dOnR3"
[7] "fullCylindricalOnR3" "fullIcoOnR3" "fullOctaOnR3" "icoOnR3" "invOnR3" "mirOnR3 "octaOnR3"
[14] "rot2dOnR2" "rot2dOnR3" "rot3dOnR3" "trivialOnR2" "trivialOnR3"    </code></pre>
<p>The methods I’ve listed instantiate a <code>gspace</code>. If you look closely, you see that they’re all composed of two strings, joined by “On”. In all instances, the second part is either <code>R2</code> or <code>R3</code>. These two are the available base spaces – <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2"> and <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E3"> – an input signal can live in. Signals can, thus, be images, made up of pixels, or three-dimensional volumes, composed of voxels. The first part refers to the group you’d like to use. Choosing a group means choosing the symmetries to be respected. For example, <code>rot2dOnR2()</code> implies equivariance as to rotations, <code>flip2dOnR2()</code> guarantees the same for mirroring actions, and <code>flipRot2dOnR2()</code> subsumes both.</p>
<p>Let’s define such a <code>gspace</code>. Here we ask for rotation equivariance on the Euclidean plane, making use of the same cyclic group – <img src="https://latex.codecogs.com/png.latex?C_4"> – we developed in our <a href="https://blogs.rstudio.com/ai/posts/2023-03-27-group-equivariant-cnn-2/">from-scratch implementation</a>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1">r2_act <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> gspaces<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rot2dOnR2</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">N =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>L)</span>
<span id="cb4-2">r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>fibergroup</span></code></pre></div></div>
<p>In this post, I’ll stay with that setup, but we could as well pick another rotation angle – <code>N = 8</code>, say, resulting in eight equivariant positions separated by forty-five degrees. Alternatively, we might want <em>any</em> rotated position to be accounted for. The group to request then would be <a href="https://en.wikipedia.org/wiki/Orthogonal_group#Special_orthogonal_group">SO(2)</a>, called the <em>special orthogonal group,</em> of continuous, distance- and orientation-preserving transformations on the Euclidean plane:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1">(gspaces<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rot2dOnR2</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">N =</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>L))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>fibergroup</span></code></pre></div></div>
<pre><code>SO(2)</code></pre>
<p>Going back to <img src="https://latex.codecogs.com/png.latex?C_4">, let’s investigate its <a href="https://en.wikipedia.org/wiki/Group_representation"><em>representations</em></a>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1">r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>representations</span></code></pre></div></div>
<pre><code>$irrep_0
C4|[irrep_0]:1

$irrep_1
C4|[irrep_1]:2

$irrep_2
C4|[irrep_2]:1

$regular
C4|[regular]:4</code></pre>
<p>A representation, in our current context <em>and</em> very roughly speaking, is a way to encode a group action as a matrix, meeting certain conditions. In <code>escnn</code>, representations are central, and we’ll see how in the next section.</p>
<p>First, let’s inspect the above output. Four representations are available, three of which share an important property: they’re all <a href="https://en.wikipedia.org/wiki/Irreducible_representation">irreducible</a>. On <img src="https://latex.codecogs.com/png.latex?C_4">, any non-irreducible representation <a href="https://en.wikipedia.org/wiki/Maschke%27s_theorem">can be decomposed</a> into into irreducible ones. These irreducible representations are what <code>escnn</code> works with internally. Of those three, the most interesting one is the second. To see its action, we need to choose a group element. How about counterclockwise rotation by ninety degrees:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb9-1">elem_1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>fibergroup<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>L)</span>
<span id="cb9-2">elem_1</span></code></pre></div></div>
<pre><code>1[2pi/4]</code></pre>
<p>Associated to this group element is the following matrix:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb11-1">r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>representations[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]](elem_1)</span></code></pre></div></div>
<pre><code>             [,1]          [,2]
[1,] 6.123234e-17 -1.000000e+00
[2,] 1.000000e+00  6.123234e-17</code></pre>
<p>This is the so-called standard representation,</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bbmatrix%7D%20%5Ccos(%5Ctheta)%20&amp;%20-%5Csin(%5Ctheta)%20%5C%5C%20%5Csin(%5Ctheta)%20&amp;%20%5Ccos(%5Ctheta)%20%5Cend%7Bbmatrix%7D%0A"></p>
<p>, evaluated at <img src="https://latex.codecogs.com/png.latex?%5Ctheta%20=%20%5Cpi/2">. (It is called the standard representation because it directly comes from how the group is defined (namely, a rotation by <img src="https://latex.codecogs.com/png.latex?%5Ctheta"> in the plane).</p>
<p>The other interesting representation to point out is the fourth: the only one that’s not irreducible.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb13-1">r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>representations[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>]](elem_1)</span></code></pre></div></div>
<pre><code>[1,]  5.551115e-17 -5.551115e-17 -8.326673e-17  1.000000e+00
[2,]  1.000000e+00  5.551115e-17 -5.551115e-17 -8.326673e-17
[3,]  5.551115e-17  1.000000e+00  5.551115e-17 -5.551115e-17
[4,] -5.551115e-17  5.551115e-17  1.000000e+00  5.551115e-17</code></pre>
<p>This is the so-called <em>regular</em> representation. The regular representation acts via permutation of group elements, or, to be more precise, of the basis vectors that make up the matrix. Obviously, this is only possible for finite groups like <img src="https://latex.codecogs.com/png.latex?C_n">, since otherwise there’d be an infinite amount of basis vectors to permute.</p>
<p>To better see the action encoded in the above matrix, we clean up a bit:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb15-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>representations[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>]](elem_1))</span></code></pre></div></div>
<pre><code>    [,1] [,2] [,3] [,4]
[1,]    0    0    0    1
[2,]    1    0    0    0
[3,]    0    1    0    0
[4,]    0    0    1    0</code></pre>
<p>This is a step-one shift to the right of the identity matrix. The identity matrix, mapped to element 0, is the non-action; this matrix instead maps the zeroth action to the first, the first to the second, the second to the third, and the third to the first.</p>
<p>We’ll see the regular representation used in a neural network soon. Internally – but that need not concern the user – <em>escnn</em> works with its decomposition into irreducible matrices. Here, that’s just the bunch of irreducible representations we saw above, numbered from one to three.</p>
<p>Having looked at how groups and representations figure in <code>escnn</code>, it is time we approach the task of building a network.</p>
</section>
<section id="representations-for-real-escnnnnfieldtype" class="level2">
<h2 class="anchored" data-anchor-id="representations-for-real-escnnnnfieldtype">Representations, for real: <code>escnn$nn$FieldType</code></h2>
<p>So far, we’ve characterized the input space (<img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2">), and specified the group action. But once we enter the network, we’re not in the plane anymore, but in a space that has been extended by the group action. Rephrasing, the group action produces <em>feature vector fields</em> that assign a feature vector to each spatial position in the image.</p>
<p>Now we have those feature vectors, we need to specify how they transform under the group action. This is encoded in an <code>escnn$nn$FieldType</code> . Informally, we could say that a field type is the <em>data type</em> of a feature space. In defining it, we indicate two things: the base space, a <code>gspace</code>, and the representation type(s) to be used.</p>
<p>In an equivariant neural network, field types play a role similar to that of channels in a convnet. Each layer has an input and an output field type. Assuming we’re working with grey-scale images, we can specify the input type for the first layer like this:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb17-1">nn <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> escnn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>nn</span>
<span id="cb17-2">feat_type_in <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">FieldType</span>(r2_act, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>trivial_repr))</span></code></pre></div></div>
<p>The <em>trivial</em> representation is used to indicate that, while the image as a whole will be rotated, the pixel values themselves should be left alone. If this were an RGB image, instead of <code>r2_act$trivial_repr</code> we’d pass a list of three such objects.</p>
<p>So we’ve characterized the input. At any later stage, though, the situation will have changed. We will have performed convolution once for every group element. Moving on to the next layer, these feature fields will have to transform equivariantly, as well. This can be achieved by requesting the <em>regular</em> representation for an output field type:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb18-1">feat_type_out <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">FieldType</span>(r2_act, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>regular_repr))</span></code></pre></div></div>
<p>Then, a convolutional layer may be defined like so:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb19-1">conv <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">R2Conv</span>(feat_type_in, feat_type_out, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>L)</span></code></pre></div></div>
</section>
<section id="group-equivariant-convolution" class="level2">
<h2 class="anchored" data-anchor-id="group-equivariant-convolution">Group-equivariant convolution</h2>
<p>What does such a convolution do to its input? Just like, in a usual convnet, capacity can be increased by having more channels, an equivariant convolution can pass on several feature vector fields, possibly of different type (assuming that makes sense). In the code snippet below, we request a list of three, all behaving according to the regular representation.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb20" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb20-1">feat_type_in <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">FieldType</span>(r2_act, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>trivial_repr))</span>
<span id="cb20-2">feat_type_out <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">FieldType</span>(</span>
<span id="cb20-3">  r2_act,</span>
<span id="cb20-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>regular_repr, r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>regular_repr, r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>regular_repr)</span>
<span id="cb20-5">)</span>
<span id="cb20-6"></span>
<span id="cb20-7">conv <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">R2Conv</span>(feat_type_in, feat_type_out, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>L)</span></code></pre></div></div>
<p>We then perform convolution on a batch of images, made aware of their “data type” by wrapping them in <code>feat_type_in</code>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb21" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb21-1">x <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> torch<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rand</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>L, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>L, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>L, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>L)</span>
<span id="cb21-2">x <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">feat_type_in</span>(x)</span>
<span id="cb21-3">y <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">conv</span>(x)</span>
<span id="cb21-4">y<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>shape <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unlist</span>()</span></code></pre></div></div>
<pre><code>[1]  2  12 30 30</code></pre>
<p>The output has twelve “channels”, this being the product of group cardinality – four distinguished positions – and number of feature vector fields (three).</p>
<p>If we choose the simplest possible, approximately, test case, we can verify that such a convolution is equivariant by direct inspection. Here’s my setup:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb23-1">feat_type_in <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">FieldType</span>(r2_act, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>trivial_repr))</span>
<span id="cb23-2">feat_type_out <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">FieldType</span>(r2_act, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>regular_repr))</span>
<span id="cb23-3">conv <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">R2Conv</span>(feat_type_in, feat_type_out, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>L)</span>
<span id="cb23-4"></span>
<span id="cb23-5">torch<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>init<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">constant_</span>(conv<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>weights, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.</span>)</span>
<span id="cb23-6">x <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> torch<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">vander</span>(torch<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arange</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">view</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tuple</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>L, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>L, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>L, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>L)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">feat_type_in</span>()</span>
<span id="cb23-7">x</span></code></pre></div></div>
<pre><code>g_tensor([[[[ 0.,  0.,  0.,  1.],
            [ 1.,  1.,  1.,  1.],
            [ 8.,  4.,  2.,  1.],
            [27.,  9.,  3.,  1.]]]], [C4_on_R2[(None, 4)]: {irrep_0 (x1)}(1)])</code></pre>
<p>Inspection could be performed using any group element. I’ll pick rotation by <img src="https://latex.codecogs.com/png.latex?%5Cpi/2">:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb25" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb25-1">all <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">iterate</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>testing_elements)</span>
<span id="cb25-2">g1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> all[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]]</span>
<span id="cb25-3">g1</span></code></pre></div></div>
<p>Just for fun, let’s see how we can – literally – come whole circle by letting this element act on the input tensor four times:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb26" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb26-1">all <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">iterate</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>testing_elements)</span>
<span id="cb26-2">g1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> all[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]]</span>
<span id="cb26-3"></span>
<span id="cb26-4">x1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> x<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">transform</span>(g1)</span>
<span id="cb26-5">x1<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tensor</span>
<span id="cb26-6">x2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> x1<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">transform</span>(g1)</span>
<span id="cb26-7">x2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tensor</span>
<span id="cb26-8">x3 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> x2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">transform</span>(g1)</span>
<span id="cb26-9">x3<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tensor</span>
<span id="cb26-10">x4 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> x3<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">transform</span>(g1)</span>
<span id="cb26-11">x4<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tensor</span></code></pre></div></div>
<pre><code>tensor([[[[ 1.,  1.,  1.,  1.],
          [ 0.,  1.,  2.,  3.],
          [ 0.,  1.,  4.,  9.],
          [ 0.,  1.,  8., 27.]]]])
          
tensor([[[[ 1.,  3.,  9., 27.],
          [ 1.,  2.,  4.,  8.],
          [ 1.,  1.,  1.,  1.],
          [ 1.,  0.,  0.,  0.]]]])
          
tensor([[[[27.,  8.,  1.,  0.],
          [ 9.,  4.,  1.,  0.],
          [ 3.,  2.,  1.,  0.],
          [ 1.,  1.,  1.,  1.]]]])
          
tensor([[[[ 0.,  0.,  0.,  1.],
          [ 1.,  1.,  1.,  1.],
          [ 8.,  4.,  2.,  1.],
          [27.,  9.,  3.,  1.]]]])</code></pre>
<p>You see that at the end, we are back at the original “image”.</p>
<p>Now, for equivariance. We could first apply a rotation, then convolve.</p>
<p>Rotate:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb28" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb28-1">x_rot <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> x<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">transform</span>(g1)</span>
<span id="cb28-2">x_rot<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tensor</span></code></pre></div></div>
<p>This is the first in the above list of four tensors.</p>
<p>Convolve:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb29" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb29-1">y <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">conv</span>(x_rot)</span>
<span id="cb29-2">y<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tensor</span></code></pre></div></div>
<pre><code>tensor([[[[ 1.1955,  1.7110],
          [-0.5166,  1.0665]],

         [[-0.0905,  2.6568],
          [-0.3743,  2.8144]],

         [[ 5.0640, 11.7395],
          [ 8.6488, 31.7169]],

         [[ 2.3499,  1.7937],
          [ 4.5065,  5.9689]]]], grad_fn=&lt;ConvolutionBackward0&gt;)</code></pre>
<p>Alternatively, we can do the convolution first, then rotate its output.</p>
<p>Convolve:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb31" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb31-1">y_conv <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">conv</span>(x)</span>
<span id="cb31-2">y_conv<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tensor</span></code></pre></div></div>
<pre><code>tensor([[[[-0.3743, -0.0905],
          [ 2.8144,  2.6568]],

         [[ 8.6488,  5.0640],
          [31.7169, 11.7395]],

         [[ 4.5065,  2.3499],
          [ 5.9689,  1.7937]],

         [[-0.5166,  1.1955],
          [ 1.0665,  1.7110]]]], grad_fn=&lt;ConvolutionBackward0&gt;)</code></pre>
<p>Rotate:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb33" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb33-1">y <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> y_conv<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">transform</span>(g1)</span>
<span id="cb33-2">y<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tensor</span></code></pre></div></div>
<pre><code>tensor([[[[ 1.1955,  1.7110],
          [-0.5166,  1.0665]],

         [[-0.0905,  2.6568],
          [-0.3743,  2.8144]],

         [[ 5.0640, 11.7395],
          [ 8.6488, 31.7169]],

         [[ 2.3499,  1.7937],
          [ 4.5065,  5.9689]]]])</code></pre>
<p>Indeed, final results are the same.</p>
<p>At this point, we know how to employ group-equivariant convolutions. The final step is to compose the network.</p>
</section>
<section id="a-group-equivariant-neural-network" class="level2">
<h2 class="anchored" data-anchor-id="a-group-equivariant-neural-network">A group-equivariant neural network</h2>
<p>Basically, we have two questions to answer. The first concerns the non-linearities; the second is how to get from extended space to the data type of the target.</p>
<p>First, about the non-linearities. This is a potentially intricate topic, but as long as we stay with point-wise operations (such as that performed by ReLU) equivariance is given intrinsically.</p>
<p>In consequence, we can already assemble a model:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb35" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb35-1">feat_type_in <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">FieldType</span>(r2_act, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>trivial_repr))</span>
<span id="cb35-2">feat_type_hid <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">FieldType</span>(</span>
<span id="cb35-3">  r2_act,</span>
<span id="cb35-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>regular_repr, r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>regular_repr, r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>regular_repr, r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>regular_repr)</span>
<span id="cb35-5">  )</span>
<span id="cb35-6">feat_type_out <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">FieldType</span>(r2_act, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(r2_act<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>regular_repr))</span>
<span id="cb35-7"></span>
<span id="cb35-8">model <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SequentialModule</span>(</span>
<span id="cb35-9">  nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">R2Conv</span>(feat_type_in, feat_type_hid, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>L),</span>
<span id="cb35-10">  nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">InnerBatchNorm</span>(feat_type_hid),</span>
<span id="cb35-11">  nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ReLU</span>(feat_type_hid),</span>
<span id="cb35-12">  nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">R2Conv</span>(feat_type_hid, feat_type_hid, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>L),</span>
<span id="cb35-13">  nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">InnerBatchNorm</span>(feat_type_hid),</span>
<span id="cb35-14">  nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ReLU</span>(feat_type_hid),</span>
<span id="cb35-15">  nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">R2Conv</span>(feat_type_hid, feat_type_out, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>L)</span>
<span id="cb35-16">)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">eval</span>()</span>
<span id="cb35-17"></span>
<span id="cb35-18">model</span></code></pre></div></div>
<pre><code>SequentialModule(
  (0): R2Conv([C4_on_R2[(None, 4)]:
       {irrep_0 (x1)}(1)], [C4_on_R2[(None, 4)]: {regular (x4)}(16)], kernel_size=3, stride=1)
  (1): InnerBatchNorm([C4_on_R2[(None, 4)]:
       {regular (x4)}(16)], eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (2): ReLU(inplace=False, type=[C4_on_R2[(None, 4)]: {regular (x4)}(16)])
  (3): R2Conv([C4_on_R2[(None, 4)]:
       {regular (x4)}(16)], [C4_on_R2[(None, 4)]: {regular (x4)}(16)], kernel_size=3, stride=1)
  (4): InnerBatchNorm([C4_on_R2[(None, 4)]:
       {regular (x4)}(16)], eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (5): ReLU(inplace=False, type=[C4_on_R2[(None, 4)]: {regular (x4)}(16)])
  (6): R2Conv([C4_on_R2[(None, 4)]:
       {regular (x4)}(16)], [C4_on_R2[(None, 4)]: {regular (x1)}(4)], kernel_size=3, stride=1)
)</code></pre>
<p>Calling this model on some input image, we get:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb37" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb37-1">x <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> torch<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">randn</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>L, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>L, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">17</span>L, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">17</span>L)</span>
<span id="cb37-2">x <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">feat_type_in</span>(x)</span>
<span id="cb37-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">model</span>(x)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>shape <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unlist</span>()</span></code></pre></div></div>
<pre><code>[1]  1  4 11 11</code></pre>
<p>What we do now depends on the task. Since we didn’t preserve the original resolution anyway – as would have been required for, say, segmentation – we probably want one feature vector per image. That we can achieve by spatial pooling:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb39" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb39-1">avgpool <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">PointwiseAvgPool</span>(feat_type_out, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">11</span>L)</span>
<span id="cb39-2">y <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">avgpool</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">model</span>(x))</span>
<span id="cb39-3">y<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>shape <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unlist</span>()</span></code></pre></div></div>
<pre><code>[1] 1 4 1 1</code></pre>
<p>We still have four “channels”, corresponding to four group elements. This feature vector is (approximately) translation-<em>in</em>variant, but rotation-<em>equi</em>variant, in the sense expressed by the choice of group. Often, the final output will be expected to be group-invariant as well as translation-invariant (as in image classification). If that’s the case, we pool over group elements, as well:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb41" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb41-1">invariant_map <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nn<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GroupPooling</span>(feat_type_out)</span>
<span id="cb41-2">y <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">invariant_map</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">avgpool</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">model</span>(x)))</span>
<span id="cb41-3">y<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tensor</span></code></pre></div></div>
<pre><code>tensor([[[[-0.0293]]]], grad_fn=&lt;CopySlices&gt;)</code></pre>
<p>We end up with an architecture that, from the outside, will look like a standard convnet, while on the inside, all convolutions have been performed in a rotation-equivariant way. Training and evaluation then are no different from the usual procedure.</p>
</section>
<section id="where-to-from-here" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="where-to-from-here">Where to from here</h2>
<p>This “introduction to an introduction” has been the attempt to draw a high-level map of the terrain, so you can decide if this is useful to you. If it’s not just useful, but interesting theory-wise as well, you’ll find lots of excellent materials linked from the <a href="https://github.com/QUVA-Lab/escnn">README</a>. The way I see it, though, this post already should enable you to actually experiment with different setups.</p>
<p>One such experiment, that would be of high interest to me, might investigate how well different types and degrees of equivariance actually work for a given task and dataset. Overall, a reasonable assumption is that, the higher “up” we go in the feature hierarchy, the less equivariance we require. For edges and corners, taken by themselves, full rotation equivariance seems desirable, as does equivariance to reflection; for higher-level features, we might want to successively restrict allowed operations, maybe ending up with equivariance to mirroring merely. Experiments could be designed to compare different ways, and levels, of restriction.<sup>8</sup></p>
<div class="no-row-height column-margin column-container"><div id="fn8"><p><sup>8</sup>&nbsp;See the documentation for <a href="https://quva-lab.github.io/escnn/api/escnn.nn.html?highlight=restrictionmodule#escnn.nn.RestrictionModule"><code>escnn$nn$RestrictionModule</code></a> for how to do this.</p></div></div><p>Thanks for reading!</p>
<div class="photo-credit">
<p>Photo by <a href="https://unsplash.com/@astrovol?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Volodymyr Tokar</a> on <a href="https://unsplash.com/photos/hwcWBLq1jO8?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
</div>


</section>


<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a> ]]></description>
  <category>Deep Learning</category>
  <category>Geometric Deep Learning</category>
  <guid>https://divergences.xyz/posts/group-equivariant-cnn-3/</guid>
  <pubDate>Tue, 09 May 2023 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/group-equivariant-cnn-3/preview.jpg" medium="image" type="image/jpeg"/>
</item>
</channel>
</rss>
