<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Neural Networks on &lt;raj.sh&#39;log&gt;</title>
    <link>https://shalemrajkumar.github.io/tags/neural-networks/</link>
    <description>Recent content in Neural Networks on &lt;raj.sh&#39;log&gt;</description>
    <generator>Hugo -- 0.148.2</generator>
    <language>en</language>
    <lastBuildDate>Mon, 01 Jan 0001 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://shalemrajkumar.github.io/tags/neural-networks/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Training SNNs</title>
      <link>https://shalemrajkumar.github.io/mydocs/training_snns/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://shalemrajkumar.github.io/mydocs/training_snns/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
this is a work in progress.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;snns-and-surrogate-gradient-descent&#34;&gt;SNNs and Surrogate gradient descent&lt;/h3&gt;
&lt;p&gt;Spiking neural networks (SNNs) are biologically inspired models that compute via discrete, sparse spikes, rather than continuous activations (non differentiable non linearity). This event driven framework not only captures &lt;strong&gt;rich temporal patterns&lt;/strong&gt; (such as inter spike intervals and cross neuron synchrony) but also powers energy efficient neuromorphic hardware.&lt;/p&gt;
&lt;p&gt;Surrogate gradient descent (SuGD) answer the most important challenging question: &amp;ldquo;how to convert precise spike timing into effective learning signals ?&amp;rdquo;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p>[!WARNING]
this is a work in progress.</p></blockquote>
<h3 id="snns-and-surrogate-gradient-descent">SNNs and Surrogate gradient descent</h3>
<p>Spiking neural networks (SNNs) are biologically inspired models that compute via discrete, sparse spikes, rather than continuous activations (non differentiable non linearity). This event driven framework not only captures <strong>rich temporal patterns</strong> (such as inter spike intervals and cross neuron synchrony) but also powers energy efficient neuromorphic hardware.</p>
<p>Surrogate gradient descent (SuGD) answer the most important challenging question: &ldquo;how to convert precise spike timing into effective learning signals ?&rdquo;</p>
<p>This problem is solved by Zenke <em>et al</em> 2018 through replacing the non differentiable spike with a smooth surrogate, thereby allowing backpropagation through time (BPTT).</p>
<p>SuGD can learn to make use of information that is not only encoded in the rate of spikes but also timing. SGD training can extract interspike intervals, spatio-temporal spike patterns or polychrony, and coincidence codes (<a href="10.1088/2634-4386/ae46d5">Ziqiao Yu <em>et al</em> 2026</a>)[1].</p>
<h4 id="example">Example</h4>
<p><u><em>Heavy side function</em></u></p>
<p>$$\Theta(x) = \begin{cases} 0 &amp; x &lt; 0 \\ 1 &amp; x \geq 0 \end{cases} \tag{1}$$</p>
<p><u><em>D(Heavy side function)</em></u></p>
<p>$$\frac{d\Theta(x)}{dx} = \begin{cases} 0 &amp; x &lt; 0 \\ \text{undefined} &amp; x \geq 0 \end{cases} \tag{2}$$</p>
<h4 id="surrogate-approximation">Surrogate approximation</h4>
<p>using a sigmoid or fast sigmoid function (kind of cheat:)</p>
<p><u><em>sigmoid or logistic function</em></u></p>
<p>$$\sigma(x) = \frac{1}{1 + e^{-\beta x}} \tag{3}$$</p>
<p><u><em>D(sigmoid or logistic function)</em></u></p>
<p>$$\frac{d\sigma(x)}{dx} = \beta\sigma(x)(1 - \sigma(x))\tag{4}$$</p>
<p><u><em>fast sigmoid function</em></u></p>
<p>$$\sigma(x) = \frac{x}{1 + |x|} \tag{5}$$</p>
<p><u><em>D(fast sigmoid function)</em></u></p>
<p>$$\frac{d\sigma(x)}{dx} = \frac{1}{(1 + |x|)^2} \tag{6}$$</p>
<p><u><strong>Code</strong></u></p>
<p>$$\text{heaviside}(x) \approx \sigma(x)$$</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SurrogateHeaviside</span>(torch<span style="color:#f92672">.</span>autograd<span style="color:#f92672">.</span>Function):
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@staticmethod</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">forward</span>(ctx, input):
</span></span><span style="display:flex;"><span>        ctx<span style="color:#f92672">.</span>save_for_backward(input)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> torch<span style="color:#f92672">.</span>heaviside(input, <span style="color:#ae81ff">0</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@staticmethod</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">backward</span>(ctx, grad_output):
</span></span><span style="display:flex;"><span>        input, <span style="color:#f92672">=</span> ctx<span style="color:#f92672">.</span>saved_tensors
</span></span><span style="display:flex;"><span>        beta <span style="color:#f92672">=</span> <span style="color:#ae81ff">5</span>
</span></span><span style="display:flex;"><span>        s <span style="color:#f92672">=</span> torch<span style="color:#f92672">.</span>sigmoid(beta<span style="color:#f92672">*</span>input)
</span></span><span style="display:flex;"><span>        grad <span style="color:#f92672">=</span> grad_output<span style="color:#f92672">*</span>beta<span style="color:#f92672">*</span>s<span style="color:#f92672">*</span>(<span style="color:#ae81ff">1</span><span style="color:#f92672">-</span>s)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> grad
</span></span><span style="display:flex;"><span>surrogate_heaviside <span style="color:#f92672">=</span> SurrogateHeaviside<span style="color:#f92672">.</span>apply
</span></span></code></pre></div><p>$$\text{heaviside}(x) \approx fast\_sigmoid(x)$$</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SurrogateHeaviside</span>(torch<span style="color:#f92672">.</span>autograd<span style="color:#f92672">.</span>Function):
</span></span><span style="display:flex;"><span>    scale <span style="color:#f92672">=</span> <span style="color:#ae81ff">100.0</span> <span style="color:#75715e"># controls steepness of surrogate gradient</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@staticmethod</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">forward</span>(ctx, input):
</span></span><span style="display:flex;"><span>        ctx<span style="color:#f92672">.</span>save_for_backward(input) <span style="color:#75715e">## storing context or input to use during backprop</span>
</span></span><span style="display:flex;"><span>        out <span style="color:#f92672">=</span> torch<span style="color:#f92672">.</span>zeros_like(input) <span style="color:#75715e">## first step make everything zero (i.e. all negative inputs are zero)</span>
</span></span><span style="display:flex;"><span>        out[input <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">1.0</span> <span style="color:#75715e">## mask the inputs &gt; 0 and make them 1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> out
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@staticmethod</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">backward</span>(ctx, grad_output):
</span></span><span style="display:flex;"><span>        input, <span style="color:#f92672">=</span> ctx<span style="color:#f92672">.</span>saved_tensors <span style="color:#75715e">## fetching previously saved input</span>
</span></span><span style="display:flex;"><span>        grad_input <span style="color:#f92672">=</span> grad_output<span style="color:#f92672">.</span>clone() <span style="color:#75715e">## safely copying the gradient output so we don&#39;t accidentally break PyTorch graph</span>
</span></span><span style="display:flex;"><span>        grad <span style="color:#f92672">=</span> grad_input<span style="color:#f92672">/</span>(SurrogateHeaviside<span style="color:#f92672">.</span>scale<span style="color:#f92672">*</span>torch<span style="color:#f92672">.</span>abs(input)<span style="color:#f92672">+</span><span style="color:#ae81ff">1.0</span>)<span style="color:#f92672">**</span><span style="color:#ae81ff">2</span> <span style="color:#75715e">## computing fast sigmoid instead of using the e^x which takes longer to run</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> grad
</span></span><span style="display:flex;"><span>surrogate_heaviside  <span style="color:#f92672">=</span> SurrogateHeaviside<span style="color:#f92672">.</span>apply
</span></span></code></pre></div><p><em>credits: Tutoral by Dan F M Goodman, 2026 FENS Chen Summer School on Learning with spikes [2]</em></p>
<h3 id="tutorial-sugd-for-training-snns"><u>Tutorial: SuGD for training SNNs</u></h3>
<br>
<blockquote>
<p><em>This section is inspired snnTorch <a href="https://snntorch.readthedocs.io/en/latest/tutorials/tutorial-5.html">tutorial-5</a> which is in turn inspired by  Friedemann Zenke&rsquo;s extensive work on SNNs. Check out his repo on surrogate gradients <a href="https://github.com/fzenke/spytorch">spytorch</a></em></p></blockquote>
<br>
<p>Now we will implement a basic supervised learing algorithm with SuGD for training spiking neurons to perform image classfication on Static MNIST.</p>
<h4 id="the-recurrent-representation-of-snns">The Recurrent representation of SNNs</h4>
<p>If you think of SNNs like a Machine learning researcher you can tell it is type of RNNs with implict recurrence with nonlinear activation function.</p>
<p>Further Infromation on RNNs are found <a href="">here</a></p>
<p><u>LIF Neuron discrete recursive form</u></p>
<div>
$$U[t+1] = \underbrace{\beta U[t]}_\text{decay} + \underbrace{WX[t+1]}_\text{input} - \underbrace{R[t]}_\text{reset} \tag{7}$$
</div>
<p>where if the membrane potential exceeds the threshold, a spike is emitted:</p>
<p>$$S[t] = \begin{cases} 1, &amp;\text{if}~U[t] &gt; U_{\rm thr} \\
0, &amp;\text{otherwise}\end{cases}{\tag{8}}$$</p>
<figure style="text-align: center;">
  <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Recurrent_neural_network_unfold.svg/500px-Recurrent_neural_network_unfold.svg.png?utm_source=en.wikipedia.org&utm_campaign=parser&utm_content=thumbnail"width="400" alt="Vanilla-RNN">
  <figcaption>image credit: wikipedia</figcaption>
</figure>
<p>Like in RNNs, we have dependence from previous state (here state variable $U[t]$), Also its not even a property of network just the neuron itself.This is illustrated using an <em>implicit</em> recurrent connection for the decay of the membrane potential.</p>
<p><u> Vanilla-RNN </u></p>
<p>hidden state: $$h_t = f\left(W_{ih} * x_t + W_{hh} * h_{t-1} + b_h)\right)$$</p>
<p>output: $$ y_t = W_{ho} * h_t + b_o $$</p>
<p>This is almost perfectly poised to take advantage of the developments in training recurrent neural networks (RNNs) and sequence-based models.</p>
<p>there is the special aspect of recurrence called <strong><em>explict recurrence</em></strong> in SNNs  where the output spike $S_{\rm out}$ is fed back to the input, i.e. neurons in same layer are connected to each other or even to itself simlar to <a href=""><code>RLeaky</code></a> in snnTorch and these connection ($W_{recurr}$ where diagonals represent self connections and off diagonal elements represent recurrent connection between different neurons) fully trainable.</p>
<p>In the figure below, the connection weighted by $-U_{\rm thr}$ represents the reset mechanism $R[t]$.</p>
<center>
<img src='https://github.com/jeshraghian/snntorch/blob/master/docs/_static/img/examples/tutorial5/unrolled_2.png?raw=true' width="800">
</center>
<p>The benefit of an unrolled graph is that it provides an explicit description of how computations are performed. The process of unfolding illustrates the flow of information forward in time (from left to right) to compute outputs and losses, and backward in time to compute gradients. The more time steps that are simulated, the deeper the graph becomes.</p>
<p>Conventional RNNs treat $\beta$ as a learnable parameter. This is also possible for SNNs, though by default, they are treated as hyperparameters. This replaces the vanishing and exploding gradient problems with a hyperparameter search.</p>
<h4 id="the-non-differentiablility-of-spikes">The non differentiablility of spikes</h4>
<p>In previous section we have discussed an example of heavy side function and how its derivative is always zero or undefined. This exactly how LIF neurons behave after thresholding.</p>
<p>An alternative way to represent the relationship between $S$ and $U$ in $(1)$ or $(8)$ is:</p>
<p>$$S[t] = \Theta(U[t] - U_{\rm thr}) \tag{9}$$</p>
<p>where $\Theta(\cdot)$ is the Heaviside step function:</p>
<center>
<img src='https://github.com/jeshraghian/snntorch/blob/master/docs/_static/img/examples/tutorial3/3_2_spike_descrip.png?raw=true' width="600">
</center>
<p>Training a network in this form poses some serious challenges. Consider a single, isolated time step of the computational graph from the previous figure titled <em>&ldquo;Recurrent representation of spiking neurons&rdquo;</em>, as shown in the <em>forward pass</em> below:</p>
<center>
<img src='https://github.com/jeshraghian/snntorch/blob/master/docs/_static/img/examples/tutorial5/non-diff.png?raw=true' width="400">
</center>
<p>The goal is to train the network using the gradient of the loss with respect to the weights, such that the weights are updated to minimize the loss. The backpropagation algorithm achieves this using the chain rule:</p>
<div>
$$\frac{\partial \mathcal{L}}{\partial W} = 
\frac{\partial \mathcal{L}}{\partial S}
\underbrace{\frac{\partial S}{\partial U}}_{\{0, \infty\}}
\frac{\partial U}{\partial I}\
\frac{\partial I}{\partial W}\ \tag{10}$$
</div>
<p>From $(7)$, $\partial I/\partial W=X$, and $\partial U/\partial I=1$. While we have not yet defined a loss function, we can assume $\partial \mathcal{L}/\partial S$ has an analytical solution, in a similar form to the cross-entropy or mean-square error loss (more on that shortly).</p>
<p>However, the term that we are going to grapple with is $\partial S/\partial U$. The derivative of the Heaviside step function from $(9)$ is the Dirac Delta function from $(2)$, which evaluates to 0 everywhere, except at the threshold $U_{\rm thr} = \theta$, where it tends to infinity. This means the gradient will almost always be nulled to zero (or saturated if $U$ sits precisely at the threshold), and no learning can take place. This is known as the <strong>dead neuron problem</strong>.</p>
<h4 id="overcoming-the-dead-neuron-problem">Overcoming the Dead Neuron Problem</h4>
<p>As we have seen in first section we keep the Heaviside function as it is during the forward pass, but swap the derivative term $\partial S/\partial U$ for something that does not kill the learning process during the backward pass, which will be denoted $\partial \tilde{S}/\partial U$.</p>
<p>This might sound weird, but it turns out that neural networks are quite robust to such approximations. This is commonly known as the <em>surrogate gradient</em> approach.</p>
<p>A variety of options exist to using surrogate gradients, The default method in snnTorch (as of v0.6.0) is to smooth the Heaviside function with the <strong>arctangent function</strong>.</p>
<p>The backward-pass derivative used is:</p>
<p>$$ \frac{\partial \tilde{S}}{\partial U} \leftarrow \frac{1}{\pi}\frac{1}{(1+[U\pi]^2)} \tag{11}$$</p>
<h4 id="backpropagation-through-time-bptt">Backpropagation Through Time (BPTT)</h4>
<p>Equation $(10)$ only calculates the gradient for one single time step (referred to as the <em>immediate influence</em> in the figure below), but the backpropagation through time (BPTT) algorithm calculates the gradient from the loss to <em>all</em> descendants and sums them together.</p>
<p>Look at the path and we can see that the current output has depedence of previous $U$ which in turn has dependence on previous $W$ and so on.</p>
<p>The weight $W$ is applied at every time step, and so imagine a loss is also calculated at every time step. The influence of the weight on present and historical losses must be summed together to define the global gradient:</p>
<div>
$$\frac{\partial \mathcal{L}}{\partial W}=\sum_t \frac{\partial\mathcal{L}[t]}{\partial W} = 
\sum_t \sum_{s\leq t} \frac{\partial\mathcal{L}[t]}{\partial W[s]}\frac{\partial W[s]}{\partial W} \tag{12} $$
</div>
<p style="font-size: 12px; color: #888; margin-top: -0.5em;">
    <u style="text-decoration: underline;">credit:</u> Claude 5 Sonnet
</p>
<blockquote>
<p>So why we have multiple $W[s]$ if all we have is one weight just across the time ?</p>
<p>The total gradient is a sum of per-timestep gradient contributions ($\frac{\partial \mathcal{L[t]}}{\partial W}$). Each of those is itself a sum over every earlier time step
$s\leq t$, because $W$ was reused at each of those steps ($W[s]$), and each reuse contributed its own path of influence on the loss at t.</p>
<p>If $W$ appeared <em>only once</em> in the whole computation, we could just chain straight through: $\mathcal{L}[t] \leftarrow U[t] \leftarrow U[t-1] \leftarrow \cdots \leftarrow W$, and $\frac{\partial \mathcal{L}[t]}{\partial W}$ would be one single product of derivatives, no sum needed. like we saw in previous figure and inital explaination in this segment.</p>
<p>But $W$ has <strong>multiple separate paths of influence</strong> on $\mathcal{L}[t]$: one path through $h[1]$, another through $U[2]$, another through $U[3]$, etc. Changing $W$ nudges <em>all</em> these usages simultaneously, and each nudge sends its own ripple forward to $\mathcal{L}[t]$.</p>
<p><strong>This is just the multivariable chain rule</strong></p>
<p>We&rsquo;ve probably seen this rule before: if $z = f(x, y)$ and both $x = x(t)$ and $y = y(t)$ depend on the same $t$, then</p>
<p>$$\frac{dz}{dt} = \frac{\partial z}{\partial x}\frac{dx}{dt} + \frac{\partial z}{\partial y}\frac{dy}{dt}$$</p>
<p>We don&rsquo;t get to pick just one path — we sum over <em>every</em> path by which $t$ reaches $z$.</p></blockquote>
<p>The point of $(12)$ is to ensure causality: by constraining $s\leq t$, we only account for the contribution of immediate and prior influences of $W$ on the loss. A recurrent system constrains the weight to be shared across all steps: $W[0]=W[1] =~&hellip; ~ = W$. Therefore, a change in $W[s]$ will have the same effect on all $W$, which implies that $\partial W[s]/\partial W=1$:</p>
<p>$$\frac{\partial \mathcal{L}}{\partial W}=
\sum_t \sum_{s\leq t} \frac{\partial\mathcal{L}[t]}{\partial W[s]} \tag{13} $$</p>
<p>As an example, isolate the prior influence due to $s = t-1$ <em>only</em>; this means the backward pass must track back in time by one step. The influence of $W[t-1]$ on the loss can be written as:</p>
<div>
$$\frac{\partial \mathcal{L}[t]}{\partial W[t-1]} = 
\frac{\partial \mathcal{L}[t]}{\partial S[t]}
\underbrace{\frac{\partial \tilde{S}[t]}{\partial U[t]}}_{Eq.~(12)}
\underbrace{\frac{\partial U[t]}{\partial U[t-1]}}_\beta
\underbrace{\frac{\partial U[t-1]}{\partial I[t-1]}}_1
\underbrace{\frac{\partial I[t-1]}{\partial W[t-1]}}_{X[t-1]} \tag{14}$$
<div>
<p>We have already dealt with all of these terms from $(11)$, except for $\partial U[t]/\partial U[t-1]$. From $(7)$, this temporal derivative term simply evaluates to $\beta$. So if we really wanted to, we now know enough to painstakingly calculate the derivative of every weight at every time step by hand, and it&rsquo;d look something like this for a single neuron:</p>
<center>
<img src='https://github.com/jeshraghian/snntorch/blob/master/docs/_static/img/examples/tutorial5/bptt.png?raw=true' width="600">
</center>
<p>But thankfully, PyTorch&rsquo;s autodiff takes care of that in the background for us.</p>
<p><em><u>Note</u>: The reset mechanism has been omitted from the above figure. In snnTorch, reset is included in the forward-pass, but detached from the backward pass.</em></p>
<h4 id="setting-up-the-loss-and-output-decoding">Setting up the loss and output decoding</h4>
<p>In a spiking neural net, there are several options to interpreting the output spikes. The most common approaches are:</p>
<ul>
<li>Rate coding: Take the neuron with the highest firing rate (or spike count) as the predicted class</li>
<li>Latency coding: Take the neuron that fires first as the predicted class</li>
</ul>
<p>This part is similar to encoding. here, we are interpreting (decoding) the output spikes, rather than encoding/converting raw input data into spikes.</p>
<p><u>Rate code</u>:. When input data is passed to the network, we want the correct neuron class to emit the most spikes over the course of the simulation run. This then corresponds to the highest average firing frequency.</p>
<p>One way to achieve this is to increase the membrane potential of the correct class to $U&gt;U_{\rm thr}$, and that of incorrect classes to $U&lt;U_{\rm thr}$. Applying the target to $U$ serves as a proxy for modulating spiking behavior from $S$.</p>
<p>This can be implemented by taking the softmax of the membrane potential for output neurons, where $C$ is the number of output classes:</p>
<div>
$$p_i[t] = \frac{e^{U_i[t]}}{\sum_{j=0}^{C}e^{U_j[t]}} \tag{15}$$
</div>
<p>The cross-entropy between $p_i$ and the target $y_i \in {0,1}^C$, which is a one-hot target vector, is obtained using:</p>
<div>
$$\mathcal{L}_{CE}[t] = -\sum_{i=0}^Cy_i{\rm log}(p_i[t]) \tag{16}$$
</div>
<p>The practical effect is that the membrane potential of the correct class is encouraged to increase while those of incorrect classes are reduced.</p>
<blockquote>
<p>In effect, this means the correct class is encouraged to fire at all time steps, while incorrect classes are suppressed at all steps. This may not be the most efficient implementation of an SNN, but it is among the simplest.</p></blockquote>
<p>This target is applied at every time step of the simulation, thus also generating a loss at every step. These losses are then summed together at the end of the simulation:</p>
<div>
$$\mathcal{L}_{CE} = \sum_t\mathcal{L}_{CE}[t] \tag{17}$$
</div>
<p>This is just one of many possible ways to apply a loss function to a spiking neural network. A few variety of approaches are available to use in  <a href="https://snntorch.readthedocs.io/en/latest/snntorch.functional.html"><code>snnTorch.functional</code></a>.</p>
<h4 id="code">code</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e">#========================== Training SNN with static MNIST dataset ==========================</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># imports</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> snntorch <span style="color:#66d9ef">as</span> snn
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> snntorch <span style="color:#f92672">import</span> spikeplot <span style="color:#66d9ef">as</span> splt
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> snntorch <span style="color:#f92672">import</span> spikegen
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> torch
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> torch.nn <span style="color:#66d9ef">as</span> nn
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> torch.utils.data <span style="color:#f92672">import</span> DataLoader
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> torchvision <span style="color:#f92672">import</span> datasets, transforms
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> matplotlib.pyplot <span style="color:#66d9ef">as</span> plt
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> numpy <span style="color:#66d9ef">as</span> np
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> itertools
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#-------------------------- Surrogate LIF neuron -------------------------</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Leaky neuron model, overriding the backward pass with a custom function</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">LeakySurrogate</span>(nn<span style="color:#f92672">.</span>Module):
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">__init__</span>(self, beta, threshold<span style="color:#f92672">=</span><span style="color:#ae81ff">1.0</span>):
</span></span><span style="display:flex;"><span>      super(LeakySurrogate, self)<span style="color:#f92672">.</span><span style="color:#a6e22e">__init__</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># initialize decay rate beta and threshold</span>
</span></span><span style="display:flex;"><span>      self<span style="color:#f92672">.</span>beta <span style="color:#f92672">=</span> beta
</span></span><span style="display:flex;"><span>      self<span style="color:#f92672">.</span>threshold <span style="color:#f92672">=</span> threshold
</span></span><span style="display:flex;"><span>      self<span style="color:#f92672">.</span>spike_gradient <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>ATan<span style="color:#f92672">.</span>apply
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># the forward function is called each time we call Leaky</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">forward</span>(self, input_, mem):
</span></span><span style="display:flex;"><span>    spk <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>spike_gradient((mem<span style="color:#f92672">-</span>self<span style="color:#f92672">.</span>threshold))  <span style="color:#75715e"># call the Heaviside function</span>
</span></span><span style="display:flex;"><span>    reset <span style="color:#f92672">=</span> (self<span style="color:#f92672">.</span>beta <span style="color:#f92672">*</span> spk <span style="color:#f92672">*</span> self<span style="color:#f92672">.</span>threshold)<span style="color:#f92672">.</span>detach() <span style="color:#75715e"># remove reset from computational graph as the surrogate gradient should only be applied to  $\partial S/\partial U$, and not $\partial R/\partial U$ </span>
</span></span><span style="display:flex;"><span>    mem <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>beta <span style="color:#f92672">*</span> mem <span style="color:#f92672">+</span> input_ <span style="color:#f92672">-</span> reset <span style="color:#75715e"># Eq (1)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> spk, mem
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># Forward pass: Heaviside function</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># Backward pass: Override Dirac Delta with the ArcTan function</span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">@staticmethod</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ATan</span>(torch<span style="color:#f92672">.</span>autograd<span style="color:#f92672">.</span>Function):
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">@staticmethod</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">forward</span>(ctx, mem):
</span></span><span style="display:flex;"><span>          spk <span style="color:#f92672">=</span> (mem <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>)<span style="color:#f92672">.</span>float() <span style="color:#75715e"># Heaviside on the forward pass: Eq(2)</span>
</span></span><span style="display:flex;"><span>          ctx<span style="color:#f92672">.</span>save_for_backward(mem)  <span style="color:#75715e"># store the membrane for use in the backward pass</span>
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">return</span> spk
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">@staticmethod</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">backward</span>(ctx, grad_output):
</span></span><span style="display:flex;"><span>          (mem,) <span style="color:#f92672">=</span> ctx<span style="color:#f92672">.</span>saved_tensors  <span style="color:#75715e"># retrieve the membrane potential </span>
</span></span><span style="display:flex;"><span>          grad <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span> <span style="color:#f92672">/</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">+</span> (np<span style="color:#f92672">.</span>pi <span style="color:#f92672">*</span> mem)<span style="color:#f92672">.</span>pow_(<span style="color:#ae81ff">2</span>)) <span style="color:#f92672">*</span> grad_output <span style="color:#75715e"># Eqn 5</span>
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">return</span> grad
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>lif1 <span style="color:#f92672">=</span> LeakySurrogate(beta<span style="color:#f92672">=</span><span style="color:#ae81ff">0.9</span>) <span style="color:#75715e"># similar to snn.Leaky(beta=0.9) as all snnTorch neurons apply surrogate gradients (Atan) by default</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#-------------------------- Load MNIST dataset -------------------------</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># dataloader arguments</span>
</span></span><span style="display:flex;"><span>batch_size <span style="color:#f92672">=</span> <span style="color:#ae81ff">128</span>
</span></span><span style="display:flex;"><span>data_path<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;/tmp/data/mnist&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>dtype <span style="color:#f92672">=</span> torch<span style="color:#f92672">.</span>float
</span></span><span style="display:flex;"><span>device <span style="color:#f92672">=</span> torch<span style="color:#f92672">.</span>device(<span style="color:#e6db74">&#34;cuda&#34;</span>) <span style="color:#66d9ef">if</span> torch<span style="color:#f92672">.</span>cuda<span style="color:#f92672">.</span>is_available() <span style="color:#66d9ef">else</span> torch<span style="color:#f92672">.</span>device(<span style="color:#e6db74">&#34;cpu&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Define a transform</span>
</span></span><span style="display:flex;"><span>transform <span style="color:#f92672">=</span> transforms<span style="color:#f92672">.</span>Compose([
</span></span><span style="display:flex;"><span>            transforms<span style="color:#f92672">.</span>Resize((<span style="color:#ae81ff">28</span>, <span style="color:#ae81ff">28</span>)),
</span></span><span style="display:flex;"><span>            transforms<span style="color:#f92672">.</span>Grayscale(),
</span></span><span style="display:flex;"><span>            transforms<span style="color:#f92672">.</span>ToTensor(),
</span></span><span style="display:flex;"><span>            transforms<span style="color:#f92672">.</span>Normalize((<span style="color:#ae81ff">0</span>,), (<span style="color:#ae81ff">1</span>,))])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>mnist_train <span style="color:#f92672">=</span> datasets<span style="color:#f92672">.</span>MNIST(data_path, train<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>, download<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>, transform<span style="color:#f92672">=</span>transform)
</span></span><span style="display:flex;"><span>mnist_test <span style="color:#f92672">=</span> datasets<span style="color:#f92672">.</span>MNIST(data_path, train<span style="color:#f92672">=</span><span style="color:#66d9ef">False</span>, download<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>, transform<span style="color:#f92672">=</span>transform)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Create DataLoaders</span>
</span></span><span style="display:flex;"><span>train_loader <span style="color:#f92672">=</span> DataLoader(mnist_train, batch_size<span style="color:#f92672">=</span>batch_size, shuffle<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>, drop_last<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)
</span></span><span style="display:flex;"><span>test_loader <span style="color:#f92672">=</span> DataLoader(mnist_test, batch_size<span style="color:#f92672">=</span>batch_size, shuffle<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>, drop_last<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#-------------------------- Define Network model -------------------------</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#............. Network Parameters .............</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Network Architecture</span>
</span></span><span style="display:flex;"><span>num_inputs <span style="color:#f92672">=</span> <span style="color:#ae81ff">28</span><span style="color:#f92672">*</span><span style="color:#ae81ff">28</span>
</span></span><span style="display:flex;"><span>num_hidden <span style="color:#f92672">=</span> <span style="color:#ae81ff">1000</span>
</span></span><span style="display:flex;"><span>num_outputs <span style="color:#f92672">=</span> <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Temporal Dynamics</span>
</span></span><span style="display:flex;"><span>num_steps <span style="color:#f92672">=</span> <span style="color:#ae81ff">25</span>
</span></span><span style="display:flex;"><span>beta <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.95</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#...............Network Class...................</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Define Network</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Net</span>(nn<span style="color:#f92672">.</span>Module):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">__init__</span>(self):
</span></span><span style="display:flex;"><span>        super()<span style="color:#f92672">.</span><span style="color:#a6e22e">__init__</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Initialize layers</span>
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>fc1 <span style="color:#f92672">=</span> nn<span style="color:#f92672">.</span>Linear(num_inputs, num_hidden)
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>lif1 <span style="color:#f92672">=</span> snn<span style="color:#f92672">.</span>Leaky(beta<span style="color:#f92672">=</span>beta)
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>fc2 <span style="color:#f92672">=</span> nn<span style="color:#f92672">.</span>Linear(num_hidden, num_outputs)
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>lif2 <span style="color:#f92672">=</span> snn<span style="color:#f92672">.</span>Leaky(beta<span style="color:#f92672">=</span>beta)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">forward</span>(self, x):
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Initialize hidden states at t=0</span>
</span></span><span style="display:flex;"><span>        mem1 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>lif1<span style="color:#f92672">.</span>init_leaky()
</span></span><span style="display:flex;"><span>        mem2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>lif2<span style="color:#f92672">.</span>init_leaky()
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Record the final layer</span>
</span></span><span style="display:flex;"><span>        spk2_rec <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>        mem2_rec <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> step <span style="color:#f92672">in</span> range(num_steps):
</span></span><span style="display:flex;"><span>            cur1 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>fc1(x)
</span></span><span style="display:flex;"><span>            spk1, mem1 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>lif1(cur1, mem1)
</span></span><span style="display:flex;"><span>            cur2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>fc2(spk1)
</span></span><span style="display:flex;"><span>            spk2, mem2 <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>lif2(cur2, mem2)
</span></span><span style="display:flex;"><span>            spk2_rec<span style="color:#f92672">.</span>append(spk2)
</span></span><span style="display:flex;"><span>            mem2_rec<span style="color:#f92672">.</span>append(mem2)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> torch<span style="color:#f92672">.</span>stack(spk2_rec, dim<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>), torch<span style="color:#f92672">.</span>stack(mem2_rec, dim<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Load the network onto CUDA if available</span>
</span></span><span style="display:flex;"><span>net <span style="color:#f92672">=</span> Net()<span style="color:#f92672">.</span>to(device)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#-------------------------- Training and Testing SNN -------------------------</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#............. Accuracy metric.............</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># pass data into the network, sum the spikes over time</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># and compare the neuron with the highest number of spikes</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># with the target</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">print_batch_accuracy</span>(data, targets, train<span style="color:#f92672">=</span><span style="color:#66d9ef">False</span>):
</span></span><span style="display:flex;"><span>    output, _ <span style="color:#f92672">=</span> net(data<span style="color:#f92672">.</span>view(batch_size, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>))
</span></span><span style="display:flex;"><span>    _, idx <span style="color:#f92672">=</span> output<span style="color:#f92672">.</span>sum(dim<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>)<span style="color:#f92672">.</span>max(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>    acc <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>mean((targets <span style="color:#f92672">==</span> idx)<span style="color:#f92672">.</span>detach()<span style="color:#f92672">.</span>cpu()<span style="color:#f92672">.</span>numpy())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> train:
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Train set accuracy for a single minibatch: </span><span style="color:#e6db74">{</span>acc<span style="color:#f92672">*</span><span style="color:#ae81ff">100</span><span style="color:#e6db74">:</span><span style="color:#e6db74">.2f</span><span style="color:#e6db74">}</span><span style="color:#e6db74">%&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Test set accuracy for a single minibatch: </span><span style="color:#e6db74">{</span>acc<span style="color:#f92672">*</span><span style="color:#ae81ff">100</span><span style="color:#e6db74">:</span><span style="color:#e6db74">.2f</span><span style="color:#e6db74">}</span><span style="color:#e6db74">%&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">train_printer</span>(
</span></span><span style="display:flex;"><span>    data, targets, epoch,
</span></span><span style="display:flex;"><span>    counter, iter_counter,
</span></span><span style="display:flex;"><span>        loss_hist, test_loss_hist, test_data, test_targets):
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Epoch </span><span style="color:#e6db74">{</span>epoch<span style="color:#e6db74">}</span><span style="color:#e6db74">, Iteration </span><span style="color:#e6db74">{</span>iter_counter<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Train Set Loss: </span><span style="color:#e6db74">{</span>loss_hist[counter]<span style="color:#e6db74">:</span><span style="color:#e6db74">.2f</span><span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Test Set Loss: </span><span style="color:#e6db74">{</span>test_loss_hist[counter]<span style="color:#e6db74">:</span><span style="color:#e6db74">.2f</span><span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>    print_batch_accuracy(data, targets, train<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)
</span></span><span style="display:flex;"><span>    print_batch_accuracy(test_data, test_targets, train<span style="color:#f92672">=</span><span style="color:#66d9ef">False</span>)
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#.............Loss and optimiser .............</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>loss <span style="color:#f92672">=</span> nn<span style="color:#f92672">.</span>CrossEntropyLoss()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Adaptive Moment Estimation (Adam) optimizer best for training RNNs</span>
</span></span><span style="display:flex;"><span>optimizer <span style="color:#f92672">=</span> torch<span style="color:#f92672">.</span>optim<span style="color:#f92672">.</span>Adam(net<span style="color:#f92672">.</span>parameters(), lr<span style="color:#f92672">=</span><span style="color:#ae81ff">5e-4</span>, betas<span style="color:#f92672">=</span>(<span style="color:#ae81ff">0.9</span>, <span style="color:#ae81ff">0.999</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#............. Training Loop ............</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>num_epochs <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>loss_hist <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>test_loss_hist <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>counter <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Outer training loop</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> epoch <span style="color:#f92672">in</span> range(num_epochs):
</span></span><span style="display:flex;"><span>    iter_counter <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>    train_batch <span style="color:#f92672">=</span> iter(train_loader)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Minibatch training loop</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> data, targets <span style="color:#f92672">in</span> train_batch:
</span></span><span style="display:flex;"><span>        data <span style="color:#f92672">=</span> data<span style="color:#f92672">.</span>to(device)
</span></span><span style="display:flex;"><span>        targets <span style="color:#f92672">=</span> targets<span style="color:#f92672">.</span>to(device)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># forward pass</span>
</span></span><span style="display:flex;"><span>        net<span style="color:#f92672">.</span>train()
</span></span><span style="display:flex;"><span>        spk_rec, mem_rec <span style="color:#f92672">=</span> net(data<span style="color:#f92672">.</span>view(batch_size, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># initialize the loss &amp; sum over time</span>
</span></span><span style="display:flex;"><span>        loss_val <span style="color:#f92672">=</span> torch<span style="color:#f92672">.</span>zeros((<span style="color:#ae81ff">1</span>), dtype<span style="color:#f92672">=</span>dtype, device<span style="color:#f92672">=</span>device)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> step <span style="color:#f92672">in</span> range(num_steps):
</span></span><span style="display:flex;"><span>            loss_val <span style="color:#f92672">+=</span> loss(mem_rec[step], targets)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Gradient calculation + weight update</span>
</span></span><span style="display:flex;"><span>        optimizer<span style="color:#f92672">.</span>zero_grad()
</span></span><span style="display:flex;"><span>        loss_val<span style="color:#f92672">.</span>backward()
</span></span><span style="display:flex;"><span>        optimizer<span style="color:#f92672">.</span>step()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Store loss history for future plotting</span>
</span></span><span style="display:flex;"><span>        loss_hist<span style="color:#f92672">.</span>append(loss_val<span style="color:#f92672">.</span>item())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Test set</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">with</span> torch<span style="color:#f92672">.</span>no_grad():
</span></span><span style="display:flex;"><span>            net<span style="color:#f92672">.</span>eval()
</span></span><span style="display:flex;"><span>            test_data, test_targets <span style="color:#f92672">=</span> next(iter(test_loader))
</span></span><span style="display:flex;"><span>            test_data <span style="color:#f92672">=</span> test_data<span style="color:#f92672">.</span>to(device)
</span></span><span style="display:flex;"><span>            test_targets <span style="color:#f92672">=</span> test_targets<span style="color:#f92672">.</span>to(device)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e"># Test set forward pass</span>
</span></span><span style="display:flex;"><span>            test_spk, test_mem <span style="color:#f92672">=</span> net(test_data<span style="color:#f92672">.</span>view(batch_size, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e"># Test set loss</span>
</span></span><span style="display:flex;"><span>            test_loss <span style="color:#f92672">=</span> torch<span style="color:#f92672">.</span>zeros((<span style="color:#ae81ff">1</span>), dtype<span style="color:#f92672">=</span>dtype, device<span style="color:#f92672">=</span>device)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">for</span> step <span style="color:#f92672">in</span> range(num_steps):
</span></span><span style="display:flex;"><span>                test_loss <span style="color:#f92672">+=</span> loss(test_mem[step], test_targets)
</span></span><span style="display:flex;"><span>            test_loss_hist<span style="color:#f92672">.</span>append(test_loss<span style="color:#f92672">.</span>item())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e"># Print train/test loss/accuracy</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> counter <span style="color:#f92672">%</span> <span style="color:#ae81ff">50</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>                train_printer(
</span></span><span style="display:flex;"><span>                    data, targets, epoch,
</span></span><span style="display:flex;"><span>                    counter, iter_counter,
</span></span><span style="display:flex;"><span>                    loss_hist, test_loss_hist,
</span></span><span style="display:flex;"><span>                    test_data, test_targets)
</span></span><span style="display:flex;"><span>            counter <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>            iter_counter <span style="color:#f92672">+=</span><span style="color:#ae81ff">1.</span> 
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#............... Plotting Loss History ............</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Plot Loss</span>
</span></span><span style="display:flex;"><span>fig <span style="color:#f92672">=</span> plt<span style="color:#f92672">.</span>figure(facecolor<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;w&#34;</span>, figsize<span style="color:#f92672">=</span>(<span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">5</span>))
</span></span><span style="display:flex;"><span>plt<span style="color:#f92672">.</span>plot(loss_hist)
</span></span><span style="display:flex;"><span>plt<span style="color:#f92672">.</span>plot(test_loss_hist)
</span></span><span style="display:flex;"><span>plt<span style="color:#f92672">.</span>title(<span style="color:#e6db74">&#34;Loss Curves&#34;</span>)
</span></span><span style="display:flex;"><span>plt<span style="color:#f92672">.</span>legend([<span style="color:#e6db74">&#34;Train Loss&#34;</span>, <span style="color:#e6db74">&#34;Test Loss&#34;</span>])
</span></span><span style="display:flex;"><span>plt<span style="color:#f92672">.</span>xlabel(<span style="color:#e6db74">&#34;Iteration&#34;</span>)
</span></span><span style="display:flex;"><span>plt<span style="color:#f92672">.</span>ylabel(<span style="color:#e6db74">&#34;Loss&#34;</span>)
</span></span><span style="display:flex;"><span>plt<span style="color:#f92672">.</span>show()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#................ Plotting test accuracy............</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>total <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>correct <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># drop_last switched to False to keep all samples</span>
</span></span><span style="display:flex;"><span>test_loader <span style="color:#f92672">=</span> DataLoader(mnist_test, batch_size<span style="color:#f92672">=</span>batch_size, shuffle<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>, drop_last<span style="color:#f92672">=</span><span style="color:#66d9ef">False</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">with</span> torch<span style="color:#f92672">.</span>no_grad():
</span></span><span style="display:flex;"><span>  net<span style="color:#f92672">.</span>eval()
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">for</span> data, targets <span style="color:#f92672">in</span> test_loader:
</span></span><span style="display:flex;"><span>    data <span style="color:#f92672">=</span> data<span style="color:#f92672">.</span>to(device)
</span></span><span style="display:flex;"><span>    targets <span style="color:#f92672">=</span> targets<span style="color:#f92672">.</span>to(device)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># forward pass</span>
</span></span><span style="display:flex;"><span>    test_spk, _ <span style="color:#f92672">=</span> net(data<span style="color:#f92672">.</span>view(data<span style="color:#f92672">.</span>size(<span style="color:#ae81ff">0</span>), <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># calculate total accuracy</span>
</span></span><span style="display:flex;"><span>    _, predicted <span style="color:#f92672">=</span> test_spk<span style="color:#f92672">.</span>sum(dim<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>)<span style="color:#f92672">.</span>max(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>    total <span style="color:#f92672">+=</span> targets<span style="color:#f92672">.</span>size(<span style="color:#ae81ff">0</span>)
</span></span><span style="display:flex;"><span>    correct <span style="color:#f92672">+=</span> (predicted <span style="color:#f92672">==</span> targets)<span style="color:#f92672">.</span>sum()<span style="color:#f92672">.</span>item()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Total correctly classified test set images: </span><span style="color:#e6db74">{</span>correct<span style="color:#e6db74">}</span><span style="color:#e6db74">/</span><span style="color:#e6db74">{</span>total<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Test Set Accuracy: </span><span style="color:#e6db74">{</span><span style="color:#ae81ff">100</span> <span style="color:#f92672">*</span> correct <span style="color:#f92672">/</span> total<span style="color:#e6db74">:</span><span style="color:#e6db74">.2f</span><span style="color:#e6db74">}</span><span style="color:#e6db74">%&#34;</span>)
</span></span></code></pre></div><h3 id="additional-reading-on-sugd">Additional reading on SuGD</h3>
<h5 id="foundational-details">Foundational details</h5>
<ul>
<li>Neftci, Mostafa &amp; Zenke (2019)</li>
<li>Eshraghian, Ward, Neftci et al. (2021/2023)</li>
</ul>
<h5 id="reset-mechanisms">reset mechanisms</h5>
<ul>
<li>Zenke &amp; Vogels (2021)</li>
</ul>
<h5 id="synapticmembrane-dynamics">synaptic/membrane dynamics</h5>
<ul>
<li>Wu et al. (2018),</li>
</ul>
<h5 id="initializations">Initializations</h5>
<ul>
<li>Rossbroich, Gygax &amp; Zenke (2022), &ldquo;Fluctuation-Driven Initialization for Spiking Neural Network Training,&rdquo; Neuromorphic Computing and Engineering</li>
<li>Micheli et al. (2024/2025), &ldquo;Deep Activity Propagation via Weight Initialization in Spiking Neural Networks&rdquo;</li>
</ul>
<h5 id="miscellaneous">miscellaneous</h5>
<ul>
<li>Direct Training High-Performance Deep Spiking Neural Networks: A Review of Theories and Methods</li>
<li>Fractional-order Spiking Neural Network</li>
</ul>
<h3 id="pytorch-tricks">Pytorch tricks</h3>
<h3 id="pytorch-drawbacks">Pytorch drawbacks</h3>
<h3 id="what-are-we-missing-in-the-snns-">what are we missing in the SNNs ?</h3>
<h3 id="references">References</h3>
<ol>
<li><a href="10.1088/2634-4386/ae46d5">Ziqiao Yu et al 2026 Neuromorph. Comput. Eng. 6 014016</a></li>
<li><a href="https://github.com/neural-reckoning/cambridge-fens-chen-summer-school-2026">Dan F M Goodman, 2026 FENS Chen Summer School on Learning with spikes</a></li>
</ol>
]]></content:encoded>
    </item>
    <item>
      <title>RNN Documentation</title>
      <link>https://shalemrajkumar.github.io/mydocs/rnn/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://shalemrajkumar.github.io/mydocs/rnn/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
This is a work in progress and may contain inaccuracies or incomplete information.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;RNNs are first working initiative to process sequential data by maintaining a hidden state that captures information about previous elements (some early varient of context) in the sequence. They are widely used in various applications such as language modeling, speech recognition, and time series prediction.&lt;/p&gt;
&lt;p&gt;Different types of sequential data include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Text data (e.g., sentences, documents)&lt;/li&gt;
&lt;li&gt;Time series data (e.g., stock prices, weather data)&lt;/li&gt;
&lt;li&gt;Audio data (e.g., speech signals, music)&lt;/li&gt;
&lt;li&gt;Video data (e.g., frames in a video sequence)&lt;/li&gt;
&lt;li&gt;Biological sequences (e.g., DNA, protein sequences)&lt;/li&gt;
&lt;li&gt;sequence of actions (e.g., user behavior, robot movements)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Different ways to implement RNNs include:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p>[!WARNING]
This is a work in progress and may contain inaccuracies or incomplete information.</p></blockquote>
<h2 id="introduction">Introduction</h2>
<p>RNNs are first working initiative to process sequential data by maintaining a hidden state that captures information about previous elements (some early varient of context) in the sequence. They are widely used in various applications such as language modeling, speech recognition, and time series prediction.</p>
<p>Different types of sequential data include:</p>
<ul>
<li>Text data (e.g., sentences, documents)</li>
<li>Time series data (e.g., stock prices, weather data)</li>
<li>Audio data (e.g., speech signals, music)</li>
<li>Video data (e.g., frames in a video sequence)</li>
<li>Biological sequences (e.g., DNA, protein sequences)</li>
<li>sequence of actions (e.g., user behavior, robot movements)</li>
</ul>
<p>Different ways to implement RNNs include:</p>
<ul>
<li>point neurons based RNNs (optimized with backpropagation through time)</li>
<li>spiking neurons based RNNs (optimized with surrogate gradient descent)</li>
</ul>
<p>Different architectures of RNNs include:</p>
<ul>
<li><a href="#vanilla-rnn">Vanilla RNNs</a></li>
<li><a href="#long-short-term-memory">Long Short-Term Memory (LSTM) networks</a></li>
<li>Gated Recurrent Units (GRU)</li>
<li>Bidirectional RNNs</li>
<li>Deep RNNs</li>
</ul>
<p>RNNs in biological neural systems are core of most cognitive function, they are responsible for information processing, maintaining working memory, and generating temporal patterns of activity that facilitate learning. Feedback is basic subunit of intelligence in biological systems!</p>
<p>Neuroscience models:</p>
<ul>
<li>Elman Networks</li>
<li>Jordan networks</li>
<li>Hopfield Networks</li>
<li>Echo State networks</li>
<li>Liquid State Machines</li>
</ul>
<p>Neuroscience applications of RNNs:</p>
<ul>
<li>Working Memory</li>
<li>Sequence Generation</li>
<li>Temporal Pattern recognition</li>
<li>Motor Control</li>
<li>Spontaneous Activity based circuit refinement</li>
</ul>
<p>Optimization techniques for RNNs:</p>
<ul>
<li>Backpropagation Through Time (BPTT)</li>
<li>Real-Time Recurrent Learning (RTRL)</li>
<li>Truncated Backpropagation Through Time (TBPTT)</li>
<li>Surrogate gradient descent (for spiking RNNs) {not really sure if they can be used}</li>
</ul>
<h2 id="point-neuron-based-rnns">Point neuron based RNNs</h2>
<h3 id="vanilla-rnn">Vanilla RNN</h3>
<figure style="text-align: center;">
  <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Recurrent_neural_network_unfold.svg/500px-Recurrent_neural_network_unfold.svg.png?utm_source=en.wikipedia.org&utm_campaign=parser&utm_content=thumbnail"width="400" alt="Vanilla-RNN">
  <figcaption>image credit: wikipedia</figcaption>
</figure>
<h4 id="algorithm">algorithm</h4>
<ol>
<li>Initialize weights and biases</li>
<li>Compute forward pass
<ul>
<li>at t=0
<ul>
<li>Initialize hidden state: $$ h_0  = 0 $$</li>
</ul>
</li>
<li>For each time step t:
<ul>
<li>Compute hidden state: $$ h_t = f\left(W_{ih} * x_t + W_{hh} * h_{t-1} + b_h)\right) $$</li>
<li>Compute output: $$ y_t = W_{ho} * h_t + b_o $$</li>
<li>[Optional] Apply activation function to output: $$ y_t = g(y_t) $$</li>
</ul>
</li>
</ul>
</li>
<li>Compute loss</li>
<li>Backpropagate errors through time</li>
<li>Update weights and biases</li>
<li>Repeat for multiple epochs</li>
</ol>
<h4 id="limitations-and-motivations-for-advanced-architectures">limitations and motivations for advanced architectures</h4>
<p>Classic RNNs can keep track of arbitrary long-term dependencies in the input sequences. But during training via back-propagation, the long-term gradients which are back-propagated can &ldquo;vanish&rdquo;, RNNs using LSTM units partially solve the vanishing gradient problem, because LSTM units allow gradients to also flow with little to no attenuation. However, LSTM networks can still suffer from the &ldquo;exploding gradient problem&rdquo;.</p>
<hr>
<hr>
<h3 id="long-short-term-memory">Long Short-Term Memory</h3>
<h4 id="introduction-1">introduction</h4>
<p>LSTMs architecture is designed to address the vanishing gradient problem in traditional RNNs by introducing <em>memory cell (or cell)</em> and <em>gating mechanisms</em> that allof the network to maintain and update information over longer sequences (usually short-term memory for RNN that can last thousands of timesteps (thus longterm))<a href="#lstmref1">1</a>.</p>
<p>An LSTM unit is typically composed of a cell and three gates:</p>
<ul>
<li><strong>Cell State (special hidden state)</strong> $C_t$:<br>
-Carries information across arbitary time steps. while the gates regulate the flow of information into and out of the cell state.</li>
<li><strong>Forget Gate</strong> $F_t$:
<ul>
<li>Determines which information from the previous cell state should be discarded.</li>
<li>It maps the previous state and the current input to a value between 0 and 1 (after rounding 0 $\rightarrow$ completely forget, 1 $\rightarrow$ completely retain)</li>
</ul>
</li>
<li><strong>Input Gate</strong> $I_t$:
<ul>
<li>Input gates decide which pieces of new information to store in the current cell state. ( similar mechanism like forget gates)</li>
</ul>
</li>
<li><strong>Output Gate</strong> $O_t$:
<ul>
<li>Output gates control which pieces of information in the current cell state to output. (similar mechanisms)</li>
</ul>
</li>
</ul>
<p>Primary goal of LSTM subunits is to be able to decide when to <code>remember</code> and when to <code>ignore inputs in the hidden state</code> via a dedicated mechanism.</p>
<hr>
<h4 id="input-output-forget-gates-and-memory-cell"><code>Input, output, forget gates and memory cell</code></h4>
<p>At each time step t, the LSTM performs the following operations:</p>
<ul>
<li>Input steam from previous hidden state ( h_{t-1} ) and current input ( x_t ) are processed ahead with 3 fully connected layers thresholded with sigmoid -&gt; (0, 1).
<img alt="image showing computaion of forget gate, input gate, output gate" loading="lazy" src="https://classic.d2l.ai/_images/lstm-0.svg"></li>
</ul>
<p>it can be written as:</p>
<ul>
<li>$$ I_t \ = \ \sigma(X_t W_{xi} + H_{t-1} W_{hi} + b_i) $$</li>
<li>$$ F_t \ = \ \sigma(X_t W_{xf} + H_{t-1} W_{hf} + b_f) $$</li>
<li>$$ O_t \ = \ \sigma(X_t W_{xo} + H_{t-1} W_{ho} + b_o) $$</li>
</ul>
<hr>
<h4 id="candidate-memory-cell-tildec_t"><code>Candidate memory cell</code> $\tilde{C_t}$</h4>
<p><img alt="image showing computation of candidate memory cell" loading="lazy" src="https://classic.d2l.ai/_images/lstm-1.svg"></p>
<ul>
<li>
<p>Memory cell ($C_t$) similar to hidden state ($H_t$) dimensions</p>
</li>
<li>
<p>$$ \tilde{C_t} = tanh(X_t W_{xc} + H_{t-1} W_{hc} + b_c) $$</p>
<ul>
<li>This computation is similar to other gates except the use of tanh activation -&gt; (-1, 1).</li>
</ul>
</li>
</ul>
<hr>
<h4 id="updating-memory-cell-c_t"><code>Updating memory cell</code> $C_t$</h4>
<p><img alt="image showing computation of updating memory cell" loading="lazy" src="https://classic.d2l.ai/_images/lstm-2.svg"></p>
<ul>
<li>
<p>Now we have $\tilde{C_t}$ and $C_{t-1}$</p>
<ul>
<li>The $I_t$ and $F_t$ ranging from 1 to 0 defines how much of $\tilde{C_t}$ and $C_{t-1}$ to keep respectively.</li>
</ul>
</li>
<li>
<p>$$ C_t = F_t \odot C_{t-1} + I_t \odot \tilde{C_t} $$</p>
</li>
</ul>
<hr>
<h4 id="computing-hidden-state-h_t"><code>Computing hidden state</code> $H_t$</h4>
<p><img alt="image showing computation of hidden state" loading="lazy" src="https://classic.d2l.ai/_images/lstm-3.svg"></p>
<ul>
<li>
<p>Output gate $O_t$ defines how much of the $C_t$ to output as hidden state $H_t$.</p>
</li>
<li>
<p>$$ H_t = O_t \odot tanh(C_t) $$</p>
</li>
<li>
<p>LSTM it is simply a gated version of the of the memory cell (final prediction).</p>
</li>
<li>
<p>So whenever output gate is closed, no $H_t$ information is passed to the next layer.</p>
</li>
</ul>
<hr>
<h2 id="optimization-techniques-for-rnns">Optimization techniques for RNNs</h2>
<h3 id="backpropagation-through-time-bptt">Backpropagation Through Time (BPTT)</h3>
<p>The tricky part about RNNs is their recurrent nature, which makes it impossible to apply standard backpropagation directly. But curret approach is to &ldquo;unroll&rdquo; the RNN through time, treating it feedforward network. Now compute the loss at each time step and backpropagate the errors through the unrolled network.</p>
<pre tabindex="0"><code>Time:         t
         
Output:       ŷₜ
              ↑        
Cell:   --&gt; [RNN] --&gt; ...
              ↑    hₜ
Input:        xₜ
</code></pre><pre tabindex="0"><code>Time:     t=0        t=1        t=2        t=3
         
Output:   ŷ₀         ŷ₁         ŷ₂         ŷ₃
           ↑          ↑          ↑          ↑
Cell:    [RNN] --&gt;  [RNN] --&gt;  [RNN] --&gt;  [RNN]
           ↑    h₀    ↑    h₁    ↑    h₂    ↑    h₃
Input:    x₀         x₁         x₂         x₃

Forward:  ────────────────────────────────────&gt;
BPTT:     &lt;────────────────────────────────────
</code></pre><h4 id="additional-resources-and-documentation">additional resources and documentation</h4>
<ul>
<li><a href="">mydocs</a></li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Recurrent Neural Networks are inspired from the neuroscience, a fully cross-coupled perceptron network is equivalent to an infinitely deep feedforward network but we train these networks with backpropagation which has limitations such as vanishing and exploding gradients. To overcome these limitations, more advanced architectures like LSTM and GRU were developed, but the fundamental question still remains, &ldquo;how our brain recurrent motifs learn ?&rdquo;</p>
<h2 id="references">References</h2>
<h3 id="lstm-references">LSTM references</h3>
<p><a id="lstmref1"></a></p>
<ol>
<li><a href="https://www.wikiwand.com/en/articles/Long_short-term_memory">wiki</a>
<a id="lstmref2"></a></li>
<li><a href="https://classic.d2l.ai/chapter_recurrent-modern/lstm.html">Dive into Deep Learning - RNNs</a></li>
</ol>
<h2 id="additional-references-and-tutorials">Additional references and tutorials</h2>
<h3 id="vanilla-rnn-tutorials">Vanilla RNN tutorials</h3>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Recurrent_neural_network">wiki</a></li>
<li><a href="https://docs.pytorch.org/docs/stable/generated/torch.nn.RNN.html">RNNs by torch</a></li>
<li><a href="https://www.kaggle.com/code/namanmanchanda/rnn-in-pytorch">RNN tutorial from kaggle</a></li>
<li><a href="https://medium.com/@noorfatimaafzalbutt/recurrent-neural-networks-rnn-with-pytorch-a-complete-guide-8c40c69032d2">RNN tutorial from medium</a></li>
<li><a href="https://www.codecademy.com/article/rnn-py-torch-time-series-tutorial-complete-guide-to-implementation">RNN tutorial from data_academy</a></li>
<li><a href="https://solardevs.com/blog/rnn-from-scratch-pytorch/">RNN tutorial by solardevs</a></li>
<li><a href="https://www.deeplearningwizard.com/deep_learning/practical_pytorch/pytorch_recurrent_neuralnetwork/#steps_2">RNN tutorial by deeplearning wizard</a></li>
<li><a href="https://www.analyticsvidhya.com/blog/2021/07/understanding-rnn-step-by-step-with-pytorch/">RNN tutorial by analyticsvidhya</a></li>
<li><a href="https://apxml.com/courses/getting-started-with-pytorch/chapter-7-introduction-common-architectures/building-simple-rnn">RNN tutorial APX</a></li>
<li><a href="https://jaketae.github.io/study/pytorch-rnn/">RNN tutorial by Jake</a></li>
<li><a href="https://www.cs.toronto.edu/~lczhang/aps360_20191/lec/w06/rnn.html">RNN tutorial by Utoronto</a></li>
<li><a href="https://towardsdatascience.com/rnns-from-theory-to-pytorch-f0af30b610e1/">RNN tutorial by towardsdatascience</a></li>
</ul>
]]></content:encoded>
    </item>
  </channel>
</rss>
