Convert/import Torch model to PyTorch

miliadis: <p>Hi,</p>
<p>Great library! I’d like to ask if it is possible to import a trained Torch model to PyTorch…</p>
<p>Thanks</p>

smth: <p>As of today, you can deserialize Lua’s .t7 files into PyTorch containing Tensors, numbers, tables, nn models (no nngraph), strings.</p>
<p>We provide the <code>load_lua</code> utility for this purpose.</p>
<p>Here’s an example of saving a tensor in Torch and loading it back in PyTorch</p>
<pre><code class="lang-auto">
th&gt; a = torch.randn(10)
                                                                      [0.0027s]
th&gt; torch.save('a.t7', a)
                                                                      [0.0010s]
th&gt; a
-1.4479
 1.3707
 0.5663
-1.0590
 0.0706
-1.6495
-1.0805
 0.8277
-0.4595
 0.1237
[torch.DoubleTensor of size 10]

                                                                      [0.0033s]
</code></pre>
<pre><code class="lang-python">In [1]: import torch

In [2]: from torch.utils.serialization import load_lua

In [3]: a = load_lua('a.t7')

In [4]: a
Out[4]:

-1.4479
 1.3707
 0.5663
-1.0590
 0.0706
-1.6495
-1.0805
 0.8277
-0.4595
 0.1237
[torch.DoubleTensor of size 10]
</code></pre>
<p>Here’s an example of loading a 2 layer sequential neural network:</p>
<pre><code class="lang-auto">th&gt; a = nn.Sequential():add(nn.Linear(10, 20)):add(nn.ReLU())
                                                                      [0.0001s]
th&gt; a
nn.Sequential {
  [input -&gt; (1) -&gt; (2) -&gt; output]
  (1): nn.Linear(10 -&gt; 20)
  (2): nn.ReLU
}
                                                                      [0.0001s]
th&gt; torch.save('a.t7', a)
                                                                      [0.0008s]
th&gt;
</code></pre>
<pre><code class="lang-python">In [5]: a = load_lua('a.t7')

In [6]: a
Out[6]:
nn.Sequential {
  [input -&gt; (0) -&gt; (1) -&gt; output]
  (0): nn.Linear(10 -&gt; 20)
  (1): nn.ReLU
}

In [7]: a.__class__
Out[7]: torch.legacy.nn.Sequential.Sequential
</code></pre>

alexis-jacq: <p>Hi,</p>
<p>Is there a simple way to convert a torch.legacy.nn module into a torch.nn module ?</p>

apaszke: <p>No, unfortunately we don’t have an automatic converter at the moment.</p>

apaszke: <p>But it should be quite simple to add.</p>

Eugenio_Culurciello: <p>Hi Guys, I am trying to load a torch7 model to use in PyTorch, this model: <a href="https://github.com/e-lab/imagenet-multiGPU.torch/blob/master/models/enet128.lua" rel="nofollow noopener">https://github.com/e-lab/imagenet-multiGPU.torch/blob/master/models/enet128.lua</a><br>
But it is not working:</p>
<pre><code>&gt;&gt;&gt; n=load_lua('/Users/eugenioculurciello/Dropbox/shared/models/enet128-demo-46/model.net')
&gt;&gt;&gt; n.forward(torch.FloatTensor(1,3,128,128))
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
  File "/usr/local/lib/python3.6/site-packages/torch/legacy/nn/Module.py", line 32, in forward
    return self.updateOutput(input)
  File "/usr/local/lib/python3.6/site-packages/torch/legacy/nn/Sequential.py", line 35, in updateOutput
    currentOutput = module.updateOutput(currentOutput)
  File "/usr/local/lib/python3.6/site-packages/torch/legacy/nn/ConcatTable.py", line 12, in updateOutput
    self.output = [module.updateOutput(input) for module in self.modules]
  File "/usr/local/lib/python3.6/site-packages/torch/legacy/nn/ConcatTable.py", line 12, in &lt;listcomp&gt;
    self.output = [module.updateOutput(input) for module in self.modules]
  File "/usr/local/lib/python3.6/site-packages/torch/legacy/nn/SpatialMaxPooling.py", line 33, in updateOutput
    if self.indices is None:
