TypeError with optimizer.step()

SoulHokib: <p>Hey guys !</p>
<p>I am currently trying to make an implementation of a CNN model with PyTorch and PySyft. But I have an issue that does appear with the optimizer.step() :<br>
“TypeError: add_() takes 1 positional argument but 2 were given”</p>
<p>Here’s my model and the way I launch it :</p>
<pre><code class="lang-auto">class Net(Module):   
    def __init__(self):
        super(Net, self).__init__()

        self.cnn_layers = Sequential(
            # Defining a 2D convolution layer
            Conv2d(3, 4, kernel_size=3, stride=1, padding=1),
            BatchNorm2d(4),
            ReLU(inplace=True),
            MaxPool2d(kernel_size=2),
            # Defining another 2D convolution layer
            Conv2d(4, 4, kernel_size=4, stride=1, padding=1),
            BatchNorm2d(4),
            ReLU(inplace=True),
            MaxPool2d(kernel_size=2),
        )

        self.linear_layers = Sequential(
            Linear(4 * 49 * 49, 1)
        )

    # Defining the forward pass    
    def forward(self, x):
        x = self.cnn_layers(x)
        print("X prev: ", x.shape)
        x = x.view(-1, 4*49*49)
        print("X after: ", x.shape)
        x = self.linear_layers(x)
        return x
        
# defining the model
model = Net()

# defining the optimizer
optimizer = Adam(model.parameters(), lr=0.07)
# defining the loss function
criterion = BCEWithLogitsLoss()
# checking if GPU is available
#if torch.cuda.is_available():
#    model = model.to(device)
#    criterion = criterion.to(device)
#    print("Model &amp; Criterion on GPU")
    
from torchsummary import summary

#vgg = models.vgg16()
#summary(model.cuda(), (3, 200, 200))
print(model)

</code></pre>
<pre><code class="lang-auto">def trains(epoch):
    model.train()
    for batch_idx, (data,target) in enumerate(train_loader):
        worker = data.location
        model.send(worker)
        #if torch.cuda.is_available():
        #    data = data.to(device)
        #   target = target.to(device)
            
        optimizer.zero_grad()
        # update the model
        output_train = model(data)
        loss = criterion(output_train, target.unsqueeze(1).type_as(output_train))
        loss.backward()
        optimizer.step()
        model.get()
     
        if batch_idx % args.log_interval == 0:
            loss = loss.get()
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * data.shape[0], len(train_loader),
                       100. * batch_idx / len(train_loader), loss.item()))
</code></pre>
<pre><code class="lang-auto">import time
t = time.time()
for epoch in range(1, args.epochs + 1):
    trains(epoch)

    
total_time = time.time() - t
print('Total', round(total_time, 20), 's')
</code></pre>

ptrblck: <p>Are you using the <code>torch.optim.Adam</code> optimizer or a custom implementation?<br>
It seems the error points to the internal update step of the optimizer.</p>

SoulHokib: <p>I am using the torch.optim.Adam. Maybe another optimizer would be more appropriate ?</p>

ptrblck: <p>No, you shouldn’t change the optimizer, because of this error message.<br>
I’ve tested your model without PySyft and it’s working fine.</p>
<p>Could you post the complete stack trace, please?</p>

SoulHokib: <p>Here it is :</p>
<pre><code class="lang-auto">---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
&lt;ipython-input-19-df22c32abf0a&gt; in &lt;module&gt;
      2 t = time.time()
      3 for epoch in range(1, args.epochs + 1):
----&gt; 4     trains(epoch)
      5 
      6 

&lt;ipython-input-17-5a23fe3794d1&gt; in trains(epoch)
     13         loss = criterion(output_train, target.unsqueeze(1).type_as(output_train))
     14         loss.backward()
---&gt; 15         optimizer.step()
     16         model.get()
     17 

/opt/conda/lib/python3.6/site-packages/torch/optim/adam.py in step(self, closure)
     93 
     94                 # Decay the first and second moment running average coefficient
---&gt; 95                 exp_avg.mul_(beta1).add_(1 - beta1, grad)
     96                 exp_avg_sq.mul_(beta2).addcmul_(1 - beta2, grad, grad)
     97                 if amsgrad:

/opt/conda/lib/python3.6/site-packages/syft/generic/frameworks/hook/hook.py in overloaded_native_method(self, *args, **kwargs)
    208                 # Send the new command to the appropriate class and get the response
    209                 method = getattr(new_self, method_name)
--&gt; 210                 response = method(*new_args, **new_kwargs)
    211 
    212                 # For inplace methods, just directly return self

/opt/conda/lib/python3.6/site-packages/syft/generic/frameworks/hook/pointers.py in overloaded_pointer_method(self, *args, **kwargs)
     82 
     83             # Send the command
---&gt; 84             response = owner.send_command(location, attr, self, args, kwargs)
     85 
     86             # For inplace methods, just directly return self

