<?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/category/deep-learning.html</link>
<atom:link href="https://divergences.xyz/category/deep-learning.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, 20 Jun 2023 00:00:00 GMT</lastBuildDate>
<item>
  <title>What are Large Language Models? What are they not?</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/llm-intro/</link>
  <description><![CDATA[ 






<div class="page-columns page-full"><blockquote class="blockquote">
<p>“At this writing, the only serious ELIZA scripts which exist are some which cause ELIZA to respond roughly as would certain psychotherapists (Rogerians). ELIZA performs best when its human correspondent is initially instructed to”talk” to it, via the typewriter of course, just as one would to a psychiatrist. This mode of conversation was chosen because the psychiatric interview is one of the few examples of categorized dyadic natural language communication in which one of the participating pair is free to assume the pose of knowing almost nothing of the real world. If, for example, one were to tell a psychiatrist “I went for a long boat ride” and he responded “Tell me about boats”, one would not assume that he knew nothing about boats, but that he had some purpose in so directing the subsequent conversation. It is important to note that this assumption is one made by the speaker. Whether it is realistic or not is an altogether separate question. In any case, it has a crucial psychological utility in that it serves the speaker to maintain his sense of being heard and understood. The speaker furher defends his impression (which even in real life may be illusory) by attributing to his conversational partner all sorts of background knowledge, insights and reasoning ability. But again, these are the speaker’s contribution to the conversation.”</p>
<p>Joseph Weizenbaum, creator of ELIZA <span class="citation" data-cites="365153.365168">(Weizenbaum 1966)</span>.</p>
</blockquote><div class="no-row-height column-margin column-container"><div id="ref-365153.365168" class="csl-entry">
Weizenbaum, Joseph. 1966. <span>“ELIZA - a Computer Program for the Study of Natural Language Communication Between Man and Machine.”</span> <em>Commun. ACM</em> (New York, NY, USA) 9 (1): 36–45. <a href="https://doi.org/10.1145/365153.365168">https://doi.org/10.1145/365153.365168</a>.
</div></div></div>
<p>GPT, the ancestor all numbered <a href="https://en.wikipedia.org/wiki/Generative_pre-trained_transformer">GPTs</a>, was released in June, 2018 – five years ago, as I write this. Five years: that’s a long time. It certainly is as measured on the time scale of deep learning, the thing that is, usually, behind when people talk of “AI”. One year later, GPT was followed by GPT-2; another year later, by GPT-3. At this point, public attention was still modest – as expected, really, for these kinds of technologies that require lots of specialist knowledge. (For GPT-2, what may have increased attention beyond the normal, a bit, was OpenAI ’s refusal to publish the complete training code and full model weights, supposedly due to the threat posed by the model’s capabilities – alternatively, as argued by others, as a marketing strategy, or yet alternatively, as a way to preserve one’s own competitive advantage just a tiny little bit longer.</p>
<p>As of 2023, with GPT-3.5 and GPT-4 having followed, everything looks different. (Almost) everyone seems to know GPT, at least when that acronym appears prefixed by a certain syllable. Depending on who you talk to, people don’t seem to stop talking about that fantastic [insert thing here] ChatGPT generated for them, about its enormous usefulness with respect to [insert goal here]… or about the flagrant mistakes it made, and the danger that legal regulation and political enforcement will never be able to catch up.</p>
<p>What made the difference? Obviously, it’s <a href="https://en.wikipedia.org/wiki/ChatGPT">ChatGPT</a>, or put differently, the fact that now, there is a means for people to make active use of such a tool, employing it for whatever their personal needs or interests are<sup>1</sup>. In fact, I’d argue it’s more than that: ChatGPT is not some impersonal tool – it <em>talks</em> to you, picking up your clarifications, changes of topic, mood… It is <em>someone</em> rather than <em>something</em>, or at least that’s how it seems. I’ll come back to that point in It’s us, really: Anthropomorphism unleashed. Before, let’s take a look at the underlying technology.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;Evidently, this is not about singling out ChatGPT as opposed to other chatbots; rather, I’m adopting it as the prototypical such application, since it is the one omnipresent in the media these days.</p></div></div><section id="large-language-models-what-they-are" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="large-language-models-what-they-are">Large Language Models: What they are</h2>
<p>How is it even possible to build a machine that talks to you? One way is to have that machine <em>listen</em> a lot. And listen is what these machines do; they do it a lot. But listening alone would never be enough to attain results as impressive as those we see. Instead, LLMs practice some form of “maximally active listening”: Continuously, they try to predict the speaker’s next utterance. By “continuously”, I mean word-by-word: At each training step, the model is asked to produce the subsequent word in a text.</p>
<p>Maybe in my last sentence, you noted the term “train”. As per common sense, “training” implies some form of supervision. It also implies some form of method. Since learning material is scraped from the internet, the true continuation is always known. The precondition for supervision is thus always fulfilled: A supervisor can just compare model prediction with what really follows in the text. Remains the question of method. That’s where we need to talk about deep learning, and we’ll do that in Model training.</p>
<section id="overall-architecture" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="overall-architecture">Overall architecture</h3>
<p>Today’s LLMs are, in some way or the other, based on an architecture known as the <em>Transformer</em>. This architecture was originally introduced in a paper catchily titled “Attention is all you need” <span class="citation" data-cites="vaswani2017attention">(Vaswani et al. 2017)</span>. Of course, this was not the first attempt at automating natural-language generation – not even in deep learning, the sub-type of machine learning whose defining characteristic are many-layered (“deep”) artificial neural networks. But there, in deep learning, it constituted some kind of paradigm change. Before, models designed to solve sequence-prediction tasks (time-series forecasting, text generation…) tended to be based on some form of recurrent architecture, introduced in the 1990’s (eternities ago, on the time scale of deep-learning) by <span class="citation" data-cites="lstm">(Hochreiter and Schmidhuber 1997)</span>. Basically, the concept of recurrence, with its associated threading of a latent state, was replaced by “attention”. That’s what the paper’s title was meant to communicate: The authors did not <em>introduce</em> “attention”<sup>2</sup>; instead, they fundamentally expanded its usage so as to render recurrence superfluous.</p>
<div class="no-row-height column-margin column-container"><div id="ref-vaswani2017attention" class="csl-entry">
Vaswani, Ashish, Noam Shazeer, Niki Parmar, et al. 2017. <em>Attention Is All You Need</em>. <a href="https://arxiv.org/abs/1706.03762">https://arxiv.org/abs/1706.03762</a>.
</div><div id="ref-lstm" class="csl-entry">
Hochreiter, Sepp, and Jürgen Schmidhuber. 1997. <span>“Long Short-Term Memory.”</span> <em>Neural Computation</em> 9 (December): 1735–80. <a href="https://doi.org/10.1162/neco.1997.9.8.1735">https://doi.org/10.1162/neco.1997.9.8.1735</a>.
</div><div id="fn2"><p><sup>2</sup>&nbsp;I’m using quotes to refer to how attention is <em>operationalized in deep learning</em>, as opposed to how it is conceptualized in cognitive science or psychology.</p></div><div id="fn3"><p><sup>3</sup>&nbsp;If you’re wondering how that is possible – shouldn’t there be a separate, top-level module for generation? – no, there need not be. That’s because training <em>implies</em> prediction.</p></div></div><p>How did that ancestral Transformer look? – One prototypical task in natural language processing is machine translation. In translation, be it done by a machine or by a human, there is an input (in one language) and an output (in another). That input, call it a <em>code</em>. Whoever wants to establish its counterpart in the target language first needs to <em>decode</em> it. Indeed, one of two top-level building blocks of the archetypal Transformer was a decoder, or rather, a stack of decoders applied in succession. At its end, out popped a phrase in the target language<sup>3</sup>. What, then, was the other high-level block? It was an <em>encoder</em>, something that takes text (or tokens, rather, i.e., something that has undergone tokenization) and converts it into a form the decoder can make sense of. (Obviously, there is no analogue to this in human translation.)</p>
<p>From this two-stack architecture, subsequent developments tended to keep just one. The GPT family, together with many others, just kept the decoder stack. Now, doesn’t the decoder need <em>some</em> kind of input – if not to translate to a different language, then to reply to, as in the chatbot scenario? Turns out that no, it doesn’t – and that’s why you can also have the bot initiate the conversation. Unbeknownst to you, there will, in fact, be an input to the model – some kind of token signifying “end of input”. In that case, the model will draw on its training experience to generate a word likely to start out a phrase. That one word will then become the new input to continue from, and so forth. Summing up so far, then, GPT-like LLMs are <em>Transformer Decoders</em>.</p>
<p>The question is, how does such a stack of decoders succeed in fulfilling the task?</p>
</section>
<section id="gpt-type-models-up-close" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="gpt-type-models-up-close">GPT-type models up close</h3>
<p>In opening the black box, we focus on its two interfaces – input and output – as well as on the internals, its core.</p>
<section id="input" class="level4 page-columns page-full">
<h4 class="anchored" data-anchor-id="input">Input</h4>
<p>For simplicity, let me speak of words, not tokens. Now imagine a machine that is to work with – more even: “understand”<sup>4</sup> – words. For a computer to process non-numeric data, a conversion to numbers necessarily has to happen. The straightforward way to effectuate this is to decide on a fixed lexicon, and assign each word a number. And this works: The way deep neural networks are trained, they don’t need semantic relationships to exist between entities in the training data to memorize formal structure. Does this mean they will appear perfect while training, but fail in real-world prediction? – If the training data are representative of how we converse, all will be fine. In a world of perfect surveillance, machines could exist that have internalized our every spoken word. Before that happens, though, the training data will be imperfect.</p>
<div class="no-row-height column-margin column-container"><div id="fn4"><p><sup>4</sup>&nbsp;Why the quotes? See Large Language Models: What they are not.</p></div><div id="fn5"><p><sup>5</sup>&nbsp;As a fascinating example from dynamical systems theory, take <a href="https://en.wikipedia.org/wiki/Takens%27s_theorem">delay coordinate embeddings</a>.</p></div><div id="fn6"><p><sup>6</sup>&nbsp;Suitably named <em>embedding layer.</em></p></div><div id="fn7"><p><sup>7</sup>&nbsp;See, for example, <span class="citation" data-cites="Caliskan_2022">(Caliskan et al. 2022)</span>.</p><div id="ref-Caliskan_2022" class="csl-entry">
Caliskan, Aylin, Pimparkar Parth Ajay, Tessa Charlesworth, Robert Wolfe, and Mahzarin R. Banaji. 2022. <span>“Gender Bias in Word Embeddings.”</span> <em>Proceedings of the 2022 <span>AAAI</span>/<span>ACM</span> Conference on <span>AI</span>, Ethics, and Society</em>, July. <a href="https://doi.org/10.1145/3514094.3534162">https://doi.org/10.1145/3514094.3534162</a>.
</div></div></div><p>A much more promising approach than to simply index words, then, is to represent them in a richer, higher-dimensional space, an <em>embedding</em> space. This idea, popular not just in deep learning but in natural language processing overall, really goes far beyond anything domain-specific – linguistic entities, say<sup>5</sup>. You may be able to fruitfully employ it in virtually any domain – provided you can devise a method to sensibly map the given data into that space. In deep learning, these embeddings are obtained in a clever way: as a by-product of sorts of the overall training workflow. Technically, this is achieved by means of a dedicated neural-network layer<sup>6</sup> tasked with evolving these mappings. Note how, smart though this strategy may be, it implies that the overall setting – everything from training data via model architecture to optimization algorithms employed – necessarily affects the resulting embeddings. And since these may be extracted and made use of in down-stream tasks, this matters<sup>7</sup>.</p>
<p>As to the GPT family, such an embedding layer constitutes part of its input interface – one “half”, so to say. Technically, the second makes use of the same type of layer, but with a different purpose. To contrast the two, let me spell out clearly what, in the part we’ve talked about already, is getting mapped to what. The mapping is between a word index – a sequence <code>1, 2, …, &lt;vocabulary size&gt;</code> – on the one hand and a set of continuous-valued vectors of some length – 100, say – on the other. (One of them could like this: <img src="https://latex.codecogs.com/png.latex?%5Cbegin%7Bbmatrix%7D%201.002%20&amp;%200.71%20&amp;%200.0004%20&amp;...%5C%5C%20%5Cend%7Bbmatrix%7D">) Thus, we obtain an embedding for every word. But language is more than an unordered assembly of words. Rearranging words, if syntactically allowed, may result in drastically changed semantics. In the pre-transformer paradigma, threading a sequentially-updated hidden state took care of this. Put differently, in that type of model, information about input order never got lost throughout the layers. Transformer-type architectures, however, need to find a different way. Here, a variety of rivaling methods exists. Some assume an underlying periodicity in semanto-syntactic structure. Others – and the GPT family, as yet and <em>insofar</em> <em>we know</em>, has been part of them<sup>8</sup> – approach the challenge in exactly the same way as for the lexical units: They make learning these so-called <em>position embeddings</em> a by-product of model training. Implementation-wise, the only difference is that now the input to the mapping looks like this: <code>1, 2, …, &lt;maximum position&gt;</code> where “maximum position” reflects choice of maximal sequence length supported.</p>
<div class="no-row-height column-margin column-container"><div id="fn8"><p><sup>8</sup>&nbsp;For GPT-4, even high-level model information has not been released.</p></div></div><p>Summing up, verbal input is thus encoded – <em>embedded</em>, enriched – twofold as it enters the machine. The two types of embedding are combined and passed on to the model core, the already-mentioned decoder stack.</p>
</section>
<section id="core-processing" class="level4 page-columns page-full">
<h4 class="anchored" data-anchor-id="core-processing">Core Processing</h4>
<p>The decoder stack is made up of some number of identical blocks (12, in the case of GPT-2). (By “identical” I mean that the architecture is the same; the <em>weights</em> – the place where a neural-network layer stores what it “knows” – are not. More on these “weights” soon.)</p>
<p>Inside each block, some sub-layers are pretty much “business as usual”. One is not: the attention module, the “magic” ingredient that enabled Transformer-based architectures to forego keeping a latent state. To explain how this works, let’s take translation as an example.</p>
<p>In the classical encoder-decoder setup, the one most intuitive for machine translation, imagine the very first decoder in the stack of decoders. It receives as input a length-seven cypher, the encoded version of an original length-seven phrase. Since, due to how the encoder blocks are built, input order is conserved, we have a faithful representation of source-language word order. In the target language, however, word order can be very different. A decoder module, in producing the translation, had rather not do this by translating each word as it appears. Instead, it would be desirable for it to know which among the already-seen tokens is most relevant right now, to generate the very next output token. Put differently, it had better know where to direct its <em>attention</em>.</p>
<p>Thus, figure out how to distribute focus is what attention modules do. How do they do it? They compute, for each available input-language token, how good a match, a fit, it is for their own current input. Remember that every token, at every processing stage, is encoded as a vector of continuous values. How good a match any of, say, three source-language vectors is is then computed by projecting one’s current input vector onto each of the three. The closer the vectors, the longer the projected vector. <sup>9</sup> Based on the projection onto each source-input token, that token is weighted, and the attention module passes on the aggregated assessments to the ensuing neural-network module.</p>
<div class="no-row-height column-margin column-container"><div id="fn9"><p><sup>9</sup>&nbsp;Mathematically, this is achieved by a pretty standard and pervasively-used, in machine learning, operation, the <em>dot product</em>.</p></div></div><p>To explain what attention modules are for, I’ve made use of the machine-translation scenario, a scenario that should lend a certain intuitiveness to the operation. But for GPT-family models, we need to abstract this a bit. First, there is no encoder stack, so “attention” is computed among decoder-resident tokens only. And second – remember I said a stack was built up of identical modules? – this happens in every decoder block. That is, when intermediate results are bubbled up the stack, at each stage the input is weighted as appropriate <em>at that stage</em>. While this is harder to intuit than what happened in the translation scenario, I’d argue that in the abstract, it makes a lot of sense. For an analogy, consider some form of hierarchical categorization of entities. As higher-level categories are built from lower-level ones, at each stage the process needs to look at its input afresh, and decide on a sensible way of subsuming similar-in-some-way categories.</p>
</section>
<section id="output" class="level4">
<h4 class="anchored" data-anchor-id="output">Output</h4>
<p>Stack of decoders traversed, the multi-dimensional codes that pop out need to be converted into something that can be compared with the actual phrase continuation we see in the training corpus. Technically, this involves a projection operation as well a strategy for picking the output word – that word in target-language vocabulary that has the highest probability. How do you decide on a strategy? I’ll say more about that in the section Mechanics of text generation, where I assume a chatbot user’s perspective.</p>
</section>
</section>
<section id="model-training" class="level3">
<h3 class="anchored" data-anchor-id="model-training">Model training</h3>
<p>Before we get there, just a quick word about model training. LLMs are deep neural networks, and as such, they are trained like any network is. First, assuming you have access to the so-called “ground truth”, you can always compare model prediction with the true target. You then quantify the difference – by which algorithm will affect training results. Then, you communicate that difference – the <em>loss</em> – to the network. It, in turn, goes through its modules, from back/top to start/bottom, and updates its stored “knowledge” – matrices of continuous numbers called <em>weights</em>. Since information is passed from layer to layer, in a direction reverse to that followed in computing predictions, this technique is known as <em>back-propagation</em>.</p>
<p>And all that is not triggered once, but iteratively, for a certain number of so-called “epochs”, and modulated by a set of so-called “hyper-parameters”. In practice, a lot of experimentation goes into deciding on the best-working configuration of these settings.</p>
</section>
<section id="mechanics-of-text-generation" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="mechanics-of-text-generation">Mechanics of text generation</h3>
<p>We already know that during model training, predictions are generated word-by-word; at every step, the model’s knowledge about what has been said so far is augmented by one token: the word that really was following at that point. If, making use of a trained model, a bot is asked to reply to a question, its response must by necessity be generated in the same way. However, the actual “correct word” is not known. The only way, then, is to feed back to the model its own most recent prediction. (By necessity, this lends to text generation a very special character, where every decision the bot makes co-determines its future behavior.)</p>
<p>Why, though, talk about decisions? Doesn’t the bot just act on behalf of the core model, the LLM – thus passing on the final output? Not quite. At each prediction step, the model yields a vector, with values as many as there are entries in the vocabulary. As per model design and training rationale, these vectors are “scores” – ratings, sort of, how good a fit a word would be in this situation. Like in life, higher is better. But that doesn’t mean you’d just pick the word with the highest value. In any case, these scores are converted to probabilities, and a suitable probability distribution is used to non-deterministically pick a likely (or likely-ish) word. The probability distribution commonly used is the multinomial distribution, appropriate for discrete choice among more than two alternatives. But what about the conversion to probabilities? Here, there is room for experimentation.</p>
<p>Technically, the algorithm employed is known as the <em>softmax</em> function. It is a simplified version of the <a href="https://en.wikipedia.org/wiki/Boltzmann_distribution">Boltzmann distribution</a>, famous in statistical mechanics, used to obtain the probability of a system’s state given that state’s energy and the temperature of the system. But for temperature<sup>10</sup>, both formulae are, in fact, identical. In physical systems, temperature modulates probabilities in the following way: The hotter the system, the closer the states’ probabilities are to each other; the colder it gets, the more distinct those probabilities. In the extreme, at very low temperatures there will be a few clear “winners” and a silent majority of “losers”.</p>
<div class="no-row-height column-margin column-container"><div id="fn10"><p><sup>10</sup>&nbsp;… and the Boltzmann constant – but that being a constant, we don’t consider it here.</p></div></div><p>In deep learning, a like effect is easy to achieve (by means of a scaling factor). That’s why you may have heard people talk about some weird thing called “temperature” that resulted in [insert adjective here] answers. If the application you use lets you vary that factor, you’ll see that a low temperature will result in deterministic-looking, repetitive, “boring” continuations, while a high one may make the machine appear as though it were on drugs.</p>
<p>That concludes our high-level overview of LLMs. Having seen the machine dissected in this way may already have left you with some sort of opinion of what these models are – not. This topic more than deserves a dedicated exposition – and papers are being written pointing to important aspects all the time – but in this text, I’d like to at least offer some input for thought.</p>
</section>
</section>
<section id="large-language-models-what-they-are-not" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="large-language-models-what-they-are-not">Large Language Models: What they are not</h2>
<p>In part one,describing LLMs technically, I’ve sometimes felt tempted to use terms like “understanding” or “knowledge” when applied to the machine. I may have ended up using them; in that case, I’ve tried to remember to always surround them with quotes. The latter, the adding quotes, stands in contrast to many texts, even ones published in an academic context <span class="citation" data-cites="bender-koller-2020-climbing">(Bender and Koller 2020)</span>. The question is, though: Why did I even feel compelled to use these terms, given I do <em>not</em> think they apply, in their usual meaning? I can think of a simple – shockingly simple, maybe – answer: It’s because us, humans, we think, talk, share our thoughts in these terms. When I say <em>understand</em>, I surmise you will <em>know</em> what I <em>mean</em>.</p>
<div class="no-row-height column-margin column-container"></div><p>Now, why do I think that these machines do not <em>understand</em> human language, in the sense we usually imply when using that word?</p>
<section id="a-few-facts" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="a-few-facts">A few facts</h3>
<p>I’ll start out briefly mentioning empirical results, conclusive thought experiments, and theoretical considerations. All aspects touched upon (and many more) are more than worthy of in-depth discussion, but such discussion is clearly out of scope for this synoptic-in-character text.</p>
<p>First, while it is hard to put a number on the quality of a chatbot’s answers, performance on standardized benchmarks is the “bread and butter” of machine learning – its reporting being an essential part of the prototypical deep-learning publication. (You could even call it the “cookie”, the driving incentive, since models usually are explicitly trained and fine-tuned for good results on these benchmarks.) And such benchmarks exist for most of the down-stream tasks the LLMs are used for: machine translation, generating summaries, text classification, and even rather ambitious-sounding setups associated with – quote/unquote – reasoning.</p>
<p>How do you assess such a capability? Here is an example from a benchmark named “Argument Reasoning Comprehension Task” <span class="citation" data-cites="habernal-etal-2018-argument">(Habernal et al. 2018)</span>.</p>
<div class="no-row-height column-margin column-container"><div id="ref-habernal-etal-2018-argument" class="csl-entry">
Habernal, Ivan, Henning Wachsmuth, Iryna Gurevych, and Benno Stein. 2018. <span>“The Argument Reasoning Comprehension Task: Identification and Reconstruction of Implicit Warrants.”</span> <em>Proceedings of the 2018 Conference of the North <span>A</span>merican Chapter of the Association for Computational Linguistics: Human Language Technologies, Volume 1 (Long Papers)</em> (New Orleans, Louisiana), June, 1930–40. <a href="https://doi.org/10.18653/v1/N18-1175">https://doi.org/10.18653/v1/N18-1175</a>.
</div></div><pre><code>Claim: Google is not a harmful monopoly
Reason: People can choose not to use Google
Warrant: Other search engines don’t redirect to Google
Alternative: All other search engines redirect to Google</code></pre>
<p>Here <em>claim</em> and <em>reason</em> together make up the <em>argument</em>. But what, exactly, is it that links them? At first look, this can even be confusing to a human. The missing link is what is called warrant here – add it in, and it all starts to make sense. The task, then, is to decide which of warrant or alternative supports the conclusion, and which one does not.</p>
<p>If you think about it, this is a surprisingly challenging task. Specifically, it seems to inescapingly require <em>world knowledge</em>. So if language models, as has been claimed, perform nearly as well as humans, it seems they must have such knowledge – no quotes added. However, in response to such claims, research has been performed to uncover the hidden mechanism that enables such seemingly-superior results. For that benchmark, it has been found <span class="citation" data-cites="abs-1907-07355">(Niven and Kao 2019)</span> that there were spurious statistical cues in the way the dataset was constructed – those removed, LLM performance was no better than random.</p>
<div class="no-row-height column-margin column-container"><div id="ref-abs-1907-07355" class="csl-entry">
Niven, Timothy, and Hung-Yu Kao. 2019. <span>“Probing Neural Network Comprehension of Natural Language Arguments.”</span> <em>CoRR</em> abs/1907.07355. <a href="http://arxiv.org/abs/1907.07355">http://arxiv.org/abs/1907.07355</a>.
</div><div id="fn11"><p><sup>11</sup>&nbsp;That choice of species is probably not a coincidence: see <a href="https://en.wikipedia.org/wiki/Cephalopod_intelligence" class="uri">https://en.wikipedia.org/wiki/Cephalopod_intelligence</a>.</p></div></div><p>World knowledge, in fact, is one of the main things an LLM lacks. Bender et al. <span class="citation" data-cites="bender-koller-2020-climbing">(Bender and Koller 2020)</span> convincingly demonstrate its essentiality by means of two thought experiments. One of them, situated on a lone island, imagines an octopus<sup>11</sup> inserting itself into some cable-mediated human communication, learning the chit-chat, and finally – having gotten bored – impersonating one of the humans. This works fine, until one day, its communication partner finds themselves in an emergency, and needs to build some rescue tool out of things given in the environment. They urgently ask for advice – and the octopus has no idea what to respond. It has no ideas what these words actually <em>refer to</em>.</p>
<p>The other argument comes directly from machine learning, and strikingly simple though it may be, it makes its point very well. Imagine an LLM trained as usual, including on lots of text involving plants. It has also been trained on a dataset of unlabeled photos, the actual task being unsubstantial – say it had to fill out masked areas. Now, we pull out a picture and ask: How many of that blackberry’s blossoms have already opened? The model has no chance to answer the question.</p>
<p>Now, please look back at the Joseph Weizenbaum quote I opened this article with. It is still true that language-generating machine have no knowledge of the world we live in.</p>
<p>Before moving on, I’d like to just quickly hint at a totally different type of consideration, brought up in a (2003!) paper by Spärck Jones <span class="citation" data-cites="Sprck2004LanguageM">(Spaerck 2004)</span>. Though written long before LLMs, and long before deep learning started its winning conquest, on an abstract level it is still very applicable to today’s situation. Today, LLMs are employed to “learn language”, i.e., for language acquisition. That skill is then built upon by specialized models, of task-dependent architecture. Popular real-world<sup>12</sup> down-stream tasks are translation, document retrieval, or text summarization. When the paper was written, there was no such two-stage pipeline. The author was questioning the fit between how language modeling was conceptualized – namely, as a form of <em>recovery</em> – and the character of these down-stream tasks. Was recovery – inferring a missing, for whatever reasons – piece of text a good model, of, say, condensing a long, detailed piece of text into a short, concise, factual one? If not, could the reason it still seemed to work just fine be of a very different nature – a technical, operational, coincidental one?</p>
<div class="no-row-height column-margin column-container"><div id="fn12"><p><sup>12</sup>&nbsp;As opposed to the aforementioned problems subsumed under “reasoning”, those having been constructed for research purposes.</p></div></div><div class="page-columns page-full"><blockquote class="blockquote">
<p>[…] the crucial characterisation of the relationship between the input and the output is in fact offloaded in the LM approach onto the choice of training data. We can use LM for summarising because we know that some set of training data consists of full texts paired with their summaries.<sup>13</sup></p>
</blockquote><div class="no-row-height column-margin column-container"><div id="fn13"><p><sup>13</sup>&nbsp;From <span class="citation" data-cites="Sprck2004LanguageM">(Spaerck 2004)</span>.</p><div id="ref-Sprck2004LanguageM" class="csl-entry">
Spaerck, Karen. 2004. <span>“Language Modelling’s Generative Model : Is It Rational?”</span>
</div></div></div></div>
<p>It seems to me that today’s two-stage process notwithstanding, this is still an aspect worth giving some thought.</p>
</section>
<section id="its-us-language-learning-shared-goals-and-a-shared-world" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="its-us-language-learning-shared-goals-and-a-shared-world">It’s us: Language learning, shared goals, and a shared world</h3>
<p>We’ve already talked about world knowledge. What else are LLMs missing out on?</p>
<p>In our world, you’ll hardly find anything that does not involve other people. This goes a lot deeper than the easily observable facts: our constantly communicating, reading and typing messages, documenting our lives on social networks… We don’t experience, explore, explain a world of our own. Instead, all these activities are inter-subjectively constructed. Feelings are<sup>14</sup>. Cognition is; meaning is. And it goes deeper yet. Implicit assumptions guide us to constantly look for meaning, be it in overheard fragments, mysterious symbols, or life events.</p>
<div class="no-row-height column-margin column-container"><div id="fn14"><p><sup>14</sup>&nbsp;See <a href="https://lisafeldmanbarrett.com/books/how-emotions-are-made/" class="uri">https://lisafeldmanbarrett.com/books/how-emotions-are-made/</a>.</p></div></div><p>How does this relate to LLMs? For one, they’re islands of their own. When you ask them for advice – to develop a research hypothesis and a matching operationalization, say, or whether a detainee should be released on parole – they have no stakes in the outcome, no motivation (be it intrinsic or extrinsic), no goals. If an innocent person is harmed, they don’t feel the remorse; if an experiment is successful but lacks explanatory power, they don’t sense the shallowness; if the world blows up, it won’t have been <em>their</em> world.</p>
<p>Secondly, it’s us who are <em>not</em> islands. In Bender et al.’s octopus scenario, the human on one side of the cable plays an active role not just when they speak. In making sense of what the octopus says, they contribute an essential ingredient: namely, what they think the octopus wants, thinks, feels, expects… Anticipating, they reflect on what the octopus anticipates.</p>
<p>As Bender et al.&nbsp;put it:</p>
<blockquote class="blockquote">
<p>It is not that O’s utterances make sense, but rather, that A can make sense of them.</p>
</blockquote>
<p>That article <span class="citation" data-cites="bender-koller-2020-climbing">(Bender and Koller 2020)</span> also brings impressive evidence from human language acquisition: Our predisposition towards language learning notwithstanding, infants don’t learn from the availability of input alone. A situation of <em>joint attention</em> is needed for them to learn. Psychologizing, one could hypothesize they need to get the impression that these sounds, these words, and the fact they’re linked together, actually matters.</p>
<div class="no-row-height column-margin column-container"><div id="ref-bender-koller-2020-climbing" class="csl-entry">
Bender, Emily M., and Alexander Koller. 2020. <span>“Climbing Towards <span>NLU</span>: <span>On</span> Meaning, Form, and Understanding in the Age of Data.”</span> <em>Proceedings of the 58th Annual Meeting of the Association for Computational Linguistics</em> (Online), July, 5185–98. <a href="https://doi.org/10.18653/v1/2020.acl-main.463">https://doi.org/10.18653/v1/2020.acl-main.463</a>.
</div></div><p>Let me conclude, then, with my final “psychologization”.</p>
</section>
<section id="its-us-really-anthropomorphism-unleashed" class="level3">
<h3 class="anchored" data-anchor-id="its-us-really-anthropomorphism-unleashed">It’s us, <em>really</em>: Anthropomorphism unleashed</h3>
<p>Yes, it is amazing what these machines do. (And that makes them incredibly dangerous power instruments.) But this in no way affects the human-machine differences that have been existing throughout history, and continue to exist today. That we are inclined to think they understand, know, mean – that maybe even they’re conscious: that’s on us. We can experience deep emotions watching a movie; hope that if we just try enough, we can sense what a distant-in-evolutionary-genealogy creature is feeling; see a cloud encouragingly smiling at us; read a sign in an arrangement of pebbles.</p>
<p>Our inclination to anthropomorphize is a gift; but it can sometimes be harmful. And nothing of this is special to the twenty-first century.</p>
<p>Like I began with him, let me conclude with Weizenbaum.</p>
<blockquote class="blockquote">
<p>Some subjects have been very hard to convince that ELIZA (with its present script) is <em>not</em> human.</p>
</blockquote>
<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</a> on <a href="https://unsplash.com/photos/8TLfX3-705M?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
</div>



</section>
</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>AI Societal Impact</category>
  <category>Philosophy (sort of)</category>
  <guid>https://divergences.xyz/posts/llm-intro/</guid>
  <pubDate>Tue, 20 Jun 2023 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/llm-intro/preview.jpg" medium="image" type="image/jpeg"/>
</item>
<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>
<item>
  <title>Deep Learning and Scientific Computing with R torch: the book</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/deep-learning-scientific-computing-R-torch/</link>
  <description><![CDATA[ 






<p>First things first: Where can you get it? As of today, you can download the <a href="https://www.taylorfrancis.com/books/mono/10.1201/9781003275923/deep-learning-scientific-computing-torch-sigrid-keydana">e-book</a> or order a <a href="https://www.routledge.com/Deep-Learning-and-Scientific-Computing-with-R-torch/Keydana/p/book/9781032231396">print copy</a> from the publisher, CRC Press; the free online edition is <a href="https://skeydan.github.io/Deep-Learning-and-Scientific-Computing-with-R-torch/">here</a>. There is, to my knowledge, no problem to perusing the online version – besides one: It doesn’t have the squirrel that’s on the book cover.</p>
<p><a href="squirrel.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://divergences.xyz/posts/deep-learning-scientific-computing-R-torch/squirrel.png" class="img-fluid" alt="A red squirrel on a tree, looking attentively."></a></p>
<p>So if you’re a lover of amazing creatures…</p>
<section id="whats-in-the-book" class="level3">
<h3 class="anchored" data-anchor-id="whats-in-the-book">What’s in the book?</h3>
<p>Deep Learning and Scientific Computing with R <code>torch</code> has three parts.</p>
<p>The first covers the indispensible basics: tensors, and how to manipulate them; automatic differentiation, the <em>sine qua non</em> of deep learning; optimization, the strategy that drives most of what we call artificial intelligence; and neural-network modules, <code>torch's</code> way of encapsulating algorithmic flow. The focus is on understanding the concepts, on how things “work” – that’s why we do things like code a neural network from scratch, something you’ll probably never do in later use.</p>
<p>Foundations laid, part two – considerably more sizeable – dives into deep-learning applications. It is here that the ecosystem surrounding core <code>torch</code> enters the spotlight. First, we see how <code>luz</code> automates and considerably simplifies many programming tasks related to network training, performance evaluation, and prediction. Making use of the wrappers and instrumentation facilities it provides, we next learn about two aspects of deep learning no real-world application can afford to neglect: How to make models generalize to unseen data, and how to accelerate training. Techniques we introduce keep re-appearing throughout the use cases we then look at: image classification and segmentation, regression on tabular data, time-series forecasting, and classifying speech utterances. It’s in working with images and sound that essential ecosystem libraries, namely, <code>torchvision</code> and <code>torchaudio</code>, make their appearance, to be used for domain-dependent functionality.</p>
<p>In part three, we move beyond deep learning, and explore how <code>torch</code> can figure in general mathematical or scientific applications. Prominent topics are regression using matrix decompositions, the Discrete Fourier Transform, and the Wavelet Transform. The primary goal here is to understand the underlying ideas, and why they are so important. That’s why, here just like in part one, we code algorithms from scratch, before introducing the speed-optimized <code>torch</code> equivalents.</p>
<p>Now that you know about the book’s content, you may be asking:</p>
</section>
<section id="whos-it-for" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="whos-it-for">Who’s it for?</h3>
<p>In short, Deep Learning and Scientific Computing with R <code>torch</code> – being the only comprehensive text, as of this writing, on this topic – addresses a wide audience. The hope is that there’s something in it for everyone (well, most everyone).</p>
<p>If you’ve never used <code>torch</code>, nor any other deep-learning framework, starting right from the beginning is the thing to do. No prior knowledge of deep learning is expected. The assumption is that you know some basic R, and are familiar with machine-learning terms such as supervised vs.&nbsp;unsupervised learning, training-validation-test set, et cetera. Having worked through part one, you’ll find that parts two and three – independently – continue right from where you left off.</p>
<p>If, on the other hand, you do have basic experience with <code>torch</code> and/or other automatic-differentiation frameworks, and are mostly interested in applied deep learning, you may be inclined to skim part one, and go to part two, checking out the applications that interest you most (or just browse, looking for inspiration). The domain-dependent examples were chosen to be rather generic and straightforward, so as to have the code generalize to a whole range of similar applications.</p>
<p>Finally, if it was the “scientific computing” in the title that caught your attention, I certainly hope that part three has something for you! (As the book’s author, I may say that writing this part was an extremely satisfying, incredibly engaging experience.<sup>1</sup>) Part three really is where it makes sense to talk of “browsing” – its topics hardly depend on each other, just look around for what appeals to you.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;though challenging, as well (<em>though</em> … or <em>because</em>?)</p></div></div><p>To wrap up, then:</p>
</section>
<section id="what-do-i-get" class="level3">
<h3 class="anchored" data-anchor-id="what-do-i-get">What do I get?</h3>
<p>Content-wise, I think I can consider this question answered. If there were other books on <code>torch</code> with R, I’d probably stress two things: First, the already-referred-to focus on concepts and understanding. Second, the usefulness of the code examples. By using off-the-shelf datasets, and performing the usual types of tasks, we write code fit to serve as a start in your own applications – providing templates ready to copy-paste and adapt to a purpose.</p>
<p>Thanks for reading, and I hope you enjoy the book!</p>


</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>Books</category>
  <category>Deep Learning</category>
  <guid>https://divergences.xyz/posts/deep-learning-scientific-computing-R-torch/</guid>
  <pubDate>Wed, 05 Apr 2023 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/deep-learning-scientific-computing-R-torch/book.jpg" medium="image" type="image/jpeg"/>
</item>
<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>
<item>
  <title>AO, NAO, ENSO: A wavelet analysis example</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/torchwavelets/</link>
  <description><![CDATA[ 






<p>Recently, we showed how to use <code>torch</code> for <a href="https://blogs.rstudio.com/ai/posts/2022-10-27-wavelets/">wavelet analysis</a>. A member of the family of spectral analysis methods, wavelet analysis bears some similarity to the Fourier Transform, and specifically, to its popular two-dimensional application, the spectrogram.</p>
<p>As explained in that book excerpt, though, there are significant differences. For the purposes of the current post, it suffices to know that frequency-domain patterns are discovered by having a little “wave” (that, really, can be of any shape) “slide” over the data, computing degree of match (or mismatch) in the neighborhood of every sample.</p>
<p>With this post, then, my goal is two-fold.</p>
<p>First, to introduce <a href="https://github.com/skeydan/torchwavelets">torchwavelets</a>, a tiny, yet useful package that automates all of the essential steps involved. Compared to the Fourier Transform and its applications, the topic of wavelets is rather “chaotic” – meaning, it enjoys much less shared terminology, and much less shared practice. Consequently, it makes sense for implementations to follow established, community-embraced approaches, whenever such are available and well documented. With <code>torchwavelets</code>, we provide an implementation of Torrence and Compo’s 1998 “Practical Guide to Wavelet Analysis” (<span class="citation" data-cites="Torrence.Compo1998">Torrence and Compo (1998)</span>), an oft-cited paper that proved influential across a wide range of application domains. Code-wise, our package is mostly a port of <a href="https://github.com/QUVA-Lab/PyTorchWavelets/">Tom Runia’s PyTorch implementation</a>, itself based on a prior implementation by <a href="https://github.com/aaren/wavelets/">Aaron O’Leary</a>.</p>
<div class="no-row-height column-margin column-container"><div id="ref-Torrence.Compo1998" class="csl-entry">
Torrence, C., and G. P. Compo. 1998. <span>“A Practical Guide to Wavelet Analysis.”</span> <em>Bulletin of the American Meteorological Society</em> 79 (1): 61–78.
</div></div><p>Second, to show an attractive use case of wavelet analysis in an area of great scientific interest and tremendous social importance (meteorology/climatology). Being by no means an expert myself, I’d hope this could be inspiring to people working in these fields, as well as to scientists and analysts in other areas where temporal data arise.</p>
<p>Concretely, what we’ll do is take three different atmospheric phenomena – El Niño–Southern Oscillation (ENSO), North Atlantic Oscillation (NAO), and Arctic Oscillation (AO) – and investigate them using wavelet analysis. In each case, we also look at the overall frequency spectrum, given by the Discrete Fourier Transform (DFT), as well as a classic time-series decomposition into trend, seasonal components, and remainder.</p>
<section id="three-oscillations" class="level2">
<h2 class="anchored" data-anchor-id="three-oscillations">Three oscillations</h2>
<p>By far the best-known – the most infamous, I should say – among the three is <a href="https://en.wikipedia.org/wiki/El_Ni%C3%B1o%E2%80%93Southern_Oscillation">El Niño–Southern Oscillation (ENSO)</a>, a.k.a. El Niño/La Niña. The term refers to a changing pattern of sea surface temperatures and sea-level pressures occurring in the equatorial Pacific. Both El Niño and La Niña can and do have catastrophic impact on people’s lives, most notably, for people in developing countries west and east of the Pacific.</p>
<p>El Niño occurs when surface water temperatures in the eastern Pacific are higher than normal, and the strong winds that normally blow from east to west are unusually weak. From April to October, this leads to hot, extremely wet weather conditions along the coasts of northern Peru and Ecuador, continually resulting in major floods. La Niña, on the other hand, causes a drop in sea surface temperatures over Southeast Asia as well as heavy rains over Malaysia, the Philippines, and Indonesia. While these are the areas most gravely impacted, changes in ENSO reverberate across the globe.</p>
<p>Less well known than ENSO, but highly influential as well, is the <a href="https://en.wikipedia.org/wiki/North_Atlantic_oscillation">North Atlantic Oscillation (NAO)</a>. It strongly affects winter weather in Europe, Greenland, and North America. Its two states relate to the size of the pressure difference between the Icelandic High and the Azores Low. When the pressure difference is high, the jet stream – those strong westerly winds that blow between North America and Northern Europe – is yet stronger than normal, leading to warm, wet European winters and calmer-than-normal conditions in Eastern North America. With a lower-than-normal pressure difference, however, the American East tends to incur more heavy storms and cold-air outbreaks, while winters in Northern Europe are colder and more dry.</p>
<p>Finally, the <a href="https://en.wikipedia.org/wiki/Arctic_oscillation">Arctic Oscillation (AO)</a> is a ring-like pattern of sea-level pressure anomalies centered at the North Pole. (Its Southern-hemisphere equivalent is the <a href="https://en.wikipedia.org/wiki/Antarctic_oscillation">Antarctic Oscillation</a>.) AO’s influence extends beyond the Arctic Circle, however; it is indicative of whether and how much Arctic air flows down into the middle latitudes. AO and NAO are strongly related, and might designate the same physical phenomenon at a fundamental level.</p>
<p>Now, let’s make these characterizations more concrete by looking at actual data.</p>
</section>
<section id="analysis-enso" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="analysis-enso">Analysis: ENSO</h2>
<p>We begin with the best-known of these phenomena: ENSO. Data are available from 1854 onwards; however, for comparability with AO, we discard all records prior to January, 1950. For analysis, we pick <code>NINO34_MEAN</code>, the monthly average sea surface temperature in the Niño 3.4 region (i.e., the area between 5° South, 5° North, 190° East, and 240° East). Finally, we convert to a <code>tsibble</code>, the format expected by <code>feasts::STL()</code>.</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>(tidyverse)</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tsibble)</span>
<span id="cb1-3"></span>
<span id="cb1-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">download.file</span>(</span>
<span id="cb1-5">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://bmcnoldy.rsmas.miami.edu/tropics/oni/ONI_NINO34_1854-2022.txt"</span>,</span>
<span id="cb1-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">destfile =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ONI_NINO34_1854-2022.txt"</span></span>
<span id="cb1-7">)</span>
<span id="cb1-8"></span>
<span id="cb1-9">enso <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;">read_table</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ONI_NINO34_1854-2022.txt"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">skip =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb1-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">yearmonth</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(YEAR, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">MON/MMM</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-01"</span>)))) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb1-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">enso =</span> NINO34_MEAN) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb1-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(x <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;">yearmonth</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1950-01"</span>), x <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">yearmonth</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2022-09"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb1-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as_tsibble</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">index =</span> x)</span>
<span id="cb1-14"></span>
<span id="cb1-15">enso</span></code></pre></div></div>
<pre><code># A tsibble: 873 x 2 [1M]
          x  enso
      &lt;mth&gt; &lt;dbl&gt;
 1 1950 Jan  24.6
 2 1950 Feb  25.1
 3 1950 Mar  25.9
 4 1950 Apr  26.3
 5 1950 May  26.2
 6 1950 Jun  26.5
 7 1950 Jul  26.3
 8 1950 Aug  25.9
 9 1950 Sep  25.7
10 1950 Oct  25.7
# … with 863 more rows</code></pre>
<p>As already announced, we want to look at seasonal decomposition, as well. In terms of seasonal periodicity, what do we expect? Unless told otherwise, <code>feasts::STL()</code> will happily pick a window size for us. However, there’ll likely be several important frequencies in the data. (Not wanting to ruin the suspense, but for AO and NAO, this will definitely be the case!). Besides, we want to compute the Fourier Transform anyway, so why not do that first?</p>
<p>Here is the power spectrum:</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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(torch)</span>
<span id="cb3-2">fft <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;">torch_fft_fft</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale</span>(enso<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>enso)))</span></code></pre></div></div>
<p>In the below plot, the x axis corresponds to frequencies, expressed as “number of times per year”. We only display frequencies up to and including the Nyquist frequency, i.e., half the sampling rate, which in our case is 12 (per year).</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">num_samples <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;">nrow</span>(enso)</span>
<span id="cb4-2">nyquist_cutoff <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;">ceiling</span>(num_samples <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;">2</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># highest discernible frequency</span></span>
<span id="cb4-3">bins_below_nyquist <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>nyquist_cutoff</span>
<span id="cb4-4"></span>
<span id="cb4-5">sampling_rate <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># per year</span></span>
<span id="cb4-6">frequencies_per_bin <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> sampling_rate <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> num_samples</span>
<span id="cb4-7">frequencies <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> frequencies_per_bin <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> bins_below_nyquist</span>
<span id="cb4-8"></span>
<span id="cb4-9">df <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;">data.frame</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">f =</span> frequencies, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(fft[<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>(nyquist_cutoff <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="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>()))</span>
<span id="cb4-10">df <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;">ggplot</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(f, y)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb4-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_line</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb4-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">xlab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"frequency (per year)"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb4-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ylab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"magnitude"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb4-14">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggtitle</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Spectrum of Niño 3.4 data"</span>)</span></code></pre></div></div>
<div class="dv-wide-figure page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="spectrum-enso.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-1" title="Frequency spectrum of monthly average sea surface temperature in the Niño 3.4 region, 1950 to present."><img src="https://divergences.xyz/posts/torchwavelets/spectrum-enso.png" class="img-fluid figure-img column-body-outset-right" alt="Frequency spectrum of monthly average sea surface temperature in the Niño 3.4 region, 1950 to present."></a></p>
<figcaption>Frequency spectrum of monthly average sea surface temperature in the Niño 3.4 region, 1950 to present.</figcaption>
</figure>
</div>
</div>
<p>There is one dominant frequency, corresponding to about once a year. From this component alone, we’d expect one El Niño event – or equivalently, one La Niña – per year. But let’s locate important frequencies more precisely. With not many other periodicities standing out, we may as well restrict ourselves to three:</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">strongest <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;">torch_topk</span>(fft[<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>(nyquist_cutoff<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;">2</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;">abs</span>(), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb5-2">strongest</span></code></pre></div></div>
<pre><code>[[1]]
torch_tensor
233.9855
172.2784
142.3784
[ CPUFloatType{3} ]

[[2]]
torch_tensor
74
21
7
[ CPULongType{3} ]</code></pre>
<p>What we have here are the magnitudes of the dominant components, as well as their respective bins in the spectrum. Let’s see which actual frequencies these correspond to:</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">important_freqs <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> frequencies[<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(strongest[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]])]</span>
<span id="cb7-2">important_freqs</span></code></pre></div></div>
<pre><code>[1] 1.00343643 0.27491409 0.08247423 </code></pre>
<p>That’s once per year, once per quarter, and once every twelve years, approximately. Or, expressed as periodicity, in terms of months (i.e., how many months are there in a period):</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">num_observations_in_season <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>important_freqs  </span>
<span id="cb9-2">num_observations_in_season</span></code></pre></div></div>
<pre><code>[1] 11.95890  43.65000 145.50000  </code></pre>
<p>We now pass these to <code>feasts::STL()</code>, to obtain a five-fold decomposition into trend, seasonal components, and remainder.</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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(feasts)</span>
<span id="cb11-2">enso <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb11-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">model</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">STL</span>(enso <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;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</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;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">44</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb11-4">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">145</span>))) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb11-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">components</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb11-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">autoplot</span>()</span></code></pre></div></div>
<div class="dv-wide-figure page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="stl_enso.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-2" title="Decomposition of ENSO data into trend, seasonal components, and remainder by feasts::STL()."><img src="https://divergences.xyz/posts/torchwavelets/stl_enso.png" class="img-fluid figure-img column-body-outset-right" alt="Decomposition of ENSO data into trend, seasonal components, and remainder by feasts::STL()."></a></p>
<figcaption>Decomposition of ENSO data into trend, seasonal components, and remainder by <code>feasts::STL()</code>.</figcaption>
</figure>
</div>
</div>
<p>According to Loess decomposition, there still is significant noise in the data – the remainder remaining high despite our hinting at important seasonalities. In fact, there is no big surprise in that: Looking back at the DFT output, not only are there many, close to one another, low- and lowish-frequency components, but in addition, high-frequency components just won’t cease to contribute. And really, as of today, ENSO forecasting – tremendously important in terms of human impact – is focused on predicting oscillation state just a year in advance. This will be interesting to keep in mind for when we proceed to the other series – as you’ll see, it’ll only get worse.</p>
<p>By now, we’re well informed about how dominant temporal rhythms determine, or fail to determine, what actually happens in atmosphere and ocean. But we don’t know anything about whether, and how, those rhythms may have varied in strength over the time span considered. This is where wavelet analysis comes in.</p>
<p>In <code spellcheck="false">torchwavelets</code>, the central operation is a call to <code>wavelet_transform()</code>, to instantiate an object that takes care of all required operations. One argument is required: <code>signal_length</code>, the number of data points in the series. And one of the defaults we <em>need</em> to override: <code>dt</code>, the time between samples, expressed in the unit we’re working with. In our case, that’s <em>year</em>, and, having monthly samples, we need to pass a value of 1/12. With all other defaults untouched, analysis will be done using the <a href="https://en.wikipedia.org/wiki/Morlet_wavelet">Morlet wavelet</a> (available alternatives are <a href="https://en.wikipedia.org/wiki/Ricker_wavelet">Mexican Hat</a> and <a href="https://reference.wolfram.com/language/ref/PaulWavelet.html">Paul</a>), and the transform will be computed in the Fourier domain (the fastest way, unless you have a GPU).</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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(torchwavelets)</span>
<span id="cb12-2">enso_idx <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> enso<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>enso <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.numeric</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;">torch_tensor</span>()</span>
<span id="cb12-3">dt <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span></span>
<span id="cb12-4">wtf <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;">wavelet_transform</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(enso_idx), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dt =</span> dt)</span></code></pre></div></div>
<p>A call to <code>power()</code> will then compute the wavelet transform:</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">power_spectrum <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> wtf<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;">power</span>(enso_idx)</span>
<span id="cb13-2">power_spectrum<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>shape</span></code></pre></div></div>
<pre><code>[1]  71 873</code></pre>
<p>The result is two-dimensional. The second dimension holds measurement times, i.e., the months between January, 1950 and September, 2022. The first dimension warrants some more explanation.</p>
<p>Namely, we have here the set of scales the transform has been computed for. If you’re familiar with the Fourier Transform and its analogue, the spectrogram, you’ll probably think in terms of time versus frequency. With wavelets, there is an additional parameter, the scale, that determines the spread of the analysis pattern.</p>

<div class="no-row-height column-margin column-container"><div class="">
<p>The aforementioned <a href="https://blogs.rstudio.com/ai/posts/2022-10-27-wavelets/">book excerpt</a> discusses this in detail.</p>
</div></div><p>Some wavelets have both a scale and a frequency, in which case these can interact in complex ways. Others are defined such that no separate frequency appears. In the latter case, you immediately end up with the time vs.&nbsp;scale layout we see in wavelet diagrams (scaleograms). In the former, most software hides the complexity by merging scale and frequency into one, leaving just scale as a user-visible parameter. In <code>torchwavelets</code>, too, the wavelet frequency (if existent) has been “streamlined away”. Consequently, we’ll end up plotting time versus scale, as well. I’ll say more when we actually see such a scaleogram.</p>
<p>For visualization, we transpose the data and put it into a <code>ggplot</code>-friendly format:</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">times <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> lubridate<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;">year</span>(enso<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> lubridate<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;">month</span>(enso<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x) <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;">12</span></span>
<span id="cb15-2">scales <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;">as.numeric</span>(wtf<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>scales)</span>
<span id="cb15-3"></span>
<span id="cb15-4">df <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;">as_tibble</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>(power_spectrum<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;">t</span>()), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.name_repair =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"universal"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb15-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">time =</span> times) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb15-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pivot_longer</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>time, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">names_to =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scale"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values_to =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"power"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb15-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scale =</span> scales[scale <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb15-8">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str_remove</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"[</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">.]{3}"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb15-9">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>()])</span>
<span id="cb15-10">df <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;">glimpse</span>()</span></code></pre></div></div>
<pre><code>Rows: 61,983
Columns: 3
$ time  &lt;dbl&gt; 1950.083, 1950.083, 1950.083, 1950.083, 195…
$ scale &lt;dbl&gt; 0.1613356, 0.1759377, 0.1918614, 0.2092263,…
$ power &lt;dbl&gt; 0.03617507, 0.05985500, 0.07948010, 0.09819…</code></pre>
<p>There is one additional piece of information to be incorporated, still: the so-called “cone of influence” (COI). Visually, this is a shading that tells us which part of the plot reflects incomplete, and thus, unreliable and to-be-disregarded, data. Namely, the bigger the scale, the more spread-out the analysis wavelet, and the more incomplete the overlap at the borders of the series when the wavelet slides over the data. You’ll see what I mean in a second.</p>
<p>The COI gets its own data frame:</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">coi <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> wtf<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;">coi</span>(times[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], times[<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(enso_idx)])</span>
<span id="cb17-2">coi_df <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;">data.frame</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(coi[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(coi[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]]))</span></code></pre></div></div>
<p>And now we’re ready to create the scaleogram:</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">labeled_scales <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;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</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;">2</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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</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;">64</span>)</span>
<span id="cb18-2">labeled_frequencies <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;">round</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(wtf<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;">fourier_period</span>(labeled_scales)), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb18-3"></span>
<span id="cb18-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(df) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb18-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_y_continuous</span>(</span>
<span id="cb18-6">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">trans =</span> scales<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;">compose_trans</span>(scales<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;">log2_trans</span>(), scales<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;">reverse_trans</span>()),</span>
<span id="cb18-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">breaks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</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;">2</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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</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;">64</span>),</span>
<span id="cb18-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">limits =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(scales), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">min</span>(scales)),</span>
<span id="cb18-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">expand =</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;">0</span>),</span>
<span id="cb18-10">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sec.axis =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dup_axis</span>(</span>
<span id="cb18-11">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> scales<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;">label_number</span>(labeled_frequencies),</span>
<span id="cb18-12">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Fourier period (years)"</span></span>
<span id="cb18-13">    )</span>
<span id="cb18-14">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb18-15">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ylab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scale (years)"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb18-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_x_continuous</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">breaks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1950</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2020</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">expand =</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;">0</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb18-17">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">xlab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"year"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb18-18">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_contour_filled</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(time, scale, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">z =</span> power), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">show.legend =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb18-19">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_viridis_d</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">option =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"turbo"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb18-20">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_ribbon</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> coi_df, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ymin =</span> y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ymax =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(scales)),</span>
<span id="cb18-21">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">alpha =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb18-22">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend.position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>)</span></code></pre></div></div>
<div class="dv-wide-figure page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="scaleogram-enso.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-3" title="Scaleogram of ENSO data."><img src="https://divergences.xyz/posts/torchwavelets/scaleogram-enso.png" class="img-fluid figure-img column-body-outset-right" alt="Scaleogram of ENSO data."></a></p>
<figcaption>Scaleogram of ENSO data.</figcaption>
</figure>
</div>
</div>
<p>What we see here is how, in ENSO, different rhythms have prevailed over time. Instead of “rhythms”, I could have said “scales”, or “frequencies”, or “periods” – all those translate into one another. Since, to us humans, wavelet scales don’t mean that much, the period (in years) is displayed on an additional y axis on the right.</p>
<p>So, we see that in the eighties, an (approximately) four-year period had exceptional influence. Thereafter, yet longer periodicities gained in dominance. And, in accordance with what we expect from prior analysis, there is a <em>basso continuo</em> of annual similarity.</p>
<p>Also, note how, at first sight, there seems to have been a decade where a six-year period stood out: right at the beginning of where (for us) measurement starts, in the fifties. However, the dark shading – the COI – tells us that, in this region, the data is not to be trusted.</p>
<p>Summing up, the two-dimensional analysis nicely complements the more compressed characterization we got from the DFT. Before we move on to the next series, however, let me just quickly address one question, in case you were wondering (if not, just read on, since I won’t be going into details anyway): How is this different from a spectrogram?</p>
<p>In a nutshell, the spectrogram splits the data into several “windows”, and computes the DFT independently on all of them. To compute the scaleogram, on the other hand, the analysis wavelet slides continuously over the data, resulting in a spectrum-equivalent for the neighborhood of each sample in the series. With the spectrogram, a fixed window size means that not all frequencies are resolved equally well: The higher frequencies appear more frequently in the interval than the lower ones, and thus, will allow for better resolution. Wavelet analysis, in contrast, is done on a set of scales deliberately arranged so as to capture a broad range of frequencies theoretically visible in a series of given length.</p>

<div class="no-row-height column-margin column-container"><div class="">
<p>Take a look at the aforementioned <a href="https://blogs.rstudio.com/ai/posts/2022-10-27-wavelets/">book excerpt</a> to see spectrogram and scaleogram compared in a nice application.</p>
</div></div></section>
<section id="analysis-nao" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="analysis-nao">Analysis: NAO</h2>
<p>The data file for NAO is in fixed-table format. After conversion to a <code>tsibble</code>, we have:</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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">download.file</span>(</span>
<span id="cb19-2"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://crudata.uea.ac.uk/cru/data//nao/nao.dat"</span>,</span>
<span id="cb19-3"> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">destfile =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"nao.dat"</span></span>
<span id="cb19-4">)</span>
<span id="cb19-5"></span>
<span id="cb19-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># needed for AO, as well</span></span>
<span id="cb19-7">use_months <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;">seq.Date</span>(</span>
<span id="cb19-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">from =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1950-01-01"</span>),</span>
<span id="cb19-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">to =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2022-09-01"</span>),</span>
<span id="cb19-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"months"</span></span>
<span id="cb19-11">)</span>
<span id="cb19-12"></span>
<span id="cb19-13">nao <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span></span>
<span id="cb19-14">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_table</span>(</span>
<span id="cb19-15">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"nao.dat"</span>,</span>
<span id="cb19-16">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col_names =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>,</span>
<span id="cb19-17">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-99.99"</span>,</span>
<span id="cb19-18">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">skip =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span></span>
<span id="cb19-19">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb19-20">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>X1, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>X14) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb19-21">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb19-22">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb19-23">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.vector</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb19-24">  .[<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>(use_months)] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb19-25">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb19-26">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> use_months,</span>
<span id="cb19-27">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">nao =</span> .</span>
<span id="cb19-28">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb19-29">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">yearmonth</span>(x)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb19-30">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fill</span>(nao) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb19-31">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as_tsibble</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">index =</span> x)</span>
<span id="cb19-32"></span>
<span id="cb19-33">nao</span></code></pre></div></div>
<pre><code># A tsibble: 873 x 2 [1M]
          x   nao
      &lt;mth&gt; &lt;dbl&gt;
 1 1950 Jan -0.16
 2 1950 Feb  0.25
 3 1950 Mar -1.44
 4 1950 Apr  1.46
 5 1950 May  1.34
 6 1950 Jun -3.94
 7 1950 Jul -2.75
 8 1950 Aug -0.08
 9 1950 Sep  0.19
10 1950 Oct  0.19
# … with 863 more rows</code></pre>
<p>Like before, we start with the spectrum:</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">fft <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;">torch_fft_fft</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale</span>(nao<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>nao)))</span>
<span id="cb21-2"></span>
<span id="cb21-3">num_samples <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;">nrow</span>(nao)</span>
<span id="cb21-4">nyquist_cutoff <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;">ceiling</span>(num_samples <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;">2</span>)</span>
<span id="cb21-5">bins_below_nyquist <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>nyquist_cutoff</span>
<span id="cb21-6"></span>
<span id="cb21-7">sampling_rate <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span> </span>
<span id="cb21-8">frequencies_per_bin <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> sampling_rate <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> num_samples</span>
<span id="cb21-9">frequencies <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> frequencies_per_bin <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> bins_below_nyquist</span>
<span id="cb21-10"></span>
<span id="cb21-11">df <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;">data.frame</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">f =</span> frequencies, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(fft[<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>(nyquist_cutoff <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="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>()))</span>
<span id="cb21-12">df <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;">ggplot</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(f, y)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb21-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_line</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb21-14">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">xlab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"frequency (per year)"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb21-15">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ylab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"magnitude"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb21-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggtitle</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Spectrum of NAO data"</span>)</span></code></pre></div></div>
<div class="dv-wide-figure page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="spectrum-nao.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-4" title="Spectrum of NAO data, 1950 to present."><img src="https://divergences.xyz/posts/torchwavelets/spectrum-nao.png" class="img-fluid figure-img column-body-outset-right" alt="Spectrum of NAO data, 1950 to present."></a></p>
<figcaption>Spectrum of NAO data, 1950 to present.</figcaption>
</figure>
</div>
</div>
<p>Have you been wondering for a tiny moment whether this was time-domain data – not spectral? It does look a lot more noisy than the ENSO spectrum for sure. And really, with NAO, predictability is much worse - forecast lead time usually amounts to just <a href="https://www.cpc.ncep.noaa.gov/products/precip/CWlink/pna/nao_index_mrf.shtml">one or two weeks</a>.</p>
<p>Proceeding as before, we pick dominant seasonalities (at least this still is possible!) to pass to <code>feasts::STL()</code>.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb22-1">strongest <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;">torch_topk</span>(fft[<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>(nyquist_cutoff<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;">2</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;">abs</span>(), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>)</span>
<span id="cb22-2">strongest</span></code></pre></div></div>
<pre><code>[[1]]
torch_tensor
102.7191
80.5129
76.1179
75.9949
72.9086
60.8281
[ CPUFloatType{6} ]

[[2]]
torch_tensor
147
99
146
59
33
78
[ CPULongType{6} ]</code></pre>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb24" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb24-1">important_freqs <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> frequencies[<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(strongest[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]])]</span>
<span id="cb24-2">important_freqs</span></code></pre></div></div>
<pre><code>[1] 2.0068729 1.3470790 1.9931271 0.7972509 0.4398625 1.0584192</code></pre>
<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">num_observations_in_season <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>important_freqs  </span>
<span id="cb26-2">num_observations_in_season</span></code></pre></div></div>
<pre><code>[1]  5.979452  8.908163  6.020690 15.051724 27.281250 11.337662</code></pre>
<p>Important seasonal periods are of length six, nine, eleven, fifteen, and twenty-seven months, approximately - pretty close together indeed! No wonder that, in STL decomposition, the remainder is even more significant than with ENSO:</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">nao <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb28-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">model</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">STL</span>(nao <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;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</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;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb28-3">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">15</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;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">27</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb28-4">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>))) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb28-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">components</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb28-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">autoplot</span>()</span></code></pre></div></div>
<div class="dv-wide-figure page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="stl-nao.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-5" title="Decomposition of NAO data into trend, seasonal components, and remainder by feasts::STL()."><img src="https://divergences.xyz/posts/torchwavelets/stl-nao.png" class="img-fluid figure-img column-body-outset-right" alt="Decomposition of NAO data into trend, seasonal components, and remainder by feasts::STL()."></a></p>
<figcaption>Decomposition of NAO data into trend, seasonal components, and remainder by <code>feasts::STL()</code>.</figcaption>
</figure>
</div>
</div>
<p>Now, what will we see in terms of temporal evolution? Much of the code that follows is the same as for ENSO, repeated here for the reader’s convenience:</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">nao_idx <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nao<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>nao <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.numeric</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;">torch_tensor</span>()</span>
<span id="cb29-2">dt <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># same interval as for ENSO</span></span>
<span id="cb29-3">wtf <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;">wavelet_transform</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(nao_idx), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dt =</span> dt)</span>
<span id="cb29-4">power_spectrum <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> wtf<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;">power</span>(nao_idx)</span>
<span id="cb29-5"></span>
<span id="cb29-6">times <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> lubridate<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;">year</span>(nao<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> lubridate<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;">month</span>(nao<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x)<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;">12</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># also same</span></span>
<span id="cb29-7">scales <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;">as.numeric</span>(wtf<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>scales) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># will be same because both series have same length</span></span>
<span id="cb29-8"></span>
<span id="cb29-9">df <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;">as_tibble</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>(power_spectrum<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;">t</span>()), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.name_repair =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"universal"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb29-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">time =</span> times) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb29-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pivot_longer</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>time, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">names_to =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scale"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values_to =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"power"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb29-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scale =</span> scales[scale <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb29-13">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str_remove</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"[</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">.]{3}"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb29-14">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>()])</span>
<span id="cb29-15"></span>
<span id="cb29-16">coi <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> wtf<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;">coi</span>(times[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], times[<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(nao_idx)])</span>
<span id="cb29-17">coi_df <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;">data.frame</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(coi[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(coi[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]]))</span>
<span id="cb29-18"></span>
<span id="cb29-19">labeled_scales <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;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</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;">2</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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</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;">64</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># same since scales are same </span></span>
<span id="cb29-20">labeled_frequencies <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;">round</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(wtf<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;">fourier_period</span>(labeled_scales)), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb29-21"></span>
<span id="cb29-22"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(df) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-23">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_y_continuous</span>(</span>
<span id="cb29-24">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">trans =</span> scales<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;">compose_trans</span>(scales<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;">log2_trans</span>(), scales<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;">reverse_trans</span>()),</span>
<span id="cb29-25">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">breaks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</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;">2</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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</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;">64</span>),</span>
<span id="cb29-26">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">limits =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(scales), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">min</span>(scales)),</span>
<span id="cb29-27">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">expand =</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;">0</span>),</span>
<span id="cb29-28">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sec.axis =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dup_axis</span>(</span>
<span id="cb29-29">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> scales<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;">label_number</span>(labeled_frequencies),</span>
<span id="cb29-30">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Fourier period (years)"</span></span>
<span id="cb29-31">    )</span>
<span id="cb29-32">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-33">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ylab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scale (years)"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-34">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_x_continuous</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">breaks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1950</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2020</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">expand =</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;">0</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-35">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">xlab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"year"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-36">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_contour_filled</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(time, scale, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">z =</span> power), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">show.legend =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-37">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_viridis_d</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">option =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"turbo"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-38">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_ribbon</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> coi_df, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ymin =</span> y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ymax =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(scales)),</span>
<span id="cb29-39">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">alpha =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-40">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend.position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>)</span></code></pre></div></div>
<div class="dv-wide-figure page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="scaleogram-nao.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-6" title="Scaleogram of NAO data."><img src="https://divergences.xyz/posts/torchwavelets/scaleogram-nao.png" class="img-fluid figure-img column-body-outset-right" alt="Scaleogram of NAO data."></a></p>
<figcaption>Scaleogram of NAO data.</figcaption>
</figure>
</div>
</div>
<p>That, literally, is a much more colorful picture than with ENSO! High frequencies are present, and continually dominant, over the whole time period.</p>
<p>Interestingly, though, we see similarities to ENSO, as well: In both, there is an important pattern, of periodicity four or slightly more years, that exerces influence during the eighties, nineties, and early two-thousands – only with ENSO, it shows peak impact during the nineties, while with NAO, its dominance is most visible in the first decade of this century. Also, both phenomena exhibit a strongly visible peak, of period two years, around 1970. So, is there a close(-ish) connection between both oscillations? This question, of course, is for the domain experts to answer. At least I found a recent study (<span class="citation" data-cites="scaife2014skillful">Scaife et al. (2014)</span>) that not only suggests there is, but uses one (ENSO, the more predictable one) to inform forecasts of the other:</p>
<div class="no-row-height column-margin column-container"><div id="ref-scaife2014skillful" class="csl-entry">
Scaife, A. A., Alberto Arribas Herranz, E. Blockley, et al. 2014. <span>“Skillful Long-Range Prediction of European and North American Winters.”</span> <em>Geophysical Research Letters</em> 41 (7): 2514–19. <a href="https://www.microsoft.com/en-us/research/publication/skillful-long-range-prediction-of-european-and-north-american-winters/">https://www.microsoft.com/en-us/research/publication/skillful-long-range-prediction-of-european-and-north-american-winters/</a>.
</div></div><blockquote class="blockquote">
<p>Previous studies have shown that the El Niño–Southern Oscillation can drive interannual variations in the NAO [Brönnimann et al., 2007] and hence Atlantic and European winter climate via the stratosphere [Bell et al., 2009]. […] this teleconnection to the tropical Paciﬁc is active in our experiments, with forecasts initialized in El Niño/La Niña conditions in November tending to be followed by negative/positive NAO conditions in winter.</p>
</blockquote>
<p>Will we see a similar relationship for AO, our third series under investigation? We might expect so, since AO and NAO are closely related (or even, two sides of the same coin).</p>
</section>
<section id="analysis-ao" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="analysis-ao">Analysis: AO</h2>
<p>First, the data:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb30" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb30-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">download.file</span>(</span>
<span id="cb30-2"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://www.cpc.ncep.noaa.gov/products/precip/CWlink/daily_ao_index/monthly.ao.index.b50.current.ascii.table"</span>,</span>
<span id="cb30-3"> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">destfile =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ao.dat"</span></span>
<span id="cb30-4">)</span>
<span id="cb30-5"></span>
<span id="cb30-6">ao <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span></span>
<span id="cb30-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_table</span>(</span>
<span id="cb30-8">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ao.dat"</span>,</span>
<span id="cb30-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col_names =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>,</span>
<span id="cb30-10">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">skip =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb30-11">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb30-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>X1) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb30-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span> </span>
<span id="cb30-14">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb30-15">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.vector</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb30-16">  .[<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>(use_months)] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb30-17">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> use_months,</span>
<span id="cb30-18">         <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ao =</span> .) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb30-19">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">yearmonth</span>(x)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb30-20">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fill</span>(ao) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb30-21">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as_tsibble</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">index =</span> x) </span>
<span id="cb30-22"></span>
<span id="cb30-23">ao</span></code></pre></div></div>
<pre><code># A tsibble: 873 x 2 [1M]
          x     ao
      &lt;mth&gt;  &lt;dbl&gt;
 1 1950 Jan -0.06 
 2 1950 Feb  0.627
 3 1950 Mar -0.008
 4 1950 Apr  0.555
 5 1950 May  0.072
 6 1950 Jun  0.539
 7 1950 Jul -0.802
 8 1950 Aug -0.851
 9 1950 Sep  0.358