AttributeError: 'SpatialMaxPooling' object has no attribute 'indices'
</code></pre>
<p>also on an AlexNet:</p>
<pre><code>&gt;&gt;&gt; n=load_lua('/Users/eugenioculurciello/Dropbox/shared/models/elab-alexowt-46/model.net')
&gt;&gt;&gt; n
nn.Sequential {
  [input -&gt; (0) -&gt; (1) -&gt; (2) -&gt; (3) -&gt; (4) -&gt; (5) -&gt; (6) -&gt; (7) -&gt; (8) -&gt; (9) -&gt; (10) -&gt; (11) -&gt; (12) -&gt; (13) -&gt; (14) -&gt; (15) -&gt; (16) -&gt; (17) -&gt; (18) -&gt; (19) -&gt; output]
  (0): nn.SpatialConvolution(3 -&gt; 64, 11x11, 4, 4, 2, 2)
  (1): nn.ReLU
  (2): nn.SpatialMaxPooling(3x3, 2, 2)
  (3): nn.SpatialConvolution(64 -&gt; 192, 5x5, 1, 1, 2, 2)
  (4): nn.ReLU
  (5): nn.SpatialMaxPooling(3x3, 2, 2)
  (6): nn.SpatialConvolution(192 -&gt; 384, 3x3, 1, 1, 1, 1)
  (7): nn.ReLU
  (8): nn.SpatialConvolution(384 -&gt; 256, 3x3, 1, 1, 1, 1)
  (9): nn.ReLU
  (10): nn.SpatialConvolution(256 -&gt; 256, 3x3, 1, 1, 1, 1)
  (11): nn.ReLU
  (12): nn.SpatialMaxPooling(3x3, 2, 2)
  (13): nn.View(9216)
  (14): nn.Linear(9216 -&gt; 4096)
  (15): nn.ReLU
  (16): nn.Linear(4096 -&gt; 4096)
  (17): nn.ReLU
  (18): nn.Linear(4096 -&gt; 46)
  (19): nn.SoftMax
}
&gt;&gt;&gt; n.forward(torch.FloatTensor(1,3,224,224))
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
  File "/usr/local/lib/python3.6/site-packages/torch/legacy/nn/Module.py", line 32, in forward
    return self.updateOutput(input)
  File "/usr/local/lib/python3.6/site-packages/torch/legacy/nn/Sequential.py", line 35, in updateOutput
    currentOutput = module.updateOutput(currentOutput)
  File "/usr/local/lib/python3.6/site-packages/torch/legacy/nn/Linear.py", line 43, in updateOutput
    assert input.dim() == 2
AssertionError
&gt;&gt;&gt; n.forward(torch.FloatTensor(3,224,224))
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
  File "/usr/local/lib/python3.6/site-packages/torch/legacy/nn/Module.py", line 32, in forward
    return self.updateOutput(input)
  File "/usr/local/lib/python3.6/site-packages/torch/legacy/nn/Sequential.py", line 35, in updateOutput
    currentOutput = module.updateOutput(currentOutput)
  File "/usr/local/lib/python3.6/site-packages/torch/legacy/nn/Linear.py", line 43, in updateOutput
    assert input.dim() == 2
AssertionError
</code></pre>
<p>Can you explain how to run old networks here? Or how to go about doing that?</p>

mvitez: <p>For Alexnet just do:</p>
<p>from torch.legacy import nn<br>
n.modules[13] = nn.View(1,9216)</p>
<p>(instead of nn.View(9216))</p>
<p>For enet it’s trickier. There are several errors in the pytorch legacy code.</p>
<p>First of all the error you get (‘SpatialMaxPooling’ object has no attribute ‘indices’) is because there is an error in <a href="http://SpatialMaxPooling.py" rel="nofollow noopener">SpatialMaxPooling.py</a> inside pytorch. In line 34, instead of<br>
if self.indices is None:, it should be<br>
if not hasattr(self, ‘indices’):<br>
This probably comes from a wrong Lua-&gt;Python conversion of the code.</p>
<p>Then in <a href="http://JoinTable.py" rel="nofollow noopener">JoinTable.py</a> line<br>
dimension = self.dimension should become dimension = self.dimension-1</p>
<p>Then in <a href="http://Padding.py" rel="nofollow noopener">Padding.py</a> nInputDim is totally missing. You can temporarily fix this by changing self.dim in lines 19 and 21 of <a href="http://Padding.py" rel="nofollow noopener">Padding.py</a> into self.dim+1.</p>
<p>Then you have again the View issue at the end.</p>
<p>After all these changes, it will run.</p>

apaszke: <p><a class="mention" href="/u/mvitez">@mvitez</a> if you have a patch for these errors, could you please send a PR?</p>

mvitez: <p>Sure, I will. This forum wants 20 characters to answer.</p>