/opt/conda/lib/python3.6/site-packages/syft/workers/base.py in send_command(self, recipient, cmd_name, target, args_, kwargs_, return_ids, return_value)
    624                 cmd_name, target, args_, kwargs_, return_ids, return_value
    625             )
--&gt; 626             ret_val = self.send_msg(message, location=recipient)
    627         except ResponseSignatureError as e:
    628             ret_val = None

/opt/conda/lib/python3.6/site-packages/syft/workers/base.py in send_msg(self, message, location)
    272 
    273         # Step 2: send the message and wait for a response
--&gt; 274         bin_response = self._send_msg(bin_message, location)
    275 
    276         # Step 3: deserialize the response

/opt/conda/lib/python3.6/site-packages/syft/workers/virtual.py in _send_msg(self, message, location)
     14             sleep(self.message_pending_time)
     15 
---&gt; 16         return location._recv_msg(message)
     17 
     18     def _recv_msg(self, message: bin) -&gt; bin:

/opt/conda/lib/python3.6/site-packages/syft/workers/virtual.py in _recv_msg(self, message)
     18     def _recv_msg(self, message: bin) -&gt; bin:
     19         """receive message"""
---&gt; 20         return self.recv_msg(message)
     21 
     22     # For backwards compatibility with Udacity course

/opt/conda/lib/python3.6/site-packages/syft/workers/base.py in recv_msg(self, bin_message)
    308 
    309         # Step 2: route message to appropriate function
--&gt; 310         response = self._message_router[type(msg)](msg)
    311 
    312         # Step 3: Serialize the message to simple python objects

/opt/conda/lib/python3.6/site-packages/syft/workers/base.py in execute_tensor_command(self, cmd)
    449     def execute_tensor_command(self, cmd: TensorCommandMessage) -&gt; PointerTensor:
    450         if isinstance(cmd.action, ComputationAction):
--&gt; 451             return self.execute_computation_action(cmd.action)
    452         else:
    453             return self.execute_communication_action(cmd.action)

/opt/conda/lib/python3.6/site-packages/syft/workers/base.py in execute_computation_action(self, action)
    487                 # TODO[jvmancuso]: figure out a good way to generalize the
    488                 # above check (#2530)
--&gt; 489                 getattr(_self, op_name)(*args_, **kwargs_)
    490                 return
    491             else:

/opt/conda/lib/python3.6/site-packages/syft/generic/frameworks/hook/hook.py in overloaded_native_method(self, *args, **kwargs)
    168                 except BaseException as e:
    169                     # we can make some errors more descriptive with this method
--&gt; 170                     raise route_method_exception(e, self, args, kwargs)
    171 
    172             else:  # means that there is a wrapper to remove

/opt/conda/lib/python3.6/site-packages/syft/generic/frameworks/hook/hook.py in overloaded_native_method(self, *args, **kwargs)
    164 
    165                 try:
--&gt; 166                     response = method(*args, **kwargs)
    167 
    168                 except BaseException as e:

TypeError: add_() takes 1 positional argument but 2 were given
</code></pre>

ptrblck: <p>Which PyTorch version are you using? Could you create a new virtual environment and install the latest stable version (or nightly)?</p>

SoulHokib: <p>I am running PyTorch 1.4 because it is needed for PySyft. So I think if I run the latest stable version on another venv, it won’t make PySyft run</p>

ptrblck: <p>Could you run this code snippet without importing PySyft to check, if PyTorch itself is raising this error:</p>
<pre><code class="lang-python">import torch
import torch.nn as nn

class Net(nn.Module):   
    def __init__(self):
        super(Net, self).__init__()

        self.cnn_layers = nn.Sequential(
            # Defining a 2D convolution layer
            nn.Conv2d(3, 4, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(4),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2),
            # Defining another 2D convolution layer
            nn.Conv2d(4, 4, kernel_size=4, stride=1, padding=1),
            nn.BatchNorm2d(4),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2),
        )

        self.linear_layers = nn.Sequential(
            nn.Linear(4 * 49 * 49, 1)
        )

    # Defining the forward pass    
    def forward(self, x):
        x = self.cnn_layers(x)
        print("X prev: ", x.shape)
        x = x.view(-1, 4*49*49)
        print("X after: ", x.shape)
        x = self.linear_layers(x)
        return x
        
# defining the model
model = Net()

# defining the optimizer
optimizer = torch.optim.Adam(model.parameters(), lr=0.07)
# defining the loss function
criterion = nn.BCEWithLogitsLoss()

output_train = model(torch.randn(1, 3, 200, 200))
loss = criterion(output_train, torch.zeros(1, 1))
loss.backward()
optimizer.step()
</code></pre>

SoulHokib: <p>The code is running fine, no error raising.</p>
<p>EDIT : Looks like the solution is to provide one optimizer to each worker in PySyft. An example is given here, in the <a href="https://github.com/OpenMined/PySyft/blob/master/examples/tutorials/Part%2002%20-%20Intro%20to%20Federated%20Learning.ipynb" rel="nofollow noopener">PySyft tutorials.</a></p>
<p>Thanks for your help !</p>