10 1950 Oct -0.379
# … with 863 more rows</code></pre>
<p>And the spectrum:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb32" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb32-1">fft <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;">torch_fft_fft</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale</span>(ao<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>ao)))</span>
<span id="cb32-2"></span>
<span id="cb32-3">num_samples <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;">nrow</span>(ao)</span>
<span id="cb32-4">nyquist_cutoff <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;">ceiling</span>(num_samples <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;">2</span>)</span>
<span id="cb32-5">bins_below_nyquist <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>nyquist_cutoff</span>
<span id="cb32-6"></span>
<span id="cb32-7">sampling_rate <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># per year</span></span>
<span id="cb32-8">frequencies_per_bin <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> sampling_rate <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> num_samples</span>
<span id="cb32-9">frequencies <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> frequencies_per_bin <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> bins_below_nyquist</span>
<span id="cb32-10"></span>
<span id="cb32-11">df <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;">data.frame</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">f =</span> frequencies, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(fft[<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>(nyquist_cutoff <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="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>()))</span>
<span id="cb32-12">df <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;">ggplot</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(f, y)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb32-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_line</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb32-14">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">xlab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"frequency (per year)"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb32-15">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ylab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"magnitude"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb32-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggtitle</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Spectrum of AO data"</span>)</span></code></pre></div></div>
<div class="dv-wide-figure page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="spectrum-ao.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-7" title="Spectrum of AO data, 1950 to present."><img src="https://divergences.xyz/posts/torchwavelets/spectrum-ao.png" class="img-fluid figure-img column-body-outset-right" alt="Spectrum of AO data, 1950 to present."></a></p>
<figcaption>Spectrum of AO data, 1950 to present.</figcaption>
</figure>
</div>
</div>
<p>Well, this spectrum looks even more random than NAO’s, in that not even a single frequency stands out. For completeness, here is the STL decomposition:</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">strongest <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;">torch_topk</span>(fft[<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>(nyquist_cutoff<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;">2</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;">abs</span>(), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>)</span>
<span id="cb33-2"></span>
<span id="cb33-3">important_freqs <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> frequencies[<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(strongest[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]])]</span>
<span id="cb33-4">important_freqs</span>
<span id="cb33-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [1] 0.01374570 0.35738832 1.77319588 1.27835052 0.06872852</span></span>
<span id="cb33-6"></span>
<span id="cb33-7">num_observations_in_season <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>important_freqs  </span>
<span id="cb33-8">num_observations_in_season</span>
<span id="cb33-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [1] 873.000000  33.576923   6.767442   9.387097 174.600000 </span></span>
<span id="cb33-10"></span>
<span id="cb33-11">ao <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb33-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">model</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">STL</span>(ao <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;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">33</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;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb33-13">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</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;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">174</span>))) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb33-14">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">components</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb33-15">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">autoplot</span>()</span></code></pre></div></div>
<div class="dv-wide-figure page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="stl-ao.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-8" title="Decomposition of AO data into trend, seasonal components, and remainder by feasts::STL()."><img src="https://divergences.xyz/posts/torchwavelets/stl-ao.png" class="img-fluid figure-img column-body-outset-right" alt="Decomposition of AO data into trend, seasonal components, and remainder by feasts::STL()."></a></p>
<figcaption>Decomposition of AO data into trend, seasonal components, and remainder by feasts::STL().</figcaption>
</figure>
</div>
</div>
<p>Finally, what can the scaleogram tell us about dominant patterns?</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb34" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb34-1">ao_idx <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ao<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>ao <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.numeric</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;">torch_tensor</span>()</span>
<span id="cb34-2">dt <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># same interval as for ENSO and NAO</span></span>
<span id="cb34-3">wtf <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;">wavelet_transform</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(ao_idx), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dt =</span> dt)</span>
<span id="cb34-4">power_spectrum <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> wtf<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;">power</span>(ao_idx)</span>
<span id="cb34-5"></span>
<span id="cb34-6">times <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> lubridate<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;">year</span>(ao<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> lubridate<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;">month</span>(ao<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x)<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;">12</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># also same</span></span>
<span id="cb34-7">scales <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;">as.numeric</span>(wtf<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>scales) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># will be same because all series have same length</span></span>
<span id="cb34-8"></span>
<span id="cb34-9">df <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;">as_tibble</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>(power_spectrum<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;">t</span>()), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.name_repair =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"universal"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb34-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">time =</span> times) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb34-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pivot_longer</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>time, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">names_to =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scale"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values_to =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"power"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb34-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scale =</span> scales[scale <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb34-13">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str_remove</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"[</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">.]{3}"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb34-14">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>()])</span>
<span id="cb34-15"></span>
<span id="cb34-16">coi <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> wtf<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;">coi</span>(times[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], times[<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(ao_idx)])</span>
<span id="cb34-17">coi_df <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;">data.frame</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(coi[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(coi[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]]))</span>
<span id="cb34-18"></span>
<span id="cb34-19">labeled_scales <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;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</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;">2</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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</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;">64</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># same since scales are same </span></span>
<span id="cb34-20">labeled_frequencies <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;">round</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(wtf<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;">fourier_period</span>(labeled_scales)), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb34-21"></span>
<span id="cb34-22"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(df) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb34-23">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_y_continuous</span>(</span>
<span id="cb34-24">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">trans =</span> scales<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;">compose_trans</span>(scales<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;">log2_trans</span>(), scales<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;">reverse_trans</span>()),</span>
<span id="cb34-25">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">breaks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</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;">2</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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</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;">64</span>),</span>
<span id="cb34-26">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">limits =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(scales), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">min</span>(scales)),</span>
<span id="cb34-27">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">expand =</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;">0</span>),</span>
<span id="cb34-28">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sec.axis =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dup_axis</span>(</span>
<span id="cb34-29">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> scales<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;">label_number</span>(labeled_frequencies),</span>
<span id="cb34-30">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Fourier period (years)"</span></span>
<span id="cb34-31">    )</span>
<span id="cb34-32">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb34-33">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ylab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scale (years)"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb34-34">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_x_continuous</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">breaks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1950</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2020</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">expand =</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;">0</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb34-35">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">xlab</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"year"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb34-36">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_contour_filled</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(time, scale, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">z =</span> power), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">show.legend =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb34-37">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_viridis_d</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">option =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"turbo"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb34-38">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_ribbon</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> coi_df, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ymin =</span> y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ymax =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(scales)),</span>
<span id="cb34-39">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">alpha =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb34-40">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend.position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>)</span></code></pre></div></div>
<div class="dv-wide-figure page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="scaleogram-ao.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-9" title="Scaleogram of AO data."><img src="https://divergences.xyz/posts/torchwavelets/scaleogram-ao.png" class="img-fluid figure-img column-body-outset-right" alt="Scaleogram of AO data."></a></p>
<figcaption>Scaleogram of AO data.</figcaption>
</figure>
</div>
</div>
<p>Having seen the overall spectrum, the lack of strongly dominant patterns in the scaleogram does not come as a big surprise. It is tempting – for me, at least – to see a reflection of ENSO around 1970, all the more since by transitivity, AO and ENSO should be related in some way. But here, qualified judgment really is reserved to the experts.</p>
</section>
<section id="conclusion" class="level2">
<h2 class="anchored" data-anchor-id="conclusion">Conclusion</h2>
<p>Like I said in the beginning, this post would be about inspiration, not technical detail or reportable results. And I hope that inspirational it has been, at least a little bit. If you’re experimenting with wavelets yourself, or plan to – or if you work in the atmospheric sciences, and would like to provide some insight on the above data/phenomena – we’d love to hear from you!</p>
<p>As always, thanks for reading!</p>
<div class="photo-credit">
<p>Photo by <a href="https://unsplash.com/@actionvance?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">ActionVance</a> on <a href="https://unsplash.com/images/nature/earth?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>Climate &amp; Weather</category>
  <guid>https://divergences.xyz/posts/torchwavelets/</guid>
  <pubDate>Thu, 19 Jan 2023 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/torchwavelets/preview.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Getting started with deep learning on graphs</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/graphdl/</link>
  <description><![CDATA[ 






<p>If, in deep-learning world, the first half of the last decade has been the age of images, and the second, that of language, one could say that now, we’re living in the age of graphs. At least, that’s what commonly cited research metrics suggest. But as we’re all aware, deep-learning research is anything but an ivory tower. To see real-world implications, it suffices to reflect on how many things can be modeled as graphs. Some things quite naturally “are” graphs, in the sense of having nodes and edges: neurons, underground stations, social networks. Other things can fruitfully be modeled as graphs: molecules, for example; or language, concepts, three-dimensional shapes … If deep learning on graphs is desirable, what are the challenges, and what do we get for free?</p>
<section id="whats-so-special-about-deep-learning-on-graphs" class="level2">
<h2 class="anchored" data-anchor-id="whats-so-special-about-deep-learning-on-graphs">What’s so special about deep learning on graphs?</h2>
<p>Graphs are different from images, language, as well as tabular data in that node numbering does not matter. In other words, graphs are permutation-invariant. Already this means that architectures established in other domains cannot be transferred verbatim. (The <em>ideas</em> underlying them can be transferred though. Thus, in the graph neural network (henceforth: GNN) model zoo you’ll see lots of allusions to “convolutional”, “attention”, and other established terms.) Put very simply, and in concordance with common sense, whatever algorithm is used, it will fundamentally be based on how nodes are connected: the edges, that is.</p>
<p>When relationships are modeled as graphs, both nodes and edges can have features. This, too, adds complexity. But not everything is harder with graphs. Think of how cumbersome it can be to obtain labeled data for supervised learning. With graphs, often an astonishingly small amount of labeled data is needed. More surprisingly still, a graph can be constructed when not a single edge is present. Put differently, learning on <em>sets</em> can morph into learning on <em>graphs</em>.</p>
<p>At this point, let me switch gears and move on to the practical part: the raison d’être of this post.</p>
</section>
<section id="matching-concepts-and-code-pytorch-geometric" class="level2">
<h2 class="anchored" data-anchor-id="matching-concepts-and-code-pytorch-geometric">Matching concepts and code: PyTorch Geometric</h2>
<p>In this (and future) posts, we’ll make use of <a href="https://pytorch-geometric.readthedocs.io/en/latest/#">PyTorch Geometric</a> (from hereon: PyG), the most popular, at this time, and fastest-growing in terms of functionality as well as user base, library dedicated to graph DL.</p>
<p>Deep learning on graphs, in its most general form, is usually characterized by the term <em>message passing</em>. Messages are passed between nodes that are linked by an edge: If node <img src="https://latex.codecogs.com/png.latex?A"> has three neighbors, it will receive three messages. Those messages have to be summarized in some meaningful way. Finally – GNNs consisting of consecutive layers – the node will have to decide how to modify its previous-layer features (a.k.a. embeddings) based on that summary.</p>
<p>Together, these make up a three-step sequence: collect messages; aggegate; update. What about the “learning” in deep learning, though? There are two places where learning can happen: Firstly, in message collection: Incoming messages could be transformed by a MLP, for example. Secondly, as part of the update step. All in all, this yields mathematical formulae like this, given in the PyG documentation:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7Bx%7D_i%5E%7B(k)%7D%20=%20%5Cgamma%5E%7B(k)%7D%20%5Cleft(%20%5Cmathbf%7Bx%7D_i%5E%7B(k-1)%7D,%20%5Csquare_%7Bj%20%5Cin%20%5Cmathcal%7BN%7D(i)%7D%20%5C,%20%5Cphi%5E%7B(k)%7D%5Cleft(%5Cmathbf%7Bx%7D_i%5E%7B(k-1)%7D,%20%5Cmathbf%7Bx%7D_j%5E%7B(k-1)%7D,%5Cmathbf%7Be%7D_%7Bj,i%7D%5Cright)%20%5Cright)%0A"></p>
<p>Scary though this looks, once we read it from the right, we see that it nicely fits the conceptual description. The <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7Bx%7D_i%5E%7B(k-1)%7D,%20%5Cmathbf%7Bx%7D_j%5E%7B(k-1)%7D,%5Cmathbf%7Be%7D_%7Bj,i%7D)"> are the three types of incoming messages a node can receive: its own state at the previous layer, the states of its neighbors (the nodes <img src="https://latex.codecogs.com/png.latex?j%20%5Cin%20%5Cmathcal%7BN%7D(i)">) at the previous layer, and features/embeddings associated to the edge in question. (I’m leaving out edge features in this discussion completely, so as to not further enhance complexity.) These messages are (optionally) transformed by the neural network <img src="https://latex.codecogs.com/png.latex?%5Cphi">, and whatever comes out is summarized by the aggregator function <img src="https://latex.codecogs.com/png.latex?%5Csquare">. Finally, a node will update itself based on that summary as well as its own previous-layer state, possibly by means of applying neural network <img src="https://latex.codecogs.com/png.latex?%5Cgamma">.</p>
<p>Now that we have this conceptual/mathematical representation, how does it map to code we see, or would like to write? PyG has excellent, extensive documentation, including at the beginner level. But here, I’d like to spell things out in detail – pedantically, if you like, but in a way that tells us a lot about how GNNs work.</p>
<p>Let’s start by the information given in one of the key documentation pages, <a href="https://pytorch-geometric.readthedocs.io/en/latest/notes/create_gnn.html">Creating message passing networks</a>:</p>
<blockquote class="blockquote">
<p>PyG provides the <code>MessagePassing</code> base class, which helps in creating such kinds of message passing graph neural networks by automatically taking care of message propagation. The user only has to define the functions <img src="https://latex.codecogs.com/png.latex?%5Cphi">, i.e.&nbsp;<code>message()</code>, and <img src="https://latex.codecogs.com/png.latex?%5Cgamma">, i.e.&nbsp;<code>update()</code>, as well as the aggregation scheme to use, i.e.&nbsp;<code>aggr="add"</code>, <code>aggr="mean"</code> or <code>aggr="max"</code>.</p>
</blockquote>
<p>Scrolling down that page and looking at the two example implementations, however, we see that an implementation of <code>update()</code> does not have to be provided; and from inspecting the <a href="https://pytorch-geometric.readthedocs.io/en/latest/_modules/torch_geometric/nn/conv/message_passing.html#MessagePassing">source code</a>, it is clear that, technically, the same holds for <code>message()</code>. (And unless we want a form of aggregation different from the default <code>add</code>, we do not even need to specify that, either.)</p>
<p>Thus, the question becomes: What happens if we code the <em>minimal PyG GNN</em>? To find out, we first need to create a minimal graph, one minimal enough for us to track what is going on.</p>
<section id="a-minimal-graph" class="level3">
<h3 class="anchored" data-anchor-id="a-minimal-graph">A minimal graph</h3>
<p>Now, a basic <code>Data</code> object is created from three tensors. The first holds the node features: two features each for five nodes. (Both features are identical on purpose, for “cognitive ease” – on our, not the algorithm’s, part.)</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch</span>
<span id="cb1-2"></span>
<span id="cb1-3">x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.tensor([[<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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</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;">3</span>], [<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">11</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">11</span>], [<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>]], dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>torch.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>)</span></code></pre></div></div>
<p>The second specifies existing connections. For undirected graphs (like ours), each edge appears twice. The tensor you see here is specified in one-edge-per-line form for convenience reasons; to the <code>Data()</code> constructor we’ll pass its transpose instead.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1">edge_index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.tensor([</span>
<span id="cb2-2">  [<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;">1</span>],</span>
<span id="cb2-3">  [<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;">0</span>],</span>
<span id="cb2-4">  [<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;">2</span>],</span>
<span id="cb2-5">  [<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;">0</span>],</span>
<span id="cb2-6">  [<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;">2</span>],</span>
<span id="cb2-7">  [<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;">1</span>],</span>
<span id="cb2-8">  [<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>
<span id="cb2-9">  [<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;">2</span>],</span>
<span id="cb2-10">  [<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;">4</span>],</span>
<span id="cb2-11">  [<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;">2</span>],</span>
<span id="cb2-12">  [<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;">4</span>],</span>
<span id="cb2-13">  [<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;">3</span>]</span>
<span id="cb2-14">], dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>torch.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">long</span>)</span></code></pre></div></div>
<p>The third tensor holds the node labels. (The task will be one of node – not edge, not graph – classification.)</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1">y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.tensor([[<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;">0</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;">1</span>], [<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]], dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>torch.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>)</span></code></pre></div></div>
<p>Constructing and inspecting the resulting graph, we have:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> torch_geometric.data <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Data</span>
<span id="cb4-2"></span>
<span id="cb4-3">data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Data(x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x, edge_index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> edge_index.t().contiguous(), y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> y)</span>
<span id="cb4-4">data.x</span>
<span id="cb4-5">data.edge_index</span>
<span id="cb4-6">data.y</span></code></pre></div></div>
<pre><code>tensor([[ 1.,  1.],
        [ 2.,  2.],
        [ 3.,  3.],
        [11., 11.],
        [12., 12.]])
        
tensor([[0, 1, 0, 2, 1, 2, 2, 3, 2, 4, 3, 4],
        [1, 0, 2, 0, 2, 1, 3, 2, 4, 2, 4, 3]])
        
tensor([[0.],
        [0.],
        [0.],
        [1.],
        [1.]])</code></pre>
<p>For our upcoming experiments, it’s more helpful, though, to visualize the graph:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> networkx <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> nx</span>
<span id="cb6-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb6-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> torch_geometric.utils <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> to_networkx</span>
<span id="cb6-4"></span>
<span id="cb6-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> visualize_graph(G, color, labels):</span>
<span id="cb6-6">    plt.figure(figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>))</span>
<span id="cb6-7">    plt.axis(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'off'</span>)</span>
<span id="cb6-8">    nx.draw_networkx(</span>
<span id="cb6-9">      G,</span>
<span id="cb6-10">      pos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nx.spring_layout(G, seed <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">777</span>),</span>
<span id="cb6-11">      labels <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> labels,</span>
<span id="cb6-12">      node_color <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> color,</span>
<span id="cb6-13">      cmap <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Set3"</span></span>
<span id="cb6-14">      )</span>
<span id="cb6-15">    plt.show()</span>
<span id="cb6-16"></span>
<span id="cb6-17">G <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> to_networkx(data, to_undirected <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, node_attrs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x"</span>])</span>
<span id="cb6-18">labels <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nx.get_node_attributes(G, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x"</span>)</span>
<span id="cb6-19">visualize_graph(G, color <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> data.y, labels <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> labels)</span></code></pre></div></div>
<p><a href="graph.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://divergences.xyz/posts/graphdl/graph.png" class="img-fluid"></a></p>
<p>Although our experiments won’t be about training performance (how could they be, with just five nodes), let me remark in passing that this graph is small, but not boring: The middle node is equally connected to both “sides”, yet feature-wise, it would pretty clearly appear to belong on just one of them. (Which is true, given the provided class labels). Such a constellation is interesting because, in the majority of networks, edges indicate similarity.</p>
</section>
<section id="a-minimal-gnn" class="level3">
<h3 class="anchored" data-anchor-id="a-minimal-gnn">A minimal GNN</h3>
<p>Now, we code and run the minimal GNN. We’re not interested in class labels (yet); we just want to see each node’s embeddings after a single pass.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> torch_geometric.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> MessagePassing</span>
<span id="cb7-2"></span>
<span id="cb7-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> IAmLazy(MessagePassing):</span>
<span id="cb7-4">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x, edge_index):</span>
<span id="cb7-5">        out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.propagate(edge_index, x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x)</span>
<span id="cb7-6">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> out</span>
<span id="cb7-7">      </span>
<span id="cb7-8">module <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> IAmLazy()</span>
<span id="cb7-9">out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> module(data.x, data.edge_index)</span>
<span id="cb7-10">out</span></code></pre></div></div>
<pre><code>tensor([[ 5.,  5.],
        [ 4.,  4.],
        [26., 26.],
        [15., 15.],
        [14., 14.]])</code></pre>
<p>Evidently, we just had to start the process – but what process, exactly? From what we know about the three stages of message passing, an essential question is what nodes do with the information that flows over the edges. Our first experiment, then, is to inspect the incoming messages.</p>
</section>
<section id="poking-into-message" class="level3">
<h3 class="anchored" data-anchor-id="poking-into-message">Poking into <code>message()</code></h3>
<p>In <code>message()</code>, we have access to a structure named <code>x_j</code>. This tensor holds, for each node <img src="https://latex.codecogs.com/png.latex?i">, the embeddings of all nodes <img src="https://latex.codecogs.com/png.latex?j"> connected to it via incoming edges. We’ll print them, and then, just return them, unchanged.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> IAmMyOthers(MessagePassing):</span>
<span id="cb9-2">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x, edge_index):</span>
<span id="cb9-3">        out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.propagate(edge_index, x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x)</span>
<span id="cb9-4">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> out</span>
<span id="cb9-5">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> message(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x_j):</span>
<span id="cb9-6">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"in message, x_j is"</span>)</span>
<span id="cb9-7">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(x_j)</span>
<span id="cb9-8">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> x_j</span>
<span id="cb9-9">      </span>
<span id="cb9-10">module <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> IAmMyOthers()</span>
<span id="cb9-11">out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> module(data.x, data.edge_index)</span>
<span id="cb9-12"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"result is:"</span>)</span>
<span id="cb9-13">out</span></code></pre></div></div>
<pre><code>in message, x_j is
tensor([[ 1.,  1.],
        [ 2.,  2.],
        [ 1.,  1.],
        [ 3.,  3.],
        [ 2.,  2.],
        [ 3.,  3.],
        [ 3.,  3.],
        [11., 11.],
        [ 3.,  3.],
        [12., 12.],
        [11., 11.],
        [12., 12.]])
        
result is:
tensor([[ 5.,  5.],
        [ 4.,  4.],
        [26., 26.],
        [15., 15.],
        [14., 14.]])</code></pre>
<p>Let me spell this out. In <code>data.edge_index</code>, repeated here for convenience:</p>
<pre><code>tensor([[0, 1, 0, 2, 1, 2, 2, 3, 2, 4, 3, 4],
        [1, 0, 2, 0, 2, 1, 3, 2, 4, 2, 4, 3]])</code></pre>
<p>the first pair denotes the edge from node <code>0</code> (that had features <code>(1, 1)</code>) to node <code>1</code>. This information is found in <code>x_j</code>’s first row. Then the second row holds the information flowing in the opposite direction, namely, the features associated with node <code>1</code>. And so on.</p>
<p>Interestingly, since we’re passing through this module just once, we can see the messages that will be sent without even running it.</p>
<p>Namely, since <code>data.edge_index[0]</code> designates the <em>source</em> nodes for each edge:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1">data.edge_index[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span></code></pre></div></div>
<p>we can index <code>into data.x</code> to pick up what will be the <em>incoming</em> features for each connection.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb13-1">data.x[data.edge_index[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]]</span></code></pre></div></div>
<pre><code>tensor([[ 1.,  1.],
        [ 2.,  2.],
        [ 1.,  1.],
        [ 3.,  3.],
        [ 2.,  2.],
        [ 3.,  3.],
        [ 3.,  3.],
        [11., 11.],
        [ 3.,  3.],
        [12., 12.],
        [11., 11.],
        [12., 12.]])</code></pre>
<p>Now, what does this tell us? Node <code>0</code>, for example, received messages from nodes <code>1</code> and <code>2</code>: <code>(2, 2)</code> and <code>(3, 3)</code>, respectively. We know that the default aggregation mode is <code>add</code>; and so, would expect an outcome of <code>(5, 5)</code>. Indeed, this is the new embedding for node <code>0</code>.</p>
<p>In a nutshell, thus, the minimal GNN updates every node’s embedding so as to prototypically reflect the node’s neighborhood. Take care though: Nodes represent their neighborhoods, but themselves, they count for nothing. We will change that now.</p>
</section>
<section id="adding-self-loops" class="level3">
<h3 class="anchored" data-anchor-id="adding-self-loops">Adding self loops</h3>
<p>All we need to do is modify the adjacency matrix to include edges going from each node back to itself.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb15-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> torch_geometric.utils <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> add_self_loops</span>
<span id="cb15-2"></span>
<span id="cb15-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> IAmMyOthersAndMyselfAsWell(MessagePassing):</span>
<span id="cb15-4">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x, edge_index):</span>
<span id="cb15-5">        edge_index, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> add_self_loops(edge_index, num_nodes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x.size(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>))</span>
<span id="cb15-6">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"in forward, augmented edge index now has shape"</span>)</span>
<span id="cb15-7">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(edge_index.shape)</span>
<span id="cb15-8">        out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.propagate(edge_index, x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x)</span>
<span id="cb15-9">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> out</span>
<span id="cb15-10">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> message(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x_j):</span>
<span id="cb15-11">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> x_j</span>
<span id="cb15-12"></span>
<span id="cb15-13">module <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> IAmMyOthersAndMyselfAsWell()</span>
<span id="cb15-14">out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> module(data.x, data.edge_index)</span>
<span id="cb15-15"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"result is:"</span>)</span>
<span id="cb15-16">out</span></code></pre></div></div>
<pre><code>in forward, augmented edge index now has shape:
torch.Size([2, 17])

result is:
tensor([[ 6.,  6.],
        [ 6.,  6.],
        [29., 29.],
        [26., 26.],
        [26., 26.]])</code></pre>
<p>As expected, the neighborhood summary at each node now includes a contribution from each node itself.</p>
<p>Now we know how to access the messages, we’d like to aggregate them in a non-standard way.</p>
</section>
<section id="customizing-aggregate" class="level3">
<h3 class="anchored" data-anchor-id="customizing-aggregate">Customizing <code>aggregate()</code></h3>
<p>Instead of <code>message()</code>, we now override <code>aggregate()</code>. If we wanted to use another of the “standard” aggregation modes (<code>mean</code>, <code>mul</code>, <code>min</code>, or <code>max</code>), we could just override <code>__init__(),</code> like so:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb17-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb17-2">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(aggr <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mean"</span>)</span></code></pre></div></div>
<p>To implement custom summaries, however, we make use of <code>torch_scatter</code> (one of PyG’s installation prerequisites) for optimal performance. Let me show this by means of a simple example.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb18-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> torch_scatter <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> scatter</span>
<span id="cb18-2"></span>
<span id="cb18-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> IAmJustTheOppositeReally(MessagePassing):</span>
<span id="cb18-4">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x, edge_index):</span>
<span id="cb18-5">        out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.propagate(edge_index, x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x)</span>
<span id="cb18-6">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> out</span>
<span id="cb18-7">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> aggregate(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, inputs, index):</span>
<span id="cb18-8">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"in aggregate, inputs is"</span>)</span>
<span id="cb18-9">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># same as x_j (incoming node features)</span></span>
<span id="cb18-10">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(inputs)</span>
<span id="cb18-11">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"in aggregate, index is"</span>)</span>
<span id="cb18-12">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># this is data.edge_index[1]</span></span>
<span id="cb18-13">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(index)</span>
<span id="cb18-14">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># see https://pytorch-scatter.readthedocs.io/en/1.3.0/index.html</span></span>
<span id="cb18-15">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># for other aggregation modes</span></span>
<span id="cb18-16">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default dim is -1</span></span>
<span id="cb18-17">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> scatter(inputs, index, dim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">reduce</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"add"</span>) </span>
<span id="cb18-18">      </span>
<span id="cb18-19">module <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> IAmJustTheOppositeReally()</span>
<span id="cb18-20">out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> module(data.x, data.edge_index)</span>
<span id="cb18-21"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"result is:"</span>)</span>
<span id="cb18-22">out</span></code></pre></div></div>
<pre><code>in aggregate, inputs is
tensor([[ 1.,  1.],
        [ 2.,  2.],
        [ 1.,  1.],
        [ 3.,  3.],
        [ 2.,  2.],
        [ 3.,  3.],
        [ 3.,  3.],
        [11., 11.],
        [ 3.,  3.],
        [12., 12.],
        [11., 11.],
        [12., 12.]])
        
in aggregate, index is
tensor([1, 0, 2, 0, 2, 1, 3, 2, 4, 2, 4, 3])

result is:
tensor([[ -5.,  -5.],
        [ -4.,  -4.],
        [-26., -26.],
        [-15., -15.],
        [-14., -14.]])</code></pre>
<p>In <code>aggregate()</code>, we have two types of tensors to work with. One, <code>inputs</code>, holds what was returned from <code>message()</code>. In our case, this is identical to <code>x_j</code>, since we didn’t make any modifications to the default behavior. The second, <code>index</code>, holds the recipe for where in the aggregation those features should go. Here, the very first tuple, <code>(1, 1)</code>, will contribute to the summary for node <code>1</code>; the second, <code>(2, 2)</code>, to that for node <code>0</code> – and so on. By the way, just like <code>x_j</code> (in a single-layer, single-pass setup) is “just” <code>data.x[data.edge_index[0]]</code>, that <code>index</code> is “just” <code>data.edge_index[1]</code>. Meaning, this is the list of target nodes connected to the edges in question.</p>
<p>At this point, all kinds of manipulations could be done on either <code>inputs</code> or <code>index</code>; however, we content ourselves with just passing them through to <code>torch_scatter.scatter()</code>, and returning the negated sums. We’ve successfully built a network of contrarians.</p>
<p>By now, we’ve played with <code>message()</code> as well as <code>aggregate()</code>. What about <code>update()</code>?</p>
</section>
<section id="adding-memory-to-update" class="level3">
<h3 class="anchored" data-anchor-id="adding-memory-to-update">Adding memory to <code>update()</code></h3>
<p>There’s one thing really strange in what we’re doing. It doesn’t jump to the eye, since we’re not simulating a real training phase; we’ve been calling the layer just once. If we hadn’t, we’d have noticed that at every call, the nodes happily forget who they were before, dutifully assuming the new identities assigned. In reality, we probably want them to evolve in a more consistent way.</p>
<p>For example:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb20" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb20-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> IDoEvolveOverTime(MessagePassing):</span>
<span id="cb20-2">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x, edge_index):</span>
<span id="cb20-3">        edge_index, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> add_self_loops(edge_index, num_nodes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>x.size(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>))</span>
<span id="cb20-4">        out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.propagate(edge_index, x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x)</span>
<span id="cb20-5">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> out</span>
<span id="cb20-6">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> update(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, inputs, x):</span>
<span id="cb20-7">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"in update, inputs is"</span>)</span>
<span id="cb20-8">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(inputs)</span>
<span id="cb20-9">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"in update, x is"</span>)</span>
<span id="cb20-10">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(x)</span>
<span id="cb20-11">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (inputs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> x)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb20-12"></span>
<span id="cb20-13">module <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> IDoEvolveOverTime()</span>
<span id="cb20-14">out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> module(data.x, data.edge_index)</span>
<span id="cb20-15"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"result is:"</span>)</span>
<span id="cb20-16">out</span></code></pre></div></div>
<pre><code>in update, inputs is
tensor([[ 6.,  6.],
        [ 6.,  6.],
        [29., 29.],
        [26., 26.],
        [26., 26.]])
in update, x is
tensor([[ 1.,  1.],
        [ 2.,  2.],
        [ 3.,  3.],
        [11., 11.],
        [12., 12.]])
result is:
tensor([[ 3.5000,  3.5000],
        [ 4.0000,  4.0000],
        [16.0000, 16.0000],
        [18.5000, 18.5000],
        [19.0000, 19.0000]])</code></pre>
<p>In <code>update()</code>, we have access to both the final message aggregate (<code>inputs</code>) and the nodes’ prior states (<code>x</code>). Here, I’m just averaging those two.</p>
<p>At this point, we’ve successfully acquainted ourselves with the three stages of message passing: acting on individual messages, aggregating them, and self-updating based on past state and new information. But none of our models so far could be called a neural network, since there was no learning involved.</p>
</section>
<section id="adding-parameters" class="level3">
<h3 class="anchored" data-anchor-id="adding-parameters">Adding parameters</h3>
<p>If we look back at the generic message passing formulation:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7Bx%7D_i%5E%7B(k)%7D%20=%20%5Cgamma%5E%7B(k)%7D%20%5Cleft(%20%5Cmathbf%7Bx%7D_i%5E%7B(k-1)%7D,%20%5Csquare_%7Bj%20%5Cin%20%5Cmathcal%7BN%7D(i)%7D%20%5C,%20%5Cphi%5E%7B(k)%7D%5Cleft(%5Cmathbf%7Bx%7D_i%5E%7B(k-1)%7D,%20%5Cmathbf%7Bx%7D_j%5E%7B(k-1)%7D,%5Cmathbf%7Be%7D_%7Bj,i%7D%5Cright)%20%5Cright)%0A"> we see two places where neural network modules can act on the computation: before message aggregation, and as part of the node update process. First, we illustrate the former option. For example, we can apply a MLP in <code>forward()</code>, before the call to <code>aggregate()</code>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb22-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> torch.nn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Sequential <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> Seq, Linear, ReLU</span>
<span id="cb22-2"></span>
<span id="cb22-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> ILearnAndEvolve(MessagePassing):</span>
<span id="cb22-4">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, in_channels, out_channels):</span>
<span id="cb22-5">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(aggr <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sum"</span>)</span>
<span id="cb22-6">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.mlp <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Seq(Linear(in_channels, out_channels),</span>
<span id="cb22-7">                       ReLU(),</span>
<span id="cb22-8">                       Linear(out_channels, out_channels))</span>
<span id="cb22-9">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x, edge_index):</span>
<span id="cb22-10">        edge_index, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> add_self_loops(edge_index, num_nodes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>x.size(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>))</span>
<span id="cb22-11">        x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.mlp(x)</span>
<span id="cb22-12">        out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.propagate(edge_index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> edge_index, x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x)</span>
<span id="cb22-13">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> out</span>
<span id="cb22-14">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> update(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, inputs, x):</span>
<span id="cb22-15">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (inputs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> x)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb22-16"></span>
<span id="cb22-17">module <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ILearnAndEvolve(<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;">2</span>)</span>
<span id="cb22-18">out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> module(data.x, data.edge_index)</span>
<span id="cb22-19"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"result is:"</span>)</span>
<span id="cb22-20">out</span></code></pre></div></div>
<pre><code>result is:
tensor([[-0.8724, -0.4407],
        [-0.9056, -0.4623],
        [-2.0229, -1.1240],
        [-1.8691, -1.0867],
        [-1.9024, -1.1082]], grad_fn=&lt;DivBackward0&gt;)</code></pre>
<p>Finally, we can apply network modules in both places, as exemplified next.</p>
</section>
<section id="general-message-passing" class="level3">
<h3 class="anchored" data-anchor-id="general-message-passing">General message passing</h3>
<p>We keep the MLP from the previous class, and add a second in <code>update()</code>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb24" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb24-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> ILearnAndEvolveDoubly(MessagePassing):</span>
<span id="cb24-2">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, in_channels, out_channels):</span>
<span id="cb24-3">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(aggr <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sum"</span>)</span>
<span id="cb24-4">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.mlp_msg <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Seq(Linear(in_channels, out_channels),</span>
<span id="cb24-5">                       ReLU(),</span>
<span id="cb24-6">                       Linear(out_channels, out_channels))</span>
<span id="cb24-7">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.mlp_upd <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Seq(Linear(out_channels, out_channels),</span>
<span id="cb24-8">                       ReLU(),</span>
<span id="cb24-9">                       Linear(out_channels, out_channels))</span>
<span id="cb24-10">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x, edge_index):</span>
<span id="cb24-11">        edge_index, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> add_self_loops(edge_index, num_nodes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>x.size(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>))</span>
<span id="cb24-12">        x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.mlp_msg(x)</span>
<span id="cb24-13">        out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.propagate(edge_index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> edge_index, x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x)</span>
<span id="cb24-14">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> out</span>
<span id="cb24-15">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> update(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, inputs, x):</span>
<span id="cb24-16">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.mlp_upd((inputs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> x)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb24-17"></span>
<span id="cb24-18">module <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ILearnAndEvolveDoubly(<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;">2</span>)</span>
<span id="cb24-19">out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> module(data.x, data.edge_index)</span>
<span id="cb24-20"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"result is:"</span>)</span>
<span id="cb24-21">out</span></code></pre></div></div>
<pre><code>result is:
tensor([[ 0.0573, -0.6988],
        [ 0.0358, -0.6894],
        [-0.1730, -0.6450],
        [-0.5855, -0.4171],
        [-0.5890, -0.4141]], grad_fn=&lt;AddmmBackward0&gt;)</code></pre>
<p>At this point, I hope you’ll feel comfortable to play around, subclassing the <code>MessagePassing</code> base class. Also, if now you consult the above-mentioned documentation page (<a href="https://pytorch-geometric.readthedocs.io/en/latest/notes/create_gnn.html">Creating message passing networks</a>), you’ll be able to map the example implementations (dedicated to popular GNN layer types) to where they “hook into” the message passing process.</p>
<p>Experimentation with <code>MessagePassing</code> was the point of this post. However, you may be wondering: How do I actually use this for node classification? Didn’t the graph have a class defined for each node? (It did: <code>data.y</code>.)</p>
<p>So let me conclude with a (minimal) end-to-end example that uses one of the above modules.</p>
</section>
<section id="a-minimal-workflow" class="level3">
<h3 class="anchored" data-anchor-id="a-minimal-workflow">A minimal workflow</h3>
<p>To that purpose, we compose that module with a linear one that performs node classification:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb26" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb26-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Network(torch.nn.Module):</span>
<span id="cb26-2">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, in_channels, out_channels, num_classes):</span>
<span id="cb26-3">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>()</span>
<span id="cb26-4">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.conv <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ILearnAndEvolveDoubly(in_channels, out_channels)</span>
<span id="cb26-5">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.classifier <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Linear(out_channels, num_classes)</span>
<span id="cb26-6">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> forward(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, x, edge_index):</span>
<span id="cb26-7">        x, edge_index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> data.x, data.edge_index</span>
<span id="cb26-8">        x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.conv(x, edge_index)</span>
<span id="cb26-9">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.classifier(x)</span>
<span id="cb26-10"></span>
<span id="cb26-11">model <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Network(<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;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) </span></code></pre></div></div>
<p>We can then train the model like any other:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb27" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb27-1"></span>
<span id="cb27-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> torch.nn.functional <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> F</span>
<span id="cb27-3"></span>
<span id="cb27-4">optimizer <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.optim.Adam(model.parameters(), lr <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>)</span>
<span id="cb27-5">model.train()</span>
<span id="cb27-6"></span>
<span id="cb27-7"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> epoch <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>):</span>
<span id="cb27-8">    optimizer.zero_grad()</span>
<span id="cb27-9">    out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> model(data.x, data.edge_index)</span>
<span id="cb27-10">    loss <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> F.binary_cross_entropy_with_logits(out, data.y)</span>
<span id="cb27-11">    loss.backward()</span>
<span id="cb27-12">    optimizer.step()</span>
<span id="cb27-13"></span>
<span id="cb27-14">preds <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> torch.sigmoid(out)</span>
<span id="cb27-15">preds</span></code></pre></div></div>
<pre><code>tensor([[0.6502],
        [0.6532],
        [0.7027],
        [0.7145],
        [0.7165]], grad_fn=&lt;SigmoidBackward0&gt;)</code></pre>
