Problems with Legacy autograd function

username: Gonzalo_A_Benavides
cooked: <p>Hello, I am new to PyTorch and I have been running into several problems when trying to run the file main.py from <a href="https://github.com/jiecaoyu/XNOR-Net-PyTorch/blob/master/MNIST/main.py" class="inline-onebox" rel="noopener nofollow ugc">XNOR-Net-PyTorch/main.py at master · jiecaoyu/XNOR-Net-PyTorch · GitHub</a>. I have the newest stable version of PyTorch with Cuda 11.7.</p>
<p>I get the following error</p>
<hr>
<p>Traceback (most recent call last):</p>
<p>File “C:\Users\saft_\miniconda3\envs\XNOR\lib\site-packages\spyder_kernels\py3compat.py”, line 356, in compat_exec<br>
exec(code, globals, locals)</p>
<p>File “c:\users\saft_\machinelearning\xnor-net-pytorch\mnist\main.py”, line 189, in <br>
train(epoch)</p>
<p>File “c:\users\saft_\machinelearning\xnor-net-pytorch\mnist\main.py”, line 39, in train<br>
output = model(data)</p>
<p>File “C:\Users\saft_\miniconda3\envs\XNOR\lib\site-packages\torch\nn\modules\module.py”, line 1190, in _call_impl<br>
return forward_call(*input, **kwargs)</p>
<p>File “C:\Users\saft_\MachineLearning\XNOR-Net-PyTorch\MNIST\models\LeNet_5.py”, line 93, in forward<br>
x = self.bin_conv2(x)</p>
<p>File “C:\Users\saft_\miniconda3\envs\XNOR\lib\site-packages\torch\nn\modules\module.py”, line 1190, in _call_impl<br>
return forward_call(*input, **kwargs)</p>
<p>File “C:\Users\saft_\MachineLearning\XNOR-Net-PyTorch\MNIST\models\LeNet_5.py”, line 53, in forward<br>
x = BinActive()(x)</p>
<p>File “C:\Users\saft_\miniconda3\envs\XNOR\lib\site-packages\torch\autograd\function.py”, line 330, in <strong>call</strong><br>
raise RuntimeError(</p>
<h2>
<a name="runtimeerror-legacy-autograd-function-with-non-static-forward-method-is-deprecated-please-use-new-style-autograd-function-with-static-forward-method-example-httpspytorchorgdocsstableautogradhtmltorchautogradfunction-1" class="anchor" href="#runtimeerror-legacy-autograd-function-with-non-static-forward-method-is-deprecated-please-use-new-style-autograd-function-with-static-forward-method-example-httpspytorchorgdocsstableautogradhtmltorchautogradfunction-1"></a>RuntimeError: Legacy autograd function with non-static forward method is deprecated. Please use new-style autograd function with static forward method. (Example: <a href="https://pytorch.org/docs/stable/autograd.html#torch.autograd.Function" class="inline-onebox" rel="noopener nofollow ugc">Automatic differentiation package - torch.autograd — PyTorch 1.13 documentation</a>)</h2>
<p>The entire code reads</p>
<pre><code class="lang-auto">from __future__ import print_function
import argparse
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import os
import sys
import models
import util
from torchvision import datasets, transforms
from torch.autograd import Variable

import util

def save_state(model, acc):
    print('==&gt; Saving model ...')
    state = {
            'acc': acc,
            'state_dict': model.state_dict(),
            }
    for key in state['state_dict'].keys():
        if 'module' in key:
            state['state_dict'][key.replace('module.', '')] = \
                    state['state_dict'].pop(key)
    torch.save(state, 'models/'+args.arch+'.best.pth.tar')

def train(epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        if args.cuda:
            data, target = data.cuda(), target.cuda()
        data, target = Variable(data), Variable(target)
        optimizer.zero_grad()

        # process the weights including binarization
        bin_op.binarization()

        output = model(data)
        loss = criterion(output, target)
        loss.backward()

        # restore weights
        bin_op.restore()
        bin_op.updateBinaryGradWeight()

        optimizer.step()
        if batch_idx % args.log_interval == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.data.item()))
    return

