Memory usage increases by at least 30 when applying model

username: mrosett
cooked: <p>Summary: With a ~100mb model and a ~400mb batch of training data, <code>model(x)</code> causes an OOM despite having 16 GB of memory available.</p>
<p>I’ve been playing around with the Recursion Pharmaceuticals competition over on Kaggle, and I’ve noticed bizarre spikes in memory usage when I call models. I’ve tried to create a minimal example <a href="https://www.kaggle.com/mrosett/rxrx-memory-issues?scriptVersionId=17208118" rel="nofollow noopener">here</a>. All of the code is present at that link, but here’s a summary of what I’m doing:</p>
<p>The data is 512x512 images with 6 channels. I’m using a pretty standard data loader to load them; the code is contained in <code>ImagesDS</code> in cell 3. (The images should be normalized, but that doesn’t seem relevant here.)</p>
<p>The model is a Resnet50 with pretrained weights. However, since I have 6 channels and 1108 outputs, I replace the first and last layer. (I’ve seen the same error with other different base models like Densenet.):</p>
<pre><code class="lang-auto">model = torchvision.models.resnext50_32x4d(pretrained=True)
model.conv1 = nn.Conv2d(6, 64, 7, 2, 3)
model.fc = nn.Linear(2048, classes, bias=True)
model.to(device)
</code></pre>
<p>Finally, I’m getting the out of memory error on the first pass through the training loop:</p>
<pre><code class="lang-auto">epochs = 10
tlen = len(loader)
for epoch in range(epochs):
    tloss = 0
    acc = np.zeros(1)
    for x, y in loader: 
        print(f'Memory allocated after tensor load to cpu: {torch.cuda.memory_allocated() / 10 ** 6} MB') # Gives ~100mb
        x = x.to(device)
        print(f'Memory allocated after tensor load to gpu: {torch.cuda.memory_allocated() / 10 ** 6} MB') # Gives ~500mb
        optimizer.zero_grad()
        # Everything explodes when we call the model on the input.
        output = model(x)
        # More training code that is never reached....
</code></pre>
<p>With a batch size of 64 (~400mb of input data), the loop causes an OOM. With a batch size of 16 (~100mb), memory usage never goes above ~500mb.</p>
<p>I can see a few possibilities here, but I’m unsure what’s most likely:</p>
<ul>
<li>I’m doing something wrong when I load the data. Maybe the input tensors need to have requires_grad=False explicitly set on them or something</li>
<li>There’s some kind of memory allocation bug in Pytorch that I’m seeing here</li>
<li>There’s something about Kaggle’s GPU set up that causes the memory error.</li>
</ul>
<p>Any ideas? The full code is at the link above, and you can easily clone it if you have a Kaggle account.</p>

username: ptrblck
cooked: <p>Note that besides the parameters and input, the training will also use memory for the forward activations, which are needed to compute the gradients (which also need to be stored on the device).<br>
Is your training working with a smaller batch size?</p>

username: mrosett
cooked: <p>Thanks for the prompt reply! I think you’re right; I underestimated how large the intermediate results are. I assumed they’d be roughly proportional to the size of the input, but now that I think about it that’s silly. Since the network works fine with a batch size of 16 images, I don’t think there’s anything else going on.</p>
<p>If you don’t mind, how can I access those intermediate forward activations? I thought code like this would work to get the output of the first layer:</p>
<pre><code class="lang-auto">result = model(x)
conv1 = next(model.children())
for b in conv1.buffers():
    print(b)
</code></pre>
<p>However, there don’t seem to be any buffers associated with that layer. Is there a simple way to see those outputs?</p>

username: ptrblck
cooked: <p>Good to hear a batch size of 16 works.<br>
Yeah, the intermediate activations can be quite huge, e.g. especially if you are using a lot of kernels in a conv layer.</p>
<p><code>.buffers</code> is used for internal tensors, which do not require gradients, e.g. the <code>running_mean</code> and <code>running_var</code> in batchnorm layers.<br>
If you want to get the intermediate outputs, you could register forward hooks as explained in <a href="https://discuss.pytorch.org/t/how-can-l-load-my-best-model-as-a-feature-extractor-evaluator/17254/6">this post</a>.</p>