<p>And that’s it for this time. Stay tuned for examples of how graph models are applied in the sciences, as well as illustrations of bleeding-edge developments in <a href="https://blogs.rstudio.com/ai/posts/2021-08-26-geometric-deep-learning/">Geometric Deep Learning</a>, the principles-based, heuristics-transcending approach to neural networks.</p>
<p>Thanks for reading!</p>
<div class="photo-credit">
<p>Photo by <a href="https://unsplash.com/@alinnnaaaa?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Alina Grubnyak</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
</div>


</section>
</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/graphdl/</guid>
  <pubDate>Tue, 22 Nov 2022 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/graphdl/net.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Forecasting El Niño-Southern Oscillation (ENSO)</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/enso-prediction/</link>
  <description><![CDATA[ 






<p>Today, we use the convLSTM introduced in a <a href="https://blogs.rstudio.com/ai/posts/2020-12-17-torch-convlstm/">previous</a> post to predict <a href="https://en.wikipedia.org/wiki/El_Ni%C3%B1o%E2%80%93Southern_Oscillation">El Niño-Southern Oscillation (ENSO).</a></p>
<section id="el-niño-la-niña" class="level1 page-columns page-full">
<h1>El Niño, la Niña</h1>
<p>ENSO refers to a changing pattern of sea surface temperatures and sea-level pressures occurring in the equatorial Pacific. From its three overall states, probably the best-known is El Niño. El Niño occurs when surface water temperatures in the eastern Pacific are higher than normal, and the strong winds that normally blow from east to west are unusually weak. The opposite conditions are termed La Niña. Everything in-between is classified as normal.</p>
<p>ENSO has great impact on the weather worldwide, and routinely harms ecosystems and societies through storms, droughts and flooding, possibly resulting in famines and economic crises. The best societies can do is try to adapt and mitigate severe consequences. Such efforts are aided by accurate forecasts, the further ahead the better.</p>
<p>Here, deep learning (DL) can potentially help: Variables like sea surface temperatures and pressures are given on a spatial grid – that of the earth – and as we know, DL is good at extracting spatial (e.g., image) features. For ENSO prediction, architectures like convolutional neural networks (<span class="citation" data-cites="dlenso">Ham et al. (2019)</span>) or convolutional-recurrent hybrids<sup>1</sup> are habitually used. One such hybrid is just our convLSTM; it operates on sequences of features given on a spatial grid. Today, thus, we’ll be training a model for ENSO forecasting. This model will have a convLSTM for its central ingredient.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;E.g., <a href="https://www.climatechange.ai/papers/neurips2019/40/paper.pdf">Forecasting El Niño with Convolutional and Recurrent Neural Networks</a>.</p></div><div id="fn2"><p><sup>2</sup>&nbsp;E.g., <a href="https://esgf-node.llnl.gov/projects/cmip5/">CMIP5</a>, <a href="http://www.umr-cnrm.fr/spip.php?article126&amp;lang=en">CNRM-CM5</a>, or <a href="https://southernocean.arizona.edu/content/hadgem2-es">HADGEM2-ES</a>.</p></div></div><p>Before we start, a note. While our model fits well with architectures described in the relevant papers, the same cannot be said for amount of training data used. For reasons of practicality, we use actual observations only; consequently, we end up with a small (relative to the task) dataset. In contrast, research papers tend to make use of climate simulations<sup>2</sup>, resulting in significantly more data to work with.</p>
<p>From the outset, then, we don’t expect stellar performance. Nevertheless, this should make for an interesting case study, and a useful code template for our readers to apply to their own data.</p>
</section>
<section id="data" class="level1 page-columns page-full">
<h1>Data</h1>
<p>We will attempt to predict monthly average sea surface temperature in the Niño 3.4 region<sup>3</sup>, as represented by the Niño 3.4 Index, plus categorization as one of <em>El Niño</em>, <em>La Niña</em> or <em>neutral</em><sup>4</sup>. Predictions will be based on prior monthly sea surface temperatures spanning a large portion of the globe.</p>
<div class="no-row-height column-margin column-container"><div id="fn3"><p><sup>3</sup>&nbsp;This region extends over latitudes from 5° south to 5° north and longitudes from 120° west to 170° west.</p></div><div id="fn4"><p><sup>4</sup>&nbsp;That classification is based on ONI (Oceanic Niño Index), a measure representing 3-month average anomalies in the Niño 3.4 Index.</p></div></div><p>On the input side, public and ready-to-use data may be downloaded from <a href="https://ds.data.jma.go.jp/tcc/tcc/index.html">Tokyo Climate Center</a>; as to prediction targets, we obtain index and classification <a href="https://bmcnoldy.rsmas.miami.edu/tropics/oni/">here</a>.</p>
<p>Input and target data both are provided monthly. They intersect in the time period ranging from 1891-01-01 to 2020-08-01; so this is the range of dates we’ll be zooming in on.</p>
<section id="input-sea-surface-temperatures" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="input-sea-surface-temperatures">Input: Sea Surface Temperatures</h2>
<p>Monthly sea surface temperatures are provided in a latitude-longitude grid of resolution 1°. Details of how the data were processed are available <a href="https://ds.data.jma.go.jp/tcc/tcc/library/MRCS_SV12/explanation/cobe_sst_e.htm">here</a>.</p>
<p>Data files are available in <a href="https://en.wikipedia.org/wiki/GRIB">GRIB</a> format; each file contains averages computed for a single month. We can either download individual files or <a href="https://ds.data.jma.go.jp/tcc/tcc/products/elnino/cobesst/cobe-sst.html">generate a text file of URLs</a> for download. Once you’ve saved these URLs to a file, you can have R get the files for you like so:</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">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;">walk</span>(</span>
<span id="cb1-2">   <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readLines</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"files"</span>),</span>
<span id="cb1-3">   <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(f) <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">download.file</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">url =</span> f, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">destfile =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">basename</span>(f))</span>
<span id="cb1-4">)</span></code></pre></div></div>
<p>From R, we can read GRIB files using <a href="https://cran.r-project.org/web/packages/stars/">stars</a>. For example:</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"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># let's just quickly load all libraries we require to start with</span></span>
<span id="cb2-2"></span>
<span id="cb2-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(torch)</span>
<span id="cb2-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tidyverse)</span>
<span id="cb2-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(stars)</span>
<span id="cb2-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(viridis)</span>
<span id="cb2-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggthemes)</span>
<span id="cb2-8"></span>
<span id="cb2-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">torch_manual_seed</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">777</span>)</span>
<span id="cb2-10"></span>
<span id="cb2-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_stars</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.path</span>(grb_dir, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sst189101.grb"</span>))</span></code></pre></div></div>
<pre><code>stars object with 2 dimensions and 1 attribute
attribute(s):
 sst189101.grb   
 Min.   :-274.9  
 1st Qu.:-272.8  
 Median :-259.1  
 Mean   :-260.0  
 3rd Qu.:-248.4  
 Max.   :-242.8  
 NA's   :21001   