mvitez: <p>Modify your Torch according to this PR</p>
<aside class="onebox githubpullrequest">
  <header class="source">
      <a href="https://github.com/pytorch/pytorch/pull/738/files" target="_blank" rel="nofollow noopener">github.com/pytorch/pytorch</a>
  </header>
  <article class="onebox-body">
    <div class="github-row">
  <div class="github-icon-container" title="Pull Request">
    <svg width="60" height="60" class="github-icon" viewBox="0 0 12 16" aria-hidden="true"><path d="M11 11.28V5c-.03-.78-.34-1.47-.94-2.06C9.46 2.35 8.78 2.03 8 2H7V0L4 3l3 3V4h1c.27.02.48.11.69.31.21.2.3.42.31.69v6.28A1.993 1.993 0 0 0 10 15a1.993 1.993 0 0 0 1-3.72zm-1 2.92c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zM4 3c0-1.11-.89-2-2-2a1.993 1.993 0 0 0-1 3.72v6.56A1.993 1.993 0 0 0 2 15a1.993 1.993 0 0 0 1-3.72V4.72c.59-.34 1-.98 1-1.72zm-.8 10c0 .66-.55 1.2-1.2 1.2-.65 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z"></path></svg>
  </div>

  <div class="github-info-container">
    <h4>
      <a href="https://github.com/pytorch/pytorch/pull/738" target="_blank" rel="nofollow noopener">Improve compatibility with Torch7</a>
    </h4>

    <div class="branches">
      <code>pytorch:master</code> ← <code>mvitez:master</code>
    </div>

    <div class="github-info">
      <div class="date">
        opened <span class="discourse-local-date" data-format="ll" data-date="2017-02-14" data-time="14:45:32" data-timezone="UTC">02:45PM - 14 Feb 17 UTC</span>
      </div>

      <div class="user">
        <a href="https://github.com/mvitez" target="_blank" rel="nofollow noopener">
          <img alt="mvitez" src="https://avatars1.githubusercontent.com/u/7524531?v=4" class="onebox-avatar-inline" width="20" height="20">
          mvitez
        </a>
      </div>

      <div class="lines" title="5 commits changed 2 files with 2 additions and 1 deletions">
        <a href="https://github.com/pytorch/pytorch/pull/738/files" target="_blank" rel="nofollow noopener">
          <span class="added">+2</span>
          <span class="removed">-1</span>
        </a>
      </div>
    </div>

  </div>
</div>

  </article>
  <div class="onebox-metadata">
    
    
  </div>
  <div style="clear: both"></div>
</aside>

<p>and process your <a href="http://model.net" rel="nofollow noopener">model.net</a> with this script:</p>
<pre><code class="lang-auto">function patch(m)
   if torch.type(m) == 'nn.Padding' and m.nInputDim == 3 then
      m.dim = m.dim+1
      m.nInputDim = 4
   end
   if torch.type(m) == 'nn.View' and #m.size == 1 then
      newsize = torch.LongStorage(2)
      newsize[1] = 1
      newsize[2] = m.size[1]
      m.size = newsize
   end
   if m.modules then
      for i =1,#m.modules do
         patch(m.modules[i])
      end
   end
end

require 'nn'
net = torch.load('model.net')
patch(net)
torch.save('model2.net',net)
</code></pre>

HarshaVardhanP: <p>Can ‘load_lua’ works for model with cudnn layers ? I am getting some serialization issues in read_lua_file.py. I had to use cudnn.convert(model,nn) and load_lua.</p>

apaszke: <p>No, it doesn’t support cudnn layers at the moment.</p>

clcarwin: <p><a href="https://github.com/clcarwin/convert_torch_to_pytorch" rel="nofollow noopener">convert_torch_to_pytorch</a><br>
This script can convert t7 model file to python source and pytorch pth model.<br>
It supports normal nn/cudnn layers. AlexNet/VGG/ResNet/ResNeXt have been validated.</p>

pronics2004: <p>When using load_lua to load a model pre-trained in torch7 , I get an error <code>KeyError: 'torch.CudaTensor'</code>. Any thoughts on how to overcome this?</p>

apaszke: <p>I think the serialization of GPU models might not be implemented. A quick workaround would be to load the checkpoint in Lua, cast it to CPU float, and try loading in PyTorch then.</p>

liuhantang: <p>When using load_lua, I got an error AttributeError: type object ‘FloatStorage’ has no attribute ‘from_buffer’, how can I solve this problem?</p>

apaszke: <p>Is there any chance your model contains CUDA tensors?</p>

melody-rain: <p>I got the same problem. Any solutions?</p>

apaszke: <p>Yes, do you have any CUDA tensors in your model?</p>

liuhantang: <p>Thanks, that solves my problem. Previously I used cudnn.convert(model, nn), but did not use model:float(), so there was still cuda tensor.</p>