def test(evaluate=False):
    global best_acc
    model.eval()
    test_loss = 0
    correct = 0

    bin_op.binarization()
    for data, target in test_loader:
        if args.cuda:
            data, target = data.cuda(), target.cuda()
        data, target = Variable(data, volatile=True), Variable(target)
        output = model(data)
        test_loss += criterion(output, target).data.item()
        pred = output.data.max(1, keepdim=True)[1]
        correct += pred.eq(target.data.view_as(pred)).cpu().sum()

    bin_op.restore()
    
    acc = 100. * float(correct) / len(test_loader.dataset)
    if (acc &gt; best_acc):
        best_acc = acc
        if not evaluate:
            save_state(model, best_acc)

    test_loss /= len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)'.format(
        test_loss * args.batch_size, correct, len(test_loader.dataset),
        100. * float(correct) / len(test_loader.dataset)))
    print('Best Accuracy: {:.2f}%\n'.format(best_acc))
    return

def adjust_learning_rate(optimizer, epoch):
    """Sets the learning rate to the initial LR decayed by 10 every 15 epochs"""
    lr = args.lr * (0.1 ** (epoch // args.lr_epochs))
    print('Learning rate:', lr)
    for param_group in optimizer.param_groups:
        param_group['lr'] = lr
    return lr

if __name__=='__main__':
    # Training settings
    parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
    parser.add_argument('--batch-size', type=int, default=128, metavar='N',
            help='input batch size for training (default: 128)')
    parser.add_argument('--test-batch-size', type=int, default=128, metavar='N',
            help='input batch size for testing (default: 128)')
    parser.add_argument('--epochs', type=int, default=60, metavar='N',
            help='number of epochs to train (default: 60)')
    parser.add_argument('--lr-epochs', type=int, default=15, metavar='N',
            help='number of epochs to decay the lr (default: 15)')
    parser.add_argument('--lr', type=float, default=0.01, metavar='LR',
            help='learning rate (default: 0.01)')
    parser.add_argument('--momentum', type=float, default=0.9, metavar='M',
            help='SGD momentum (default: 0.9)')
    parser.add_argument('--weight-decay', '--wd', default=1e-5, type=float,
            metavar='W', help='weight decay (default: 1e-5)')
    parser.add_argument('--no-cuda', action='store_true', default=False,
            help='disables CUDA training')
    parser.add_argument('--seed', type=int, default=1, metavar='S',
            help='random seed (default: 1)')
    parser.add_argument('--log-interval', type=int, default=100, metavar='N',
            help='how many batches to wait before logging training status')
    parser.add_argument('--arch', action='store', default='LeNet_5',
            help='the MNIST network structure: LeNet_5')
    parser.add_argument('--pretrained', action='store', default=None,
            help='pretrained model')
    parser.add_argument('--evaluate', action='store_true', default=False,
            help='whether to run evaluation')
    args = parser.parse_args()
    args.cuda = not args.no_cuda and torch.cuda.is_available()

    print(args)
    
    torch.manual_seed(args.seed)
    if args.cuda:
        torch.cuda.manual_seed(args.seed)
    
    # load data
    kwargs = {'num_workers': 1, 'pin_memory': True} if args.cuda else {}
    train_loader = torch.utils.data.DataLoader(
            datasets.MNIST('data', train=True, download=True,
                transform=transforms.Compose([
                    transforms.ToTensor(),
                    transforms.Normalize((0.1307,), (0.3081,))
                    ])),
                batch_size=args.batch_size, shuffle=True, **kwargs)
    test_loader = torch.utils.data.DataLoader(
            datasets.MNIST('data', train=False, transform=transforms.Compose([
                transforms.ToTensor(),
                transforms.Normalize((0.1307,), (0.3081,))
                ])),
            batch_size=args.test_batch_size, shuffle=True, **kwargs)
    
    # generate the model
    if args.arch == 'LeNet_5':
        model = models.LeNet_5()
    else:
        print('ERROR: specified arch is not suppported')
        exit()

    if not args.pretrained:
        best_acc = 0.0
    else:
        pretrained_model = torch.load(args.pretrained)
        best_acc = pretrained_model['acc']
        model.load_state_dict(pretrained_model['state_dict'])

    if args.cuda:
        model.cuda()
    
    print(model)
    param_dict = dict(model.named_parameters())
    params = []
    
    base_lr = 0.1
    
    for key, value in param_dict.items():
        params += [{'params':[value], 'lr': args.lr,
            'weight_decay': args.weight_decay,
            'key':key}]
    
    optimizer = optim.Adam(params, lr=args.lr,
            weight_decay=args.weight_decay)

    criterion = nn.CrossEntropyLoss()

    # define the binarization operator
    bin_op = util.BinOp(model)

    if args.evaluate:
        test(evaluate=True)
        exit()

    for epoch in range(1, args.epochs + 1):
        adjust_learning_rate(optimizer, epoch)
        train(epoch)
        test()
</code></pre>
<p>I am aware that Variable is deprecated, but I really don’t know how to solve this issue.</p>

username: soulitzer
cooked: <p>The legacy autograd Function usage needs to be updated. Here’s the new version:</p>
<pre><code class="lang-auto">class BinActive(torch.autograd.Function):
    '''
    Binarize the input activations and calculate the mean across channel dimension.
    '''
    @staticmethod
    def forward(ctx, input):
        ctx.save_for_backward(input)
        size = input.size()
        input = input.sign()
        return input

    @staticmethod
    def backward(ctx, grad_output):
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input.ge(1)] = 0
        grad_input[input.le(-1)] = 0
        return grad_input
</code></pre>
<p>This line needs to be updated:</p>
<aside class="onebox githubblob" data-onebox-src="https://github.com/jiecaoyu/XNOR-Net-PyTorch/blob/master/MNIST/models/LeNet_5.py#L53">
  <header class="source">

      <a href="https://github.com/jiecaoyu/XNOR-Net-PyTorch/blob/master/MNIST/models/LeNet_5.py#L53" target="_blank" rel="noopener nofollow ugc">github.com</a>
  </header>

  <article class="onebox-body">
    <h4><a href="https://github.com/jiecaoyu/XNOR-Net-PyTorch/blob/master/MNIST/models/LeNet_5.py#L53" target="_blank" rel="noopener nofollow ugc">jiecaoyu/XNOR-Net-PyTorch/blob/master/MNIST/models/LeNet_5.py#L53</a></h4>



    <pre class="onebox"><code class="lang-py">
      <ol class="start lines" start="43" style="counter-reset: li-counter 42 ;">
          <li>    else:</li>
          <li>        if self.previous_conv:</li>
          <li>            self.bn = nn.BatchNorm2d(int(input_channels/size), eps=1e-4, momentum=0.1, affine=True)</li>
          <li>        else:</li>
          <li>            self.bn = nn.BatchNorm1d(input_channels, eps=1e-4, momentum=0.1, affine=True)</li>
          <li>        self.linear = nn.Linear(input_channels, output_channels)</li>
          <li>    self.relu = nn.ReLU(inplace=True)</li>
          <li>
          </li>
<li>def forward(self, x):</li>
          <li>    x = self.bn(x)</li>
          <li class="selected">    x = BinActive()(x)</li>
          <li>    if self.dropout_ratio!=0:</li>
          <li>        x = self.dropout(x)</li>
          <li>    if not self.Linear:</li>
          <li>        x = self.conv(x)</li>
          <li>    else:</li>
          <li>        if self.previous_conv:</li>
          <li>            x = x.view(x.size(0), self.input_channels)</li>
          <li>        x = self.linear(x)</li>
          <li>    x = self.relu(x)</li>
          <li>    return x</li>
      </ol>
    </code></pre>



  </article>

  <div class="onebox-metadata">
    
    
  </div>

  <div style="clear: both"></div>
</aside>

<p>It should be changed to:</p>
<pre><code class="lang-auto">x = BinActive.apply(x)
</code></pre>