dimension(s):
  from  to offset delta                       refsys point values    
x    1 360      0     1 Coordinate System importe...    NA   NULL [x]
y    1 180     90    -1 Coordinate System importe...    NA   NULL [y]</code></pre>
<p>So in this GRIB file, we have one attribute - which we know to be sea surface temperature – on a two-dimensional grid. As to the latter, we can complement what <code>stars</code> tells us with additional info found in the <a href="https://ds.data.jma.go.jp/tcc/tcc/library/MRCS_SV12/explanation/cobe_sst_e.htm">documentation</a>:</p>
<blockquote class="blockquote">
<p>The east-west grid points run eastward from 0.5ºE to 0.5ºW, while the north-south grid points run northward from 89.5ºS to 89.5ºN.</p>
</blockquote>
<p>We note a few things we’ll want to do with this data. For one, the temperatures seem to be given in Kelvin, but with minus signs.<sup>5</sup> We’ll remove the minus signs and convert to degrees Celsius for convenience. We’ll also have to think about what to do with the <code>NA</code>s that appear for all non-maritime coordinates.</p>
<div class="no-row-height column-margin column-container"><div id="fn5"><p><sup>5</sup>&nbsp;This may also be an artifact produced by the software stack involved in reading the file.</p></div></div><p>Before we get there though, we need to combine data from all files into a single data frame. This adds an additional dimension, time, ranging from 1891/01/01 to 2020/01/12:</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">grb <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;">read_stars</span>(</span>
<span id="cb4-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.path</span>(grb_dir, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">map</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readLines</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"files"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">warn =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>), basename)), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">along =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"time"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb4-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">st_set_dimensions</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>,</span>
<span id="cb4-4">                    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1891-01-01"</span>), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2020-12-01"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"months"</span>),</span>
<span id="cb4-5">                    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">names =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"time"</span></span>
<span id="cb4-6">                    )</span>
<span id="cb4-7"></span>
<span id="cb4-8">grb</span></code></pre></div></div>
<pre><code>stars object with 3 dimensions and 1 attribute
attribute(s), summary of first 1e+05 cells:
 sst189101.grb   
 Min.   :-274.9  
 1st Qu.:-273.3  
 Median :-258.8  
 Mean   :-260.0  
 3rd Qu.:-247.8  
 Max.   :-242.8  
 NA's   :33724   
dimension(s):
     from   to offset delta                       refsys point                    values    
