Splitting neurons, best practice

rb11: <p>I want to implement neurons splitting and don’t know how to approach this using Pytorch.<br>
I’m working on an RL problem and observe that the number of dead ReLU neurons (i.e. neurons which are zero forever) in my model constantly increases.<br>
This is at least partially related to the fact that the distribution of the training data changes over time as behavior becomes more complex.</p>
<p>I’d like to try to substitute dead neurons by copies of “alive”:<br>
If neuron A is alive and B is dead (forever zero) I can copy weights/bias of A to B and divide weights for A in the next layer between A and B, like Anew = Bnew = 0.5 * A.<br>
As I understand this won’t change net’s output at all.<br>
To make splitted neurons drift from each other over time I’m going to add some noise, but this is a different story.</p>
<p>So the question is how to implement it in Pytorch in the most correct way?<br>
The problem is how can I tell what’s the next layer and what indices in it correspond to indices in the current one.<br>
The problem may seem trivial if the model consists of a couple of fc layers, but I’ve got a rather complicated model with residual connections, splits and concatenations…<br>
Pytorch builds computation graph, is it possible to extract this information from it?<br>
I would really like not to build workaround here as my model (code) would become extremely fragile.</p>
<p>Roman</p>

albanD: <p>Hi,</p>
<p>I am not sure to follow what you want to do here.</p>
<blockquote>
<p>As I understand this won’t change net’s output at all.</p>
</blockquote>
<p>If you change the value of the activations, it will change the value of the next layer. Because it is very unlikely that the next layer treats A and B exactly the same way.<br>
If you change the value of the weights, it will change also as it is very unlikely that the input for A and B are the same.</p>
<p>But what if you want to do is update the weights of a single layer (let’s say Linear), you can simply do</p>
<pre data-code-wrap="python"><code class="lang-python">with torch.no_grad():
  layer.weight[alive_idx] /= 2
  layer.weight[dead_idx] = layer.weight[alive_idx]

  layer.bias[alive_idx] /= 2
  layer.bias[alive_idx] = layer.bias[alive_idx]
</code></pre>

rb11: <p>Thank you for the reply!</p>
<p>Here is my very simple diagram of what I want to achieve:<br>
<div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/9/2/9212dc77cf41835c9eb1eb7d315b9eca24632a5b.png" data-download-href="https://discuss.pytorch.org/uploads/default/9212dc77cf41835c9eb1eb7d315b9eca24632a5b" title="split"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/9/2/9212dc77cf41835c9eb1eb7d315b9eca24632a5b_2_337x500.png" alt="split" data-base62-sha1="kQe7tGMcAA0rK0CSc6IflN1l6WL" width="337" height="500" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/9/2/9212dc77cf41835c9eb1eb7d315b9eca24632a5b_2_337x500.png, https://discuss.pytorch.org/uploads/default/optimized/3X/9/2/9212dc77cf41835c9eb1eb7d315b9eca24632a5b_2_505x750.png 1.5x, https://discuss.pytorch.org/uploads/default/original/3X/9/2/9212dc77cf41835c9eb1eb7d315b9eca24632a5b.png 2x" data-dominant-color="FCFBFB"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">split</span><span class="informations">540×800 44.6 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div><br>
I understand that identical subnets will forever be the same, so I’m going to add some noise to them to make them drift away from each other over time.<br>
Also new weights don’t have to be 50% of original, they only need to sum to original.<br>
But again this is a different story.</p>
<p>My question is is it possible somehow automatically (probably hacky) to detect indices in the next layer corresponding to the given dead/alive neurons in the current layer?<br>
For a simple feed forward net like on the diagram it’s trivial, but in my model after relu I split/concatenate/sum tensors so it’s rather problematic to keep that knowledge “externally”.<br>
I know Pytorch builds calculation graph as operations get applied to the tensors, is it possible to extract such kind of info from the graph?</p>
<p>Roman</p>

albanD: <p>Hi,</p>
<p>There is no automatic way to do that for sure.<br>
And it’s not even always true that you will have a change to the downstream layer that will give you what you want. In particular, you mention “sum”, that could lead to multiple nodes being used with the same weights and so you cannot modify these ways for some nodes and not others.</p>

rb11: <p>Thanks for clarification!</p>

J_Johnson: <p>I’m actually working on an algorithm for this very thing. Currently, the algorithm only works on Linear and Conv2D layers. GRU is in the works. Additionally, it will be updated soon to include removing target neurons.<br>
How neurons are targetted for splitting is by taking an average of the absolute value of the gradients during an epoch. Then any neurons that are above the cutoff will get split.<br>
But what you are referring to is not neuron splitting, but neuron removal. When that update is released, it will target neurons with gradient movement below the threshold you set.</p>
<p>Here is the Github with working examples and tests showing how this performs vs non-splitting.</p>
<aside class="onebox githubfolder">
  <header class="source">
      <img src="https://github.githubassets.com/favicons/favicon.svg" class="site-icon" width="32" height="32">
      <a href="https://github.com/CerebralSeed/Neural-Splitting-with-Dice-Roll-in-Pytorch" target="_blank" rel="noopener nofollow ugc">github.com</a>
  </header>
  <article class="onebox-body">
    <div class="aspect-image" style="--aspect-ratio:420/420;"><img src="https://avatars.githubusercontent.com/u/78452949?s=400&amp;amp;v=4" class="thumbnail" width="420" height="420"></div>

<h3><a href="https://github.com/CerebralSeed/Neural-Splitting-with-Dice-Roll-in-Pytorch" target="_blank" rel="noopener nofollow ugc">CerebralSeed/Neural-Splitting-with-Dice-Roll-in-Pytorch</a></h3>


  <p><span class="label1">This demonstrates a dynamic neural splitting module implemented in Pytorch and combined with a "best of n" starting parameters. </span></p>

  </article>
  <div class="onebox-metadata">
    
    
  </div>
  <div style="clear: both"></div>
</aside>

<p>Please note that you can use this code for personal or research projects freely. If for other purposes, such as commercial, please contact me for licensing and support/development.</p>

J_Johnson: <p>Sorry for the delay. I have updated the function to be able to remove neurons deterministically via <code>cutoffrem</code>. I’ve also provided some simple steps for setup and a script that can be imported. Link is already provided above. It works for Linear and Conv2D layers for now. RNN layers like GRU or LSTM cannot be handled this way due to the hidden state(tried several approaches, but all suffered poor performance). Please let me know if you find any bugs.</p>