<?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/</link>
<atom:link href="https://divergences.xyz/index.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>Sat, 13 Jul 2024 00:00:00 GMT</lastBuildDate>
<item>
  <title>Free will? The “Sapolsky-Mitchell debate”</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/free-will-sapolsky-mitchell/</link>
  <description><![CDATA[ 






<div class="epigraph">
<blockquote class="blockquote">
<p>Oh, that worry, that your prizes will feel empty. –<br>
Robert Sapolsky</p>
</blockquote>
</div>
<p>True, that title’s been meant to let you know what this is about, and – if I’m lucky – to even make you curious. In reality, there is no such debate: not in a factual, concrete way, and not with regard to content/substance either. Rather, there are two books, <a href="https://mitpressbookstore.mit.edu/book/9780525560975">Determined – The Science of Life without Free Will</a> by Robert Sapolsky, author of <em>Behave</em>, and <a href="https://press.princeton.edu/books/hardcover/9780691226231/free-agents">Free Agents</a> by Kevin Mitchell, author of <em>Innate</em>. For me, the link has been there ever since I listened to <a href="https://brainsciencepodcast.com/bsp/2023/213-kevin-mitchell">Brain Science Podcast episode 213</a>, featuring Kevin Mitchell and his new book. Somehow, there was mention of another book forthcoming on that topic, by Robert Sapolsky. I immediately pre-ordered the book.</p>
<p>Here, then, follow my impressions from reading both books… – although, and I may be totally wrong there, “free will”, for many people, is not, not really, a topic up for discussion. Instead, you already know the answer; it is there, deep inside you. (To be frank, that’s how it is to me.) The answer is Yes, or No, and whichever it is, it is as a matter of course. Why then this post? Because still, you might be curious whether “the other side” had any new and/or convincing arguments, and because still, you might want to learn how someone like-minded addresses open questions and implications.</p>
<section id="sapolsky" class="level2">
<h2 class="anchored" data-anchor-id="sapolsky">Sapolsky</h2>
<p>“Determined” is wonderfully written, an easier read than “Behave”. If you’ve read “Behave”, you won’t find too much new content here. But the focus is different; Sapolsky dedicates the major portion of this book striving to answer the, undoubtedly, hardest question: If there is no free will, how should we organize our living together? If no-one is responsible for their actions, no sensible meaning assignable to “self control” and “willpower”, if there is no “crime” and no “punishment”, how can we make our societies safe to live in? (As an aside, this is not the only societal question ensuing. Sapolsky doesn’t discuss this at length – it being more urgent to address the “crime question” – but: If, just like responsibility, merit dissolves into nothing, how then do we build a just, a fair, an ethical society?)</p>
<p>Why is there no free will? Because at all times, what you think, what you feel, what you consider doing depends on how your life looked before. Here Sapolsky gives a condensation of what 800-page “Behave” is dedicated to: hormonal, neurological, environmental, genetic, cultural, evolutionary factors that have shaped, every moment of your life and even when still in the womb, how your brain, your body, your ways of feeling and thinking look now.</p>
<p>Following that summarization, he addresses popular arguments, as brought up by various flavors of free-will proponents. Some are philosophical, meaning they’re profferred by professional philosophers. For example, Sapolsky cites Daniel Dennett arguing that, sure, there’s luck, or bad luck, but in the long run, it will all average out : “[…] a good runner who starts at the back of the pack, if he is really good enough to DESERVE winning, will probably have had plenty of opportunity to overcome the initial disadvantage”. (Capitalization by Sapolsky, who I imagine to have felt some complex emotion of revulsion and “cruel satisfaction” in citing this passage.) – But does luck average out? Not in the marathon, where the person starting from the back is, in some endurance sports, likely to have to wait in pile-ups, causing them to lose yet more time, and to arrive at refreshment stations already emptied from all the good stuff. And not in life either – in that regard, Sapolsky’s book is full to the brim of shocking, disillusioning, depressing examples.</p>
<p>Supplementing this “averaging out” postulate is the view, popular among philosophers as well as “normal people”, that sure, you might sense whatever strong temptation, but if so, you just have to resist – to exercise self-control, a.k.a. discipline, a.k.a. “will power”. If you’re driven to binge-eat, throw the food out; if you can’t motivate yourself to study, give yourself a kick in the ass; if you’re tempted to give up, then just don’t give up. This certainly makes for narratives more than compliant with Western, meritocracy-shaped societies. But where does that two-step process – “me no. 1”, the subject that’s driven, coerced to do (or let be) something, and “me no. 2”, the boss who orders and forbids – come from? If your past determines what you feel and think now, then certainly it, too, decides over access to willpower now.</p>
<p>Unsurprisingly, Sapolsky does not seem to be much impressed by arguments like this. What about a second line of arguments, based on results from the natural sciences (first and foremost, from physics, the science that keeps us all in awe and subjection)? There are three fields frequently advertised as exhibiting indeterminism, and thus – it is said – to enable free will, and to each of those Sapolsky dedicates two chapters: one giving a readable, lucid introduction, and one, discussing the claim that from the quantum (right…) of indeterminacy they introduce, free will does emerge.</p>
<p>I won’t lie and admit upfront that in all three cases, either I find his respective arguments very convincing, or I’d want to put things even more sharply, or both.</p>
<p>First, there is the fascinating subject of chaos theory. But magical though its results may look to us humans, all it implies is unpredictability – not randomness, however, not indeterminism. Chaos theory is all about how, under certain circumstances, deterministic processes can produce completely different results. All that’s needed are incredibly small differences in initial conditions, i.e., the starting state. In practice this means – seeing how we never can determine a starting state to 100% precision - that no reliable forecast is possible. But that’s very different from randomness.</p>
<p>Second, there’s complexity science, with its impressive demonstrations that out of very simple building blocks, complex, organized behavior can emerge. But here again, we struggle to demonstrate a sensible connection to free will. Not only are the building blocks – neurons – anything but simple. Also, the emergent behavior – ant foraging, say, or network layout – has nothing to do with free will. Concretely, the individua – single ant, network node - aren’t acquiring free will (nor do they gain in agency, to use the term figuring prominently in Mitchell’s text).</p>
<p>Finally, and most importantly, there’s quantum physics. Most importantly, because this is the field whose theories and findings have inspired people, physicists and non-physicists alike, to the wildest fantasies. (I’ll talk a little bit about this when we get to Mitchell, in a minute.) The fundamental problem is very down-to-earth, though. Whatever tiny structure is claimed to possess quantum-like indeterminacy, we need to show how from that layer, bigger structures arise that, in the end, produce what we call free will. Sapolsky calls it the bubbling-up, and of this, it looks like there does not exist any even half-way plausible account.</p>
</section>
<section id="mitchell" class="level2">
<h2 class="anchored" data-anchor-id="mitchell">Mitchell</h2>
<p>A convincing story of that bubbling up, that’s exactly what Mitchell is trying to present. He presents a detailed account of how, with the very first appearance of life, something like agency came into being, and how from there, it continued to evolve. It’s a fascinating read, especially as regards the very early stages. To me, at least, it is; I’ve never thought about very simple organisms – single-celled organisms! – in that way.</p>
<p>Mitchell explains how life itself is intimately linked to agency: No life is possible without continuous work being done to keep an “inner” separate from an “outer”.</p>
<p>In consequence, to not go extinct, organisms have to evolve in the direction of – call it however you want: agency (in the sense of actively shaping one’s fate), intelligent decision-making, creative adaptation… I find this super convincing - it’s just that, employing words like “intelligent” or “creative”, I’m using the language I’m accustomed to think in. Just like I use them for describing my own behavior, I can use them referring to any living organism’s actions. (Yes, I think that there’s no more “truth” in using these terms for humans than for other creatures. To me, it’s totally fine to use them, though, as long as I’m conscious of my just utilizing some form of agreed-upon linguistic code here.)</p>
<p>End of “irresistible aside”, back to Mitchell. I like the agency story, but I fail to see how from – gradual, as he himself demonstrates so nicely! – increase in complexity might arise what we call free will. In fact, Mitchell dedicates several chapters to elaborating this link. The main role is played by quantum physics. Mitchell cites prominent contemporary physicists who have built whole high-level (and quite audacious, I’d say, sometimes) world views on quantum-scale indeterminacy. For example (citing Mitchell):</p>
<blockquote class="blockquote">
<p>“Gisin and del Santo argue that, beyond some decimal point, the numbers describing the physical parameters of classical systems in the future simply become indefinite. It’s not just that we don’t know what they are—the universe also doesn’t know what they are, not yet. The numbers are not given with infinite precision all at once; instead, they evolve as processes through time. Again, it is through interaction that these parameters take on a definite value, paralleling the “collapse” of quantum possibilities. It’s not that these parameters can take any value; they’re not completely random. They are constrained, often tightly, by the physics of the system in question —they are, however, not completely constrained.”</p>
</blockquote>
<p>This, to me, reads, in a way, as “indefinite” as the future parameters are posited to be. What does Mitchell make of it?</p>
<blockquote class="blockquote">
<p>“Indeed, the behavior of everything in the universe would have been predetermined from the dawn of time, including me writing this sentence and you reading it. In my view, this claim is absurd on its face. That is not an argument for whether it’s true or not but just an observation that this claim jars so bracingly with our actual experience of the world that we should be strongly suspicious of it. […] There is only one future open, with no possibilities, no deciding or acting, no purposiveness, no mattering, no trying, no goals, or functions—it would be literally a meaningless universe.”</p>
</blockquote>
<p>I’m sorry, but all I can think reading this is “wishful thinking”. We don’t want to live in a meaningless universe. But meaning is something we construct, or fail to construct, in our lives, not something outside.</p>
<p>More on the “hard facts side”, even the big bang jumps in to help:</p>
<blockquote class="blockquote">
<p>“Indeed, the observed organization of matter in the universe depended on the presence of quantum fluctuations in a phase of “cosmic inflation” that preceded the “hot” Big Bang. As the universe was rapidly expanding, it was initially a homogeneous field of energy. Without random quantum fluctuations that broke up this symmetrical field, matter and energy would have been so evenly distributed that pretty much nothing would have happened. Instead, these tiny blips introduced enough inhomogeneity into gravitational fields that galaxies and stars and planets could form.“</p>
</blockquote>
<p>I can say absolutely nothing about the facts, but again, I have problems following Mitchell’s conclusions:</p>
<blockquote class="blockquote">
<p>“No, the really crucial point is that the introduction of chance undercuts necessity’s monopoly on causation. The low-level physical details and forces are not causally comprehensive: they are not sufficient to determine how a system will evolve from state to state. This opens the door for higher-level features to have some causal influence in determining which way the physical system will evolve. This influence is exerted by establishing contextual constraints: in other words, the way the system is organized can also do some causal work. In the brain, that organization embodies knowledge, beliefs, goals, and motivations—our reasons for doing things. This means some things are driven neither by necessity nor by chance; instead, they are up to us.”</p>
</blockquote>
<p>“This opens the door…” – does it? How exactly do higher-level features come to exert causal influence?</p>
<p>Wrapping up,- - when Mitchell cites Bergson, his concept of durations, I am, emotionally, all on his side: I like that concept a lot, I find it totally emotionally attractive. But still, I think it’s a description of how our reality feels to us. If you don’t want to make statements about an external reality (and I’m lucky there; I don’t want to) that’s fine, but otherwise you somehow have to, in Sapolsky’s words, make that “bubbling up” plausible.</p>
<p>Speaking of Sapolsky – I think that as a proponent of free will, you also have to explain of how bubbling up, if it’s successful, circumvents his account of how the past – your past, immediate, recent and less recent; your environment, starting from when in the womb; and finally, evolution, both cultural and biological – make up what you are now.</p>
<div class="photo-credit">
<p>Foto von <a href="https://unsplash.com/de/@timschmidbauer?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Tim Schmidbauer</a> auf <a href="https://unsplash.com/de/fotos/blauer-und-gruner-pfau-in-nahaufnahmen-1XBPTR08SJA?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">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>Books</category>
  <category>Philosophy (sort of)</category>
  <guid>https://divergences.xyz/posts/free-will-sapolsky-mitchell/</guid>
  <pubDate>Sat, 13 Jul 2024 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/free-will-sapolsky-mitchell/tim-schmidbauer-1XBPTR08SJA-unsplash.jpg" medium="image" type="image/jpeg"/>
</item>
<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>A book I’d say everyone should read if such were a kind of thing I’d say</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/solms_consciousness/</link>
  <description><![CDATA[ 






<p>A few years ago, I found myself wanting to learn and, maybe, try to make up my mind, about a topic I – surprisingly – never had given much thought to, before: consciousness.</p>
<p>However, having read around some (first and foremost, Blackmore &amp; Troscianko’s <em>Consciousness: An Introduction</em>, which I liked a lot), I certainly had <em>not</em> made up my mind. All I knew was that, certainly, I would not want to be a dualist; that the dominant neuroscience-guided theories felt simplistic; that I wasn’t convinced quantum physics were all we need; and finally, that Daniel Dennett’s <em>multiple drafts</em> formulation seemed most in concordance with my views. Otherwise though, there seemed to be a <em>Rashomon</em>-like touch to the topic: We’d be confronted with a set of parallel narratives – just that, here, it wasn’t too clear they were even trying to tell the same story.</p>
<p>In most cases, though, at least I got an impression of the kind of story they wanted to tell. With one exception. Before I came across Blackmore &amp; Troscianko, I did the obvious thing: look up “consciousness” in the Stanford Encyclopedia of Philosophy. And there they were: the mysterious <em>qualia. (Very</em> roughly, the “<em>what it is like</em>”s of perceiving something.<sup>1</sup>) Now perception, to me, is of high interest; and it is something we know a lot about, on various levels. But that question: What is it like to see “red”? What is it like <em>to</em> <em>see</em>? – What does it matter? The whole topic of qualia seemed utterly unimportant.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;They have their own Stanford Encyclopedia entry: https://plato.stanford.edu/entries/qualia/.</p></div></div><p>But that is not something you say out loud, is it? When centuries of philosophical tradition speak otherwise.</p>
<section id="intrepid" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="intrepid">Intrepid</h3>
<p>Now comes Mark Solms and, based on a solid foundation of brain science research, does away with the thing all in one sweep. Not at all impressed by scholarly authority, Solms argues with what I can’t call by any other name than <em>common sense</em>.</p>
<p>Just like other modes of perception, vision works without consciousness, without us being aware of what we see. It does not matter what I “feel” when I see red; all that matters is that I react in a situationally adequate way. (Depending on the circumstances, the correct thing to do may be to pick the raspberry, or to stop in front of the traffic light; but in neither case would it be fruitful for me to halt and reflect on the redness of the thing in question.)</p>
<p>Wait – let me repeat that phrase: <em>what I feel when</em> […]. In that phrase, the magical term has made its appearance. This is the narrative, the intrepid narrative, Solms is telling: <em>Consciousness</em> equals <em>awareness</em> equals <em>feeling</em>.</p>
<p><em>Why</em>?</p>
<p>The first equality, linking consciousness and awareness, would not appear that “far-fetched”, from a more mainstream angle of view. And the essential thing about <em>feelings</em> is that <em>this their whole point</em>: <em>for us to be aware of them</em>.</p>
<p>For Solms, feelings don’t start with the dramatic, the intricate, or the sublime. Hunger is a feeling; being hypothermic is; both of them of different type, but comparable in function to the primary emotions fear, rage, panic/grief, seeking, play, and care<sup>2</sup>. What all feelings have in common is that they guide our behavior. With “primitive” emotions such as thirst and freezing, the behavior they induce may directly be related to survival; “higher” emotions help in, as Solms nicely puts it, “feeling our way” through the vast (if left unfiltered) space of behavioral options opening up at any moment.</p>
<div class="no-row-height column-margin column-container"><div id="fn2"><p><sup>2</sup>&nbsp;This is the taxonomy of primary emotions embraced by Solms. It is mainly due to the work of Jaak Panksepp (see, e.g., Panksepp &amp; Biven, The Archeology of Mind).</p></div></div><p>In a nutshell, thus, the argumentation goes like this: How can a feeling guide me if I’m not aware? The hunger I don’t feel won’t make me eat.</p>
<p>This, it seems to me, is the first great achievement of Solms’ common sense: Forget about the redness of red and the blueness of blue; instead think how it feels to be <em>feeling</em>. I <em>feel</em>, therefore I’m conscious.</p>
<p>The second achievement of common sense, however, is the principal reason I am writing this text; and it is of yet greater importance. Before we get to it, one last remark.</p>
<p>Above, I was already suggesting that, with the topic of consciousness, we may sometimes feel like we’re lost in a cobweb of narratives, with different nodes in that graph more or less densely connected, more or less compatible, more or less sharing a common language. And really it seems to me, now, that this is largely a matter of language; of the phrases we use, the economies of neural effort we derive from following the trodden paths of learned linguistic habits. When Solms challenges the neocortex-centric, analytic, higher-order, reflective view of consciousness, he does not just fight against the very solid windmills of anthropocentrism (the next thing I’ll be talking about). He also has to counteract the effects of highly established, utterly present-in-our-minds expressions like “stream of consciousness”. In fact, when first making my way through Blackmore &amp; Troscianko’s labyrinth of viewpoints, I found myself trying to relate those to familiar concepts like this one. Stream of consciousness, that perpetual narrator’s voice … And maybe even more than people with visual imagination, those with aphantasia<sup>3</sup>, who can’t see a thing “in their mind’s eye” – do have a mind’s <em>ear</em>, and may not ever know how to turn off that voice. These stories they can’t help listening to, be they more like comments or like dissections, seem to suggest that consciousness were “on top of”, were <em>more</em> than what really there is. –</p>
<div class="no-row-height column-margin column-container"><div id="fn3"><p><sup>3</sup>&nbsp;As of this writing, the only book I know on this topic is Alan Kendle’s Aphantasia: Experiences, Perceptions, and Insights.</p></div></div><p>Now, on to where common sense gets revolutionary.</p>
</section>
<section id="the-next-and-desperately-needed-copernican-turn" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="the-next-and-desperately-needed-copernican-turn">The next (and desperately needed!) Copernican turn</h3>
<p>Without further ado, let me spell it out. There are kids, born without a cortex<sup>4</sup>, who, in situations, look like they’re enjoying themselves, have fun, feel happy about something. (Probably they also feel bad, in other situations, but these are not the situations Solms relates in this book.) If they look like they enjoy “it”, and joy is a feeling, <em>why would we doubt that they’re feeling something</em>? Isn’t this common sense, as well: If someone, a person – let me go further already – a <em>being</em> looks like they sense, feel, experience, what justification do we have to claim they are <em>just</em> <em>looking it</em>?</p>
<div class="no-row-height column-margin column-container"><div id="fn4"><p><sup>4</sup>&nbsp;This condition is called hydranencephaly.</p></div></div><p>Concretely, what Solms says is that at least all mammals, but to different degrees, also other classes share the brain structures associated with feeling. <em>Various degrees</em>: There is no on-or-off, no conscious-yes-or-no. If your cat purrs as you stroke her, would you assume she doesn’t have fun? If I see a crow monitoring the grasslands, should I think there’s no intent there, no purpose? If the flower aligns itself with the sun –</p>
<p>We don’t know. All we can surmise is that there is a continuum; of intent, of experience, of feeling. Maybe there also is an other-ness, something we humans don’t have concepts for. In any case, what follows from this is that we have no right to impose ourselves on nature, the way we’ve been doing for millennia. Not only is there no creation, – man (or woman) certainly is not its culmination.</p>
<p>And this is why I would like to write: Everyone should read this book. It is time, high time, for the next Copernican revolution, after the planetary one and the evolutionary one. We could call it the revolution of neuroscience, grounded as it is in brain science results, but I’d rather stay with what makes me think it’s so over-due: the revolution of common sense.</p>
</section>
<section id="a-necessary-remark-and-two-quick-comments" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="a-necessary-remark-and-two-quick-comments">A necessary remark, and two quick comments</h3>
<p>Before I end, I want to make sure I haven’t, through this text, provoked a misunderstanding that might keep you from reading the book. I am not a neuroscientist, and in this write-up, I have distilled what in my view is the book’s main message. But this is not a book of groundless opinions, and less even, of politics or zealotism. The (subcortical) structures that create consciousness <em>do</em> have names; the book has fifty pages of end notes linking to studies underpinning the conclusions drawn. You don’t have to believe my condensation – just read the book and see for yourself.</p>
<p>Finally, to make clear that this is about the book’s constitutive message, and not a product of all-embracing exaltation, let me name the two threads of argumentation I do not agree with.</p>
<p>First – and here, again, I have to stress that I am not a neuroscientist, and thus, can only speak from what is, it seems to me, an instance of common sense as well: Solms’ espousal of Karl Friston and his framework seems to go, well, pretty far<sup>5</sup>. Nothing more convincing than the view that the brain works by constantly updating its predictions; you don’t have to be an expert in Bayes to believe that. But from there to the precise equations postulated by Friston it is a long way… (Truth be told, neither can I get over what I read in the Guardian, in 2020, when Covid was on its first steep surge … <a href="https://www.theguardian.com/world/2020/may/31/covid-19-expert-karl-friston-germany-may-have-more-immunological-dark-matter">See for yourself</a>.)</p>
<div class="no-row-height column-margin column-container"><div id="fn5"><p><sup>5</sup>&nbsp;For details on the theory, see also the paper he wrote with Friston, <a href="https://discovery.ucl.ac.uk/id/eprint/10057681/1/Friston_Paper.pdf">How and Why Consciousness Arises: Some Considerations from Physics and Physiology</a>.</p></div></div><p>Secondly, I cannot <em>not</em> mention I have significant problems with how the book ends: the announcement that Solms and his group will try to engineer artificial consciousness. The arguments in favor are all too familiar: We need to do this to prove our scientific hypothesis; if we don’t do it, someone else will; we’ll patent the whole process; and as soon as it’s done (hypothesis confirmed), we’ll shut it off. Oh brave new world …</p>
<p>Those are comments I felt I needed to make, but neither takes away anything from the fact that, yes, this book is eminently important, and that yes, we, human beings, animals, earth all need that revolution to happen.</p>
<div class="photo-credit">
<p>Photo by <a href="https://unsplash.com/@hansjurgen007?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Hans-Jurgen Mager</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>Books</category>
  <category>Philosophy (sort of)</category>
  <guid>https://divergences.xyz/posts/solms_consciousness/</guid>
  <pubDate>Mon, 01 Aug 2022 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/solms_consciousness/polar.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>
<item>
  <title>AI ethics is not an optimization problem</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/ai_ethics_optimization_problem/</link>
  <description><![CDATA[ 






<section id="why-this-post-why-now" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="why-this-post-why-now">Why this post, why now</h3>
<p>When you work in a field as intellectually-satisfying, challenging and inspiring as software design for machine learning, it is easy to focus on the technical, keeping out of sight the broader context. Some would even say it is required. How else can you keep up the necessary level of concentration?</p>
<p>But even for someone who hasn’t been in the field that long, it is evident that with every year that passes, with deep-learning-based technologies<sup>1</sup> progressing faster and faster over ever-shorter time spans, misuse of these technologies has increased as well, not just in selected countries but all over the world.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;For brevity, I’ll be subsuming deep learning and other contemporary machine learning methods under <em>AI</em>, following common(-ish) usage.</p></div><div id="fn2"><p><sup>2</sup>&nbsp;&nbsp;I can’t hope to express this better than Maciej Cegłowski did <a href="https://idlewords.com/talks/superintelligence.htm">here</a>, so I won’t elaborate on that topic any further.</p></div></div><p>To eliminate one possible misunderstanding right from the start: When I’m talking about “faster and faster” progress, I’m not over-hyping things. I’m far from thinking that <em>AI</em> is close to “solving” problems like language understanding, concept learning and their likes — the kind of problems <a href="https://arxiv.org/abs/2002.06177">some would argue hybrid models were needed for</a>. The thing is that it doesn’t matter. It is exactly the kinds of things <em>AI</em> <em>does</em> do so well that lend themselves to misuse. It is people we should be afraid of, not machines<sup>2</sup>.</p>
<p>Back to the why. Over time, it increasingly appeared to me that writing regularly about <em>AI</em> in a technical way, but <em>not ever</em> writing about its misuse, was ethically questionable in itself. However, I also became increasingly conscious of the fact that with a topic like this, once you enter the political realm — and that we <em>have to</em> is the main point of this text –, likelihood rises that people will disagree, or worse, feel offended for reasons not anticipated by the writer. Some will find this too radical, some not radical (<em>explicit</em>) enough.</p>
<p>But when the alternative is to stay silent, it seems better to try and do one’s best.</p>
<p>Let’s start with two terms whose recent rise in popularity matches that of <em>AI</em> as a whole: bias and fairness.</p>
</section>
<section id="but-we-are-doing-a-lot-to-improve-fairness-and-remove-bias-arent-we" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="but-we-are-doing-a-lot-to-improve-fairness-and-remove-bias-arent-we">But we are doing a lot to improve fairness and remove bias, aren’t&nbsp;we?</h3>
<p>Search for “algorithmic fairness”, “deep learning fairness” or something similar, and you’ll find lots of papers, tools, guidelines&nbsp;… more than you have time to read. And bias: Haven’t we all heard about image recognition models failing on black people, women, and black women in particular; about machine translation incorporating gender stereotypes; about search engine results reflecting racism and discrimination? Given enough media attention, the respective algorithms get “fixed”; what’s more, we may also safely assume that overall, researchers developing new models will probe for these exact kinds of failures. So as a community, we <em>do</em> care about bias, don’t we?</p>
<p>Re: fairness. It’s true that there are many attempts to increase algorithmic fairness. But as Ben Green, who I’ll cite a lot in this text, <a href="https://dl.acm.org/doi/10.1145/3351095.3372840">points out</a>, most of this work does so via rigid formalization, as if fairness — its <a href="https://www.youtube.com/watch?v=jIXIuYdnyyk">conflicting definitions</a> notwithstanding — were an objective quantity, a metric that could be optimized just like the metrics we usually optimize in machine learning.</p>
<p>Assume we were willing to stay in the formalist frame. Even then there is no satisfying solution to this optimization problem. Take the perspective of <em>group fairness</em>, where a fair algorithm is one that results in equal outcomes between groups. <a href="https://doi.org/10.1089/big.2016.0047">Chouldechova</a> then shows that when an algorithm achieves predictive parity (a.k.a. precision), but prevalence differs between groups, it is not possible to also have <em>both</em> equal false positive <em>and</em> equal false negative rates.</p>
<p>That said, here I mainly want to focus on why pure formalism is not enough.</p>
<p>What with bias? That datasets can be (or rather: are) biased is hardly something anyone working in <em>AI</em> would object to. What amount of bias is being admitted to in other components/stages of the machine learning process varies between people.</p>
<p><a href="http://arxiv.org/abs/1901.10002">Harini and Guttag</a> map sources of bias to the model development and deployment process. Forms of bias they distinguish include representation (think: dataset), measurement (think: metrics<sup>3</sup>), aggregation (think: model<sup>4</sup>), and evaluation (similar to the two preceding ones, but at test time) bias. The paper is written in a technical style; in fact, there is even a diagram where the authors attempt to formalize the process in a mathematical way. (Personally, I find it hard to see what value is added by this diagram; its existence can probably best be explained by [perceived] requirements of the genre, namely, <em>research paper</em>.)</p>
<div class="no-row-height column-margin column-container"><div id="fn3"><p><sup>3</sup>&nbsp;More on that below. Metrics used in machine learning mostly are proxies for things we really care about; there are lots of ways this can go wrong.</p></div><div id="fn4"><p><sup>4</sup>&nbsp;Meaning, a one-model-fits-all approach puts some groups at a disadvantage.</p></div></div><p>Now, so far I’ve left out the remaining two sources of bias they name. Mapped to the end of the process is deployment bias, when a system “is used or interpreted in inappropriate ways”. That raises a question. Is this about the <em>end</em> of a process, or is what happens now a <em>completely different process</em> (or: processes)? For an in-house data scientist, it may well be the end of a process; for a scientist, or for a consultant, it is not. Once a scientist has developed and published a model, they have no control over who uses it and how. From the collection of humankind’s empirical truths: Once a technology exists, and it is useful, it <em>will be used</em>.</p>
<p>Things get further out of control at the other side of the timeline. Here we have <a href="http://arxiv.org/abs/1901.10002">historical bias</a>:</p>
<blockquote class="blockquote">
<p><em>Historical bias arises when there is a misalignment between the world as it is and the values or objectives to be encoded and propagated in a model. It is a normative concern with the state of the world, and exists even given perfect sampling and feature selection.</em></p>
</blockquote>
<p>I’ll get back to why this is called “historical bias” later; the definition is a lot broader though. Now definitely we’re beyond the realm of formalization: We’re entering the realm of normativity, of ways of viewing the world, of ethics.</p>
<p>Please don’t get me wrong. I’m not criticizing the paper; in fact, it provides a useful categorization that may be used as a “check list” by practitioners. But given the formalist style, a reader is likely to focus on the readily-formalizable parts. It is then easy to dismiss the two others as not really being relevant to one’s work, and certainly not something one could exert influence on. (I’ll get back to that later.)</p>
<p>One little aside before we leave formalism: I do believe that a lot of the formalist work on <em>AI</em> ethics is done in good intent; however, work in this area also benefits organizations that undertake it. Citing <a href="https://tomslee.github.io/publication/oup_private_sector_ai/">Tom Slee</a>,</p>
<blockquote class="blockquote">
<p><em>Standards set public benchmarks and provide protection from future accusations. Auditable criteria incorporated into product development and release processes can confirm compliance. There are also financial incentives to adopt a technical approach: standards that demand expertise and investment create barriers to entry by smaller firms, just as risk management regulations create barriers to entry in the financial and healthcare industries.</em></p>
</blockquote>
</section>
<section id="not-everything-in-life-is-an-optimization-problem" class="level3">
<h3 class="anchored" data-anchor-id="not-everything-in-life-is-an-optimization-problem">Not everything in life is an optimization problem</h3>
<p>Stephen Boyd, who teaches convex optimization at Stanford, is said to often start the<a href="https://www.youtube.com/watch?v=McLq1hEq3UY">introductory lecture</a> with the phrase “everything is an optimization problem”. It sounds intriguing at first; certainly a lot of things in my life can be thought of like that. It may become awkward once you start to compare, say, time spent with loved ones and time spent working; it becomes completely unfeasible when comparing <em>across people</em>.</p>
<p>We saw that even under formalist thinking, there is no impartial way to optimize for fairness. But it’s not just about choosing between different types of fairness. How do you weigh fairness against stakeholder interests? No algorithm will be deployed that does not serve an organization’s purpose.</p>
<p>There is thus an intimate link between <em>metrics</em>, <em>objectives</em> and <em>power</em>.</p>
</section>
<section id="metrics-and-power" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="metrics-and-power">Metrics and&nbsp;power</h3>
<p>Even though terminologically, in deep learning, we distinguish between optimization and metrics, the metrics really <a href="http://arxiv.org/abs/2002.08512">are what we are optimizing for</a>: Goodhart’s law — <em>When a measure becomes a target, it ceases to be a good measure</em><sup>5</sup>— does not seem to apply. Still, they deserve the same questioning and inspection as metrics in other contexts.</p>
<div class="no-row-height column-margin column-container"><div id="fn5"><p><sup>5</sup>&nbsp;cited after <a href="https://arxiv.org/abs/2002.08512">Thomas and Uminsky</a></p></div></div><p>In AI as elsewhere, optimization objectives are proxies; they “stand in” for the thing we’re really interested in. That proxying process could <a href="http://arxiv.org/abs/1803.04585">fail in many ways</a>, but failure is not the only applicable category to think in here.</p>
<p>For one, objectives are chosen according to the dominant paradigm. <a href="http://arxiv.org/abs/1912.01172">Dotan and Milli</a> show how a technology’s perceived success feeds back into the criteria used to evaluate other technologies (as well as future instances of itself). Imagine a world where models were ranked not just for classification accuracy, but also for, say, climate friendliness, robustness to adversarial attacks, or reliable quantification of uncertainty.</p>
<p>Objectives thus do not emerge out of nothing. That they reflect paradigms may still sound abstract; that they serve existing power structures less so. Power structures are complex; they reflect more than just who “has the say”. As pointed out in various “classics” of critical race theory, intersectionalist feminism and related areas<sup>6</sup>, we should ask ourselves: <em>Who profits?</em></p>
<div class="no-row-height column-margin column-container"><div id="fn6"><p><sup>6</sup>&nbsp;see e.g., <a href="https://data-feminism.mitpress.mit.edu/">DataFeminism</a> and <a href="https://www.ruhabenjamin.com/race-after-technology">RaceAfterTechnology</a>.</p></div></div><p>This is an all but simple question, comparable in difficulty, it seems to me, to the request that we question the unspoken premises that underlie our theories. The main point about such premises is that we aren’t aware of them: If we were, we could have stated them explicitly. Similarly, if I noticed I was profiting from someone, I would — hopefully — take some action. Hard as the task may be, though, we have to do our best.</p>
<p>If it’s hard to see how one is privileged, can’t we just be neutral? Objective? Isn’t this getting too <em>political</em>?</p>
</section>
<section id="there-is-no-objectivity-and-all-is-politics" class="level3">
<h3 class="anchored" data-anchor-id="there-is-no-objectivity-and-all-is-politics">There is no objectivity, and all is&nbsp;politics</h3>
<p>To some people, the assertion that objectivity cannot exist is too self-evident to require much dwelling on. Are numbers objective? Maybe in some formal way; but once I use them <em>in communication</em>, they convey a message, independently of my intention.&nbsp;</p>
<p>Let’s say I want to convey the fact, taken from <a href="https://www.ipcc.ch/sr15/chapter/spm/">the IPCC website</a>, that between 2030 and 2052, global warming is likely to reach 1.5°C. Let’s also assume that I look up the pre-industrial average for the place where I happen to live (15°C, say), and that I want to show off my impressive meteorological knowledge. Thus I say, “… so here, guys, temperature will rise from 288.15 Kelvin to 289.65 Kelvin, on average”. This surely is an objective statement. But what if the people I’m talking to don’t know that — even though the absolute values, when expressed in Kelvin, are so much higher than when expressed in degrees Celsius — the relative differences are the same? They might get the impression, totally unintended, that not much warming is going to happen.</p>
<p>If even numbers, once used in communication, lose their objectivity, this must hold even more for anything that involves more design choices: visualizations, APIs, and, of course, written text. For visualizations, this is nicely illustrated in d’Ignazio and Klein’s <em>Data Feminism</em>.</p>
<p>That book is also an exemplary exercise in what appears like the only way to “deal with” the fact that objectivity is impossible: Trying to be as clear as possible about where we stand, who we are, what are the circumstances that have influenced our way of thinking. Of course, like with the unspoken premises and assumptions discussed above, this is not easy; in fact, it’s impossible to do in perfection. But one can try.</p>
<p>In fact, the above “Trying to be as clear as possible&nbsp;…” is deliberately ambiguous. It refers to two things: For one, to <em>me</em> striving to analyze how I’m privileged, and secondly, to me giving information to <em>others</em>. The first alone is laudable but necessarily limited; the second, as exercised by d’Ignazio and Klein, opens the door not just for better mutual understanding, but also, for feedback and learning. The person I’m talking to might lead me to insights I wouldn’t have gotten otherwise.</p>
<p>Putting things slightly differently, there is no objectivity because there’s always a context. Focus on metrics and formalization detract from that context. Green <a href="https://dl.acm.org/doi/10.1145/3351095.3372840">relates</a> an interesting parallel from American law history. Until the early twentieth century, US law was dominated by a formalist ethos. Ideally, all rules should be traceable to a small number of universal principles, derived from natural rights. Autonomy being such a right, in a famous 1905 case, the U.S. Supreme Court concluded that a law limiting the working hours of employees represented “unreasonable, unnecessary and arbitrary interference with the right and liberty of the individual to contract”. <a href="https://dl.acm.org/doi/10.1145/3351095.3372840">However</a>,</p>
<blockquote class="blockquote">
<p><em>In his dissent, Justice Oliver Wendell Holmes argued that the Court failed to consider the context of the case, noting, “General propositions do not decide concrete cases”.</em></p>
</blockquote>
<p>Thus, the <em>context</em> matters. Autonomy is good; but it can’t be used as an excuse for exploitation.</p>
<p>The same context dependence holds in <em>AI</em>. It is always developed and deployed in a context, — a context shaped by <em>history</em>. History determines what datasets we work with; what we optimize for; <em>who</em> tells us what to optimize for. An daunting example is so-called “predictive policing”. Datasets used to train prediction algorithms incorporate a history of racial injustice: The very definition of “crime” they rely on was<a href="https://dl.acm.org/doi/10.1145/3351095.3372840">shaped by racist and classist practice</a>. The new method then perpetuates — more than that: exacerbates — the current system, creating a vicious cycle of positive feedback that makes it look like the algorithm was successful.</p>
<p>Summing up: When there is no neutrality, everything is politics. Not acting is acting. Citing <a href="https://www.benzevgreen.com/wp-content/uploads/2019/11/19-ai4sg.pdf">Green</a>,</p>
<blockquote class="blockquote">
<p><em>But efforts for reform are no more political than efforts to resist reform or even the choice simply to not act, both of which preserve existing systems.</em></p>
</blockquote>
</section>
<section id="but-im-just-an-engineer" class="level3">
<h3 class="anchored" data-anchor-id="but-im-just-an-engineer">But I’m just an&nbsp;engineer</h3>
<p>When machine learning people, or computer science people in general, are asked about their views on the societal impact of modern <em>AI</em>, an often-heard answer is: “But I’m just an engineer…”. This is completely understandable. Most of us are just tiny cogs in those big machines, wired together in a complex network, that run the world. We’re not in control; how could we be accountable?</p>
<p>For the <em>AI</em> scientist, though normally all but sitting in an “ivory tower”, it’s nevertheless the <em>ML engineers</em> who are responsible of how a model gets deployed, and to what consequences⁷. The ML engineer may delegate to the head of IT, who in turn had no choice but implement what was requested “by business”. And so on and so forth, ad infinitum.</p>
<p>That said, it is hard to come up with a moral imperative here. In the line of thinking exercised above: There is always a context. In some parts of the world, you have more choices than in others. Options vary based on race, gender, abledness, and more. Maybe you can just quit and get another job; maybe you can’t.</p>
<p>There is another — related — point though, on which I’d like to dwell a little longer.</p>
</section>
<section id="technology-optimism" class="level3">
<h3 class="anchored" data-anchor-id="technology-optimism">Technology optimism</h3>
<p>Sometimes, it’s not that the people working in AI are fully aware of, but don’t see how to counter, the harm that is being done in their field. On the contrary. They are convinced that they’re doing good. Just do a quick search for “AI for good”, and you’ll be presented with a great number of projects and initiatives. But how, actually, is decided what <em>is</em> good? Who decides? Who profits?</p>
<p>Ben Green, again, <a href="https://dl.acm.org/doi/10.1145/3351095.3372840">relates</a> an instructive example:</p>
<blockquote class="blockquote">
<p><em>USC’s Center for Artificial Intelligence in Society (CAIS) is emblematic of how computer science projects labeled as promoting “social good” can cause harm by wading into hotly contested political territory with a regressive perspective. One of the group’s projects involved deploying game theory and machine learning to predict and prevent behavior from “adversarial groups.” Although CAIS motivated the project by discussing “extremist organizations such as ISIS and Jabhat al-Nusra,” it quickly slipped into focusing on “criminal street gangs” [43]. In fact, the project’s only publication was a controversial paper that used neural networks to classify crimes in Los Angeles as gang-related [28, 41].</em></p>
</blockquote>
<p>Predictive policing, already mentioned above, can also be seen in this category. At first thought, isn’t it a good thing? Wouldn’t it be nice if we could make our world a bit more secure?</p>
<p>Phillip Rogaway, who I’ll mention again in the concluding section, talks a lot about <a href="https://eprint.iacr.org/2015/1162">how technology optimism dominates among his students</a>; he seems to be as intrigued by it as I am. Personally, I think that whether someone “intrinsically” tends to be a technology optimist or a pessimist is a persistent trait; it seems to be a matter of personality and socialization (or just call it <em>fate</em>: I don’t want to go into any nature-nurture debates here). That glass, is it half full or half empty?</p>
<p>All I could say to a hardcore technology optimist is that their utopia may be another person’s dystopia. Especially if that other person is poor, or black, or poor and a black woman&nbsp;… and so on.</p>
<p>Let me just end this section with a citation from a <a href="https://ali-alkhatib.com/blog/anthropological-intelligence">blog post</a> by Ali Alkhatib centered around the launch of the <a href="https://hai.stanford.edu/">Stanford institute for human-centered artificial intelligence (HAI)</a>. Referring to the <a href="https://hai.stanford.edu/blog/smart-interfaces-human-centered-ai">director’s accompanying blog post</a>, he writes</p>
<blockquote class="blockquote">
<p><em>James opens with a story of an office that senses you slouching, registers that you’re fatigued, intuits that your mood has shifted, and alters the ambiance accordingly to keep you alert throughout the day.</em></p>
</blockquote>
<p>You can head to Alkhatib’s post (very worth reading) and read the continuation, expanding on the scenario. But for some people, this single sentence may already be enough.</p>
</section>
<section id="and-now" class="level3">
<h3 class="anchored" data-anchor-id="and-now">And now?</h3>
<p>At some point, an author is expected to wrap up and present ideas for improvement. With most of the topics touched upon here, this is yet another intimidating task. I’ll give it a try anyway. The best synthesis I can come up with at the moment looks about like this.</p>
<p>First, some approaches filed by Green under “formalist” can still make for a good start, or rather, can constitute a set of default measures, to be taken routinely. Most prominently, these include <a href="https://ui.adsabs.harvard.edu/abs/2018arXiv180309010G">dataset</a> and <a href="https://ui.adsabs.harvard.edu/abs/2018arXiv181003993M">model</a> documentation.</p>
<p>Beyond the technical, I like the advice given in Baumer and Silberman’s <a href="https://doi.org/10.1145/1978942.1979275"><em>When the Implication is Not to Design</em></a>. If the consequences, especially on socially marginalized groups, of a technological approach are unforeseeable, think whether the problem can be solved in a “low-tech” way. By all means, do <em>not</em> start with the solution and then, go find a problem.</p>
<p>In some cases, even that may not be enough. With some goals, don’t look for alternative ways to achieve them. Sometimes the goal itself has to be questioned.</p>
<p>The same can be said for the other direction. With some technologies, there is no goal that could justify their application. This is because such a technology is <em>certain</em> to get misused. Facial recognition is one example.</p>
<p>Lastly, let me finish on a speculative note. <a href="https://eprint.iacr.org/2015/1162">Rogaway</a>, already referred to above for his comments on technology optimism, calls on his colleagues, fellow cryptographers, to devise protocols in such a way that private communication stays private, that breaches are, in plain terms, impossible. While I personally can’t think see how to port the analogy to <em>AI</em>, maybe others will be able to, drawing inspiration from his text. Until then, changes in politics and legislation seem to be the only recourse.</p>
<hr>


</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>
  <guid>https://divergences.xyz/posts/ai_ethics_optimization_problem/</guid>
  <pubDate>Tue, 22 Sep 2020 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/ai_ethics_optimization_problem/raven.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>The hard problem of privacy</title>
  <dc:creator>Sigrid Keydana</dc:creator>
  <link>https://divergences.xyz/posts/hard_problem_privacy/</link>
  <description><![CDATA[ 






<p>Why “the hard problem” of privacy? Inspired by the study of consciousness, where some authors posit a difference between a “hard” and an “easy” problem, it is tempting to talk about privacy – as perceived by machine learning (ML) people – in a similar way. The “easy” part would be technical realization; including differential privacy (see e.g.&nbsp;<a href="https://blogs.rstudio.com/tensorflow/posts/2019-12-20-differential-privacy/">Differential privacy with TensorFlow</a>) and federated learning (to be featured on the <a href="https://blogs.rstudio.com/tensorflow/">TensorFlow for R</a> blog soon). The hard part would be to have people think that privacy actually <em>matters</em>.</p>
<p>Of course, it depends on what is understood by privacy, or conversely, by surveillance. Last summer, Rachel Thomas, widely known as co-founder of <em>fast.ai</em> and founding director of the USF Center for Applied Data Ethics, wrote about <a href="https://www.fast.ai/2019/08/07/surveillance/">8 Things You Need to Know about Surveillance</a>. Everything she writes is highly important and deserves being thought about every single day. However, in a well-meaning community there should be little controversy about how justified these points are. My starting point here is different, and it, like the whole of this post, is highly subjective.</p>
<section id="soul-mates" class="level3">
<h3 class="anchored" data-anchor-id="soul-mates">Soul mates</h3>
<p>Recently, I listened to an episode of Lex Fridman’s <a href="https://www.youtube.com/watch?v=EYIKy_FM9x0&amp;list=PLrAXtmErZgOdP_8GztsuKi9nrraNbKKp4">AI Podcast</a>. Depending on one’s interest, this podcast has the most interesting guest list one could imagine: Donald Knuth, Daniel Kahneman, Gilbert Strang, Leonard Susskind, Yoshua Bengio … to name just a few. But the podcast is nice to listen to not just because of the guest list. The interviewer himself impresses by his modest, warm and deeply human ways.</p>
<p>On that backgound, I was immensely surprised when in the <a href="https://www.youtube.com/watch?v=nkWmiNRPU-c&amp;list=PLrAXtmErZgOdP_8GztsuKi9nrraNbKKp4&amp;index=7">episode with Cristos Goodrow</a>, VP of Engineering at Google and head of Search and Discovery at YouTube, Fridman not just highly extolled Youtube’s recommendation algorithm, but also expressed a “dream” that Youtube, besides similar-as-per-the-recommendation-algorithm videos, also suggested similar-as-per-the-algorithm <em>users</em> (min. 30/31):</p>
<blockquote class="blockquote">
<p>I know I’m gonna ask for things that are impossible but I would love to cluster human beings, I would love to know who has similar trajectories as me …” .</p>
</blockquote>
<p>The algorithm as a helper to find friends, or people like oneself, one would never get to meet otherwise. The most human dream ever, finding soul mates, powered by Youtube.</p>
<p>Incapable to get over my surprise, I started thinking whether I should write this post.</p>
</section>
<section id="why-arent-we-more-scared" class="level3">
<h3 class="anchored" data-anchor-id="why-arent-we-more-scared">Why aren’t we more scared?</h3>
<p>It’s not the most fruitful question, but helpless inability to understand probably starts like this, asking: Why aren’t we more scared?</p>
<p>For starters, we’re used to getting stuff recommended all the time. On Amazon, I may have bought some books that were <em>purchased by similar users</em>. (On the other hand, the overall quality of recommendations is not like I would start to feel threatened by the magic.) So, advantages jump to the eye, while disadvantages are not immediately visible.</p>
<p>Secondly, traditionally, in many cultures, we are accustomed to associate surveillance with the government, not private companies, just simply because historically, it was in fact the government, and the government only, that had the power to surveil and oppress. This is the classic picture of totalitarianism.</p>
<p>However, as described by Shoshana Zuboff in her lengthy-ish, not-without-redundancies, but immensely important book <em>The age of surveillance capitalism</em>, the modern reality – at least in the US and Europe – is less direct and less easy to see through. (Evidently, the story is different in a country like China, where a social credit system has already been established.)</p>
</section>
<section id="surveillance-capitalism-in-a-nutshell" class="level3">
<h3 class="anchored" data-anchor-id="surveillance-capitalism-in-a-nutshell">Surveillance capitalism in a nutshell</h3>
<p>Zuboff’s book recounts, in great detail and citing a lot of sources public and private, how we ended up living in a world where each of our online actions is tracked by numerous “interested parties”, where our phones send who-knows-what to whichever distant beneficiary, and where more and more of our “real-world”, physical activity is recorded by agents we may not even know of.</p>
<p>In short, the mechanism she describes is this: Companies big and small, the so-called “surveillance capitalists”, have their business based on gathering, extracting, and processing information – more than is needed to improve a product or a service. Instead, this “behavioral surplus” – in processed form – is sold to customers, which may be everything from other businesses over banks and insurance companies to intelligence agencies and governments.</p>
<p>With such a business model, <em>of course</em> a company is interested in optimizing its ML algorithms. The better the algorithms, the better the results that are sold to customers, and the better those results, the more that company is willing to pay. Online ads may look like a triviality, but they are in fact a great illustration of the principle: If our customer pays per click, and our business is “full-service” – “don’t worry about anything, we’ll place your ad for you”, then it’s in my own best interest to show ads to the most susceptible users, in the most favorable circumstances. <em>Of course</em> we’ll get creative, thinking about how we can best determine those users and moments.</p>
<p>As already hinted at above, with private companies in possession of data and inferences not publicly available, governments, intelligence services, and the military re-enter the picture. Zuboff describes how intelligence agencies craved information that was hard to obtain legally, but could be provided by companies operating in a lawless zone, always ahead of legislation. 9/11 was just a door opener; from then on, massive interlacing ensued between politics and companies in the information business, as seen for example, but not only, in the 2012 and 2016 US elections.</p>
<p>Zuboff’s book is full of interesting details, but this post is not meant to be a “best of”. Rather, I’m wondering how the extent, as well as the importance of, what’s going on could be made more salient to ML people – not just to those to whom doubt and skepticism come naturally, but to well-meaning idealists as well.</p>
</section>
<section id="why-we-should-care" class="level3">
<h3 class="anchored" data-anchor-id="why-we-should-care">Why we should care</h3>
<p>Trying to make this more salient, I can think of three things:</p>
<ul>
<li><p>Concisely stating a few facts that, important though they are in Zuboff’s argumentation, deserve being pointed out even outside the concrete contexts in which they appear in the book;</p></li>
<li><p>Calling attention, in a few lines instead of a time-consuming narrative, to what has <em>already happened</em>, in the “real world”, in the very short time span since those new dynamics took off; and (the most impressive to me, personally)</p></li>
<li><p>Relating, by <em>citation</em>, the actual “utopias” behind the reality.</p></li>
</ul>
</section>
<section id="a-few-things-to-consider" class="level3">
<h3 class="anchored" data-anchor-id="a-few-things-to-consider">(1) A few things to consider</h3>
<p>A common way to think about this is: We’re using a free service (like a search engine), we have to pay somehow, so we’re paying with our data. But – data is collected and processed by products we <em>buy</em>, as well, be it phones, wearables or household appliances.</p>
<p>Also, it is not like we get to <em>decide</em> whether and if so, how much privacy we’re willing to give up in exchange for a service. As often noted, no-one has time to read through all the “privacy policies” we’re bombarded with; and sometimes we’re just told “you can disable this; but then feature x [[[some vital feature, probably the one we bought it for]]] can’t be guaranteed to be working anymore”.</p>
</section>
<section id="not-just-online" class="level3">
<h3 class="anchored" data-anchor-id="not-just-online">(2) Not just online</h3>
<p>Even to people who spend most of their day online, being tracked on the web may still seem like a minor inconvenience. Of course, through our smartphones, we carry the web with us. Evidently, depending on what apps one has installed, one may receive friendly “invitations” to, say, pay a visit to McDonald’s just as one passes there on one’s lunchtime run. (Or maybe not, depending on whether the type of ongoing activity is already being taken into account.)</p>
<p>Sure, such notifications disrupt the flow of whatever one’s doing. But still: For some of us, it’s easy to think that <em>who cares? I don’t have those kinds of apps installed anyway</em>. Put more generally: Yes, all this online tracking is annoying. But as long as all that happens is me seeing more “targeted” ads, why bother?</p>
<p>The recent years have seen a consistent and speedy extension of tracking in the real world.</p>
<p>There was <a href="https://en.wikipedia.org/wiki/Google_Street_View">Google Street View</a>, filming the outdoors, a space once thought of as ephemeral, and subsequent attempts, by various actors, to map public <em>interiors</em>.</p>
<p>There was <a href="https://en.wikipedia.org/wiki/Google_Glass">Google glass</a>, allowing to video- and audiotape people without their knowledge.</p>
<p>Then, there are all kinds of wearables, also able to record not just the wearer’s, but bystanders’ data. The wearers themselves must be aware that <em>some</em> data is collected (why else buy the thing?), but don’t necessarily know how it’s being made use of, and what other data may be collected.</p>
<p>Then, there are all kinds of IoT devices: TVs, vacuum cleaners, thermostats… Again, they are bought for a reason; but no-one can expect a TV to record what you’re saying, or a vacuum cleaner to transmit back your floor plan.</p>
<p>Then – and this makes for a perfect transition to the last section, “utopias” – there is <em>smart city</em>: not a utopia, but actual reality, <a href="https://en.wikipedia.org/wiki/Sidewalk_Toronto">already being implemented in Toronto</a>. Here is an excerpt from an <a href="https://www.youtube.com/watch?v=JXN9QHHD8eA">interview with CEO Dan Doctoroff</a>:</p>
<blockquote class="blockquote">
<p>In effect, what we’re doing is replicating the digital experience in physical space … So ubiquitous connectivity; incredible computing power including artificial intelligence and machine learning; the ability to display data; sensing, including cameras and location data as well as other specialized sensors… We fund it all… through a very novel advertising model… We can actually then target ads to people in proximity, and then obviously over time track them through things like beacons and location services as well as their browsing activity.</p>
</blockquote>
<p>From these publicly made statements, it is hard to doubt what <a href="https://www.theglobeandmail.com/business/article-sidewalk-labs-document-reveals-companys-early-plans-for-data/">The Globe and Mail reported</a> re. a leaked document, the <em>yellow book</em>, containing detailed outlines about how this system would be implemented:</p>
<blockquote class="blockquote">
<p>Early on, the company notes that a Sidewalk neighbourhood would collect real-time position data “for all entities” – including people. The company would also collect a “historical record of where things have been” and “about where they are going.” Furthermore, unique data identifiers would be generated for “every person, business or object registered in the district,” helping devices communicate with each other.</p>
</blockquote>
<p>The newspaper continues,</p>
<blockquote class="blockquote">
<p>The document also describes reputation tools that would lead to a “new currency for community co-operation,” effectively establishing a social credit system. Sidewalk could use these tools to “hold people or businesses accountable” while rewarding good behaviour, such as by rewarding a business’s good customer service with an easier or cheaper renewal process on its licence.</p>
</blockquote>
<p>If, to people in “Western cultures”, China, with its open endorsement and implementation of a social credit system, tended to seem “far, far away” – now nothing seems as it did before.</p>
<p>At this point, it’s time to switch gears and look at the world view(s) behind those recent developments.</p>
</section>
<section id="utopias" class="level3">
<h3 class="anchored" data-anchor-id="utopias">(3) Utopias</h3>
<p>Here is part of the vision of Mark Zuckerberg. Zuboff, aggregating information from several Zuckerberg-quoting, publicly available sources, writes (p.402)</p>
<blockquote class="blockquote">
<p>Predictive models would enable the corporation to “tell you to what bar to go to” when you arrive in a strange city. The vision is detailed: when you arrive at the bar, the bartender has your favorite drink waiting, and you’re able to look around the room and identify people just like you.</p>
</blockquote>
<p>Extending this to life overall, here we have a vision of total boredom; no exploration, no curiosity, no surprise, no learning. How probable is it that evolutionarily, humans are prepared to live in a world without uncertainty?</p>
<p>How about regulation? Here is <a href="https://www.businessinsider.com/google-ceo-larry-page-wants-a-place-for-experiments-2013-5">Larry Page on laws and experimentation</a>:</p>
<blockquote class="blockquote">
<p>If you look at the different kinds of laws we make, they’re very old. The laws when we went public we’re 50 years old. A law can’t be right if it’s 50 years old, like it’s before the Internet, that’s a pretty major change, in how you may go public.[…]</p>
</blockquote>
<blockquote class="blockquote">
<p>The other thing in my mind is we also haven’t built mechanisms to allow experimentation. There’s many, many exciting and important things you could do that you just can’t do because they’re illegal, or they’re not allowed by regulation, and that makes sense, we don’t want the world to change too fast. Maybe we should set aside a small part of the world …[…]</p>
</blockquote>
<blockquote class="blockquote">
<p>I think as technologists we should have some safe places where we can try out some new things and figure out what is the effect on society, what’s the effect on people, without having to deploy kind of into the normal world. […]</p>
</blockquote>
<p>Or Zuckerberg again, on the <a href="https://www.theguardian.com/technology/2010/jan/11/facebook-privacy">ancient social norm of privacy</a>:</p>
<blockquote class="blockquote">
<p>“People have really gotten comfortable not only sharing more information and different kinds, but more openly and with more people,” he said. “That social norm is just something that has evolved over time.”</p>
</blockquote>
<p>In this light, is a social credit system – perhaps packaged differently – really so far away, in Western cultures? Sometimes, a scenario sounds so silly that it’s hard to see it as a real threat; but that may be just us being naive. Here is <a href="https://www.newyorker.com/magazine/2015/01/19/know-feel">another vision</a>, from Rana el Kaliouby, CEO at Affectiva:</p>
<blockquote class="blockquote">
<p>We have been in conversations with a company in that space. It is an advertising-rewards company, and its business is based on positive moments. So if you set a goal to run three miles and you run three miles, that’s a moment. Or if you set the alarm for six o’clock and you actually do get up, that’s a moment. And they monetize these moments. They sell them. Like Kleenex can send you a coupon – I don’t know – when you get over a sad moment. Right now, this company is making assumptions about what those moments are. And we’re like, ‘Guess what? We can capture them.’</p>
</blockquote>
<p>If that sounds like it needn’t be taken seriously, here is a quote from <em>Social Physics</em>, a highly acclaimed opus by Alex Pentland, director of MIT Media Lab:</p>
<blockquote class="blockquote">
<p>The social physics approach to getting everyone to cooperate is to use social network incentives rather than to use individual market incentives or to provide additional information. That is, we focus on changing the connections between people rather than focussing on getting people individually to change their behavior. The logic here is clear: Since exchanges between people are of enormous value to the participants, we can leverage those exchanges to generate social pressure for change.[…]</p>
</blockquote>
<blockquote class="blockquote">
<p>Social network incentives act by generating social pressure around the problem of finding cooperative behaviors, and so people experiment with new behaviors to find ones that are better.</p>
</blockquote>
<p>At this point, I feel like nothing has to be added to those visions. But let’s get back to the beginning, Lex Fridman’s fantasy of clustering users.</p>
</section>
<section id="expressing-oneself" class="level3">
<h3 class="anchored" data-anchor-id="expressing-oneself">Expressing oneself</h3>
<p>Zuboff quotes Zuckerberg saying, “humans have such a deep need to express themselves” (p.&nbsp;403). Evidently, this is what motivates Fridman’s wish, and yes, it’s a, or <em>the</em>, profoundly human thing.</p>
<p>But, doesn’t expressing oneself, if seen as <em>sharing</em>, involve a free decision when, what, and with whom to share?</p>
<p>And what does it do to an experience if it is not just “there”, a thing valuable in itself, but a means for others, to control and profit?</p>
<p>Thanks for reading!</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>AI Societal Impact</category>
  <guid>https://divergences.xyz/posts/hard_problem_privacy/</guid>
  <pubDate>Thu, 27 Feb 2020 00:00:00 GMT</pubDate>
  <media:content url="https://divergences.xyz/posts/hard_problem_privacy/kitten.jpg" medium="image" type="image/jpeg"/>
</item>
</channel>
</rss>