x       1  360      0     1 Coordinate System importe...    NA                      NULL [x]
y       1  180     90    -1 Coordinate System importe...    NA                      NULL [y]
time    1 1560     NA    NA                         Date    NA 1891-01-01,...,2020-12-01    </code></pre>
<p>Let’s visually inspect the spatial distribution of monthly temperatures for one year, 2020:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_stars</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> grb <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;">filter</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">between</span>(time, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2020-01-01"</span>), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2020-12-01"</span>))), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">alpha =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">facet_wrap</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"time"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_viridis</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">coord_equal</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_map</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend.position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>) </span></code></pre></div></div>
<p><a href="enso.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://divergences.xyz/posts/enso-prediction/enso.png" class="img-fluid" alt="Monthly sea surface temperatures, 2020/01/01 - 2020/01/12."></a></p>
</section>
<section id="target-niño-3.4-index" class="level2">
<h2 class="anchored" data-anchor-id="target-niño-3.4-index">Target: Niño 3.4 Index</h2>
<p>For the Niño 3.4 Index, we download the monthly <a href="https://bmcnoldy.rsmas.miami.edu/tropics/oni/ONI_NINO34_1854-2020.txt">data</a> and, among the provided features, zoom in on two: the index itself (column <code>NINO34_MEAN</code>) and <code>PHASE</code>, which can be <code>E</code> (El Niño), <code>L</code> (La Niño) or <code>N</code> (neutral).</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">nino <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;">read_table2</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ONI_NINO34_1854-2020.txt"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">skip =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb7-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">month =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(YEAR, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">MON/MMM</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-01"</span>))) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb7-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(month, NINO34_MEAN, PHASE) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb7-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">between</span>(month, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1891-01-01"</span>), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2020-08-01"</span>))) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb7-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">phase_code =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.factor</span>(PHASE)))</span>
<span id="cb7-6"></span>
<span id="cb7-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(nino)</span></code></pre></div></div>
<pre><code>1556</code></pre>
<p>Next, we look at how to get the data into a format convenient for training and prediction.</p>
</section>
</section>
<section id="preprocessing" class="level1 page-columns page-full">
<h1>Preprocessing</h1>
<section id="input" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="input">Input</h2>
<p>First, we remove all input data for points in time where ground truth data are still missing.</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">sst <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> grb <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;">filter</span>(time <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2020-08-01"</span>))</span></code></pre></div></div>
<p>Next, as is done by e.g. <span class="citation" data-cites="dlenso">Ham et al. (2019)</span>, we only use grid points between 55° south and 60° north. This has the additional advantage of reducing memory requirements.</p>
<div class="no-row-height column-margin column-container"><div id="ref-dlenso" class="csl-entry">
Ham, Yoo-Geun, Jeong-Hwan Kim, and Jing-Jia Luo. 2019. <span>“<span class="nocase">Deep learning for multi-year ENSO forecasts</span>.”</span> 573 (7775): 568–72. <a href="https://doi.org/10.1038/s41586-019-1559-7">https://doi.org/10.1038/s41586-019-1559-7</a>.
</div></div><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">sst <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> grb <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;">filter</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">between</span>(y,<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;">55</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>))</span>
<span id="cb10-2"></span>
<span id="cb10-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(sst)</span></code></pre></div></div>
<pre><code>360, 115, 1560</code></pre>
<p>As already alluded to, with the little data we have we can’t expect much in terms of generalization. Still, we set aside a small portion of the data for validation, since we’d like for this post to serve as a useful template to be used with bigger datasets.</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">sst_train <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> sst <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;">filter</span>(time <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1990-01-01"</span>))</span>
<span id="cb12-2">sst_valid <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> sst <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;">filter</span>(time <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.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1990-01-01"</span>))</span></code></pre></div></div>
<p>From here on, we work with R arrays.</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">sst_train <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;">as.tbl_cube.stars</span>(sst_train)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>mets[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb13-2">sst_valid <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;">as.tbl_cube.stars</span>(sst_valid)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>mets[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span></code></pre></div></div>
<p>Conversion to degrees Celsius is not strictly necessary, as initial experiments showed a slight performance increase due to normalizing the input, and we’re going to do that anyway. Still, it reads nicer to humans than Kelvin.</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">sst_train <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> sst_train <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">273.15</span></span>
<span id="cb14-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quantile</span>(sst_train, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span></code></pre></div></div>
<pre><code>     0%     25%     50%     75%    100% 
-1.8000 12.9975 21.8775 26.8200 34.3700 </code></pre>
<p>Not at all surprisingly, global warming is evident from inspecting temperature distribution on the validation set (which was chosen to span the last thirty-one years).</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">sst_valid <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> sst_valid <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">273.15</span></span>
<span id="cb16-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quantile</span>(sst_valid, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span></code></pre></div></div>
<pre><code>    0%    25%    50%    75%   100% 
-1.800 13.425 22.335 27.240 34.870 </code></pre>
<p>The next-to-last step normalizes both sets according to training mean and variance.</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">train_mean <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;">mean</span>(sst_train, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb18-2">train_sd <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;">sd</span>(sst_train, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb18-3"></span>
<span id="cb18-4">sst_train <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> (sst_train <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> train_mean) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> train_sd</span>
<span id="cb18-5"></span>
<span id="cb18-6">sst_valid <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> (sst_valid <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> train_mean) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> train_sd</span></code></pre></div></div>
<p>Finally, what should we do about the <code>NA</code> entries? We set them to zero, the (training set) mean. That may not be enough of an action though: It means we’re feeding the network roughly 30% misleading data. This is something we’re not done with yet.</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">sst_train[<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(sst_train)] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb19-2">sst_valid[<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(sst_valid)] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span></code></pre></div></div>
</section>
<section id="target" class="level2">
<h2 class="anchored" data-anchor-id="target">Target</h2>
<p>The target data are split analogously. Let’s check though: Are phases (categorizations) distributedly similarly in both sets?</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">nino_train <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nino <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;">filter</span>(month <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1990-01-01"</span>))</span>
<span id="cb20-2">nino_valid <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nino <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;">filter</span>(month <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.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1990-01-01"</span>))</span>
<span id="cb20-3"></span>
<span id="cb20-4">nino_train <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;">group_by</span>(phase_code, PHASE) <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;">summarise</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">count =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">n</span>(), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">avg =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(NINO34_MEAN))</span></code></pre></div></div>
<pre><code># A tibble: 3 x 4
# Groups:   phase_code [3]
  phase_code PHASE count   avg
       &lt;dbl&gt; &lt;chr&gt; &lt;int&gt; &lt;dbl&gt;
1          1 E       301  27.7
2          2 L       333  25.6
3          3 N       554  26.7</code></pre>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb22-1">nino_valid <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;">group_by</span>(phase_code, PHASE) <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;">summarise</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">count =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">n</span>(), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">avg =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(NINO34_MEAN))</span></code></pre></div></div>
<pre><code># A tibble: 3 x 4
# Groups:   phase_code [3]
  phase_code PHASE count   avg
       &lt;dbl&gt; &lt;chr&gt; &lt;int&gt; &lt;dbl&gt;
1          1 E        93  28.1
2          2 L        93  25.9
3          3 N       182  27.2</code></pre>
<p>This doesn’t look too bad. Of course, we again see the overall rise in temperature, irrespective of phase.</p>
<p>Lastly, we normalize the index, same as we did for the input data.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb24" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb24-1">train_mean_nino <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;">mean</span>(nino_train<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>NINO34_MEAN)</span>
<span id="cb24-2">train_sd_nino <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;">sd</span>(nino_train<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>NINO34_MEAN)</span>
<span id="cb24-3"></span>
<span id="cb24-4">nino_train <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nino_train <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;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">NINO34_MEAN =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale</span>(NINO34_MEAN, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">center =</span> train_mean_nino, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scale =</span> train_sd_nino))</span>
<span id="cb24-5">nino_valid <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nino_valid <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;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">NINO34_MEAN =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale</span>(NINO34_MEAN, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">center =</span> train_mean_nino, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scale =</span> train_sd_nino))</span></code></pre></div></div>
<p>On to the <code>torch</code> dataset.</p>
</section>
</section>
<section id="torch-dataset" class="level1">
<h1><code>Torch</code> dataset</h1>
<p>The dataset is responsible for correctly matching up inputs and targets.</p>
<p>Our goal is to take six months of global sea surface temperatures and predict the Niño 3.4 Index for the following month. Input-wise, the model will expect the following format semantics:</p>
<p><code>batch_size * timesteps * width * height * channels</code>, where</p>
<ul>
<li><p><code>batch_size</code> is the number of observations worked on in one round of computations,</p></li>
<li><p><code>timesteps</code> chains consecutive observations from adjacent months,</p></li>
<li><p><code>width</code> and <code>height</code> together constitute the spatial grid, and</p></li>
<li><p><code>channels</code> corresponds to available visual channels in the “image”.</p></li>
</ul>
<p>In <code>.getitem()</code>, we select the consecutive observations, starting at a given index, and stack them in dimension one. (One, not two, as batches will only start to exist once the <code>dataloader</code> comes into play.)</p>
<p>Now, what about the target? Our ultimate goal was – is – predicting the Niño 3.4 Index. However, as you see we define three targets: One is the index, as expected; an additional one holds the spatially-gridded sea surface temperatures for the prediction month. Why? Our main instrument, the most prominent constituent of the model, will be a convLSTM, an architecture designed for <em>spatial</em> prediction. Thus, to train it efficiently, we want to give it the opportunity to predict values on a spatial grid. So far so good; but there’s one more target, the phase/category. This was added for experimentation purposes: Maybe predicting <em>both</em> index <em>and</em> phase helps in training?</p>
<p>Finally, here is the code for the dataset. In our experiments, we based predictions on inputs from the preceding six months (<code>n_timesteps &lt;- 6</code>). This is a parameter you might want to play with, though.</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">n_timesteps <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span></span>
<span id="cb25-2"></span>
<span id="cb25-3">enso_dataset <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;">dataset</span>(</span>
<span id="cb25-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"enso_dataset"</span>,</span>
<span id="cb25-5">  </span>
<span id="cb25-6">  <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>(sst, nino, n_timesteps) {</span>
<span id="cb25-7">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>sst <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> sst</span>
<span id="cb25-8">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>nino <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> nino</span>
<span id="cb25-9">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_timesteps <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> n_timesteps</span>
<span id="cb25-10">  },</span>
<span id="cb25-11">  </span>
<span id="cb25-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.getitem =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(i) {</span>
<span id="cb25-13">    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;">torch_tensor</span>(self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>sst[, , i<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>(n_timesteps <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i <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="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (360, 115, n_timesteps)</span></span>
<span id="cb25-14">    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;">$</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;">3</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;">2</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;">unsqueeze</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;"># (n_timesteps, 1, 360, 115))</span></span>
<span id="cb25-15">    </span>
<span id="cb25-16">    y1 <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;">torch_tensor</span>(self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>sst[, , n_timesteps <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i])<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="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (1, 360, 115)</span></span>
<span id="cb25-17">    y2 <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;">torch_tensor</span>(self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>nino<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>NINO34_MEAN[n_timesteps <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i])</span>
<span id="cb25-18">    y3 <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;">torch_tensor</span>(self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>nino<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>phase_code[n_timesteps <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i])<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;">squeeze</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;">to</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">torch_long</span>())</span>
<span id="cb25-19">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y1 =</span> y1, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y2 =</span> y2, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y3 =</span> y3)</span>
<span id="cb25-20">  },</span>
<span id="cb25-21">  </span>
<span id="cb25-22">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.length =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>() {</span>
<span id="cb25-23">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>nino) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> n_timesteps</span>
<span id="cb25-24">  }</span>
<span id="cb25-25">  </span>
<span id="cb25-26">)</span>
<span id="cb25-27"></span>
<span id="cb25-28">valid_ds <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;">enso_dataset</span>(sst_valid, nino_valid, n_timesteps)</span></code></pre></div></div>
</section>
<section id="dataloaders" class="level1">
<h1>Dataloaders</h1>
<p>After the custom dataset, we create the – pretty typical – <code>dataloader</code>s, making use of a batch size of 4.</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">batch_size <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span></span>
<span id="cb26-2"></span>
<span id="cb26-3">train_dl <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> train_ds <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;">dataloader</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">batch_size =</span> batch_size, <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="cb26-4"></span>
<span id="cb26-5">valid_dl <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> valid_ds <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;">dataloader</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">batch_size =</span> batch_size)</span></code></pre></div></div>
<p>Next, we proceed to model creation.</p>
</section>
<section id="model" class="level1">
<h1>Model</h1>
<p>The model’s main ingredient is the convLSTM introduced in a <a href="https://blogs.rstudio.com/ai/posts/2020-12-17-torch-convlstm/">prior post</a>. For convenience, we reproduce the code in the appendix.</p>
<p>Besides the convLSTM, the model makes use of three convolutional layers, a batchnorm layer and five linear layers. The logic is the following.</p>
<p>First, the convLSTM job is to predict the next month’s sea surface temperatures on the spatial grid. For that, we <em>almost</em> just return its final state, - almost: We use <code>self$conv1</code> to reduce the number channels to one.</p>
<p>For predicting index and phase, we then need to flatten the grid, as we require a single value each. This is where the additional conv layers come in. We <em>do</em> hope they’ll aid in learning, but we also want to reduce the number of parameters a bit, downsizing the grid (<code>strides = 2</code> and <code>strides = 3</code>, resp.) a bit before the upcoming <code>torch_flatten()</code>.</p>
<p>Once we have a flat structure, learning is shared between the tasks of index and phase prediction (<code>self$linear</code>), until finally their paths split (<code>self$cont</code> and <code>self$cat</code>, resp.), and they return their separate outputs.</p>
<p>(The batchnorm? I’ll comment on that in the Discussion.)</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb27" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb27-1">model <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="cb27-2">  </span>
<span id="cb27-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>(channels_in,</span>
<span id="cb27-4">                        convlstm_hidden,</span>
<span id="cb27-5">                        convlstm_kernel,</span>
<span id="cb27-6">                        convlstm_layers) {</span>
<span id="cb27-7">    </span>
<span id="cb27-8">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> convlstm_layers</span>
<span id="cb27-9">    </span>
<span id="cb27-10">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>convlstm <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;">convlstm</span>(</span>
<span id="cb27-11">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">input_dim =</span> channels_in,</span>
<span id="cb27-12">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hidden_dims =</span> convlstm_hidden,</span>
<span id="cb27-13">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_sizes =</span> convlstm_kernel,</span>
<span id="cb27-14">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_layers =</span> convlstm_layers</span>
<span id="cb27-15">    )</span>
<span id="cb27-16">    </span>
<span id="cb27-17">    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></span>
<span id="cb27-18">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nn_conv2d</span>(</span>
<span id="cb27-19">        <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;">32</span>,</span>
<span id="cb27-20">        <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="cb27-21">        <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="cb27-22">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">padding =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb27-23">      )</span>
<span id="cb27-24">    </span>
<span id="cb27-25">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>conv2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span></span>
<span id="cb27-26">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nn_conv2d</span>(</span>
<span id="cb27-27">        <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;">32</span>,</span>
<span id="cb27-28">        <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;">32</span>,</span>
<span id="cb27-29">        <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="cb27-30">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">stride =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb27-31">      )</span>
<span id="cb27-32">    </span>
<span id="cb27-33">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>conv3 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span></span>
<span id="cb27-34">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nn_conv2d</span>(</span>
<span id="cb27-35">        <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;">32</span>,</span>
<span id="cb27-36">        <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;">32</span>,</span>
<span id="cb27-37">        <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="cb27-38">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">stride =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span></span>
<span id="cb27-39">      )</span>
<span id="cb27-40">    </span>
<span id="cb27-41">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>linear <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_linear</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">33408</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>)</span>
<span id="cb27-42">    </span>
<span id="cb27-43">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>b1 <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_batch_norm1d</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">num_features =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>)</span>
<span id="cb27-44">        </span>
<span id="cb27-45">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cont <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_linear</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>)</span>
<span id="cb27-46">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cat <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_linear</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>)</span>
<span id="cb27-47">    </span>
<span id="cb27-48">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cont_output <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_linear</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb27-49">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cat_output <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_linear</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb27-50">    </span>
<span id="cb27-51">  },</span>
<span id="cb27-52">  </span>
<span id="cb27-53">  <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="cb27-54">    </span>
<span id="cb27-55">    ret <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> 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;">convlstm</span>(x)</span>
<span id="cb27-56">    layer_last_states <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ret[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]]</span>
<span id="cb27-57">    last_hidden <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> layer_last_states[[self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers]][[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb27-58">    </span>
<span id="cb27-59">    next_sst <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> last_hidden <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span> 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>
<span id="cb27-60">    </span>
<span id="cb27-61">    c2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> last_hidden <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span> 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;">conv2</span>() </span>
<span id="cb27-62">    c3 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> c2 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span> 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;">conv3</span>() </span>
<span id="cb27-63">    </span>
<span id="cb27-64">    flat <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;">torch_flatten</span>(c3, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">start_dim =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb27-65">    common <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> 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;">linear</span>(flat) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span> 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;">b3</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;">nnf_relu</span>()</span>
<span id="cb27-66"></span>
<span id="cb27-67">    next_temp <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> common <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span> 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;">cont</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;">nnf_relu</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span> 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;">cont_output</span>()</span>
<span id="cb27-68">    next_nino <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> common <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span> 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;">cat</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;">nnf_relu</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span> 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;">cat_output</span>()</span>
<span id="cb27-69">    </span>
<span id="cb27-70">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(next_sst, next_temp, next_nino)</span>
<span id="cb27-71">    </span>
<span id="cb27-72">  }</span>
<span id="cb27-73">  </span>
<span id="cb27-74">)</span></code></pre></div></div>
<p>Next, we instantiate a pretty small-ish model. You’re more than welcome to experiment with larger models, but training time as well as GPU memory requirements will increase.</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">net <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;">model</span>(</span>
<span id="cb28-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">channels_in =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb28-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">convlstm_hidden =</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;">16</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>),</span>
<span id="cb28-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">convlstm_kernel =</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;">3</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;">5</span>),</span>
<span id="cb28-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">convlstm_layers =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span></span>
<span id="cb28-6">)</span>
<span id="cb28-7"></span>
<span id="cb28-8">device <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;">torch_device</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cuda_is_available</span>()) <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cuda"</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cpu"</span>)</span>
<span id="cb28-9"></span>
<span id="cb28-10">net <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> net<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;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> device)</span>
<span id="cb28-11">net</span></code></pre></div></div>
<pre><code>An `nn_module` containing 2,389,605 parameters.

── Modules ───────────────────────────────────────────────────────────────────────────────
● convlstm: &lt;nn_module&gt; #182,080 parameters
● conv1: &lt;nn_conv2d&gt; #801 parameters
● conv2: &lt;nn_conv2d&gt; #25,632 parameters
● conv3: &lt;nn_conv2d&gt; #25,632 parameters
● linear: &lt;nn_linear&gt; #2,138,176 parameters
● b1: &lt;nn_batch_norm1d&gt; #128 parameters
● cont: &lt;nn_linear&gt; #8,320 parameters
● cat: &lt;nn_linear&gt; #8,320 parameters
● cont_output: &lt;nn_linear&gt; #129 parameters
● cat_output: &lt;nn_linear&gt; #387 parameters</code></pre>
</section>
<section id="training" class="level1">
<h1>Training</h1>
<p>We have three model outputs. How should we combine the losses?</p>
<p>Given that the main goal is predicting the index, and the other two outputs are essentially means to an end, I found the following combination rather effective:</p>
<pre><code># weight for sea surface temperature prediction
lw_sst &lt;- 0.2

# weight for prediction of El Nino 3.4 Index
lw_temp &lt;- 0.4

# weight for phase prediction
lw_nino &lt;- 0.4</code></pre>
<p>The training process follows the pattern seen in all <code>torch</code> posts so far: For each epoch, loop over the training set, backpropagate, check performance on validation set.</p>
<p><em>But</em>, when we did the pre-processing, we were aware of an imminent problem: the missing temperatures for continental areas, which we set to zero. As a sole measure, this approach is clearly insufficient. What if we had chosen to use latitude-dependent averages? Or interpolation? Both may be better than a global average, but both have their problems as well. Let’s at least alleviate negative consequences by not using the respective pixels for spatial loss calculation. This is taken care of by the following line below:</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">sst_loss <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;">nnf_mse_loss</span>(sst_output[sst_target <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;">0</span>], sst_target[sst_target <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;">0</span>])</span></code></pre></div></div>
<p>Here, then, is the complete training code.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb32" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb32-1"></span>
<span id="cb32-2">optimizer <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;">optim_adam</span>(net<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>parameters, <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;">0.001</span>)</span>
<span id="cb32-3"></span>
<span id="cb32-4">num_epochs <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span></span>
<span id="cb32-5"></span>
<span id="cb32-6">train_batch <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(b) {</span>
<span id="cb32-7">  </span>
<span id="cb32-8">  optimizer<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;">zero_grad</span>()</span>
<span id="cb32-9">  output <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;">net</span>(b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</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;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> device))</span>
<span id="cb32-10">  </span>
<span id="cb32-11">  sst_output <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> output[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb32-12">  sst_target <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y1<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;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> device)</span>
<span id="cb32-13">  </span>
<span id="cb32-14">  sst_loss <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;">nnf_mse_loss</span>(sst_output[sst_target <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;">0</span>], sst_target[sst_target <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;">0</span>])</span>
<span id="cb32-15">  temp_loss <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;">nnf_mse_loss</span>(output[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]], b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y2<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;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> device))</span>
<span id="cb32-16">  nino_loss <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;">nnf_cross_entropy</span>(output[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>]], b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y3<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;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> device))</span>
<span id="cb32-17">  </span>
<span id="cb32-18">  loss <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> lw_sst <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> sst_loss <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> lw_temp <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> temp_loss <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> lw_nino <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> nino_loss</span>
<span id="cb32-19">  loss<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;">backward</span>()</span>
<span id="cb32-20">  optimizer<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;">step</span>()</span>
<span id="cb32-21"></span>
<span id="cb32-22">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(sst_loss<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;">item</span>(), temp_loss<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;">item</span>(), nino_loss<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;">item</span>(), loss<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;">item</span>())</span>
<span id="cb32-23">  </span>
<span id="cb32-24">}</span>
<span id="cb32-25"></span>
<span id="cb32-26">valid_batch <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(b) {</span>
<span id="cb32-27">  </span>
<span id="cb32-28">  output <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;">net</span>(b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</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;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> device))</span>
<span id="cb32-29">  </span>
<span id="cb32-30">  sst_output <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> output[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb32-31">  sst_target <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y1<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;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> device)</span>
<span id="cb32-32">  </span>
<span id="cb32-33">  sst_loss <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;">nnf_mse_loss</span>(sst_output[sst_target <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;">0</span>], sst_target[sst_target <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;">0</span>])</span>
<span id="cb32-34">  temp_loss <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;">nnf_mse_loss</span>(output[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]], b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y2<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;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> device))</span>
<span id="cb32-35">  nino_loss <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;">nnf_cross_entropy</span>(output[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>]], b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y3<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;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> device))</span>
<span id="cb32-36">  </span>
<span id="cb32-37">  loss <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span></span>
<span id="cb32-38">    lw_sst <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> sst_loss <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> lw_temp <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> temp_loss <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> lw_nino <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> nino_loss</span>
<span id="cb32-39"></span>
<span id="cb32-40">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(sst_loss<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;">item</span>(),</span>
<span id="cb32-41">       temp_loss<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;">item</span>(),</span>
<span id="cb32-42">       nino_loss<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;">item</span>(),</span>
<span id="cb32-43">       loss<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;">item</span>())</span>
<span id="cb32-44">}</span>
<span id="cb32-45"></span>
<span id="cb32-46"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (epoch <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_epochs) {</span>
<span id="cb32-47">  </span>
<span id="cb32-48">  net<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;">train</span>()</span>
<span id="cb32-49">  </span>
<span id="cb32-50">  train_loss_sst <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;">c</span>()</span>
<span id="cb32-51">  train_loss_temp <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;">c</span>()</span>
<span id="cb32-52">  train_loss_nino <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;">c</span>()</span>
<span id="cb32-53">  train_loss <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;">c</span>()</span>
<span id="cb32-54"></span>
<span id="cb32-55">  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;">loop</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (b <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> train_dl) {</span>
<span id="cb32-56">    losses <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;">train_batch</span>(b)</span>
<span id="cb32-57">    train_loss_sst <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;">c</span>(train_loss_sst, losses[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]])</span>
<span id="cb32-58">    train_loss_temp <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;">c</span>(train_loss_temp, losses[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]])</span>
<span id="cb32-59">    train_loss_nino <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;">c</span>(train_loss_nino, losses[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>]])</span>
<span id="cb32-60">    train_loss <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;">c</span>(train_loss, losses[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>]])</span>
<span id="cb32-61">  })</span>
<span id="cb32-62">  </span>
<span id="cb32-63">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(</span>
<span id="cb32-64">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sprintf</span>(</span>
<span id="cb32-65">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">Epoch %d, training: loss: %3.3f sst: %3.3f temp: %3.3f nino: %3.3f </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb32-66">      epoch, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(train_loss), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(train_loss_sst), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(train_loss_temp), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(train_loss_nino)</span>
<span id="cb32-67">    )</span>
<span id="cb32-68">  )</span>
<span id="cb32-69">  </span>
<span id="cb32-70">  net<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="cb32-71">  </span>
<span id="cb32-72">  valid_loss_sst <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;">c</span>()</span>
<span id="cb32-73">  valid_loss_temp <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;">c</span>()</span>
<span id="cb32-74">  valid_loss_nino <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;">c</span>()</span>
<span id="cb32-75">  valid_loss <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;">c</span>()</span>
<span id="cb32-76"></span>
<span id="cb32-77">  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;">loop</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (b <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> valid_dl) {</span>
<span id="cb32-78">    losses <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;">valid_batch</span>(b)</span>
<span id="cb32-79">    valid_loss_sst <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;">c</span>(valid_loss_sst, losses[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]])</span>
<span id="cb32-80">    valid_loss_temp <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;">c</span>(valid_loss_temp, losses[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]])</span>
<span id="cb32-81">    valid_loss_nino <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;">c</span>(valid_loss_nino, losses[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>]])</span>
<span id="cb32-82">    valid_loss <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;">c</span>(valid_loss, losses[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>]])</span>
<span id="cb32-83">    </span>
<span id="cb32-84">  })</span>
<span id="cb32-85">  </span>
<span id="cb32-86">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(</span>
<span id="cb32-87">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sprintf</span>(</span>
<span id="cb32-88">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">Epoch %d, validation: loss: %3.3f sst: %3.3f temp: %3.3f nino: %3.3f </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb32-89">      epoch, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(valid_loss), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(valid_loss_sst), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(valid_loss_temp), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(valid_loss_nino)</span>
<span id="cb32-90">    )</span>
<span id="cb32-91">  )</span>
<span id="cb32-92">  </span>
<span id="cb32-93">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">torch_save</span>(net, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb32-94">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"model_"</span>, epoch, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_"</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(train_loss), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_"</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(valid_loss), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".pt"</span></span>
<span id="cb32-95">  ))</span>
<span id="cb32-96">  </span>
<span id="cb32-97">}</span></code></pre></div></div>
<p>When I ran this, performance on the training set decreased in a not-too-fast, but continuous way, while validation set performance kept fluctuating. For reference, total (composite) losses looked like this:</p>
<pre><code>Epoch     Training    Validation
   
   10        0.336         0.633
   20        0.233         0.295
   30        0.135         0.461
   40        0.099         0.903
   50        0.061         0.727</code></pre>
