Two optimizers for one model

agadetsky: <p>Is there a way to use two optimizers for one model in more beautiful way?<br>
Now, as I understand we should do something like these:</p>
<pre><code class="lang-auto">net = Model()
part_1_parameters = ...
part_2_parameters = ...
opt1 = optimizer_1(part_1_parameters)
opt2 = optimizer_2(part_1_parameters)

### train epoch
opt1.zero_grad()
opt2.zero_grad()

loss = ...
loss.backward()

opt1.step()
opt2.step()
</code></pre>

SimonW: <p>What is not beautiful about this?</p>

agadetsky: <p>If there are more than two optimizers, we will have many opt.steps <img src="//discuss.pytorch.org/images/emoji/apple/smiley.png?v=5" title=":smiley:" class="emoji" alt=":smiley:"><br>
Maybe it’s good to code some wrapper for optimizers, which will update different model parameters with different optimizers, as we do it in case with different learning rates and etc for different model parameters using one optimizer.</p>

Royi: <p>What would you do for training a network with 2 exit points?</p>

agadetsky: <p>For example, for faster SkipGram word2vec training it is better to use sparse embeddings, and sparse embeddings must be updated using sparseadam or sparsesgd optimizers, other parameters by common optimizers, so there are two optimizers which will work together.</p>

SimonW: <p>You can store them in a list. The code will still look fairly simple It will be some work, but I don’t think this is a common use case so we are not probably not implementing something for multiple optimizers in this regard, at least in near future.</p>

matthew_zeng: <p>Use the basic knowledge of software engineering.</p>
<pre><code class="lang-auto">
class MultipleOptimizer(object):
    def __init__(*op):
        self.optimizers = op

    def zero_grad(self):
        for op in self.optimizers:
            op.zero_grad()

    def step(self):
        for op in self.optimizers:
            op.step()


opt = MultipleOptimizer(optimizer1(params1, lr=lr1), 
                        optimizer2(params2, lr=lr2))

loss.backward()
opt.zero_grad()
opt.step()
</code></pre>

Royi: <p>How would you implement 2 exit points?</p>

SimonW: <p>What do you mean by exit point?</p>

Royi: <p>Let’s say I want to train a deep net for classification of 10 classes.<br>
Let’s say the model is few Convolution Layers and then Fully Connected Layers.</p>
<p>Then, in order to push data to the first layers I can create an exit point for, let’s say the 2 first convolution layers.<br>
This will drive information to it.</p>
<p>Thank You.</p>

SimonW: <p>Sorry, I still can’t understand. Could you explain what an exit point is in context of deep learning?</p>

Royi: <p>I will try again.</p>
<p>Let’s assume we describe a net with 2 main blocks:</p>
<ol>
<li>The main layers.</li>
<li>The final layer which takes output of the previous block and its output size is according to the task.</li>
</ol>
<p>For instance, for 10 classes classification problem we can do what ever we want in the first block and the second block is a fully connected with output size of 10.</p>
<p>Now imaging we have have Block A1 and block B1.<br>
The net is:</p>
<p><code>input -&gt; A1 -&gt; B1</code></p>
<p>Now we have A2 as well and we build a net like:</p>
<p><code>input -&gt; A1 -&gt; A2 -&gt; B2</code></p>
<p>But in this case gradients doesn’t affect A1 as much as we want.</p>
<p>So we build:</p>
<pre><code class="lang-auto">input -&gt; A1 -&gt; A2 -&gt; B2
             |--&gt; B1
</code></pre>
<p>Now we have exit point at B1 and B2.<br>
In training we first do a step of the net input -&gt; A1 -&gt; B1.<br>
Then a step for <code>input -&gt; A1 -&gt; A2 -&gt; B2</code>.</p>
<p>How can we do that in PyTorch?</p>

SimonW: <p>Do you mean like this?</p>
<pre><code class="lang-auto">def forward(self, input):
  t = self.A1(input)
  res1 = self.B1(input)
  res2 = self.B2(self.A2(input))
  return res1, res2
</code></pre>
<p>Then in train script</p>
<pre><code class="lang-auto">res1, res2 = net(input)
loss1 = criterion(res1, target)
loss2 = criterion(res2, target)
loss = loss1 + loss2
loss.backward()
</code></pre>

Royi: <p>First, you taught me that <code>forward</code> can output 2 output items which I wasn’t aware.</p>
<p>My intuition, based on what you showed me, would be:</p>
<pre><code class="lang-auto">res1, res2 = net(input)
loss1 = criterion(res1, target)
loss2 = criterion(res2, target)
loss1.backward()
loss2.backward()
hNetOptimizer1.step()
hNetOptimizer2.step()
</code></pre>
<p>Namely, I would like to have 2 optimizer.<br>
One updates the A1 block according to output of B1 and another updated A1, A2 according to B2.</p>
<p>Does it make any sense?</p>

SimonW: <p>Oh I see that you want to use two optimizers for two paths. The simplest way is to activate twice, and backward+step after each activate.</p>
<p>It’s kinda tricky if you don’t wanft to calculate <code>A1(input)</code> twice. You would need to do something like:</p>
<pre><code class="lang-auto">optim1.zero_grad()
temp = A1(input)
temp_d = temp.detach()
temp_d.requires_grad = True
res1 = B1(temp_d)
loss1 = critertion(res1, target)
temp.backward(autograd.grad(loss1, temp_d, only_input=False)[0], retain_graph=True)
optim1.step()

optim2.zero_grad()
res2 = B2(A2(temp))
loss2 = critertion(res2, target)
loss2.backward()
optim2.step()
</code></pre>

Royi: <p>I don’t mind calculating twice.<br>
I just want to understand how to do it.<br>
I prefer the most readable code over “Tricky” one to save some computations.</p>
<p>The problem I face is how to define the optimizers as optimizer is defined on a net yet both use the same net.</p>
<p><a class="mention" href="/u/simonw">@SimonW</a>, By the way, could you explain the difference between <a href="https://discuss.pytorch.org/t/two-optimizers-for-one-model/11085/13">what you posted at first</a> and <a href="https://discuss.pytorch.org/t/two-optimizers-for-one-model/11085/15">the second</a>?</p>

Royi: <p>Hi <a class="mention" href="/u/simonw">@SimonW</a>.<br>
Any chance you address my last post?</p>
<p>Thank You.</p>

SimonW: <p>The first one puts all gradients from both loss1 and loss2 on A1 together. The second one separates them so each optimizer can operate on gradients from different “source”.</p>

MrPositron: <p>don’t forget to put <code>self</code> inside of <code>__init__()</code>, like<code>__init__(self, *op)</code></p>

falmasri: <p><code>opt.zero_grad()</code> should be after backward</p>