<?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/cnn.html</link>
<atom:link href="https://divergences.xyz/tag/cnn.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>Mon, 27 Mar 2023 00:00:00 GMT</lastBuildDate>
<item>
  <title>Implementing rotation equivariance: Group-equivariant CNN from scratch</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/group-equivariant-cnn-2/</link>
  <description><![CDATA[ 






<p>Convolutional neural networks (CNNs) are great – they’re able to detect features in an image no matter where. Well, not exactly. They’re not indifferent to just any kind of movement. Shifting up or down, or left or right, is fine; rotating around an axis is not. That’s because of how convolution works: traverse by row, then traverse by column (or the other way round). If we want “more” (e.g., successful detection of an upside-down object), we need to extend convolution to an operation that is <em>rotation-equivariant</em>. An operation that is <em>equivariant</em> to some type of action will not only register the moved feature per se, but also, keep track of which concrete action made it appear where it is.</p>
<p><strong>This is the second post in a series that introduces group-equivariant CNNs (GCNNs)</strong><em>.</em> The <a href="https://blogs.rstudio.com/ai/posts/2023-03-09-group-equivariant-cnn-1/">first</a> was a high-level introduction to why we’d want them, and how they work. There, we introduced the key player, the symmetry group, which specifies what kinds of transformations are to be treated equivariantly. If you haven’t, please take a look at that post first, since here I’ll make use of terminology and concepts it introduced.</p>
<p>Today, we code a simple GCNN from scratch. Code and presentation tightly follow a <a href="https://github.com/phlippe/uvadlc_notebooks/blob/master/docs/tutorial_notebooks/DL2/Geometric_deep_learning/tutorial1_regular_group_convolutions.ipynb">notebook</a> provided as part of University of Amsterdam’s 2022 <a href="https://uvadl2c.github.io/">Deep Learning Course</a>. They can’t be thanked enough for making available such excellent learning materials.</p>
<p>In what follows, my intent is to explain the general thinking, and how the resulting architecture is built up from smaller modules, each of which is assigned a clear purpose. For that reason, I won’t reproduce all the code here; instead, I’ll make use of the package <a href="https://github.com/skeydan/gcnn"><code>gcnn</code></a>. Its methods are heavily annotated; so to see some details, don’t hesitate to look at the code.</p>
<p>As of today, <code>gcnn</code> implements one symmetry group: <img src="https://latex.codecogs.com/png.latex?C_4">, the one that serves as a running example throughout post one. It is straightforwardly extensible, though, making use of class hierarchies throughout.</p>
<section id="step-1-the-symmetry-group-c_4" class="level2">
<h2 class="anchored" data-anchor-id="step-1-the-symmetry-group-c_4">Step 1: The symmetry group <img src="https://latex.codecogs.com/png.latex?C_4"></h2>
<p>In coding a GCNN, the first thing we need to provide is an implementation of the symmetry group we’d like to use. Here, it is <img src="https://latex.codecogs.com/png.latex?C_4">, the four-element group that rotates by 90 degrees.</p>
<p>We can ask <code>gcnn</code> to create one for us, and inspect its elements.</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="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># remotes::install_github("skeydan/gcnn")</span></span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(gcnn)</span>
<span id="cb1-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(torch)</span>
<span id="cb1-4"></span>
<span id="cb1-5">C_4 <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;">CyclicGroup</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">order =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>)</span>
<span id="cb1-6">elems <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> C_4<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;">elements</span>()</span>
<span id="cb1-7">elems</span></code></pre></div></div>
<pre><code>torch_tensor
 0.0000
 1.5708
 3.1416
 4.7124
[ CPUFloatType{4} ]</code></pre>
<p>Elements are represented by their respective rotation angles: <img src="https://latex.codecogs.com/png.latex?0">, <img src="https://latex.codecogs.com/png.latex?%5Cfrac%7B%5Cpi%7D%7B2%7D">, <img src="https://latex.codecogs.com/png.latex?%5Cpi">, and <img src="https://latex.codecogs.com/png.latex?%5Cfrac%7B3%20%5Cpi%7D%7B2%7D">.</p>
<p>Groups are aware of the identity, and know how to construct an element’s inverse:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">C_4<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>identity</span>
<span id="cb3-2"></span>
<span id="cb3-3">g1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> elems[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]</span>
<span id="cb3-4">C_4<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;">inverse</span>(g1)</span></code></pre></div></div>
<pre><code>torch_tensor
 0
[ CPUFloatType{1} ]

torch_tensor
4.71239
[ CPUFloatType{} ]</code></pre>
<p>Here, what we care about most is the group elements’ <em>action</em>. Implementation-wise, we need to distinguish between them acting on each other, and their action on the vector space <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2">, where our input images live. The former part is the easy one: It may simply be implemented by adding angles. In fact, this is what <code>gcnn</code> does when we ask it to let <code>g1</code> act on <code>g2</code>:</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">g2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> elems[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>]</span>
<span id="cb5-2"></span>
<span id="cb5-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># in C_4$left_action_on_H(), H stands for the symmetry group</span></span>
<span id="cb5-4">C_4<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;">left_action_on_H</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">torch_tensor</span>(g1)<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;">unsqueeze</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">torch_tensor</span>(g2)<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;">unsqueeze</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span></code></pre></div></div>
<pre><code>torch_tensor
 4.7124
[ CPUFloatType{1,1} ]</code></pre>
<p>What’s with the <code>unsqueeze()</code>s? Since <img src="https://latex.codecogs.com/png.latex?C_4">’s ultimate <em>raison d’être</em> is to be part of a neural network, <code>left_action_on_H()</code> works with batches of elements, not scalar tensors.</p>
<p>Things are a bit less straightforward where the group action on <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2"> is concerned. Here, we need the concept of a <a href="https://en.wikipedia.org/wiki/Group_representation">group representation</a>. This is an involved topic, which we won’t go into here. In our current context, it works about like this: We have an input signal, a tensor we’d like to operate on in some way. (That “some way” will be convolution, as we’ll see soon.) To render that operation group-equivariant, we first have the representation apply the <em>inverse</em> group action to the input. That accomplished, we go on with the operation as though nothing had happened.</p>
<p>To give a concrete example, let’s say the operation is a measurement. Imagine a runner, standing at the foot of some mountain trail, ready to run up the climb. We’d like to record their height. One option we have is to take the measurement, then let them run up. Our measurement will be as valid up the mountain as it was down here. Alternatively, we might be polite and not make them wait. Once they’re up there, we ask them to come down, and when they’re back, we measure their height. The result is the same: Body height is equivariant (more than that: invariant, even) to the action of running up or down. (Of course, height is a pretty dull measure. But something more interesting, such as heart rate, would not have worked so well in this example.)</p>
<p>Returning to the implementation, it turns out that group actions are encoded as matrices. There is one matrix for each group element. For <img src="https://latex.codecogs.com/png.latex?C_4">, the so-called <em>standard</em> representation is a rotation matrix:</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>In <code>gcnn</code>, the function applying that matrix is <code>left_action_on_R2()</code>. Like its sibling, it is designed to work with batches (of group elements as well as <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2"> vectors). Technically, what it does is rotate the grid the image is defined on, and then, re-sample the image. To make this more concrete, that method’s code looks about as follows.</p>
<p>Here is a goat.</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">img_path <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;">system.file</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"imgs"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"z.jpg"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">package =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gcnn"</span>)</span>
<span id="cb7-2">img <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> torchvision<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;">base_loader</span>(img_path) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> torchvision<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_to_tensor</span>()</span>
<span id="cb7-3">img<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;">permute</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</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;">as.array</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;">as.raster</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;">plot</span>()</span></code></pre></div></div>
<p><a href="z.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://divergences.xyz/posts/group-equivariant-cnn-2/z.jpg" class="img-fluid" alt="A goat sitting comfortably on a meadow."></a></p>
<p>First, we call <code>C_4$left_action_on_R2()</code> to rotate the grid.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Grid shape is [2, 1024, 1024], for a 2d, 1024 x 1024 image.</span></span>
<span id="cb8-2">img_grid_R2 <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;">torch_stack</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;">torch_meshgrid</span>(</span>
<span id="cb8-3">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb8-4">      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;">torch_linspace</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>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(img)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]),</span>
<span id="cb8-5">      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;">torch_linspace</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>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(img)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>])</span>
<span id="cb8-6">    )</span>
<span id="cb8-7">))</span>
<span id="cb8-8"></span>
<span id="cb8-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Transform the image grid with the matrix representation of some group element.</span></span>
<span id="cb8-10">transformed_grid <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> C_4<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;">left_action_on_R2</span>(C_4<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;">inverse</span>(g1)<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;">unsqueeze</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), img_grid_R2)</span></code></pre></div></div>
<p>Second, we re-sample the image on the transformed grid. The goat now looks up to the sky.</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">transformed_img <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;">nnf_grid_sample</span>(</span>
<span id="cb9-2">  img<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;">unsqueeze</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), transformed_grid,</span>
<span id="cb9-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">align_corners =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mode =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bilinear"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">padding_mode =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"zeros"</span></span>
<span id="cb9-4">)</span>
<span id="cb9-5"></span>
<span id="cb9-6">transformed_img[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</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;">permute</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</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;">as.array</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;">as.raster</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;">plot</span>()</span></code></pre></div></div>
<p><a href="z2.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-2"><img src="https://divergences.xyz/posts/group-equivariant-cnn-2/z2.jpg" class="img-fluid" alt="Same goat, rotated up by 90 degrees."></a></p>
</section>
<section id="step-2-the-lifting-convolution" class="level2">
<h2 class="anchored" data-anchor-id="step-2-the-lifting-convolution">Step 2: The lifting convolution</h2>
<p>We want to make use of existing, efficient <code>torch</code> functionality as much as possible. Concretely, we want to use <code>nn_conv2d()</code>. What we need, though, is a convolution kernel that’s equivariant not just to translation, but also to the action of <img src="https://latex.codecogs.com/png.latex?C_4">. This can be achieved by having one kernel for each possible rotation.</p>
<p>Implementing that idea is exactly what <code>LiftingConvolution</code> does. The principle is the same as before: First, the grid is rotated, and then, the kernel (weight matrix) is re-sampled to the transformed grid.</p>
<p>Why, though, call this a <em>lifting convolution</em>? The usual convolution kernel operates on <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2">; while our extended version operates on combinations of <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2"> and <img src="https://latex.codecogs.com/png.latex?C_4">. In math speak, it has been <em>lifted</em> to the <a href="https://en.wikipedia.org/wiki/Semidirect_product">semi-direct product</a> <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2%5Crtimes%20C_4">.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb10-1">lifting_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;">LiftingConvolution</span>(</span>
<span id="cb10-2">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">group =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">CyclicGroup</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">order =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>),</span>
<span id="cb10-3">    <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;">5</span>,</span>
<span id="cb10-4">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">in_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>,</span>
<span id="cb10-5">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">out_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span></span>
<span id="cb10-6">  )</span>
<span id="cb10-7"></span>
<span id="cb10-8">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;">torch_randn</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>))</span>
<span id="cb10-9">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;">lifting_conv</span>(x)</span>
<span id="cb10-10">y<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>shape</span></code></pre></div></div>
<pre><code>[1]  2  8  4 28 28</code></pre>
<p>Since, internally, <code>LiftingConvolution</code> uses an additional dimension to realize the product of translations and rotations, the output is not four-, but five-dimensional.</p>
</section>
<section id="step-3-group-convolutions" class="level2">
<h2 class="anchored" data-anchor-id="step-3-group-convolutions">Step 3: Group convolutions</h2>
<p>Now that we’re in “group-extended space”, we can chain a number of layers where both input and output are <em>group convolution</em> layers. For example:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb12-1">group_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;">GroupConvolution</span>(</span>
<span id="cb12-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">group =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">CyclicGroup</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">order =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>),</span>
<span id="cb12-3">    <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;">5</span>,</span>
<span id="cb12-4">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">in_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>,</span>
<span id="cb12-5">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">out_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span></span>
<span id="cb12-6">)</span>
<span id="cb12-7"></span>
<span id="cb12-8">z <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;">group_conv</span>(y)</span>
<span id="cb12-9">z<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>shape</span></code></pre></div></div>
<pre><code>[1]  2 16  4 24 24</code></pre>
<p>All that remains to be done is package this up. That’s what <code>gcnn::GroupEquivariantCNN()</code> does.</p>
</section>
<section id="step-4-group-equivariant-cnn" class="level2">
<h2 class="anchored" data-anchor-id="step-4-group-equivariant-cnn">Step 4: Group-equivariant CNN</h2>
<p>We can call <code>GroupEquivariantCNN()</code> like so.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb14-1">cnn <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;">GroupEquivariantCNN</span>(</span>
<span id="cb14-2">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">group =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">CyclicGroup</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">order =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>),</span>
<span id="cb14-3">    <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;">5</span>,</span>
<span id="cb14-4">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">in_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb14-5">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">out_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb14-6">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">num_hidden =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># number of group convolutions</span></span>
<span id="cb14-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hidden_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># number of channels per group conv layer</span></span>
<span id="cb14-8">)</span>
<span id="cb14-9"></span>
<span id="cb14-10">img <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;">torch_randn</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>))</span>
<span id="cb14-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cnn</span>(img)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>shape</span></code></pre></div></div>
<pre><code>[1] 4 1</code></pre>
<p>At casual glance, this <code>GroupEquivariantCNN</code> looks like any old CNN … weren’t it for the <code>group</code> argument.</p>
<p>Now, when we inspect its output, we see that the additional dimension is gone. That’s because after a sequence of group-to-group convolution layers, the module projects down to a representation that, for each batch item, retains channels only. It thus averages not just over locations – as we normally do – but over the group dimension as well. A final linear layer will then provide the requested classifier output (of dimension <code>out_channels</code>).</p>
<p>And there we have the complete architecture. It is time for a real-world(<em>ish</em>) test.</p>
</section>
<section id="rotated-digits" class="level2">
<h2 class="anchored" data-anchor-id="rotated-digits">Rotated digits!</h2>
<p>The idea is to train two convnets, a “normal” CNN and a group-equivariant one, on the usual MNIST training set. Then, both are evaluated on an augmented test set where each image is randomly rotated by a continuous rotation between 0 and 360 degrees. We don’t expect <code>GroupEquivariantCNN</code> to be “perfect” – not if we equip with <img src="https://latex.codecogs.com/png.latex?C_4"> as a symmetry group. Strictly, with <img src="https://latex.codecogs.com/png.latex?C_4">, equivariance extends over four positions only. But we do hope it will perform significantly better than the shift-equivariant-only standard architecture.</p>
<p>First, we prepare the data; in particular, the augmented test set.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb16-1">dir <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/tmp/mnist"</span></span>
<span id="cb16-2"></span>
<span id="cb16-3">train_ds <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> torchvision<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;">mnist_dataset</span>(</span>
<span id="cb16-4">  dir,</span>
<span id="cb16-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">download =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb16-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">transform =</span> torchvision<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>transform_to_tensor</span>
<span id="cb16-7">)</span>
<span id="cb16-8"></span>
<span id="cb16-9">test_ds <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> torchvision<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;">mnist_dataset</span>(</span>
<span id="cb16-10">  dir,</span>
<span id="cb16-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">train =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>,</span>
<span id="cb16-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">transform =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(x) {</span>
<span id="cb16-13">    x <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb16-14">      torchvision<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_to_tensor</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb16-15">      torchvision<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_random_rotation</span>(</span>
<span id="cb16-16">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">degrees =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</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;">360</span>),</span>
<span id="cb16-17">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">resample =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="cb16-18">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb16-19">      )</span>
<span id="cb16-20">  }</span>
<span id="cb16-21">)</span>
<span id="cb16-22"></span>
<span id="cb16-23">train_dl <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;">dataloader</span>(train_ds, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">batch_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shuffle =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb16-24">test_dl <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;">dataloader</span>(test_ds, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">batch_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>)</span></code></pre></div></div>
<p>How does it look?</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">test_images <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> coro<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;">collect</span>(</span>
<span id="cb17-2">  test_dl, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb17-3">)[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</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;">32</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</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;">as.array</span>()</span>
<span id="cb17-4"></span>
<span id="cb17-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">par</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mfrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mar =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rep</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="at" style="color: #657422;
background-color: null;
font-style: inherit;">mai =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rep</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>
<span id="cb17-6">test_images <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb17-7">  purrr<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;">array_tree</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb17-8">  purrr<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;">map</span>(as.raster) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb17-9">  purrr<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;">iwalk</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> {</span>
<span id="cb17-10">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(.x)</span>
<span id="cb17-11">  })</span></code></pre></div></div>
<p><a href="rotated-mnist.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3"><img src="https://divergences.xyz/posts/group-equivariant-cnn-2/rotated-mnist.png" class="img-fluid" alt="32 digits, rotated randomly."></a></p>
<p>We first define and train a conventional CNN. It is as similar to <code>GroupEquivariantCNN()</code>, architecture-wise, as possible, and is given twice the number of hidden channels, so as to have comparable capacity overall.</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"> default_cnn <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;">nn_module</span>(</span>
<span id="cb18-2">   <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"default_cnn"</span>,</span>
<span id="cb18-3">   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">initialize =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(kernel_size, in_channels, out_channels, num_hidden, hidden_channels) {</span>
<span id="cb18-4">     self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>conv1 <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;">nn_conv2d</span>(in_channels, hidden_channels, kernel_size)</span>
<span id="cb18-5">     self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>convs <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;">nn_module_list</span>()</span>
<span id="cb18-6">     <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>num_hidden) {</span>
<span id="cb18-7">       self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>convs<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;">append</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;">nn_conv2d</span>(hidden_channels, hidden_channels, kernel_size))</span>
<span id="cb18-8">     }</span>
<span id="cb18-9">     self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>avg_pool <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;">nn_adaptive_avg_pool2d</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb18-10">     self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>final_linear <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;">nn_linear</span>(hidden_channels, out_channels)</span>
<span id="cb18-11">   },</span>
<span id="cb18-12">   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">forward =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(x) {</span>
<span id="cb18-13">     x <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;">|&gt;</span></span>
<span id="cb18-14">       self<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;">conv1</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb18-15">       (\(.) 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;">nnf_layer_norm</span>(., .<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</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;">4</span>]))() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb18-16">       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;">nnf_relu</span>()</span>
<span id="cb18-17">     <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</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;">length</span>(self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>convs))) {</span>
<span id="cb18-18">       x <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;">|&gt;</span></span>
<span id="cb18-19">         self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>convs[[i]]() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb18-20">         (\(.) 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;">nnf_layer_norm</span>(., .<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</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;">4</span>]))() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb18-21">         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;">nnf_relu</span>()</span>
<span id="cb18-22">     }</span>
<span id="cb18-23">     x <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;">|&gt;</span></span>
<span id="cb18-24">       self<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;">avg_pool</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb18-25">       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;">torch_squeeze</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb18-26">       self<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;">final_linear</span>()</span>
<span id="cb18-27">     x</span>
<span id="cb18-28">   }</span>
<span id="cb18-29"> )</span>
<span id="cb18-30"></span>
<span id="cb18-31">fitted <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> default_cnn <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb18-32">    luz<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;">setup</span>(</span>
<span id="cb18-33">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">loss =</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;">nn_cross_entropy_loss</span>(),</span>
<span id="cb18-34">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">optimizer =</span> torch<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>optim_adam,</span>
<span id="cb18-35">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">metrics =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb18-36">        luz<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;">luz_metric_accuracy</span>()</span>
<span id="cb18-37">      )</span>
<span id="cb18-38">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb18-39">    luz<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;">set_hparams</span>(</span>
<span id="cb18-40">      <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;">5</span>,</span>
<span id="cb18-41">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">in_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb18-42">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">out_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>,</span>
<span id="cb18-43">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">num_hidden =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb18-44">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hidden_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span></span>
<span id="cb18-45">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb18-46">    luz<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;">set_opt_hparams</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lr =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">weight_decay =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-4</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb18-47">    luz<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;">fit</span>(train_dl, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">epochs =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">valid_data =</span> test_dl) </span></code></pre></div></div>
<pre><code>Train metrics: Loss: 0.0498 - Acc: 0.9843
Valid metrics: Loss: 3.2445 - Acc: 0.4479</code></pre>
<p>Unsurprisingly, accuracy on the test set is not that great.</p>
<p>Next, we train the group-equivariant version.</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">fitted <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> GroupEquivariantCNN <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb20-2">  luz<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;">setup</span>(</span>
<span id="cb20-3">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">loss =</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;">nn_cross_entropy_loss</span>(),</span>
<span id="cb20-4">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">optimizer =</span> torch<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>optim_adam,</span>
<span id="cb20-5">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">metrics =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb20-6">      luz<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;">luz_metric_accuracy</span>()</span>
<span id="cb20-7">    )</span>
<span id="cb20-8">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb20-9">  luz<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;">set_hparams</span>(</span>
<span id="cb20-10">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">group =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">CyclicGroup</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">order =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>),</span>
<span id="cb20-11">    <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;">5</span>,</span>
<span id="cb20-12">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">in_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb20-13">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">out_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>,</span>
<span id="cb20-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">num_hidden =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb20-15">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hidden_channels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span></span>
<span id="cb20-16">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb20-17">  luz<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;">set_opt_hparams</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lr =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">weight_decay =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-4</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb20-18">  luz<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;">fit</span>(train_dl, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">epochs =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">valid_data =</span> test_dl)</span></code></pre></div></div>
<pre><code>Train metrics: Loss: 0.1102 - Acc: 0.9667
Valid metrics: Loss: 0.4969 - Acc: 0.8549</code></pre>
<p>For the group-equivariant CNN, accuracies on test and training sets are a lot closer. That is a nice result! Let’s wrap up today’s exploit resuming a thought from the first, more high-level post.</p>
</section>
<section id="a-challenge" class="level2">
<h2 class="anchored" data-anchor-id="a-challenge">A challenge</h2>
<p>Going back to the augmented test set, or rather, the samples of digits displayed, we notice a problem. In row two, column four, there is a digit that “under normal circumstances”, should be a 9, but, most probably, is an upside-down 6. (To a human, what suggests this is the squiggle-like thing that seems to be found more often with sixes than with nines.) However, you could ask: does this <em>have</em> to be a problem? Maybe the network just needs to learn the subtleties, the kinds of things a human would spot?</p>
<p>The way I view it, it all depends on the context: What really should be accomplished, and how an application is going to be used. With digits on a letter, I’d see no reason why a single digit should appear upside-down; accordingly, full rotation equivariance would be counter-productive. In a nutshell, we arrive at the same canonical imperative advocates of fair, just machine learning keep reminding us of:</p>
<blockquote class="blockquote">
<p>Always think of the way an application is going to be used!</p>
</blockquote>
<p>In our case, though, there is another aspect to this, a technical one. <code>gcnn::GroupEquivariantCNN()</code> is a simple wrapper, in that its layers all make use of the same symmetry group. In principle, there is no need to do this. With more coding effort, different groups can be used depending on a layer’s position in the feature-detection hierarchy.</p>
<p>Here, let me finally tell you why I chose the goat picture. The goat is seen through a red-and-white fence, a pattern – slightly rotated, due to the viewing angle – made up of squares (or edges, if you like). Now, for such a fence, types of rotation equivariance such as that encoded by <img src="https://latex.codecogs.com/png.latex?C_4"> make a lot of sense. The goat itself, though, we’d rather not have look up to the sky, the way I illustrated <img src="https://latex.codecogs.com/png.latex?C_4"> action before. Thus, what we’d do in a real-world image-classification task is use rather flexible layers at the bottom, and increasingly restrained layers at the top of the hierarchy.</p>
<p>Thanks for reading!</p>
<div class="photo-credit">
<p>Photo by <a href="https://unsplash.com/@marjan_blan?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Marjan Blan | <span class="citation" data-cites="marjanblan">@marjanblan</span></a> on <a href="https://unsplash.com/photos/kvOnuo8OvN4?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-2/</guid>
  <pubDate>Mon, 27 Mar 2023 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/group-equivariant-cnn-2/preview.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Upside down, a cat’s still a cat: Evolving image recognition with Geometric Deep Learning</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/group-equivariant-cnn-1/</link>
  <description><![CDATA[ 






<p>This is the first in a series of posts on group-equivariant convolutional neural networks (GCNNs). Today, we keep it short, high-level, and conceptual; examples and implementations will follow. In looking at GCNNs, we are resuming a topic we first <a href="https://blogs.rstudio.com/ai/posts/2021-08-26-geometric-deep-learning/">wrote about</a> in 2021: <a href="https://geometricdeeplearning.com/">Geometric Deep Learning</a>, a principled, math-driven approach to network design that, since then, has only risen in scope and impact.</p>
<section id="from-alchemy-to-science-geometric-deep-learning-in-two-minutes" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="from-alchemy-to-science-geometric-deep-learning-in-two-minutes">From alchemy to science: Geometric Deep Learning in two minutes</h2>
<p>In a nutshell, Geometric Deep Learning is all about deriving network structure from two things: the domain, and the task. The posts will go into a lot of detail, but let me give a quick preview here:</p>
<ul>
<li>By domain, I’m referring to the underlying physical space, and the way it is represented in the input data. For example, images are usually coded as a two-dimensional grid, with values indicating pixel intensities.<sup>1</sup></li>
<li>The task is what we’re training the network to do: classification, say, or segmentation. Tasks may be different at different stages in the architecture. At each stage, the task in question will have its word to say about how layer design should look.</li>
</ul>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;That’s when the image has a single channel. Otherwise, there are a number of grids, each mapped to a separate channel.</p></div></div><p>For instance, take MNIST. The dataset consists of images of ten digits, 0 to 10, all gray-scale. The task – unsurprisingly – is to assign each image the digit represented.</p>
<p>First, consider the domain. A <img src="https://latex.codecogs.com/png.latex?7"> is a <img src="https://latex.codecogs.com/png.latex?7"> wherever it appears on the grid. We thus need an operation that is <em>translation-equivariant</em>: It flexibly adapts to shifts (translations) in its input. More concretely, in our context, <em>equivariant</em> operations are able to detect some object’s properties even if that object has been moved, vertically and/or horizontally, to another location. <em>Convolution</em>, ubiquitous not just in deep learning, is just such a shift-equivariant operation.</p>
<p>Let me call special attention to the fact that, in equivariance, the essential thing is that “flexible adaptation.” Translation-equivariant operations <em>do</em> care about an object’s new position; they record a feature not abstractly, but at the object’s new position. To see why this is important, consider the network as a whole. When we compose convolutions, we build a hierarchy of feature detectors. That hierarchy should be functional no matter where in the image. In addition, it has to be consistent: Location information needs to be preserved between layers.</p>
<p>Terminology-wise, thus, it is important to distinguish equivariance from <em>invariance</em>. An invariant operation, in our context, would still be able to spot a feature wherever it occurs; however, it would happily forget where that feature happened to be. Clearly, then, to build up a hierarchy of features, translation-<em>invariance</em> is not enough.</p>
<p>What we’ve done right now is derive a requirement from the domain, the input grid. What about the task? If, finally, all we’re supposed to do is name the digit, now suddenly location does not matter anymore. In other words, once the hierarchy exists, invariance <em>is</em> enough. In neural networks, <em>pooling</em> is an operation that forgets about (spatial) detail. It only cares about the mean, say, or the maximum value itself. This is what makes it suited to “summing up” information about a region, or a complete image, if at the end we only care about returning a class label.</p>
<p>In a nutshell, we were able to formulate a design wishlist based on (1) what we’re given and (2) what we’re tasked with.</p>
<p>After this high-level sketch of Geometric Deep Learning, we zoom in on this series of posts’ designated topic: <em>group-equivariant</em> convolutional neural networks.</p>
<p>The why of “equivariant” should not, by now, pose too much of a riddle. What about that “group” prefix, though?</p>
</section>
<section id="the-group-in-group-equivariance" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="the-group-in-group-equivariance">The “group” in group-equivariance</h2>
<p>As you may have guessed from the introduction, talking of “principled” and “math-driven”, this <em>really</em> is about groups in the “math sense.” Depending on your background, the last time you heard about groups was in school, and with not even a hint at why they matter. I’m certainly not qualified to summarize the whole richness of what they’re good for, but I hope that by the end of this post, their importance in deep learning will make intuitive sense.</p>
<section id="groups-from-symmetries" class="level3">
<h3 class="anchored" data-anchor-id="groups-from-symmetries">Groups from symmetries</h3>
<p>Here is a square.</p>
<p><a href="square1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://divergences.xyz/posts/group-equivariant-cnn-1/square1.png" class="img-fluid" alt="A square in its default position, aligned horizontally to a virtual (invisible) x-axis."></a></p>
<p>Now close your eyes.</p>
<p>Now look again. Did something happen to the square?</p>
<p><a href="square1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2"><img src="https://divergences.xyz/posts/group-equivariant-cnn-1/square1.png" class="img-fluid" alt="A square in its default position, aligned horizontally to a virtual (invisible) x-axis."></a></p>
<p>You can’t tell. Maybe it was rotated; maybe it was not. On the other hand, what if the vertices were numbered?</p>
<p><a href="square2.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3"><img src="https://divergences.xyz/posts/group-equivariant-cnn-1/square2.png" class="img-fluid" alt="A square in its default position, with vertices numbered from 1 to 4, starting in the lower right corner and counting ant-clockwise."></a></p>
<p>Now you’d know.</p>
<p>Without the numbering, could I have rotated the square in any way I wanted? Evidently not. This would not go through unnoticed:</p>
<p><a href="square3.png" class="lightbox" data-gallery="quarto-lightbox-gallery-4"><img src="https://divergences.xyz/posts/group-equivariant-cnn-1/square3.png" class="img-fluid" alt="A square, rotated anti-clockwise by a few degrees."></a></p>
<p>There are exactly four ways I could have rotated the square without raising suspicion. Those ways can be referred to in different ways; one simple way is by degree of rotation: 90, 180, or 270 degrees. Why not more? Any further addition of 90 degrees would result in a configuration we’ve already seen.</p>
<p><a href="square4.png" class="lightbox" data-gallery="quarto-lightbox-gallery-5"><img src="https://divergences.xyz/posts/group-equivariant-cnn-1/square4.png" class="img-fluid" alt="Four squares, with numbered vertices each. The first has vertex 1 on the lower right, the second one rotation up, on the upper right, and so on."></a></p>
<p>The above picture shows three squares, but I’ve listed three possible rotations. What about the situation on the left, the one I’ve taken as an initial state? It could be reached by rotating 360 degrees (or twice that, or thrice, or …) But the way this is handled, in math, is by treating it as some sort of “null rotation”, analogously to how <img src="https://latex.codecogs.com/png.latex?0"> acts in addition, <img src="https://latex.codecogs.com/png.latex?1"> in multiplication, or the identity matrix in linear algebra.</p>
<p>Altogether, we thus have four <em>actions</em> that could be performed on the square (an un-numbered square!) that would leave it as-is, or <em>invariant</em>. These are called the <em>symmetries</em> of the square. A symmetry, in math/physics, is a quantity that remains the same no matter what happens as time evolves. And this is where groups come in. <em>Groups</em> – concretely, their <em>elements</em> – effectuate actions like rotation.</p>
<p>Before I spell out how, let me give another example. Take this sphere.</p>
<p><a href="sphere.png" class="lightbox" data-gallery="quarto-lightbox-gallery-6"><img src="https://divergences.xyz/posts/group-equivariant-cnn-1/sphere.png" class="img-fluid" alt="A sphere, colored uniformly."></a></p>
<p>How many symmetries does a sphere have? Infinitely many. This implies that whatever group is chosen to act on the square, it won’t be much good to represent the symmetries of the sphere.</p>
</section>
<section id="viewing-groups-through-the-action-lens" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="viewing-groups-through-the-action-lens">Viewing groups through the <em>action</em> lens</h3>
<p>Following these examples, let me generalize. Here is typical definition. <sup>2</sup></p>
<div class="no-row-height column-margin column-container"><div id="fn2"><p><sup>2</sup>&nbsp;From <a href="https://mathworld.wolfram.com/Group.html">Wolfram Alpha</a> (beginning of article).</p></div></div><blockquote class="blockquote">
<p>A group <img src="https://latex.codecogs.com/png.latex?G"> is a finite or infinite set of elements together with a binary operation (called the group operation) that together satisfy the four fundamental properties of closure, associativity, the identity property, and the inverse property. The operation with respect to which a group is defined is often called the “group operation,” and a set is said to be a group “under” this operation. Elements <img src="https://latex.codecogs.com/png.latex?A">, <img src="https://latex.codecogs.com/png.latex?B">, <img src="https://latex.codecogs.com/png.latex?C">, … with binary operation between <img src="https://latex.codecogs.com/png.latex?A"> and <img src="https://latex.codecogs.com/png.latex?B"> denoted <img src="https://latex.codecogs.com/png.latex?AB"> form a group if</p>
<ol type="1">
<li><p>Closure: If <img src="https://latex.codecogs.com/png.latex?A"> and <img src="https://latex.codecogs.com/png.latex?B"> are two elements in <img src="https://latex.codecogs.com/png.latex?G">, then the product <img src="https://latex.codecogs.com/png.latex?AB"> is also in <img src="https://latex.codecogs.com/png.latex?G">.</p></li>
<li><p>Associativity: The defined multiplication is associative, i.e., for all <img src="https://latex.codecogs.com/png.latex?A">,<img src="https://latex.codecogs.com/png.latex?B">,<img src="https://latex.codecogs.com/png.latex?C"> in <img src="https://latex.codecogs.com/png.latex?G">, <img src="https://latex.codecogs.com/png.latex?(AB)C=A(BC)">.</p></li>
<li><p>Identity: There is an identity element <img src="https://latex.codecogs.com/png.latex?I"> (a.k.a. <img src="https://latex.codecogs.com/png.latex?1">, <img src="https://latex.codecogs.com/png.latex?E">, or <img src="https://latex.codecogs.com/png.latex?e">) such that <img src="https://latex.codecogs.com/png.latex?IA=AI=A"> for every element <img src="https://latex.codecogs.com/png.latex?A"> in <img src="https://latex.codecogs.com/png.latex?G">.</p></li>
<li><p>Inverse: There must be an inverse (a.k.a. reciprocal) of each element. Therefore, for each element <img src="https://latex.codecogs.com/png.latex?A"> of <img src="https://latex.codecogs.com/png.latex?G">, the set contains an element <img src="https://latex.codecogs.com/png.latex?B=A%5E%7B-1%7D"> such that <img src="https://latex.codecogs.com/png.latex?AA%5E%7B-1%7D=A%5E%7B-1%7DA=I">.</p></li>
</ol>
</blockquote>
<p>In action-speak, group elements specify allowable actions; or more precisely, ones that are distinguishable from each other. Two actions can be composed; that’s the “binary operation”. The requirements now make intuitive sense:</p>
<ol type="1">
<li>A combination of two actions – two rotations, say – is still an action of the same type (a rotation).</li>
<li>If we have three such actions, it doesn’t matter how we group them. (Their order of application has to remain the same, though.)</li>
<li>One possible action is always the “null action”. (Just like in life.) As to “doing nothing”, it doesn’t make a difference if that happens before or after a “something”; that “something” is always the final result.</li>
<li>Every action needs to have an “undo button”. In the squares example, if I rotate by 180 degrees, and then, by 180 degrees again, I am back in the original state. It is if I had done <em>nothing</em>.</li>
</ol>
<p>Resuming a more “birds-eye view”, what we’ve seen right now is the definition of a group by how its elements act on each other. But if groups are to matter “in the real world”, they need to act on something outside (neural network components, for example). How this works is the topic of the following posts, but I’ll briefly outline the intuition here.</p>
</section>
</section>
<section id="outlook-group-equivariant-cnn" class="level2">
<h2 class="anchored" data-anchor-id="outlook-group-equivariant-cnn">Outlook: Group-equivariant CNN</h2>
<p>Above, we noted that, in image classification, a <em>translation</em>-invariant operation (like convolution) is needed: A <img src="https://latex.codecogs.com/png.latex?1"> is a <img src="https://latex.codecogs.com/png.latex?1"> whether moved horizontally, vertically, both ways, or not at all. What about rotations, though? Standing on its head, a digit is still what it is. Conventional convolution does not support this type of action.</p>
<p>We can add to our architectural wishlist by specifying a symmetry group. What group? If we wanted to detect squares aligned to the axes, a suitable group would be <img src="https://latex.codecogs.com/png.latex?C_4">, the cyclic group of order four. (Above, we saw that we needed four elements, and that we could <em>cycle</em> through the group.) If, on the other hand, we don’t care about alignment, we’d want <em>any</em> position to count. In principle, we should end up in the same situation as we did with the sphere. However, images live on discrete grids; there won’t be an unlimited number of rotations in practice.</p>
<p>With more realistic applications, we need to think more carefully. Take digits. When <em>is</em> a number “the same”? For one, it depends on the context. Were it about a hand-written address on an envelope, would we accept a <img src="https://latex.codecogs.com/png.latex?7"> as such had it been rotated by 90 degrees? Maybe. (Although we might wonder what would make someone change ball-pen position for just a single digit.) What about a <img src="https://latex.codecogs.com/png.latex?7"> standing on its head? On top of similar psychological considerations, we should be seriously unsure about the intended message, and, at least, down-weight the data point were it part of our training set.</p>
<p>Importantly, it also depends on the digit itself. A <img src="https://latex.codecogs.com/png.latex?6">, upside-down, is a <img src="https://latex.codecogs.com/png.latex?9">.</p>
<p>Zooming in on neural networks, there is room for yet more complexity. We know that CNNs build up a hierarchy of features, starting from simple ones, like edges and corners. Even if, for later layers, we may not want rotation equivariance, we would still like to have it in the initial set of layers. (The output layer – we’ve hinted at that already – is to be considered separately in any case, since its requirements result from the specifics of what we’re tasked with.)</p>
<p>That’s it for today. Hopefully, I’ve managed to illuminate a bit of <em>why</em> we would want to have group-equivariant neural networks. The question remains: How do we get them? This is what the subsequent posts in the series will be about.</p>
<p>Till then, and thanks for reading!</p>
<div class="photo-credit">
<p>Photo by <a href="https://unsplash.com/@oinua?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Ihor OINUA</a> on <a href="https://unsplash.com/photos/1kKXKmcEtiA?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-1/</guid>
  <pubDate>Thu, 09 Mar 2023 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/group-equivariant-cnn-1/preview.jpg" medium="image" type="image/jpeg"/>
</item>
</channel>
</rss>