<p>Thinking of the size of the validation set - thirty-one years, or equivalently, 372 data points – those fluctuations may not be all too surprising.</p>
</section>
<section id="predictions" class="level1 page-columns page-full">
<h1>Predictions</h1>
<p>Now losses tend to be abstract; let’s see what actually gets predicted. We obtain predictions for index values and phases like so …</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb34" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb34-1">net<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="cb34-2"></span>
<span id="cb34-3">pred_index <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;">c</span>()</span>
<span id="cb34-4">pred_phase <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;">c</span>()</span>
<span id="cb34-5"></span>
<span id="cb34-6">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;">loop</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (b <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> valid_dl) {</span>
<span id="cb34-7"></span>
<span id="cb34-8">  output <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;">net</span>(b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</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;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> device))</span>
<span id="cb34-9"></span>
<span id="cb34-10">  pred_index <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;">c</span>(pred_index, output[[<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="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cpu"</span>))</span>
<span id="cb34-11">  pred_phase <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;">rbind</span>(pred_phase, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>(output[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</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;">to</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cpu"</span>)))</span>
<span id="cb34-12"></span>
<span id="cb34-13">})</span></code></pre></div></div>
<p>… and combine these with the ground truth, stripping off the first six rows (six was the number of timesteps used as predictors):</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">valid_perf <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;">data.frame</span>(</span>
<span id="cb35-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">actual_temp =</span> nino_valid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>NINO34_MEAN[(batch_size <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="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(nino_valid)] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> train_sd_nino <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> train_mean_nino,</span>
<span id="cb35-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">actual_nino =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(nino_valid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>phase_code[(batch_size <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="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(nino_valid)]),</span>
<span id="cb35-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pred_temp =</span> pred_index <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> train_sd_nino <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> train_mean_nino,</span>
<span id="cb35-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pred_nino =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(pred_phase <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;">apply</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, which.max))</span>
<span id="cb35-6">)</span></code></pre></div></div>
<p>For the phase, we can generate a confusion matrix:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb36" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb36-1">yardstick<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;">conf_mat</span>(valid_perf, actual_nino, pred_nino)</span></code></pre></div></div>
<pre><code>          Truth
Prediction   1   2   3
         1  70   0  43
         2   0  47  10
         3  23  46 123</code></pre>
<p>This looks better than expected (based on the losses). Phases 1 and 2 correspond to El Niño and La Niña, respectively, and these get sharply separated.</p>
<p>What about the Niño 3.4 Index? Let’s plot predictions versus ground truth:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb38" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb38-1">valid_perf <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> valid_perf <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span> </span>
<span id="cb38-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">actual =</span> actual_temp, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">predicted =</span> pred_temp) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span> </span>
<span id="cb38-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">add_column</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">month =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1990-07-01"</span>), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2020-08-01"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"months"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb38-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pivot_longer</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>month, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">names_to =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Index"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values_to =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"temperature"</span>)</span>
<span id="cb38-5"></span>
<span id="cb38-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(valid_perf, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> month, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> temperature, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> Index)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb38-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_line</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb38-8">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_color_manual</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#006D6F"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#B2FFFF"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb38-9">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_classic</span>()</span></code></pre></div></div>
<div class="dv-wide-figure page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="preds.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-2" title="Nino 3.4 Index: Ground truth vs.&nbsp;predictions (validation set)."><img src="https://divergences.xyz/posts/enso-prediction/preds.png" class="img-fluid figure-img column-body-outset-right" alt="Nino 3.4 Index: Ground truth vs.&nbsp;predictions (validation set)."></a></p>
<figcaption>Nino 3.4 Index: Ground truth vs.&nbsp;predictions (validation set).</figcaption>
</figure>
</div>
</div>
<p>This does not look bad either. However, we need to keep in mind that we’re predicting just a single time step ahead. We probably should not overestimate the results. Which leads directly to the discussion.</p>
</section>
<section id="discussion" class="level1">
<h1>Discussion</h1>
<p>When working with small amounts of data, a lot can be learned by quick-ish experimentation. However, when <em>at the same time</em>, the task is complex, one should be cautious extrapolating.</p>
<p>For example, well-established regularizers such as batchnorm and dropout, while intended to improve generalization to the validation set, may turn out to severely impede training itself. This is the story behind the one batchnorm layer I kept (I did try having more), and it is also why there is no dropout.</p>
<p>One lesson to learn from this experience then is: Make sure the amount of data matches the complexity of the task. This is what we see in the ENSO prediction papers published on arxiv.</p>
<p>If we should treat the results with caution, why even publish the post?</p>
<p>For one, it shows an application of convLSTM to real-world data, employing a reasonably complex architecture and illustrating techniques like custom losses and loss masking. Similar architectures and strategies should be applicable to a wide range of real-world tasks – basically, whenever predictors in a time-series problem are given on a spatial grid.</p>
<p>Secondly, the application itself – forecasting an atmospheric phenomenon that greatly affects ecosystems as well as human well-being – seems like an excellent use of deep learning. Applications like these stand out as all the more worthwhile as the same cannot be said of everything deep learning is – and will be, barring effective regulation – used for.</p>
<p>Thanks for reading!</p>
</section>
<section id="appendix-convlstm-code" class="level1">
<h1>Appendix: <code>convlstm</code> code</h1>
<p>For an in-depth explanation of <code>convlstm</code>, see <a href="https://blogs.rstudio.com/ai/posts/2020-12-17-torch-convlstm/">the blog post</a>.</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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(torch)</span>
<span id="cb39-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(torchvision)</span>
<span id="cb39-3"></span>
<span id="cb39-4">convlstm_cell <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="cb39-5">  </span>
<span id="cb39-6">  <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>(input_dim, hidden_dim, kernel_size, bias) {</span>
<span id="cb39-7">    </span>
<span id="cb39-8">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> hidden_dim</span>
<span id="cb39-9">    </span>
<span id="cb39-10">    padding <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> kernel_size <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;">2</span></span>
<span id="cb39-11">    </span>
<span id="cb39-12">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>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;">nn_conv2d</span>(</span>
<span id="cb39-13">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">in_channels =</span> input_dim <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim,</span>
<span id="cb39-14">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># for each of input, forget, output, and cell gates</span></span>
<span id="cb39-15">      <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;">4</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim,</span>
<span id="cb39-16">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_size =</span> kernel_size,</span>
<span id="cb39-17">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">padding =</span> padding,</span>
<span id="cb39-18">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bias =</span> bias</span>
<span id="cb39-19">    )</span>
<span id="cb39-20">  },</span>
<span id="cb39-21">  </span>
<span id="cb39-22">  <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, prev_states) {</span>
<span id="cb39-23"></span>
<span id="cb39-24">    h_prev <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> prev_states[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb39-25">    c_prev <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> prev_states[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]]</span>
<span id="cb39-26">    </span>
<span id="cb39-27">    combined <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;">torch_cat</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(x, h_prev), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dim =</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;"># concatenate along channel axis</span></span>
<span id="cb39-28">    combined_conv <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> 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;">conv</span>(combined)</span>
<span id="cb39-29">    gate_convs <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;">torch_split</span>(combined_conv, self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dim =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb39-30">    cc_i <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> gate_convs[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb39-31">    cc_f <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> gate_convs[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]]</span>
<span id="cb39-32">    cc_o <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> gate_convs[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>]]</span>
<span id="cb39-33">    cc_g <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> gate_convs[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>]]</span>
<span id="cb39-34">    </span>
<span id="cb39-35">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># input, forget, output, and cell gates (corresponding to torch's LSTM)</span></span>
<span id="cb39-36">    i <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;">torch_sigmoid</span>(cc_i)</span>
<span id="cb39-37">    f <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;">torch_sigmoid</span>(cc_f)</span>
<span id="cb39-38">    o <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;">torch_sigmoid</span>(cc_o)</span>
<span id="cb39-39">    g <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;">torch_tanh</span>(cc_g)</span>
<span id="cb39-40">    </span>
<span id="cb39-41">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># cell state</span></span>
<span id="cb39-42">    c_next <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> f <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> c_prev <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> g</span>
<span id="cb39-43">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># hidden state</span></span>
<span id="cb39-44">    h_next <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> o <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_tanh</span>(c_next)</span>
<span id="cb39-45">    </span>
<span id="cb39-46">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(h_next, c_next)</span>
<span id="cb39-47">    </span>
<span id="cb39-48">  },</span>
<span id="cb39-49">  </span>
<span id="cb39-50">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">init_hidden =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(batch_size, height, width) {</span>
<span id="cb39-51">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">torch_zeros</span>(batch_size, self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim, height, width, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>conv<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>weight<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>device),</span>
<span id="cb39-52">         <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">torch_zeros</span>(batch_size, self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim, height, width, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>conv<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>weight<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>device))</span>
<span id="cb39-53">  }</span>
<span id="cb39-54">)</span>
<span id="cb39-55"></span>
<span id="cb39-56">convlstm <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="cb39-57">  </span>
<span id="cb39-58">  <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>(input_dim, hidden_dims, kernel_sizes, n_layers, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bias =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>) {</span>
<span id="cb39-59"> </span>
<span id="cb39-60">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> n_layers</span>
<span id="cb39-61">    </span>
<span id="cb39-62">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cell_list <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_list</span>()</span>
<span id="cb39-63">    </span>
<span id="cb39-64">    <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>n_layers) {</span>
<span id="cb39-65">      cur_input_dim <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (i <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>) input_dim <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> hidden_dims[i <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>
<span id="cb39-66">      self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cell_list<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>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">convlstm_cell</span>(cur_input_dim, hidden_dims[i], kernel_sizes[i], bias))</span>
<span id="cb39-67">    }</span>
<span id="cb39-68">  },</span>
<span id="cb39-69">  </span>
<span id="cb39-70">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we always assume batch-first</span></span>
<span id="cb39-71">  <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="cb39-72">    </span>
<span id="cb39-73">    batch_size <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;">size</span>()[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb39-74">    seq_len <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;">size</span>()[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]</span>
<span id="cb39-75">    height <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;">size</span>()[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>]</span>
<span id="cb39-76">    width <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;">size</span>()[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>]</span>
<span id="cb39-77">   </span>
<span id="cb39-78">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># initialize hidden states</span></span>
<span id="cb39-79">    init_hidden <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;">vector</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;">"list"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers)</span>
<span id="cb39-80">    <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>self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers) {</span>
<span id="cb39-81">      init_hidden[[i]] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cell_list[[i]]<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;">init_hidden</span>(batch_size, height, width)</span>
<span id="cb39-82">    }</span>
<span id="cb39-83">    </span>
<span id="cb39-84">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># list containing the outputs, of length seq_len, for each layer</span></span>
<span id="cb39-85">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># this is the same as h, at each step in the sequence</span></span>
<span id="cb39-86">    layer_output_list <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;">vector</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;">"list"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers)</span>
<span id="cb39-87">    </span>
<span id="cb39-88">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># list containing the last states (h, c) for each layer</span></span>
<span id="cb39-89">    layer_state_list <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;">vector</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;">"list"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers)</span>
<span id="cb39-90"></span>
<span id="cb39-91">    cur_layer_input <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> x</span>
<span id="cb39-92">    hidden_states <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> init_hidden</span>
<span id="cb39-93">    </span>
<span id="cb39-94">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># loop over layers</span></span>
<span id="cb39-95">    <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>self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers) {</span>
<span id="cb39-96">      </span>
<span id="cb39-97">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># every layer's hidden state starts from 0 (non-stateful)</span></span>
<span id="cb39-98">      h_c <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> hidden_states[[i]]</span>
<span id="cb39-99">      h <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> h_c[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb39-100">      c <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> h_c[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]]</span>
<span id="cb39-101">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># outputs, of length seq_len, for this layer</span></span>
<span id="cb39-102">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># equivalently, list of h states for each time step</span></span>
<span id="cb39-103">      output_sequence <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;">vector</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;">"list"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> seq_len)</span>
<span id="cb39-104">      </span>
<span id="cb39-105">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># loop over timesteps</span></span>
<span id="cb39-106">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (t <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>seq_len) {</span>
<span id="cb39-107">        h_c <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cell_list[[i]](cur_layer_input[ , t, , , ], <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(h, c))</span>
<span id="cb39-108">        h <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> h_c[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb39-109">        c <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> h_c[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]]</span>
<span id="cb39-110">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># keep track of output (h) for every timestep</span></span>
<span id="cb39-111">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># h has dim (batch_size, hidden_size, height, width)</span></span>
<span id="cb39-112">        output_sequence[[t]] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> h</span>
<span id="cb39-113">      }</span>
<span id="cb39-114"></span>
<span id="cb39-115">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># stack hs for all timesteps over seq_len dimension</span></span>
<span id="cb39-116">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># stacked_outputs has dim (batch_size, seq_len, hidden_size, height, width)</span></span>
<span id="cb39-117">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># same as input to forward (x)</span></span>
<span id="cb39-118">      stacked_outputs <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;">torch_stack</span>(output_sequence, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dim =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb39-119">      </span>
<span id="cb39-120">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># pass the list of outputs (hs) to next layer</span></span>
<span id="cb39-121">      cur_layer_input <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> stacked_outputs</span>
<span id="cb39-122">      </span>
<span id="cb39-123">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># keep track of list of outputs or this layer</span></span>
<span id="cb39-124">      layer_output_list[[i]] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> stacked_outputs</span>
<span id="cb39-125">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># keep track of last state for this layer</span></span>
<span id="cb39-126">      layer_state_list[[i]] <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;">list</span>(h, c)</span>
<span id="cb39-127">    }</span>
<span id="cb39-128"> </span>
<span id="cb39-129">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(layer_output_list, layer_state_list)</span>
<span id="cb39-130">      </span>
<span id="cb39-131">  }</span>
<span id="cb39-132">    </span>
<span id="cb39-133">)</span></code></pre></div></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>Climate &amp; Weather</category>
  <guid>https://divergences.xyz/posts/enso-prediction/</guid>
  <pubDate>Wed, 02 Feb 2022 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/enso-prediction/pic.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Beyond alchemy: A first look at geometric deep learning</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/geometric-deep-learning/</link>
  <description><![CDATA[ 






<p>To the practitioner, it may often seem that with deep learning, there is a lot of magic involved. Magic in how hyper-parameter choices affect performance, for example. More fundamentally yet, magic in the impacts of architectural decisions. Magic, sometimes, in that it even works (or not). Sure, papers abound that strive to mathematically prove why, for specific solutions, in specific contexts, this or that technique will yield better results. But theory and practice are strangely dissociated: If a technique <em>does</em> turn out to be helpful in practice, doubts may still arise to whether that is, in fact, due to the purported mechanism. Moreover, level of generality often is low.</p>
<p>In this situation, one may feel grateful for approaches that aim to elucidate, complement, or replace some of the magic. By “complement or replace”, I’m alluding to attempts to incorporate domain-specific knowledge into the training process. Interesting examples exist in several sciences, and I certainly hope to be able to showcase a few of these, on this blog at a later time. As for the “elucidate”, this characterization is meant to lead on to the topic of this post: the program of <em>geometric deep learning</em>.</p>
<section id="geometric-deep-learning-an-attempt-at-unification" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="geometric-deep-learning-an-attempt-at-unification">Geometric deep learning: An attempt at unification</h2>
<p>Geometric deep learning (henceforth: GDL) is what a group of researchers, including Michael Bronstein, Joan Bruna, Taco Cohen, and Petar Velicković, call their attempt to build a framework that places deep learning (DL) on a solid mathematical basis.</p>
<p>Prima facie, this is a scientific endeavor: They take existing architectures and practices and show where these fit into the “DL blueprint”. DL research being all but confined to the ivory tower, though, it’s fair to assume that this is not all: From those mathematical foundations, it should be possible to derive new architectures, new techniques to fit a given task. Who, then, should be interested in this? Researchers, for sure; to them, the framework may well prove highly inspirational. Secondly, everyone interested in the mathematical constructions themselves — this probably goes without saying. Finally, the rest of us, as well: Even understood at a purely conceptual level, the framework offers an exciting, inspiring view on DL architectures that – I think – is worth getting to know about as an end in itself. The goal of this post is to provide a high-level introduction .</p>
<p>Before we get started though, let me mention the primary source for this text: <a href="https://arxiv.org/abs/2104.13478">Geometric Deep Learning: Grids, Groups, Graphs, Geodesics, and Gauges</a> (<span class="citation" data-cites="GDL">Bronstein et al. (2021)</span>).</p>
<div class="no-row-height column-margin column-container"><div id="ref-GDL" class="csl-entry">
Bronstein, Michael M., Joan Bruna, Taco Cohen, and Petar Velickovic. 2021. <span>“Geometric Deep Learning: Grids, Groups, Graphs, Geodesics, and Gauges.”</span> <em>CoRR</em> abs/2104.13478. <a href="https://arxiv.org/abs/2104.13478">https://arxiv.org/abs/2104.13478</a>.
</div></div><section id="geometric-priors" class="level3">
<h3 class="anchored" data-anchor-id="geometric-priors">Geometric priors</h3>
<p>A <em>prior</em>, in the context of machine learning, is a constraint imposed on the learning task. A generic prior could come about in different ways; a <em>geometric</em> prior, as defined by the GDL group, arises, originally, from the underlying <em>domain</em> of the task. Take image classification, for example. The domain is a two-dimensional grid. Or graphs: The domain consists of collections of nodes and edges.</p>
<p>In the GDL framework, two all-important geometric priors are symmetry and scale separation.</p>
<section id="symmetry" class="level4">
<h4 class="anchored" data-anchor-id="symmetry">Symmetry</h4>
<p>A symmetry, in physics and mathematics, is a transformation that leaves some property of an object unchanged. The appropriate meaning of “unchanged” depends on what sort of property we’re talking about. Say the property is some “essence”, or identity — what object something is. If I move a few steps to the left, I’m still myself: The essence of being “myself” is shift-<em>invarian</em>t. (Or: translation-invariant.) But say the property is location. If I move to the left, my location moves to the left. Location is shift-<em>equivariant</em>. (Translation-equivariant.)</p>
<p>So here we have two forms of symmetry: invariance and equivariance. One means that when we transform an object, the thing we’re interested in stays the same. The other means that we have to transform that thing as well.</p>
<p>The next question then is: What are possible transformations? Translation we already mentioned; on images, rotation or flipping are others. Transformations are composable; I can rotate the digit <code>3</code> by thirty degrees, then move it to the left by five units; I could also do things the other way around. (In this case, though not necessarily in general, the results are the same.) Transformations can be undone: If first I rotate, in some direction, by five degrees, I can then rotate in the opposite one, also by five degrees, and end up in the original position. We’ll see why this matters when we cross the bridge from the domain (grids, sets, etc.) to the learning algorithm.</p>
</section>
<section id="scale-separation" class="level4">
<h4 class="anchored" data-anchor-id="scale-separation">Scale separation</h4>
<p>After symmetry, another important geometric prior is scale separation. Scale separation means that even if something is very “big” (extends a long way in, say, one or two dimensions), we can still start from small patches and “work our way up”. For example, take a cuckoo clock. To discern the hands, you don’t need to pay attention to the pendulum. And vice versa. And once you’ve taken inventory of hands and pendulum, you don’t have to care about their texture or exact position anymore.</p>
<p>In a nutshell, given scale separation, the top-level structure can be determined through successive steps of <em>coarse-graining</em>. We’ll see this prior nicely reflected in some neural-network algorithms.</p>
</section>
</section>
<section id="from-domain-priors-to-algorithmic-ones" class="level3">
<h3 class="anchored" data-anchor-id="from-domain-priors-to-algorithmic-ones">From domain priors to algorithmic ones</h3>
<p>So far, all we’ve really talked about is the <em>domain</em>, using the word in the colloquial sense of “on what structure”, or “in terms of what structure”, something is given. In mathematical language, though, domain is used in a more narrow way, namely, for the “input space” of a <em>function</em>. And a function, or rather, two of them, is what we need to get from priors on the (physical) domain to priors on neural networks.</p>
<p>The first function maps from the physical domain to <em>signal</em> space. If, for images, the domain was the two-dimensional grid, the signal space now consists of images the way they are represented in a computer, and will be worked with by a learning algorithm. For example, in the case of RGB images, that representation is three-dimensional, with a color dimension on top of the inherited spatial structure. What matters is that by this function, the priors are preserved. If something is translation-invariant before “real-to-virtual” conversion, it will still be translation-invariant thereafter.</p>
<p>Next, we have another function: the algorithm, or neural network, acting on signal space. Ideally, this function, again, would preserve the priors. Below, we’ll see how basic neural-network architectures typically preserve some important symmetries, but not necessarily all of them. We’ll also see how, at this point, the actual <em>task</em> makes a difference. Depending on what we’re trying to achieve, we may want to maintain some symmetry, but not care about another. The task here is analogous to the property in physical space. Just like in physical space, a movement to the left does not alter <em>identity</em>, a <em>classifier</em>, presented with that same shift, won’t care at all. But a <em>segmentation</em> algorithm will – mirroring the real-world shift in <em>position</em>.</p>
<p>Now that we’ve made our way to algorithm space, the above requirement, formulated on physical space – that transformations be composable – makes sense in another light: Composing functions is exactly what neural networks do; we want these compositions to work just as deterministically as those of real-world transformations.</p>
<p>In sum, the geometric priors and the way they impose constraints, or desiderates, rather, on the learning algorithm lead to what the GDL group call their deep learning “blueprint”. Namely, a network should be composed of the following types of modules:</p>
<ul>
<li><p>Linear group-equivariant layers. (Here group is the group of transformations whose symmetries we’re interested to preserve.)</p></li>
<li><p>Nonlinearities. (This really does not follow from geometric arguments, but from the observation, often stated in introductions to DL, that without nonlinearities, there is no hierarchical composition of features, since all operations can be implemented in a single matrix multiplication.)</p></li>
<li><p>Local pooling layers. (These achieve the effect of coarse-graining, as enabled by the scale separation prior.)</p></li>
<li><p>A group-invariant layer (global pooling). (Not every task will require such a layer to be present.)</p></li>
</ul>
<p>Having talked so much about the concepts, which are highly fascinating, this list may seem a bit underwhelming. That’s what we’ve been doing anyway, right? Maybe; but once you look at a few domains and associated network architectures, the picture gets colorful again. So colorful, in fact, that we can only present a very sparse selection of highlights.</p>
</section>
</section>
<section id="domains-priors-architectures" class="level2">
<h2 class="anchored" data-anchor-id="domains-priors-architectures">Domains, priors, architectures</h2>
<p>Given cues like “local” and “pooling”, what better architecture is there to start with than CNNs, the (still) paradigmatic deep learning architecture? Probably, it’s also the one a prototypic practitioner would be most familiar with.</p>
<section id="images-and-cnns" class="level3">
<h3 class="anchored" data-anchor-id="images-and-cnns">Images and CNNs</h3>
<p>Vanilla CNNs are easily mapped to the four types of layers that make up the blueprint. Skipping over the nonlinearities, which, in this context, are of least interest, we next have two kinds of pooling.</p>
<p>First, a local one, corresponding to max- or average-pooling layers with small strides (2 or 3, say). This reflects the idea of successive coarse-graining, where, once we’ve made use of some fine-grained information, all we need to proceed is a summary.</p>
<p>Second, a global one, used to effectively remove the spatial dimensions. In practice, this would usually be global average pooling. Here, there’s an interesting detail worth mentioning. A common practice, in image classification, is to replace global pooling by a combination of flattening and one or more feedforward layers. Since with feedforward layers, position in the input matters, this will do away with translation invariance.</p>
<p>Having covered three of the four layer types, we come to the most interesting one. In CNNs, the local, group-equivariant layers are the convolutional ones. What kinds of symmetries does convolution preserve? Think about how a kernel slides over an image, computing a dot product at every location. Say that, through training, it has developed an inclination toward singling out penguin bills. It will detect, and mark, one everywhere in an image — be it shifted left, right, top or bottom in the image. What about rotational motion, though? Since kernels move vertically and horizontally, but not in a circle, a rotated bill will be missed. Convolution is shift-equivariant, not rotation-invariant.</p>
<p>There is something that can be done about this, though, while fully staying within the framework of GDL. Convolution, in a more generic sense, does not have to imply constraining filter movement to horizontal and vertical translation. When reflecting a general <em>group convolution</em>, that motion is determined by whatever transformations constitute the group action. If, for example, that action included translation by sixty degrees, we could rotate the filter to all valid positions, then take these filters and have them slide over the image. In effect, we’d just wind up with more channels in the subsequent layer – the intended base number of filters times the number of attainable positions.</p>
<p>This, it must be said, it just one way to do it. A more elegant one is to apply the filter in the Fourier domain, where convolution maps to multiplication. The Fourier domain, however, is as fascinating as it is out of scope for this post.</p>
<p>The same goes for extensions of convolution from the Euclidean grid to manifolds, where distances are no longer measured by a straight line as we know it. Often on manifolds, we’re interested in invariances beyond translation or rotation: Namely, algorithms may have to support various types of deformation. (Imagine, for example, a moving rabbit, with its muscles stretching and contracting as it hobbles.) If you’re interested in these kinds of problems, the GDL book goes into those in great detail.</p>
<p>For group convolution on grids – in fact, we may want to say “on things that can be arranged in a grid” – the authors give two illustrative examples. (One thing I like about these examples is something that extends to the whole book: Many applications are from the world of natural sciences, encouraging some optimism as to the role of deep learning (“AI”) in society.)</p>
<p>One example is from medical volumetric imaging (MRI or CT, say), where signals are represented on a three-dimensional grid. Here the task calls not just for translation in all directions, but also, rotations, of some sensible degree, about all three spatial axes. The other is from DNA sequencing, and it brings into play a new kind of invariance we haven’t mentioned yet: reverse-complement symmetry. This is because once we’ve decoded one strand of the double helix, we already know the other one.</p>
<p>Finally, before we wrap up the topic of CNNs, let’s mention how through creativity, one can achieve – or put cautiously, try to achieve – certain invariances by means other than network architecture. A great example, originally associated mostly with images, is data augmentation. Through data augmentation, we may hope to make training invariant to things like slight changes in color, illumination, perspective, and the like.</p>
</section>
<section id="graphs-and-gnns" class="level3">
<h3 class="anchored" data-anchor-id="graphs-and-gnns">Graphs and GNNs</h3>
<p>Another type of domain, underlying many scientific and non-scientific applications, are graphs. Here, we are going to be a lot more brief. One reason is that so far, we have not had many posts on deep learning on graphs, so to the readers of this blog, the topic may seem fairly abstract. The other reason is complementary: That state of affairs is exactly something we’d like to see changing. Once we write more about graph DL, occasions to talk about respective concepts will be plenty.</p>
<p>In a nutshell, though, the dominant type of invariance in graph DL is permutation equivariance. Permutation, because when you stack a node and its features in a matrix, it doesn’t matter whether node one is in row three or row fifteen. Equivariance, because once you <em>do</em> permute the nodes, you also have to permute the adjacency matrix, the matrix that captures which node is linked to what other nodes. This is very different from what holds for images: We can’t just randomly permute the pixels.</p>
</section>
<section id="sequences-and-rnns" class="level3">
<h3 class="anchored" data-anchor-id="sequences-and-rnns">Sequences and RNNs</h3>
<p>With RNNs, we are going be very brief as well, although for a different reason. My impression is that so far, this area of research – meaning, GDL as it relates to sequences – has not received too much attention yet, and (maybe) for that reason, seems of lesser impact on real-world applications.</p>
<p>In a nutshell, the authors refer two types of symmetry: First, translation-invariance, as long as a sequence is left-padded for a sufficient number of steps. (This is due to the hidden units having to be initialized <em>somehow</em>.) This holds for RNNs in general.</p>
<p>Second, <em>time warping</em>: If a network can be trained that correctly works on a sequence measured on some time scale, there is another network, of the same architecture but likely with different weights, that will work equivalently on re-scaled time. This invariance only applies to <em>gated</em> RNNs, such as the LSTM.</p>
</section>
<section id="whats-next" class="level3">
<h3 class="anchored" data-anchor-id="whats-next">What’s next?</h3>
<p>At this point, we conclude this conceptual introduction. If you want to learn more, and are not too scared by the math, definitely check out the book. (I’d also say it lends itself well to incremental understanding, as in, iteratively going back to some details once one has acquired more background.)</p>
<p>Something else to wish for certainly is practice. There is an intimate connection between GDL and deep learning on graphs; which is one reason we’re hoping to be able to feature the latter more frequently in the future. The other is the wealth of interesting applications that take graphs as their input. Until then, thanks for reading!</p>
<div class="photo-credit">
<p>Photo by <a href="https://unsplash.com/@nasa?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">NASA</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
</div>



</section>
</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/geometric-deep-learning/</guid>
  <pubDate>Thu, 26 Aug 2021 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/geometric-deep-learning/preview.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Starting to think about AI Fairness</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/ai-fairness/</link>
  <description><![CDATA[ 






<p>If you use deep learning for unsupervised part-of-speech tagging of Sanskrit <sup>1</sup>, or knowledge discovery in physics <sup>2</sup>, you probably don’t need to worry about model fairness. If you’re a data scientist working at a place where decisions are made about <em>people</em>, however, or an academic researching models that will be used to such ends, chances are that you’ve already been thinking about this topic. — Or feeling that you should. And thinking about this is hard.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;<span class="citation" data-cites="Srivastava">Srivastava et al. (2018)</span></p><div id="ref-Srivastava" class="csl-entry">
Srivastava, Prakhar, Kushal Chauhan, Deepanshu Aggarwal, Anupam Shukla, Joydip Dhar, and Vrashabh Prasad Jain. 2018. <span>“Deep Learning Based Unsupervised POS Tagging for Sanskrit.”</span> <em>Proceedings of the 2018 International Conference on Algorithms, Computing and Artificial Intelligence</em> (New York, NY, USA), ACAI 2018. <a href="https://doi.org/10.1145/3302425.3302487">https://doi.org/10.1145/3302425.3302487</a>.
</div></div><div id="fn2"><p><sup>2</sup>&nbsp;<span class="citation" data-cites="Cranmer">Cranmer et al. (2020)</span></p><div id="ref-Cranmer" class="csl-entry">
Cranmer, Miles D., Alvaro Sanchez-Gonzalez, Peter W. Battaglia, et al. 2020. <span>“Discovering Symbolic Models from Deep Learning with Inductive Biases.”</span> <em>CoRR</em> abs/2006.11287. <a href="https://arxiv.org/abs/2006.11287">https://arxiv.org/abs/2006.11287</a>.
</div></div></div><p>It is hard for several reasons. In this text, I will go into <em>just one</em>.</p>
<section id="the-forest-for-the-trees" class="level2">
<h2 class="anchored" data-anchor-id="the-forest-for-the-trees">The forest for the trees</h2>
<p>Nowadays, it is hard to find a modeling framework that does <em>not</em> include functionality to assess fairness. (Or is at least planning to.) And the terminology sounds so familiar, as well: “calibration”, “predictive parity”, “equal true [false] positive rate”… It almost seems as though we could just take the metrics we make use of anyway (recall or precision, say), test for equality across groups, and that’s it. Let’s assume, for a second, it really was that simple. Then the question still is: Which metrics, exactly, do we choose?</p>
<p>In reality things are <em>not</em> simple. And it gets worse. For very good reasons, there is a close connection in the ML fairness literature to concepts that are primarily treated in other disciplines, such as the legal sciences: <em>discrimination</em> and <em>disparate impact</em> (both not being far from yet another statistical concept, <em>statistical parity</em>). Statistical parity means that if we have a classifier, say to decide whom to hire, it should result in as many applicants from the disadvantaged group (e.g., Black people) being hired as from the advantaged one(s). But that is quite a different requirement from, say, equal true/false positive rates!</p>
<p>So despite all that abundance of software, guides, and decision trees, even: This is not a simple, technical decision. It is, in fact, a technical decision only to a small degree.</p>
</section>
<section id="common-sense-not-math" class="level2">
<h2 class="anchored" data-anchor-id="common-sense-not-math">Common sense, not math</h2>
<p>Let me start this section with a disclaimer: Most of the sources referenced in this text appear, or are implied on the <a href="http://aif360.mybluemix.net/resources#guidance">“Guidance” page</a> of IBM’s framework AI Fairness 360. If you read that page, and everything that’s said and not said there appears clear from the outset, then you may not need this more verbose exposition. If not, I invite you to read on.</p>
<p>Papers on fairness in machine learning, as is common in fields like computer science, abound with formulae. Even the papers referenced here, though selected not for their theorems and proofs but for the ideas they harbor, are no exception. But to start thinking about fairness as it might apply to an ML process at hand, common language – and common sense – will do just fine. If, after analyzing your use case, you judge that the more technical results <em>are</em> relevant to the process in question, you will find that their verbal characterizations will often suffice. It is only when you doubt their correctness that you will need to work through the proofs.</p>
<p>At this point, you may be wondering what it is I am contrasting those “more technical results” with. This is the topic of the next section, where I’ll try to give a birds-eye characterization of fairness criteria and what they imply.</p>
</section>
<section id="situating-fairness-criteria" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="situating-fairness-criteria">Situating fairness criteria</h2>
<p>Think back to the example of a hiring algorithm. What does it mean for this algorithm to be fair? We approach this question under two – incompatible, mostly – assumptions:</p>
<ol type="1">
<li><p>The algorithm is fair if it behaves the same way independent of which demographic group it is applied to. Here demographic group could be defined by ethnicity, gender, abledness, or in fact any categorization suggested by the context.</p></li>
<li><p>The algorithm is fair if it does not discriminate against any demographic group.</p></li>
</ol>
<p>I’ll call these the technical and societal views, respectively.</p>
<section id="fairness-viewed-the-technical-way" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="fairness-viewed-the-technical-way">Fairness, viewed the technical way</h3>
<p>What does it mean for an algorithm to “behave the same way” regardless of which group it is applied to?</p>
<p>In a classification setting, we can view the relationship between prediction (<img src="https://latex.codecogs.com/png.latex?%5Chat%7BY%7D">) and target (<img src="https://latex.codecogs.com/png.latex?Y">) as a doubly directed path. In one direction: Given true target <img src="https://latex.codecogs.com/png.latex?Y">, how accurate is prediction <img src="https://latex.codecogs.com/png.latex?%5Chat%7BY%7D">? In the other: Given <img src="https://latex.codecogs.com/png.latex?%5Chat%7BY%7D">, how well does it predict the true class <img src="https://latex.codecogs.com/png.latex?Y">?</p>
<p>Based on the direction they operate in, metrics popular in machine learning overall can be split into two categories. In the first, starting from the true target, we have <em>recall</em>, together with “the <em>rate</em>s”: true positive, true negative, false positive, false negative. In the second, we have <em>precision</em>, together with positive (negative, resp.) <em>predictive value</em>.</p>
<p>If now we demand that these metrics be the same across groups, we arrive at corresponding fairness criteria: equal false positive rate, equal positive predictive value, etc. In the inter-group setting, the two types of metrics may be arranged under headings “equality of opportunity” and “predictive parity”. You’ll encounter these as actual headers in the summary table at the end of this text. (Said table organizes concepts from different areas into a three-category format. The overall narrative builds up towards that “map” in a bottom-up way – meaning, most entries will not make sense at this point.}</p>
<p>While overall, the terminology around metrics can be confusing (to me it is), these headings have some mnemonic value. <em>Equality of opportunity</em> suggests that people similar in real life (<img src="https://latex.codecogs.com/png.latex?Y">) get classified similarly (<img src="https://latex.codecogs.com/png.latex?%5Chat%7BY%7D">). <em>Predictive parity</em> suggests that people classified similarly (<img src="https://latex.codecogs.com/png.latex?%5Chat%7BY%7D">) are, in fact, similar (<img src="https://latex.codecogs.com/png.latex?Y">).</p>
<p>The two criteria can concisely be characterized using the language of statistical independence. Following <span class="citation" data-cites="barocas">Barocas et al. (2019)</span>, these are:</p>
<div class="no-row-height column-margin column-container"></div><ul>
<li><p>Separation: Given true target <img src="https://latex.codecogs.com/png.latex?Y">, prediction <img src="https://latex.codecogs.com/png.latex?%5Chat%7BY%7D"> is independent of group membership (<img src="https://latex.codecogs.com/png.latex?%5Chat%7BY%7D%20%5Cperp%20A%20%7C%20Y">).</p></li>
<li><p>Sufficiency: Given prediction <img src="https://latex.codecogs.com/png.latex?%5Chat%7BY%7D">, target <img src="https://latex.codecogs.com/png.latex?Y"> is independent of group membership (<img src="https://latex.codecogs.com/png.latex?Y%20%5Cperp%20A%20%7C%20%5Chat%7BY%7D">).</p></li>
</ul>
<p>Given those two fairness criteria – and two sets of corresponding metrics – the natural question arises: Can we satisfy both? Above, I was mentioning precision and recall on purpose: to maybe “prime” you to think in the direction of “precision-recall trade-off”. And really, these two categories reflect different preferences; usually, it is impossible to optimize for both. The most famous, probably, result is due to <span class="citation" data-cites="2016arXiv161007524C">Chouldechova (2016)</span> : It says that predictive parity (testing for sufficiency) is incompatible with error rate balance (separation) when prevalence differs across groups. This is a theorem (yes, we’re in the realm of theorems and proofs here) that may not be surprising, in light of Bayes’ theorem, but is of great practical importance nonetheless: Unequal prevalence usually is the norm, not the exception.</p>
<div class="no-row-height column-margin column-container"><div id="ref-2016arXiv161007524C" class="csl-entry">
Chouldechova, Alexandra. 2016. <span>“<span class="nocase">Fair prediction with disparate impact: A study of bias in recidivism prediction instruments</span>.”</span> <em>arXiv e-Prints</em>, October, arXiv:1610.07524. <a href="https://arxiv.org/abs/1610.07524">https://arxiv.org/abs/1610.07524</a>.
</div><div id="ref-abs-1808-08619" class="csl-entry">
Yeom, Samuel, and Michael Carl Tschantz. 2018. <span>“Discriminative but Not Discriminatory: <span>A</span> Comparison of Fairness Definitions Under Different Worldviews.”</span> <em>CoRR</em> abs/1808.08619. <a href="http://arxiv.org/abs/1808.08619">http://arxiv.org/abs/1808.08619</a>.
</div></div><p>This necessarily means we have to make a choice. And this is where the theorems and proofs <em>do</em> matter. For example, <span class="citation" data-cites="abs-1808-08619">Yeom and Tschantz (2018)</span> show that in this framework – the strictly technical approach to fairness – separation should be preferred over sufficiency, because the latter allows for arbitrary disparity amplification. Thus, <em>in this framework</em>, we may have to work through the theorems.</p>
<p>What is the alternative?</p>
</section>
<section id="fairness-viewed-as-a-social-construct" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="fairness-viewed-as-a-social-construct">Fairness, viewed as a social construct</h3>
<p>Starting with what I just wrote: No one will likely challenge fairness <em>being</em> a social construct. But what does that entail?</p>
<p>Let me start with a biographical reminiscence. In undergraduate psychology (a long time ago), probably the most hammered-in distinction relevant to experiment planning was that between a hypothesis and its operationalization. The hypothesis is what you want to substantiate, conceptually; the operationalization is what you measure. There necessarily can’t be a one-to-one correspondence; we’re just striving to implement the best operationalization possible.</p>
<p>In the world of datasets and algorithms, all we have are measurements. And often, these are treated <em>as though</em> they were the concepts. This will get more concrete with an example, and we’ll stay with the hiring software scenario.</p>
<p>Assume the dataset used for training, assembled from scoring previous employees, contains a set of predictors (among which, high-school grades) and a target variable, say an indicator whether an employee did “survive” probation. There is a concept-measurement mismatch on both sides.</p>
<p>For one, say the grades are intended to reflect ability to learn, and motivation to learn. But depending on the circumstances, there are influence factors of much higher impact: socioeconomic status, constantly having to struggle with prejudice, overt discrimination, and more.</p>
<p>And then, <em>the target variable</em>. If the thing it’s supposed to measure is “was hired for seemed like a good fit, and was retained since was a good fit”, then all is good. But normally, HR departments are aiming for more than just a strategy of “keep doing what we’ve always been doing”.</p>
<p>Unfortunately, that concept-measurement mismatch is even more fatal, and even less talked about, when it’s about the target and not the predictors. (Not accidentally, we also call the target the “ground truth”.) An infamous example is recidivism prediction, where what we really want to measure – whether someone did, in fact, commit a crime – is replaced, for measurability reasons, by whether they were convicted. These are not the same: Conviction depends on more then what someone has done – for instance, if they’ve been under intense scrutiny from the outset.</p>
<p>Fortunately, though, the mismatch is clearly pronounced in the AI fairness literature. <span class="citation" data-cites="FriedlerSV16">Friedler et al. (2016)</span> distinguish between the <em>construct</em> and <em>observed</em> spaces; depending on whether a near-perfect mapping is assumed between these, they talk about two “worldviews”: “We’re all equal” (WAE) vs.&nbsp;“What you see is what you get” (WYSIWIG). If we’re all equal, membership in a societally disadvantaged group should not – in fact, may not – affect classification. In the hiring scenario, any algorithm employed thus has to result in the same proportion of applicants being hired, regardless of which demographic group they belong to. If “What you see is what you get”, we don’t question that the “ground truth” <em>is</em> the truth.</p>
<div class="no-row-height column-margin column-container"></div><p>This talk of worldviews may seem unnecessary philosophical, but the authors go on and clarify: All that matters, in the end, is whether the data is seen as reflecting reality in a naïve, take-at-face-value way.</p>
<p>For example, we might be ready to concede that there could be small, albeit uninteresting effect-size-wise, statistical differences between men and women as to spatial vs.&nbsp;linguistic abilities, respectively. We know for sure, though, that there are much greater effects of socialization, starting in the core family and reinforced, progressively, as adolescents go through the education system. We therefore apply WAE, trying to (partly) compensate for historical injustice. This way, we’re effectively applying affirmative action, <a href="https://www.law.cornell.edu/wex/affirmative_action">defined as</a></p>
<blockquote class="blockquote">
<p>A set of procedures designed to eliminate unlawful discrimination among applicants, remedy the results of such prior discrimination, and prevent such discrimination in the future.</p>
</blockquote>
<p>In the already-mentioned summary table, you’ll find the WYSIWIG principle mapped to both equal opportunity and predictive parity metrics. WAE maps to the third category, one we haven’t dwelled upon yet: <em>demographic parity</em>, also known as <em>statistical parity</em>. In line with what was said before, the requirement here is for each group to be present in the positive-outcome class in proportion to its representation in the input sample. For example, if thirty percent of applicants are Black, then at least thirty percent of people selected should be Black, as well. A term commonly used for cases where this does <em>not</em> happen is <em>disparate impact</em>: The algorithm affects different groups in different ways.</p>
<p>Similar in spirit to demographic parity, but possibly leading to different outcomes in practice, is conditional demographic parity <sup>3</sup>. Here we additionally take into account other predictors in the dataset; to be precise: <em>all</em> other predictors. The desiderate now is that for any choice of attributes, outcome proportions should be equal, given the protected attribute <strong>and</strong> the other attributes in question. I’ll come back to why this may sound better in theory than work in practice in the next section.</p>
<div class="no-row-height column-margin column-container"><div id="fn3"><p><sup>3</sup>&nbsp;<span class="citation" data-cites="abs-2005-05906">Wachter et al. (2020b)</span></p></div></div><p>Summing up, we’ve seen commonly used fairness metrics organized into three groups, two of which share a common assumption: that the data used for training can be taken at face value. The other starts from the outside, contemplating what historical events, and what political and societal factors have made the given data look as they do.</p>
<p>Before we conclude, I’d like to try a quick glance at other disciplines, beyond machine learning and computer science, domains where fairness figures among the central topics. This section is necessarily limited in every respect; it should be seen as a flashlight, an invitation to read and reflect rather than an orderly exposition. The short section will end with a word of caution: Since drawing analogies can feel highly enlightening (and is intellectually satisfying, for sure), it is easy to abstract away practical realities. But I’m getting ahead of myself.</p>
</section>
</section>
<section id="a-quick-glance-at-neighboring-fields-law-and-political-philosophy" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="a-quick-glance-at-neighboring-fields-law-and-political-philosophy">A quick glance at neighboring fields: law and political philosophy</h2>
<p>In jurisprudence, fairness and discrimination constitute an important subject. A recent paper that caught my attention is <span class="citation" data-cites="nondisc">Wachter et al. (2020a)</span> . From a machine learning perspective, the interesting point is the classification of metrics into bias-preserving and bias-transforming. The terms speak for themselves: Metrics in the first group reflect biases in the dataset used for training; ones in the second do not. In that way, the distinction parallels <span class="citation" data-cites="FriedlerSV16">Friedler et al. (2016)</span> ’s confrontation of two “worldviews”. But the exact words used also hint at how guidance by metrics feeds back into society: Seen as strategies, one preserves existing biases; the other, to consequences unknown a priori, <em>changes the world</em>.</p>
<div class="no-row-height column-margin column-container"></div><p>To the ML practitioner, this framing is of great help in evaluating what criteria to apply in a project. Helpful, too, is the systematic mapping provided of metrics to the two groups; it is here that, as alluded to above, we encounter <em>conditional demographic parity</em> among the bias-transforming ones. I agree that in spirit, this metric can be seen as bias-transforming; if we take two sets of people who, per all available criteria, are equally qualified for a job, and then find the whites favored over the Blacks, fairness is clearly violated. But the problem here is “available”: per all <em>available</em> criteria. What if we have reason to assume that, in a dataset, all predictors are biased? Then it will be very hard to prove that discrimination has occurred.</p>
<p>A similar problem, I think, surfaces when we look at the field of political philosophy, and consult theories on <a href="https://plato.stanford.edu/entries/justice-distributive/">distributive justice</a> for guidance. <span class="citation" data-cites="abs-1809-03400">Heidari et al. (2018)</span> have written a paper comparing the three criteria – demographic parity, equality of opportunity, and predictive parity – to egalitarianism, equality of opportunity (EOP) in the Rawlsian sense, and EOP seen through the glass of luck egalitarianism, respectively. While the analogy is fascinating, it too assumes that we may take what is in the data at face value. In their likening predictive parity to luck egalitarianism, they have to go to especially great lengths, in assuming that the <em>predicted</em> class reflects <em>effort exerted</em>. In the below table, I therefore take the liberty to disagree, and map a libertarian view of distributive justice to both equality of opportunity and predictive parity metrics.</p>
<div class="no-row-height column-margin column-container"><div id="ref-abs-1809-03400" class="csl-entry">
Heidari, Hoda, Michele Loi, Krishna P. Gummadi, and Andreas Krause. 2018. <span>“A Moral Framework for Understanding of Fair <span>ML</span> Through Economic Models of Equality of Opportunity.”</span> <em>CoRR</em> abs/1809.03400. <a href="http://arxiv.org/abs/1809.03400">http://arxiv.org/abs/1809.03400</a>.
</div></div><p>In summary, we end up with two highly controversial categories of fairness criteria, one bias-preserving, “what you see is what you get”-assuming, and libertarian, the other bias-transforming, “we’re all equal”-thinking, and egalitarian. Here, then, is that often-announced table.</p>
<table class="caption-top table">
<colgroup>
<col style="width: 17%">
<col style="width: 34%">
<col style="width: 21%">
<col style="width: 25%">
</colgroup>
<thead>
<tr class="header">
<th></th>
<th>Demographic parity</th>
<th>Equality of opportunity</th>
<th>Predictive parity</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>A.K.A. / subsumes / related concepts</strong></td>
<td>statistical parity, group fairness, disparate impact, conditional demographic parity <sup>4</sup></td>
<td>equalized odds, equal false positive / negative rates</td>
<td>equal positive / negative predictive values, calibration by group</td>
</tr>
<tr class="even">
<td><strong>Statistical independence criterion</strong> <sup>5</sup></td>
<td><p>independence</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Chat%7BY%7D%20%20%5Cperp%20A"></p></td>
<td><p>separation</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Chat%7BY%7D%20%20%5Cperp%20A%20%7C%20Y"></p></td>
<td><p>sufficiency</p>
<p><img src="https://latex.codecogs.com/png.latex?Y%20%5Cperp%20%20A%20%7C%20%5Chat%7BY%7D"></p></td>
</tr>
<tr class="odd">
<td><strong>Individual / group</strong></td>
<td>group</td>
<td>group (most) or individual (fairness through awareness)</td>
<td>group</td>
</tr>
<tr class="even">
<td><strong>Distributive Justice</strong></td>
<td>egalitarian</td>
<td>libertarian (contra Heidari et al., see above)</td>
<td>libertarian (contra Heidari et al., see above)</td>
</tr>
<tr class="odd">
<td><strong>Effect on bias</strong> <sup>6</sup></td>
<td>transforming</td>
<td>preserving</td>
<td>preserving</td>
</tr>
<tr class="even">
<td><strong>Policy / “worldview”</strong> <sup>7</sup></td>
<td>We’re all equal (WAE)</td>
<td>What you see is what you get (WYSIWIG)</td>
<td>What you see is what you get (WYSIWIG)</td>
</tr>
</tbody>
</table>
<div class="no-row-height column-margin column-container"><div id="fn4"><p><sup>4</sup>&nbsp;<span class="citation" data-cites="abs-2005-05906">Wachter et al. (2020b)</span></p><div id="ref-abs-2005-05906" class="csl-entry">
Wachter, Sandra, Brent D. Mittelstadt, and Chris Russell. 2020b. <span>“Why Fairness Cannot Be Automated: Bridging the Gap Between <span>EU</span> Non-Discrimination Law and <span>AI</span>.”</span> <em>CoRR</em> abs/2005.05906. <a href="https://arxiv.org/abs/2005.05906">https://arxiv.org/abs/2005.05906</a>.
</div></div><div id="fn5"><p><sup>5</sup>&nbsp;<span class="citation" data-cites="barocas">Barocas et al. (2019)</span></p><div id="ref-barocas" class="csl-entry">
Barocas, Solon, Moritz Hardt, and Arvind Narayanan. 2019. <em>Fairness and Machine Learning</em>. Fairmlbook.org.
</div></div><div id="fn6"><p><sup>6</sup>&nbsp;<span class="citation" data-cites="nondisc">Wachter et al. (2020a)</span></p><div id="ref-nondisc" class="csl-entry">
Wachter, Sandra, Brent D. Mittelstadt, and Chris Russell. 2020a. <span>“Bias Preservation in Machine Learning: The Legality of Fairness Metrics Under EU Non-Discrimination Law.”</span> <em>West Virginia Law Review, Forthcoming</em> abs/2005.05906. <a href="https://ssrn.com/abstract=3792772">https://ssrn.com/abstract=3792772</a>.
</div></div><div id="fn7"><p><sup>7</sup>&nbsp;<span class="citation" data-cites="FriedlerSV16">Friedler et al. (2016)</span></p><div id="ref-FriedlerSV16" class="csl-entry">
Friedler, Sorelle A., Carlos Scheidegger, and Suresh Venkatasubramanian. 2016. <span>“On the (Im)possibility of Fairness.”</span> <em>CoRR</em> abs/1609.07236. <a href="http://arxiv.org/abs/1609.07236">http://arxiv.org/abs/1609.07236</a>.
</div></div></div></section>
<section id="a-conclusion" class="level2">
<h2 class="anchored" data-anchor-id="a-conclusion">(A) Conclusion</h2>
<p>In line with its original goal – to provide some help in starting to think about AI fairness metrics – this article does not end with recommendations. It does, however, end with an observation. As the last section has shown, amidst all theorems and theories, all proofs and memes, it makes sense to not lose sight of the concrete: the data trained on, and the ML process as a whole. Fairness is not something to be evaluated post hoc; the <em>feasibility of fairness</em> is to be reflected on right from the beginning.</p>
<p>In that regard, assessing impact on fairness is not that different from that essential, but often toilsome and non-beloved, stage of modeling that precedes the modeling itself: exploratory data analysis.</p>
<p>Thanks for reading!</p>
<div class="photo-credit">
<p>Photo by <a href="https://unsplash.com/@andersjilden?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Anders Jildén</a> on <a href="https://unsplash.com/?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>AI Societal Impact</category>
  <category>Deep Learning</category>
  <guid>https://divergences.xyz/posts/ai-fairness/</guid>
  <pubDate>Thu, 15 Jul 2021 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/ai-fairness/preview.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Convolutional LSTM for spatial forecasting</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/torch-convlstm/</link>
  <description><![CDATA[ 






<p>This post is the first in a loose series exploring forecasting of spatially-determined data over time. By spatially-determined I mean that whatever the quantities we’re trying to predict – be they univariate or multivariate time series, of spatial dimensionality or not – the <em>input</em> data are given on a spatial grid.</p>
<p>For example, the input could be atmospheric measurements, such as sea surface temperature or pressure, given at some set of latitudes and longitudes. The target to be predicted could then span that same (or another) grid. Alternatively, it could be a univariate time series, like a meteorological index.</p>
<p>But wait a second, you may be thinking. For time-series prediction, we have that time-honored set of recurrent architectures (e.g., LSTM, GRU), right? Right. We do; but, once we feed spatial data to an RNN, treating different locations as different input features, we lose an essential structural relationship. Importantly, we need to operate in both space and time. We want both: recurrence relations and convolutional filters. Enter <em>convolutional RNNs</em>.</p>
<section id="what-to-expect-from-this-post" class="level2">
<h2 class="anchored" data-anchor-id="what-to-expect-from-this-post">What to expect from this post</h2>
<p>Today, we won’t jump into real-world applications just yet. Instead, we’ll take our time to build a convolutional LSTM (henceforth: convLSTM) in <code>torch</code>. For one, we have to – there is no official PyTorch implementation.</p>
<p>(Keras, on the other hand, has one. If you’re interested in quickly playing around with a Keras convLSTM, check out <a href="https://tensorflow.rstudio.com/guide/keras/examples/conv_lstm/">this nice example</a>.)</p>
<p>What’s more, this post can serve as an introduction to building your own modules. This is something you may be familiar with from Keras or not – depending on whether you’ve used custom models or rather, preferred the declarative <em>define -&gt; compile -&gt; fit</em> style. (Yes, I’m implying there’s some transfer going on if one comes to <code>torch</code> from Keras custom training. Syntactic and semantic details may be different, but both share the object-oriented style that allows for great flexibility and control.)</p>
<p>Last but not least, we’ll also use this as a hands-on experience with RNN architectures (the LSTM, specifically). While the general concept of recurrence may be easy to grasp, it is not necessarily self-evident how those architectures should, or could, be coded. Personally, I find that independent of the framework used, RNN-related documentation leaves me confused. What exactly <em>is</em> being returned from calling an LSTM, or a GRU? (In Keras this depends on how you’ve defined the layer in question.) I suspect that once we’ve decided what we <em>want</em> to return, the actual code won’t be that complicated. Consequently, we’ll take a detour clarifying what it is that <code>torch</code> and Keras are giving us. Implementing our convLSTM will be a lot more straightforward thereafter.</p>
</section>
<section id="a-torch-convlstm" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="a-torch-convlstm">A <code>torch</code> convLSTM</h2>
<p>The code discussed here may be found on <a href="https://github.com/skeydan/convlstm">GitHub</a>. (Depending on when you’re reading this, the code in that repository may have evolved though.)</p>
<p>My starting point was one of the PyTorch implementations found on the net, namely, <a href="https://github.com/ndrplz/ConvLSTM_pytorch/blob/master/convlstm.py">this one</a>. If you search for “PyTorch convGRU” or “PyTorch convLSTM”, you will find stunning discrepancies in how these are realized – discrepancies not just in syntax and/or engineering ambition, but on the semantic level, right at the center of what the architectures may be expected to do. As they say, let the buyer beware. (Regarding the implementation I ended up porting, I am confident that while numerous optimizations will be possible, the basic mechanism matches my expectations.)</p>
<p>What do I expect? Let’s approach this task in a top-down way.</p>
<section id="input-and-output" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="input-and-output">Input and output</h3>
<p>The convLSTM’s input will be a time series of spatial data, each observation being of size <code>(time steps, channels, height, width)</code>.</p>
<p>Compare this with the usual RNN input format, be it in <code>torch</code> or Keras. In both frameworks, RNNs expect tensors of size <code>(timesteps, input_dim)</code><sup>1</sup>. <code>input_dim</code> is <img src="https://latex.codecogs.com/png.latex?1"> for univariate time series and greater than <img src="https://latex.codecogs.com/png.latex?1"> for multivariate ones. Conceptually, we may match this to convLSTM’s <code>channels</code> dimension: There could be a single channel, for temperature, say – or there could be several, such as for pressure, temperature, and humidity. The two additional dimensions found in convLSTM, <code>height</code> and <code>width</code>, are spatial indexes into the data.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;Leaving aside the batch dimension in this discussion.</p></div></div><p>In sum, we want to be able to pass data that:</p>
<ul>
<li><p>consist of one or more features,</p></li>
<li><p>evolve in time, and</p></li>
<li><p>are indexed in two spatial dimensions.</p></li>
</ul>
<p>How about the output? We want to be able to return forecasts for as many time steps as we have in the input sequence. This is something that <code>torch</code> RNNs do by default, while Keras equivalents do not. (You have to pass <code>return_sequences = TRUE</code> to obtain that effect.) If we’re interested in predictions for just a single point in time, we can always pick the last time step in the output tensor.</p>
<p>However, with RNNs, it is not all about outputs. RNN architectures also carry through hidden states.</p>
<p>What are hidden states? I carefully phrased that sentence to be as general as possible – deliberately circling around the confusion that, in my view, often arises at this point. We’ll attempt to clear up some of that confusion in a second, but let’s first finish our high-level requirements specification.</p>
<p>We want our convLSTM to be usable in different contexts and applications. Various architectures exist that make use of hidden states, most prominently perhaps, encoder-decoder architectures. Thus, we want our convLSTM to return those as well. Again, this is something a <code>torch</code> LSTM does by default, while in Keras it is achieved using <code>return_state = TRUE</code>.</p>
<p>Now though, it really is time for that interlude. We’ll sort out the ways things are called by both <code>torch</code> and Keras, and inspect what you get back from their respective GRUs and LSTMs.</p>
</section>
<section id="interlude-outputs-states-hidden-values-whats-what" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="interlude-outputs-states-hidden-values-whats-what">Interlude: Outputs, states, hidden values … what’s what?</h3>
<p>For this to remain an interlude, I summarize findings on a high level. The code snippets in the appendix show how to arrive at these results. Heavily commented, they probe return values from both Keras and <code>torch</code> GRUs and LSTMs. Running these will make the upcoming summaries seem a lot less abstract.</p>
<p>First, let’s look at the ways you create an LSTM in both frameworks. (I will generally use LSTM as the “prototypical RNN example”, and just mention GRUs when there are differences significant in the context in question.)</p>
<p>In Keras, to create an LSTM you may write something like this:</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">lstm <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;">layer_lstm</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">units =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span></code></pre></div></div>
<p>The <code>torch</code> equivalent would be:</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">lstm <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_lstm</span>(</span>
<span id="cb2-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">input_size =</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 input features</span></span>
<span id="cb2-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hidden_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># number of hidden (and output!) features</span></span>
<span id="cb2-4">)</span></code></pre></div></div>
<p>Don’t focus on <code>torch</code>‘s <code>input_size</code> parameter for this discussion. (It’s the number of features in the input tensor.) The parallel occurs between Keras’ <code>units</code> and <code>torch</code>’s <code>hidden_size</code>. If you’ve been using Keras, you’re probably thinking of <code>units</code> as the thing that determines output size (equivalently, the number of features in the output). So when <code>torch</code> lets us arrive at the same result using <code>hidden_size</code>, what does that mean? It means that somehow we’re specifying the same thing, using different terminology. And it does make sense, since at every time step current input and previous hidden state are added<sup>2</sup>:</p>
<div class="no-row-height column-margin column-container"><div id="fn2"><p><sup>2</sup>&nbsp;In theory, it would be possible for them to be of different sizes if the respective weight matrices transformed their operands to the same output size.</p></div></div><p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7Bh%7D_t%20=%20%5Cmathbf%7BW%7D_%7Bx%7D%5Cmathbf%7Bx%7D_t%20+%20%5Cmathbf%7BW%7D_%7Bh%7D%5Cmathbf%7Bh%7D_%7Bt-1%7D%0A"></p>
<p>Now, <em>about those hidden states</em>.</p>
<p>When a Keras LSTM is defined with <code>return_state = TRUE</code>, its return value is a structure of three entities called output, memory state, and carry state. In <code>torch</code>, the same entities are referred to as output, hidden state, and cell state. (In <code>torch</code>, we always get all of them.)</p>
<p>So are we dealing with three different types of entities? We are not.</p>
<p>The cell, or carry state is that special thing that sets apart LSTMs from GRUs deemed responsible for the “long” in “long short-term memory”. Technically, it could be reported to the user at all points in time; as we’ll see shortly though, it is not.</p>
<p>What about outputs and hidden, or memory states? Confusingly, these really are the same thing. Recall that for each input <em>item</em> in the input <em>sequence</em>, we’re combining it with the previous state, resulting in a new state, to be made used of in the next step<sup>3</sup>:</p>
<div class="no-row-height column-margin column-container"><div id="fn3"><p><sup>3</sup>&nbsp;Yes, this is the same formula as above.</p></div></div><p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7Bh%7D_t%20=%20%5Cmathbf%7BW%7D_%7Bx%7D%5Cmathbf%7Bx%7D_t%20+%20%5Cmathbf%7BW%7D_%7Bh%7D%5Cmathbf%7Bh%7D_%7Bt-1%7D%0A"></p>
<p>Now, say that we’re interested in looking at just the final time step – that is, the default output of a Keras LSTM. From that point of view, we can consider those intermediate computations as “hidden”. Seen like that, output and hidden states feel different.</p>
<p>However, we can also request to see the outputs for every time step. If we do so, there is no difference – the output<strong>s</strong> (plural) equal the hidden states. This can be verified using the code in the appendix.</p>
<p>Thus, of the three things returned by an LSTM, two are really the same. How about the GRU, then? As there is no “cell state”, we really have just one type of thing left over – call it outputs or hidden states.</p>
<p>Let’s summarize this in a table.</p>
<div class="column-body-outset-right">
<table class="caption-top table">
<caption>Table 1: RNN terminology. Comparing torch-speak and Keras-speak. In row 1, the terms are parameter names. In rows 2 and 3, they are pulled from current documentation.</caption>
<colgroup>
<col style="width: 80%">
<col style="width: 9%">
<col style="width: 9%">
</colgroup>
<thead>
<tr class="header">
<th>Referring to this entity:</th>
<th><code>torch</code> says:</th>
<th>Keras says:</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><p><em>Number of features in the output</em></p>
<p>This determines both how many output features there are <em>and</em> the dimensionality of the hidden states.</p></td>
<td><code>hidden_size</code></td>
<td><code>units</code></td>
</tr>
<tr class="even">
<td><p><em>Per-time-step output; latent state; intermediate state …</em></p>
<p>This could be named “public state” in the sense that we, the users, are able to obtain <em>all values.</em></p></td>
<td>hidden state</td>
<td>memory state</td>
</tr>
<tr class="odd">
<td><p><em>Cell state; inner state … (LSTM only)</em></p>
<p>This could be named “private state” in that we are able to obtain a value <em>only for the last time step</em>. More on that in a second.</p></td>
<td>cell state</td>
<td>carry state</td>
</tr>
</tbody>
</table>
</div>
<p>Now, about that public vs.&nbsp;private distinction. In both frameworks, we can obtain outputs (hidden states) for every time step. The cell state, however, we can access only for the very last time step. This is purely an implementation decision. As we’ll see when building our own recurrent module, there are no obstacles inherent in keeping track of cell states and passing them back to the user.</p>
<p>If you dislike the pragmatism of this distinction, you can always go with the math. When a new cell state has been computed (based on prior cell state, input, forget, and cell gates – the specifics of which we are not going to get into here), it is transformed to the hidden (a.k.a. output) state making use of yet another, namely, the output gate:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ah_t%20=%20o_t%20%5Codot%20%5Ctanh(c_t)%0A"></p>
<p>Definitely, then, hidden state (output, resp.) builds on cell state, adding additional modeling power.</p>
<p>Now it is time to get back to our original goal and build that convLSTM. First though, let’s summarize the return values obtainable from <code>torch</code> and Keras.</p>
<div class="column-body-outset-right">
<table class="caption-top table">
<caption>Table 2: Contrasting ways of obtaining various return values in <code>torch</code> vs.&nbsp;Keras. Cf. the appendix for complete examples.</caption>
<colgroup>
<col style="width: 54%">
<col style="width: 12%">
<col style="width: 31%">
</colgroup>
<thead>
<tr class="header">
<th>To achieve this goal:</th>
<th>in <code>torch</code> do:</th>
<th>in Keras do:</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>access all intermediate outputs ( = per-time-step outputs)</td>
<td><code>ret[[1]]</code></td>
<td><code>return_sequences = TRUE</code></td>
</tr>
<tr class="even">
<td>access both “hidden state” (output) and “cell state” from final time step (only!)</td>
<td><code>ret[[2]]</code></td>
<td><code>return_state = TRUE</code></td>
</tr>
<tr class="odd">
<td>access all intermediate outputs and the final “cell state”</td>
<td>both of the above</td>
<td><code>return_sequences = TRUE, return_state = TRUE</code></td>
</tr>
<tr class="even">
<td>access all intermediate outputs and “cell states” from all time steps</td>
<td>no way</td>
<td>no way</td>
</tr>
</tbody>
</table>
</div>
</section>
<section id="convlstm-the-plan" class="level3">
<h3 class="anchored" data-anchor-id="convlstm-the-plan"><code>convLSTM</code>, the plan</h3>
<p>In both <code>torch</code> and Keras RNN architectures, single time steps are processed by corresponding <code>Cell</code> classes: There is an LSTM Cell matching the LSTM, a GRU Cell matching the GRU, and so on. We do the same for ConvLSTM. In <code>convlstm_cell()</code>, we first define what should happen to a single observation; then in <code>convlstm()</code>, we build up the recurrence logic.</p>
<p>Once we’re done, we create a dummy dataset, as reduced-to-the-essentials as can be. With more complex datasets, even artificial ones, chances are that if we don’t see any training progress, there are hundreds of possible explanations. We want a sanity check that, if failed, leaves no excuses. Realistic applications are left to future posts.</p>
</section>
<section id="a-single-step-convlstm_cell" class="level3">
<h3 class="anchored" data-anchor-id="a-single-step-convlstm_cell">A single step: <code>convlstm_cell</code></h3>
<p>Our <code>convlstm_cell</code>’s constructor takes arguments <code>input_dim</code> , <code>hidden_dim</code>, and <code>bias</code>, just like a <code>torch</code> LSTM Cell.</p>
<p>But we’re processing two-dimensional input data. Instead of the usual affine combination of new input and previous state, we use a convolution of kernel size <code>kernel_size</code>. Inside <code>convlstm_cell</code>, it is <code>self$conv</code> that takes care of this.</p>
<p>Note how the <code>channels</code> dimension, which in the original input data would correspond to different variables, is creatively used to consolidate four convolutions into one: Each channel output will be passed to just one of the four cell gates. Once in possession of the convolution output, <code>forward()</code> applies the gate logic, resulting in the two types of states it needs to send back to the caller.</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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(torch)</span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(zeallot)</span>
<span id="cb3-3"></span>
<span id="cb3-4">convlstm_cell <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="cb3-5">  </span>
<span id="cb3-6">  <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>(input_dim, hidden_dim, kernel_size, bias) {</span>
<span id="cb3-7">    </span>
<span id="cb3-8">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> hidden_dim</span>
<span id="cb3-9">    </span>
<span id="cb3-10">    padding <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> kernel_size <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;">2</span></span>
<span id="cb3-11">    </span>
<span id="cb3-12">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>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;">nn_conv2d</span>(</span>
<span id="cb3-13">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">in_channels =</span> input_dim <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim,</span>
<span id="cb3-14">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># for each of input, forget, output, and cell gates</span></span>
<span id="cb3-15">      <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;">4</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim,</span>
<span id="cb3-16">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_size =</span> kernel_size,</span>
<span id="cb3-17">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">padding =</span> padding,</span>
<span id="cb3-18">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bias =</span> bias</span>
<span id="cb3-19">    )</span>
<span id="cb3-20">  },</span>
<span id="cb3-21">  </span>
<span id="cb3-22">  <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, prev_states) {</span>
<span id="cb3-23"></span>
<span id="cb3-24">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(h_prev, c_prev) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&lt;-%</span> prev_states</span>
<span id="cb3-25">    </span>
<span id="cb3-26">    combined <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;">torch_cat</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(x, h_prev), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dim =</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;"># concatenate along channel axis</span></span>
<span id="cb3-27">    combined_conv <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> 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;">conv</span>(combined)</span>
<span id="cb3-28">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(cc_i, cc_f, cc_o, cc_g) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&lt;-%</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">torch_split</span>(combined_conv, self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dim =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb3-29">    </span>
<span id="cb3-30">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># input, forget, output, and cell gates (corresponding to torch's LSTM)</span></span>
<span id="cb3-31">    i <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;">torch_sigmoid</span>(cc_i)</span>
<span id="cb3-32">    f <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;">torch_sigmoid</span>(cc_f)</span>
<span id="cb3-33">    o <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;">torch_sigmoid</span>(cc_o)</span>
<span id="cb3-34">    g <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;">torch_tanh</span>(cc_g)</span>
<span id="cb3-35">    </span>
<span id="cb3-36">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># cell state</span></span>
<span id="cb3-37">    c_next <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> f <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> c_prev <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> g</span>
<span id="cb3-38">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># hidden state</span></span>
<span id="cb3-39">    h_next <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> o <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_tanh</span>(c_next)</span>
<span id="cb3-40">    </span>
<span id="cb3-41">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(h_next, c_next)</span>
<span id="cb3-42">  },</span>
<span id="cb3-43">  </span>
<span id="cb3-44">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">init_hidden =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(batch_size, height, width) {</span>
<span id="cb3-45">    </span>
<span id="cb3-46">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb3-47">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">torch_zeros</span>(batch_size, self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim, height, width, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>conv<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>weight<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>device),</span>
<span id="cb3-48">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">torch_zeros</span>(batch_size, self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>hidden_dim, height, width, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">device =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>conv<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>weight<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>device))</span>
<span id="cb3-49">  }</span>
<span id="cb3-50">)</span></code></pre></div></div>
<p>Now <code>convlstm_cell</code> has to be called for every time step. This is done by <code>convlstm</code>.</p>
</section>
<section id="iteration-over-time-steps-convlstm" class="level3">
<h3 class="anchored" data-anchor-id="iteration-over-time-steps-convlstm">Iteration over time steps: <code>convlstm</code></h3>
<p>A <code>convlstm</code> may consist of several layers, just like a <code>torch</code> LSTM. For each layer, we are able to specify hidden and kernel sizes individually.</p>
<p>During initialization, each layer gets its own <code>convlstm_cell</code>. On call, <code>convlstm</code> executes two loops. The outer one iterates over layers. At the end of each iteration, we store the final pair <code>(hidden state, cell state)</code> for later reporting. The inner loop runs over input sequences, calling <code>convlstm_cell</code> at each time step.</p>
<p>We also keep track of intermediate outputs, so we’ll be able to return the complete list of <code>hidden_state</code>s seen during the process. Unlike a <code>torch</code> LSTM, we do this <em>for every layer</em>.</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">convlstm <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="cb4-2">  </span>
<span id="cb4-3">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># hidden_dims and kernel_sizes are vectors, with one element for each layer in n_layers</span></span>
<span id="cb4-4">  <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>(input_dim, hidden_dims, kernel_sizes, n_layers, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bias =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>) {</span>
<span id="cb4-5"> </span>
<span id="cb4-6">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> n_layers</span>
<span id="cb4-7">    </span>
<span id="cb4-8">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cell_list <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_list</span>()</span>
<span id="cb4-9">    </span>
<span id="cb4-10">    <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>n_layers) {</span>
<span id="cb4-11">      cur_input_dim <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (i <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>) input_dim <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> hidden_dims[i <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>
<span id="cb4-12">      self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cell_list<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>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">convlstm_cell</span>(cur_input_dim, hidden_dims[i], kernel_sizes[i], bias))</span>
<span id="cb4-13">    }</span>
<span id="cb4-14">  },</span>
<span id="cb4-15">  </span>
<span id="cb4-16">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we always assume batch-first</span></span>
<span id="cb4-17">  <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="cb4-18">    </span>
<span id="cb4-19">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(batch_size, seq_len, num_channels, height, width) <span class="sc" style="color: #5E5E5E;
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;">size</span>()</span>
<span id="cb4-20">   </span>
<span id="cb4-21">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># initialize hidden states</span></span>
<span id="cb4-22">    init_hidden <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;">vector</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;">"list"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers)</span>
<span id="cb4-23">    <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>self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers) {</span>
<span id="cb4-24">      init_hidden[[i]] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cell_list[[i]]<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;">init_hidden</span>(batch_size, height, width)</span>
<span id="cb4-25">    }</span>
<span id="cb4-26">    </span>
<span id="cb4-27">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># list containing the outputs, of length seq_len, for each layer</span></span>
<span id="cb4-28">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># this is the same as h, at each step in the sequence</span></span>
<span id="cb4-29">    layer_output_list <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;">vector</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;">"list"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers)</span>
<span id="cb4-30">    </span>
<span id="cb4-31">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># list containing the last states (h, c) for each layer</span></span>
<span id="cb4-32">    layer_state_list <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;">vector</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;">"list"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers)</span>
<span id="cb4-33"></span>
<span id="cb4-34">    cur_layer_input <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> x</span>
<span id="cb4-35">    hidden_states <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> init_hidden</span>
<span id="cb4-36">    </span>
<span id="cb4-37">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># loop over layers</span></span>
<span id="cb4-38">    <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>self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n_layers) {</span>
<span id="cb4-39">      </span>
<span id="cb4-40">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># every layer's hidden state starts from 0 (non-stateful)</span></span>
<span id="cb4-41">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(h, c) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&lt;-%</span> hidden_states[[i]]</span>
<span id="cb4-42">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># outputs, of length seq_len, for this layer</span></span>
<span id="cb4-43">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># equivalently, list of h states for each time step</span></span>
<span id="cb4-44">      output_sequence <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;">vector</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;">"list"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> seq_len)</span>
<span id="cb4-45">      </span>
<span id="cb4-46">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># loop over time steps</span></span>
<span id="cb4-47">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (t <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>seq_len) {</span>
<span id="cb4-48">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(h, c) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&lt;-%</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cell_list[[i]](cur_layer_input[ , t, , , ], <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(h, c))</span>
<span id="cb4-49">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># keep track of output (h) for every time step</span></span>
<span id="cb4-50">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># h has dim (batch_size, hidden_size, height, width)</span></span>
<span id="cb4-51">        output_sequence[[t]] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> h</span>
<span id="cb4-52">      }</span>
<span id="cb4-53"></span>
<span id="cb4-54">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># stack hs for all time steps over seq_len dimension</span></span>
<span id="cb4-55">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># stacked_outputs has dim (batch_size, seq_len, hidden_size, height, width)</span></span>
<span id="cb4-56">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># same as input to forward (x)</span></span>
<span id="cb4-57">      stacked_outputs <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;">torch_stack</span>(output_sequence, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dim =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb4-58">      </span>
<span id="cb4-59">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># pass the list of outputs (hs) to next layer</span></span>
<span id="cb4-60">      cur_layer_input <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> stacked_outputs</span>
<span id="cb4-61">      </span>
<span id="cb4-62">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># keep track of list of outputs or this layer</span></span>
<span id="cb4-63">      layer_output_list[[i]] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> stacked_outputs</span>
<span id="cb4-64">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># keep track of last state for this layer</span></span>
<span id="cb4-65">      layer_state_list[[i]] <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;">list</span>(h, c)</span>
<span id="cb4-66">    }</span>
<span id="cb4-67"> </span>
<span id="cb4-68">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(layer_output_list, layer_state_list)</span>
<span id="cb4-69">  }</span>
<span id="cb4-70">    </span>
<span id="cb4-71">)</span></code></pre></div></div>
</section>
<section id="calling-the-convlstm" class="level3">
<h3 class="anchored" data-anchor-id="calling-the-convlstm">Calling the <code>convlstm</code></h3>
<p>Let’s see the input format expected by <code>convlstm</code>, and how to access its different outputs.</p>
<p>Here is a suitable input tensor.</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"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># batch_size, seq_len, channels, height, width</span></span>
<span id="cb5-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;">torch_rand</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;">4</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;">16</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>))</span></code></pre></div></div>
<p>First we make use of a single layer.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1">model <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;">convlstm</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">input_dim =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hidden_dims =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_sizes =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_layers =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb6-2"></span>
<span id="cb6-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(layer_outputs, layer_last_states) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&lt;-%</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">model</span>(x)</span></code></pre></div></div>
<p>We get back a list of length two, which we immediately split up into the two types of output returned: intermediate outputs from all layers, and final states (of both types) for the last layer.</p>
<p>With just a single layer, <code>layer_outputs[[1]]</code>holds all of the layer’s intermediate outputs, stacked on dimension two.</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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(layer_outputs[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]])</span>
<span id="cb7-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [1]  2  4  5 16 16</span></span></code></pre></div></div>
<p><code>layer_last_states[[1]]</code>is a list of tensors, the first of which holds the single layer’s final hidden state, and the second, its final cell state.</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="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(layer_last_states[[<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>
<span id="cb8-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [1]  2  5 16 16</span></span>
<span id="cb8-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(layer_last_states[[<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;">2</span>]])</span>
<span id="cb8-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [1]  2  5 16 16</span></span></code></pre></div></div>
<p>For comparison, this is how return values look for a multi-layer architecture.</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">model <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;">convlstm</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">input_dim =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hidden_dims =</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;">5</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_sizes =</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;">3</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_layers =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb9-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(layer_outputs, layer_last_states) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&lt;-%</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">model</span>(x)</span>
<span id="cb9-3"></span>
<span id="cb9-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># for each layer, tensor of size (batch_size, seq_len, hidden_size, height, width)</span></span>
<span id="cb9-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(layer_outputs[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]])</span>
<span id="cb9-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2  4  5 16 16</span></span>
<span id="cb9-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(layer_outputs[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>]])</span>
<span id="cb9-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2  4  1 16 16</span></span>
<span id="cb9-9"></span>
<span id="cb9-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># list of 2 tensors for each layer</span></span>
<span id="cb9-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str</span>(layer_last_states)</span>
<span id="cb9-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># List of 3</span></span>
<span id="cb9-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#  $ :List of 2</span></span>
<span id="cb9-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   ..$ :Float [1:2, 1:5, 1:16, 1:16]</span></span>
<span id="cb9-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   ..$ :Float [1:2, 1:5, 1:16, 1:16]</span></span>
<span id="cb9-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#  $ :List of 2</span></span>
<span id="cb9-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   ..$ :Float [1:2, 1:5, 1:16, 1:16]</span></span>
<span id="cb9-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   ..$ :Float [1:2, 1:5, 1:16, 1:16]</span></span>
<span id="cb9-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#  $ :List of 2</span></span>
<span id="cb9-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   ..$ :Float [1:2, 1:1, 1:16, 1:16]</span></span>
<span id="cb9-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   ..$ :Float [1:2, 1:1, 1:16, 1:16]</span></span>
<span id="cb9-22"></span>
<span id="cb9-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># h, of size (batch_size, hidden_size, height, width)</span></span>
<span id="cb9-24"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(layer_last_states[[<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>
<span id="cb9-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2  1 16 16</span></span>
<span id="cb9-26"></span>
<span id="cb9-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># c, of size (batch_size, hidden_size, height, width)</span></span>
<span id="cb9-28"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(layer_last_states[[<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;">2</span>]])</span>
<span id="cb9-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2  1 16 16</span></span></code></pre></div></div>
<p>Now we want to sanity-check this module with the simplest-possible dummy data.</p>
</section>
<section id="sanity-checking-the-convlstm" class="level3">
<h3 class="anchored" data-anchor-id="sanity-checking-the-convlstm">Sanity-checking the <code>convlstm</code></h3>
<p>We generate black-and-white “movies” of diagonal beams successively translated in space.</p>
<p>Each sequence consists of six time steps, and each beam of six pixels. Just a single sequence is created manually. To create that one sequence, we start from a single beam:</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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(torchvision)</span>
<span id="cb10-2"></span>
<span id="cb10-3">beams <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;">vector</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;">"list"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>)</span>
<span id="cb10-4">beam <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;">torch_eye</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</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;">nnf_pad</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;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>)) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># left, right, top, bottom</span></span>
<span id="cb10-5">beams[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> beam</span></code></pre></div></div>
<p>Using <code>torch_roll()</code> , we create a pattern where this beam moves up diagonally, and stack the individual tensors along the <code>timesteps</code> dimension.</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"><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;">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;">6</span>) {</span>
<span id="cb11-2">  beams[[i]] <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;">torch_roll</span>(beam, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>(i<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">-1</span>),i<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;">c</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;">2</span>))</span>
<span id="cb11-3">}</span>
<span id="cb11-4"></span>
<span id="cb11-5">init_sequence <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;">torch_stack</span>(beams, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dim =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span></code></pre></div></div>
<p>That’s a single sequence. Thanks to <code>torchvision::transform_random_affine()</code>, we almost effortlessly produce a dataset of a hundred sequences. Moving beams start at random points in the spatial frame, but they all share that upward-diagonal motion.</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">sequences <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;">vector</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;">"list"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>)</span>
<span id="cb12-2">sequences[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> init_sequence</span>
<span id="cb12-3"></span>
<span id="cb12-4"><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;">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;">100</span>) {</span>
<span id="cb12-5">  sequences[[i]] <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;">transform_random_affine</span>(init_sequence, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">degrees =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">translate =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>))</span>
<span id="cb12-6">}</span>
<span id="cb12-7"></span>
<span id="cb12-8">input <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;">torch_stack</span>(sequences, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dim =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb12-9"></span>
<span id="cb12-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># add channels dimension</span></span>
<span id="cb12-11">input <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> input<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;">3</span>)</span>
<span id="cb12-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(input)</span>
<span id="cb12-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># [1] 100   6  1  24  24</span></span></code></pre></div></div>
<p>That’s it for the raw data. Now we still need a <code>dataset</code> and a <code>dataloader</code>. Of the six time steps, we use the first five as input and try to predict the last one.</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">dummy_ds <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;">dataset</span>(</span>
<span id="cb13-2">  </span>
<span id="cb13-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>(data) {</span>
<span id="cb13-4">    self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> data</span>
<span id="cb13-5">  },</span>
<span id="cb13-6">  </span>
<span id="cb13-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.getitem =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(i) {</span>
<span id="cb13-8">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>data[i, <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;">5</span>, ..], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>data[i, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, ..])</span>
<span id="cb13-9">  },</span>
<span id="cb13-10">  </span>
<span id="cb13-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.length =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>() {</span>
<span id="cb13-12">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(self<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>data)</span>
<span id="cb13-13">  }</span>
<span id="cb13-14">)</span>
<span id="cb13-15"></span>
<span id="cb13-16">ds <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;">dummy_ds</span>(input)</span>
<span id="cb13-17">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>(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;">100</span>)</span></code></pre></div></div>
<p>Here is a tiny-ish convLSTM, trained for motion prediction:</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">model <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;">convlstm</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">input_dim =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hidden_dims =</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;">64</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_sizes =</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;">3</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_layers =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb14-2"></span>
<span id="cb14-3">optimizer <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;">optim_adam</span>(model<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>parameters)</span>
<span id="cb14-4"></span>
<span id="cb14-5">num_epochs <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span></span>
<span id="cb14-6"></span>
<span id="cb14-7"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (epoch <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_epochs) {</span>
<span id="cb14-8">  </span>
<span id="cb14-9">  model<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;">train</span>()</span>
<span id="cb14-10">  batch_losses <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;">c</span>()</span>
<span id="cb14-11">  </span>
<span id="cb14-12">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (b <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">enumerate</span>(dl)) {</span>
<span id="cb14-13">    </span>
<span id="cb14-14">    optimizer<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;">zero_grad</span>()</span>
<span id="cb14-15">    </span>
<span id="cb14-16">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># last-time-step output from last layer</span></span>
<span id="cb14-17">    preds <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;">model</span>(b<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;">2</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;">1</span>]]</span>
<span id="cb14-18">  </span>
<span id="cb14-19">    loss <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;">nnf_mse_loss</span>(preds, b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y)</span>
<span id="cb14-20">    batch_losses <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;">c</span>(batch_losses, loss<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;">item</span>())</span>
<span id="cb14-21">    </span>
<span id="cb14-22">    loss<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;">backward</span>()</span>
<span id="cb14-23">    optimizer<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;">step</span>()</span>
<span id="cb14-24">  }</span>
<span id="cb14-25">  </span>
<span id="cb14-26">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (epoch <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;">10</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;">0</span>)</span>
<span id="cb14-27">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sprintf</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">Epoch %d, training loss:%3f</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>, epoch, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(batch_losses)))</span>
<span id="cb14-28">}</span></code></pre></div></div>
<pre><code>Epoch 10, training loss:0.008522

