How to prevent this memory leak?

username: Sam_P
cooked: <p>Here’s a simple repro of my situation:</p>
<pre><code class="lang-auto">import torch
import torch.nn as nn

feature_size = 1000
window = torch.zeros((5, feature_size, 1), requires_grad=True).cuda()
input_encoder = nn.Linear(3 * 224 * 224, feature_size).cuda()
step_count = 0

while True:
    # Create new element
    new_element = torch.zeros((1, 3, 224, 224), requires_grad=True).cuda()
    new_element = input_encoder(new_element.reshape(1, -1))

    # Remove last element and add the new one
    window = window[:-1]
    window = torch.cat((new_element.unsqueeze(-1), window), dim=0)

    step_count += 1
    print("Step: {}".format(step_count))
</code></pre>
<p>On my Titan Xp (12GB memory) this runs out of memory after just over 9300 steps. I tested initially using PyTorch 1.0.0 with cuda 8, but I repro’d it also with PyTorch 1.1.0 and cuda 10. In both cases using Python 3.5 on Windows 10.</p>
<p>Now ideally, what’s happening is that a new element gets added to window, and one gets removed, and all of the resources associated with the old one get cleared. I’m not sure how best to achieve freeing the old one though. Neither gc.collect() nor torch.cuda.empty_cache() have fixed the problem.</p>
<p>If I define memReport:</p>
<pre><code class="lang-auto">def memReport():
    for obj in gc.get_objects():
        if torch.is_tensor(obj):
            print(type(obj), obj.size())
</code></pre>
<p>and run it in the loop, the result is always:</p>
<pre><code class="lang-auto">&lt;class 'torch.Tensor'&gt; torch.Size([1, 1000])
&lt;class 'torch.Tensor'&gt; torch.Size([5, 1000, 1])
&lt;class 'torch.nn.parameter.Parameter'&gt; torch.Size([1000, 150528])
&lt;class 'torch.nn.parameter.Parameter'&gt; torch.Size([1000])
</code></pre>
<p>Anyone have any advice or pointers on freeing resources and preventing this memory leak?</p>
<p>Thanks!</p>

username: jerinphilip
cooked: <p>Can you perhaps post a more complete sample. This one doesn’t have <code>backward(...)</code> which actually removes the graph and frees up memory. I’m assuming you omitted it to post pseudocode. From the example:</p>
<pre><code class="lang-python">    window = torch.cat((new_element.unsqueeze(-1), window), dim=0)
</code></pre>
<p>The above particular line possibly keeps things in memory. If it’s okay for you, try:</p>
<pre><code class="lang-python">    window = torch.cat((new_element.detach().unsqueeze(-1), window), dim=0)
</code></pre>

username: Sam_P
cooked: <p>Very interesting. Adding:</p>
<pre><code class="lang-auto">loss = window.mean()
loss.backward(retain_graph=True)
</code></pre>
<p>to the loop fixes the leak. Can you (or someone) explain what exactly is happening here?</p>
<p>It’s true that this is a min repro of a bigger codebase (though I did not intend it as pseudocode), but my actual code also does not run backward terribly frequently at the moment, by design, and it would be useful if there was a way I could prevent this memory accumulation between backwards in some way? (Or if it’s unavoidable I’d like to understand why, at least.)</p>
<p>What would the ramifications be of doing backward on some, essentially, fake loss, and then later (when I’m ready) actually doing the full zero_grad/backward/step with my real loss? What is backward(retain_graph=True) actually doing here to free my memory? (I initially assumed retain_graph would mean there was no benefit to running it, but I seem to have been wrong about that.) Is there some less hacky way of doing this?</p>
<p>Regarding your other question: With the detach it does not run out of memory, but it does break the graph chain I’m trying to construct, I’m pretty sure. But anyway the backward seems to have been the issue.</p>
<p>Thanks so much for your insight, I definitely assumed that backward only freed the tensors created by the previous backward; I did not know it also impacted tensors created during the forward pass.</p>

username: jerinphilip
cooked: <p>See if one of the following threads suits your case.</p>
<aside class="quote quote-modified" data-post="1" data-topic="30020">
  <div class="title">
    <div class="quote-controls"></div>
    <img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/sachinruk/48/6542_2.png" class="avatar">
    <a href="https://discuss.pytorch.org/t/accumulating-gradients/30020">Accumulating Gradients</a> 
  </div>
  <blockquote>
    I want to accumulate the gradients before I do a backward pass. So wondering what the right way of doing it is. According to <a href="https://medium.com/huggingface/training-larger-batches-practical-tips-on-1-gpu-multi-gpu-distributed-setups-ec88c3e51255" rel="noopener nofollow ugc">this article</a> it’s (let’s assume equal batch sizes): 
model.zero_grad()                                   # Reset gradients tensors
for i, (inputs, labels) in enumerate(training_set):
    predictions = model(inputs)                     # Forward pass
    loss = loss_function(predictions, labels)       # Compute loss function
    loss = loss / accumulation_steps             …
  </blockquote>
</aside>

<aside class="quote quote-modified" data-post="1" data-topic="15500">
  <div class="title">
    <div class="quote-controls"></div>
    <img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/adrianjav/48/12404_2.png" class="avatar">
    <a href="https://discuss.pytorch.org/t/implementing-truncated-backpropagation-through-time/15500">Implementing Truncated Backpropagation Through Time</a> <a class="badge-category__wrapper " href="/c/autograd/7"><span data-category-id="7" data-drop-close="true" class="badge-category " title="A category of posts relating to the autograd engine itself."><span class="badge-category__name">autograd</span></span></a>
  </div>
  <blockquote>
    Hello, 
I’m implementing a recursive network that is going to be trained with very long sequences. I had memory problems when training because of that excessive length and I decided to use a truncated-BPTT algorithm to train it as described <a href="https://machinelearningmastery.com/gentle-introduction-backpropagation-time/" rel="noopener nofollow ugc">here</a>, that is, 
every k1 steps backpropagate taking k2 back steps 
checking some <a href="https://github.com/pytorch/examples/blob/master/word_language_model/main.py" rel="noopener nofollow ugc">examples</a>  I could easily write the case when k1 = k2. However, I haven’t been able to implement the general case yet. 
My first idea was to freeze the  gradient graph after the f…
  </blockquote>
</aside>


username: Sam_P
cooked: <p>Oh, I was wrong. My apologies. Adding the backward just slowed the network down so much it just looks like the memory usage was flat, and I was just not paying enough attention - by step 4k the memory was half consumed (doing a backward every 1000).</p>
<p>My issue is basically that I know what I need to remove, I just don’t have any way to sever it completely from the graph as far as I can tell. The links are interesting but are really not quite what I’m looking for. And window[-1] = window[-1].detach() doesn’t do much as far as I can tell, when window is a tensor.</p>
<p>I think I’ve decided what I want basically just can’t be done for some reason. Once an element is put into a tensor, it seems to live in some form in the graph that I can’t purge even when the element is no longer in the tensor.</p>
<p>This would have been convenient for other reasons in my graph, but I think I’ll just have to make do with a list and torch.stack.</p>
<p>Anyway, thanks for the help. <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=9" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>