Epoch 20, training loss:0.008079

Epoch 30, training loss:0.006187

Epoch 40, training loss:0.003828

Epoch 50, training loss:0.002322

Epoch 60, training loss:0.001594

Epoch 70, training loss:0.001376

Epoch 80, training loss:0.001258

Epoch 90, training loss:0.001218

Epoch 100, training loss:0.001171</code></pre>
<p>Loss decreases, but that in itself is not a guarantee the model has learned anything. Has it? Let’s inspect its forecast for the very first sequence and see.</p>
<p>For printing, I’m zooming in on the relevant region in the 24x24-pixel frame. Here is the ground truth for time step six:</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">b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y[<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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</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;">15</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</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;">19</span>]</span></code></pre></div></div>
<pre><code>0  0  0  0  0  0  0  0  0  0
0  0  0  0  0  0  0  0  0  0
0  0  1  0  0  0  0  0  0  0
0  0  0  1  0  0  0  0  0  0
0  0  0  0  1  0  0  0  0  0
0  0  0  0  0  1  0  0  0  0
0  0  0  0  0  0  1  0  0  0
0  0  0  0  0  0  0  1  0  0
0  0  0  0  0  0  0  0  0  0
0  0  0  0  0  0  0  0  0  0</code></pre>
<p>And here is the forecast. This does not look bad at all, given there was neither experimentation nor tuning involved.</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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.matrix</span>(preds[<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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</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;">15</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</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;">19</span>]), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span></code></pre></div></div>
<pre><code>       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9] [,10]
 [1,]  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00     0
 [2,] -0.02  0.36  0.01  0.06  0.00  0.00  0.00  0.00  0.00     0
 [3,]  0.00 -0.01  0.71  0.01  0.06  0.00  0.00  0.00  0.00     0
 [4,] -0.01  0.04  0.00  0.75  0.01  0.06  0.00  0.00  0.00     0
 [5,]  0.00 -0.01 -0.01 -0.01  0.75  0.01  0.06  0.00  0.00     0
 [6,]  0.00  0.01  0.00 -0.07 -0.01  0.75  0.01  0.06  0.00     0
 [7,]  0.00  0.01 -0.01 -0.01 -0.07 -0.01  0.75  0.01  0.06     0
 [8,]  0.00  0.00  0.01  0.00  0.00 -0.01  0.00  0.71  0.00     0
 [9,]  0.00  0.00  0.00  0.01  0.01  0.00  0.03 -0.01  0.37     0
[10,]  0.00  0.00  0.00  0.00  0.00  0.00 -0.01 -0.01 -0.01     0</code></pre>
<p>This should suffice for a sanity check. If you made it till the end, thanks for your patience! In the best case, you’ll be able to apply this architecture (or a similar one) to your own data – but even if not, I hope you’ve enjoyed learning about <code>torch</code> model coding and/or RNN weirdness ;-)</p>
<p>I, for one, am certainly looking forward to exploring convLSTMs on real-world problems in the near future. Thanks for reading!</p>
</section>
</section>
<section id="appendix" class="level2">
<h2 class="anchored" data-anchor-id="appendix">Appendix</h2>
<p>This appendix contains the code used to create tables 1 and 2 above.</p>
<section id="keras" class="level3">
<h3 class="anchored" data-anchor-id="keras">Keras</h3>
<section id="lstm" class="level4">
<h4 class="anchored" data-anchor-id="lstm">LSTM</h4>
<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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(keras)</span>
<span id="cb20-2"></span>
<span id="cb20-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># batch of 3, with 4 time steps each and a single feature</span></span>
<span id="cb20-4">input <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;">k_random_normal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shape =</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;">3</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;">1</span>L))</span>
<span id="cb20-5">input</span>
<span id="cb20-6"></span>
<span id="cb20-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default args</span></span>
<span id="cb20-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return shape = (batch_size, units)</span></span>
<span id="cb20-9">lstm <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;">layer_lstm</span>(</span>
<span id="cb20-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">units =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb20-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb20-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">recurrent_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb20-13">)</span>
<span id="cb20-14"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lstm</span>(input)</span>
<span id="cb20-15"></span>
<span id="cb20-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return_sequences = TRUE</span></span>
<span id="cb20-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return shape = (batch_size, time steps, units)</span></span>
<span id="cb20-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span></span>
<span id="cb20-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># note how for each item in the batch, the value for time step 4 equals that obtained above</span></span>
<span id="cb20-20">lstm <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;">layer_lstm</span>(</span>
<span id="cb20-21">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">units =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb20-22">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">return_sequences =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb20-23">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb20-24">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">recurrent_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb20-25">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># bias is by default initialized to 0</span></span>
<span id="cb20-26">)</span>
<span id="cb20-27"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lstm</span>(input)</span>
<span id="cb20-28"></span>
<span id="cb20-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return_state = TRUE</span></span>
<span id="cb20-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return shape = list of:</span></span>
<span id="cb20-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#                - outputs, of shape: (batch_size, units)</span></span>
<span id="cb20-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#                - "memory states" for the last time step, of shape: (batch_size, units)</span></span>
<span id="cb20-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#                - "carry states" for the last time step, of shape: (batch_size, units)</span></span>
<span id="cb20-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span></span>
<span id="cb20-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># note how the first and second list items are identical!</span></span>
<span id="cb20-36">lstm <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;">layer_lstm</span>(</span>
<span id="cb20-37">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">units =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb20-38">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">return_state =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb20-39">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb20-40">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">recurrent_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb20-41">)</span>
<span id="cb20-42"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lstm</span>(input)</span>
<span id="cb20-43"></span>
<span id="cb20-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return_state = TRUE, return_sequences = TRUE</span></span>
<span id="cb20-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return shape = list of:</span></span>
<span id="cb20-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#                - outputs, of shape: (batch_size, time steps, units)</span></span>
<span id="cb20-47"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#                - "memory" states for the last time step, of shape: (batch_size, units)</span></span>
<span id="cb20-48"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#                - "carry states" for the last time step, of shape: (batch_size, units)</span></span>
<span id="cb20-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span></span>
<span id="cb20-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># note how again, the "memory" state found in list item 2 matches the final-time step outputs reported in item 1</span></span>
<span id="cb20-51">lstm <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;">layer_lstm</span>(</span>
<span id="cb20-52">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">units =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb20-53">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">return_sequences =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb20-54">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">return_state =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb20-55">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb20-56">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">recurrent_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb20-57">)</span>
<span id="cb20-58"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lstm</span>(input)</span></code></pre></div></div>
</section>
<section id="gru" class="level4">
<h4 class="anchored" data-anchor-id="gru">GRU</h4>
<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"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default args</span></span>
<span id="cb21-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return shape = (batch_size, units)</span></span>
<span id="cb21-3">gru <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;">layer_gru</span>(</span>
<span id="cb21-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">units =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb21-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb21-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">recurrent_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb21-7">)</span>
<span id="cb21-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gru</span>(input)</span>
<span id="cb21-9"></span>
<span id="cb21-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return_sequences = TRUE</span></span>
<span id="cb21-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return shape = (batch_size, time steps, units)</span></span>
<span id="cb21-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span></span>
<span id="cb21-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># note how for each item in the batch, the value for time step 4 equals that obtained above</span></span>
<span id="cb21-14">gru <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;">layer_gru</span>(</span>
<span id="cb21-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">units =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb21-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">return_sequences =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb21-17">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb21-18">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">recurrent_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb21-19">)</span>
<span id="cb21-20"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gru</span>(input)</span>
<span id="cb21-21"></span>
<span id="cb21-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return_state = TRUE</span></span>
<span id="cb21-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return shape = list of:</span></span>
<span id="cb21-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#    - outputs, of shape: (batch_size, units)</span></span>
<span id="cb21-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#    - "memory" states for the last time step, of shape: (batch_size, units)</span></span>
<span id="cb21-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span></span>
<span id="cb21-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># note how the list items are identical!</span></span>
<span id="cb21-28">gru <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;">layer_gru</span>(</span>
<span id="cb21-29">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">units =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb21-30">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">return_state =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb21-31">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb21-32">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">recurrent_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb21-33">)</span>
<span id="cb21-34"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gru</span>(input)</span>
<span id="cb21-35"></span>
<span id="cb21-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return_state = TRUE, return_sequences = TRUE</span></span>
<span id="cb21-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return shape = list of:</span></span>
<span id="cb21-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#    - outputs, of shape: (batch_size, time steps, units)</span></span>
<span id="cb21-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#    - "memory states" for the last time step, of shape: (batch_size, units)</span></span>
<span id="cb21-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span></span>
<span id="cb21-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># note how again, the "memory state" found in list item 2 matches the final-time-step outputs reported in item 1</span></span>
<span id="cb21-42">gru <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;">layer_gru</span>(</span>
<span id="cb21-43">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">units =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb21-44">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">return_sequences =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb21-45">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">return_state =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb21-46">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb21-47">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">recurrent_initializer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initializer_constant</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb21-48">)</span>
<span id="cb21-49"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gru</span>(input)</span></code></pre></div></div>
</section>
</section>
<section id="torch" class="level3">
<h3 class="anchored" data-anchor-id="torch"><code>torch</code></h3>
<section id="lstm-non-stacked-architecture" class="level4">
<h4 class="anchored" data-anchor-id="lstm-non-stacked-architecture">LSTM (non-stacked architecture)</h4>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb22-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(torch)</span>
<span id="cb22-2"></span>
<span id="cb22-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># batch of 3, with 4 time steps each and a single feature</span></span>
<span id="cb22-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we will specify batch_first = TRUE when creating the LSTM</span></span>
<span id="cb22-5">input <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;">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;">3</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>
<span id="cb22-6">input</span>
<span id="cb22-7"></span>
<span id="cb22-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default args</span></span>
<span id="cb22-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return shape = (batch_size, units)</span></span>
<span id="cb22-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span></span>
<span id="cb22-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># note: there is an additional argument num_layers that we could use to specify a stacked LSTM - effectively composing two LSTM modules</span></span>
<span id="cb22-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default for num_layers is 1 though </span></span>
<span id="cb22-13">lstm <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_lstm</span>(</span>
<span id="cb22-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">input_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># number of input features</span></span>
<span id="cb22-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hidden_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># number of hidden (and output!) features</span></span>
<span id="cb22-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">batch_first =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># for easy comparability with Keras</span></span>
<span id="cb22-17">)</span>
<span id="cb22-18"></span>
<span id="cb22-19"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nn_init_constant_</span>(lstm<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>weight_ih_l1, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb22-20"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nn_init_constant_</span>(lstm<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>weight_hh_l1, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb22-21"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nn_init_constant_</span>(lstm<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>bias_ih_l1, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb22-22"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nn_init_constant_</span>(lstm<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>bias_hh_l1, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb22-23"></span>
<span id="cb22-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># returns a list of length 2, namely</span></span>
<span id="cb22-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   - outputs, of shape (batch_size, time steps, hidden_size) - given we specified batch_first</span></span>
<span id="cb22-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#       Note 1: If this is a stacked LSTM, these are the outputs from the last layer only.</span></span>
<span id="cb22-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#               For our current purpose, this is irrelevant, as we're restricting ourselves to single-layer LSTMs.</span></span>
<span id="cb22-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#       Note 2: hidden_size here is equivalent to units in Keras - both specify number of features</span></span>
<span id="cb22-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#  - list of:</span></span>
<span id="cb22-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#    - hidden state for the last time step, of shape (num_layers, batch_size, hidden_size)</span></span>
<span id="cb22-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#    - cell state for the last time step, of shape (num_layers, batch_size, hidden_size)</span></span>
<span id="cb22-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#      Note 3: For a single-layer LSTM, the hidden states are already provided in the first list item.</span></span>
<span id="cb22-33"></span>
<span id="cb22-34"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lstm</span>(input)</span></code></pre></div></div>
</section>
<section id="gru-non-stacked-architecture" class="level4">
<h4 class="anchored" data-anchor-id="gru-non-stacked-architecture">GRU (non-stacked architecture)</h4>
<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"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default args</span></span>
<span id="cb23-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># return shape = (batch_size, units)</span></span>
<span id="cb23-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span></span>
<span id="cb23-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># note: there is an additional argument num_layers that we could use to specify a stacked GRU - effectively composing two GRU modules</span></span>
<span id="cb23-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default for num_layers is 1 though </span></span>
<span id="cb23-6">gru <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_gru</span>(</span>
<span id="cb23-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">input_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># number of input features</span></span>
<span id="cb23-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hidden_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># number of hidden (and output!) features</span></span>
<span id="cb23-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">batch_first =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># for easy comparability with Keras</span></span>
<span id="cb23-10">)</span>
<span id="cb23-11"></span>
<span id="cb23-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nn_init_constant_</span>(gru<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>weight_ih_l1, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb23-13"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nn_init_constant_</span>(gru<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>weight_hh_l1, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb23-14"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nn_init_constant_</span>(gru<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>bias_ih_l1, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb23-15"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nn_init_constant_</span>(gru<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>bias_hh_l1, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb23-16"></span>
<span id="cb23-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># returns a list of length 2, namely</span></span>
<span id="cb23-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   - outputs, of shape (batch_size, time steps, hidden_size) - given we specified batch_first</span></span>
<span id="cb23-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#       Note 1: If this is a stacked GRU, these are the outputs from the last layer only.</span></span>
<span id="cb23-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#               For our current purpose, this is irrelevant, as we're restricting ourselves to single-layer GRUs.</span></span>
<span id="cb23-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#       Note 2: hidden_size here is equivalent to units in Keras - both specify number of features</span></span>
<span id="cb23-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#  - list of:</span></span>
<span id="cb23-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#    - hidden state for the last time step, of shape (num_layers, batch_size, hidden_size)</span></span>
<span id="cb23-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#    - cell state for the last time step, of shape (num_layers, batch_size, hidden_size)</span></span>
<span id="cb23-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#       Note 3: For a single-layer GRU, these values are already provided in the first list item.</span></span>
<span id="cb23-26"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gru</span>(input)</span></code></pre></div></div>


</section>
</section>
</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>
  <guid>https://divergences.xyz/posts/torch-convlstm/</guid>
  <pubDate>Thu, 17 Dec 2020 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/torch-convlstm/preview.jpeg" medium="image" type="image/jpeg"/>
</item>
</channel>
</rss>
