Torchscript frcnn model with nms in python and load in C++ libtorch

daroo: <p>I’ve been trying to get a FRCNN model trained in Python to be “torchscripted” and saved off so that it can be loaded into C++ using libtorch.  To reduce the situation to a fairly simple example, I’m now just trying to use torchvision’s pretrained model to get my custom model out of the picture.  I’m still running into problems when loading the scripted model in libtorch complaining about nms being an “Unknown builtin op”.</p>
<p>Here’s the reduced python code:</p>
<pre><code class="lang-auto">import torch
import torchvision

print(f'Torch version: {torch.__version__}')
print(f'Torchvision version: {torchvision.__version__}')

tvfrcnnModel = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained = True)
scriptTvfrcnnModel = torch.jit.script(tvfrcnnModel)
torch.jit.save(scriptTvfrcnnModel, 'torchvision_frcnn_scripted.pt')

print(f'Torchvision pretrained frcnn scripted and saved')
</code></pre>
<p>that outputs the following:</p>
<pre><code class="lang-auto">Torch version: 1.10.0a0+gitf69cf3c
Torchvision version: 0.11.0a0+9275cc6
Torchvision pretrained frcnn scripted and saved
</code></pre>
<p>Here’s the C++ code, then, that attempts to load the scripted model:</p>
<pre><code class="lang-auto">//According to: https://github.com/pytorch/vision/#c-api
//  In order to get the torchvision operators registered with
//  torch (eg. for the JIT), all you need to do is to ensure
//  that you #include &lt;torchvision/vision.h&gt; in your project.
#include &lt;torchvision/vision.h&gt;
#include &lt;torch/torch.h&gt;
#include &lt;torch/script.h&gt;
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;string&gt;

int main()
{
  string scriptedModelFname = "torchvision_frcnn_scripted.pt";
  ifstream scriptedModelF;
  scriptedModelF.open(scriptedModelFname);
  torch::jit::script::Module tvfcrnnModel;
  tvfcrnnModel = torch::jit::load(scriptedModelF);
  scriptedModelF.close();

  return 0;
}
</code></pre>
<p>When I run this program, though, it reports:</p>
<pre><code class="lang-auto">terminate called after throwing an instance of 'torch::jit::ErrorReport'
  what():  
Unknown builtin op: torchvision::nms.
Could not find any similar ops to torchvision::nms. This op may not exist or may not be currently supported in TorchScript.
:
  File "/usr/local/lib64/python3.6/site-packages/torchvision-0.11.0a0+9275cc6-py3.6-linux-x86_64.egg/torchvision/ops/boxes.py", line 35
    """
    _assert_has_ops()
    return torch.ops.torchvision.nms(boxes, scores, iou_threshold)
           ~~~~~~~~~~~~~~~~~~~~~~~~~ &lt;--- HERE
...
</code></pre>
<p>I’ve seen a small handful of similar questions out there (e.g. <a href="https://github.com/pytorch/pytorch/issues/48932" class="inline-onebox" rel="noopener nofollow ugc">Unknown builtin op: torchvision::nms when loading scripted FasterRCNN · Issue #48932 · pytorch/pytorch · GitHub</a>) but I haven’t found any that seem to come to a resolution.  Is torchscripting a FRCNN model with nm something that is supposed to be supported currently, and if so, are there are any suggestions as to what might be going wrong here?</p>
<p>Thanks!</p>

tom: <p>But isn’t the solution given there - the link to <a href="https://github.com/pytorch/vision/#using-the-models-on-c" class="inline-onebox" rel="noopener nofollow ugc">GitHub - pytorch/vision: Datasets, Transforms and Models specific to Computer Vision</a> ?</p>

daroo: <p>Well, I don’t think so.  I mean, I believe I’ve followed the steps in that link.  To be sure, I went through it again and made a new project directory with minimal code. I still get the same error:</p>
<pre><code class="lang-auto">terminate called after throwing an instance of 'torch::jit::ErrorReport'
  what():  
Unknown builtin op: torchvision::nms.
Could not find any similar ops to torchvision::nms. This op may not exist or may not be currently supported in TorchScript.
:
  File "/usr/local/lib64/python3.6/site-packages/torchvision-0.11.0a0+a2b4c65-py3.6-linux-x86_64.egg/torchvision/ops/boxes.py", line 35
    """
    _assert_has_ops()
    return torch.ops.torchvision.nms(boxes, scores, iou_threshold)
           ~~~~~~~~~~~~~~~~~~~~~~~~~ &lt;--- HERE
</code></pre>
<p>My CMakeLists.txt looks like this:</p>
<pre><code class="lang-auto">cmake_minimum_required(VERSION 3.10)
project(testTorch)

# The first thing do is to tell cmake to find the TorchVision library.
# The package pulls in all the necessary torch libraries,
# so there is no need to also add `find_package(Torch)` here.
find_package(TorchVision REQUIRED)

add_executable(testTorch main.cpp)

include_directories(testTorch ${OpenCV_INCLUDE_DIRS})

# We now need to link the TorchVision library to our executable.
# We can do that by using the TorchVision::TorchVision target,
# which also adds all the necessary torch dependencies.
target_compile_features(testTorch PUBLIC cxx_range_for)
target_link_libraries(testTorch TorchVision::TorchVision)
set_property(TARGET testTorch PROPERTY CXX_STANDARD 14)
</code></pre>
<p>I hope I’m not missing something obvious here, I tried many things before posting the question.  Any help would be appreciated…</p>

tom: <p>I’ll have to check, but you probably tried using PUBLIC for linking torchvision, right?</p>

daroo: <p>I have tried it now.  Didn’t seem to make a difference - same error.  Thanks for the suggestion!</p>
<p>When building pytorch, I didn’t specify “-DSELECTED_OP_LIST”, because I saw a post indicating that the default was to include all ops and that you only needed SELECTED_OP_LIST if you wanted to restrict it to a subset of the ops.</p>

pav: <p>I have similar problem! So in the end did you manage to sort it out? How did you specify the operators with -DSELECTED_OP_LIST <a class="mention" href="/u/daroo">@daroo</a> ? Could you please post some commands / cmake config files?</p>

daroo: <p>No, I haven’t got past this yet.  as indicated in the previous post, I didn’t end up specifying -DSELECTED_OP_LIST since I deemed it not necessary given the posts I read.</p>

pav: <p><a class="mention" href="/u/daroo">@daroo</a> its so weird. When I try to load scripted model I get the same error. When I try to register the ops manually with something like:</p>
<p>‘’‘static auto registry = torch::RegisterOperators().op(“torchvision::nms”, &amp;vision::ops::nms);’’’</p>
<p>I get:</p>
<p>‘’‘libc++abi: terminating with uncaught exception of type c10::Error: Tried to register an operator (torchvision::nms(Tensor _0, Tensor _1, float _2) → (Tensor _0)) with the same name and overload name multiple times. Each overload’s schema should only be registered with a single call to def(). Duplicate registration: registered by RegisterOperators. Original registration: registered at /tmp/torchvision-20210615-98076-1ldvglh/vision-0.10.0/torchvision/csrc/ops/nms.cpp:18’’’</p>

daroo: <p>Hmm, that is odd.  I have not been playing much with trying to register the ops myself because it sounded like they would have been registered just by doing “<span class="hashtag">#include</span> &lt;torchvision/vision.h&gt;”, which I have included in the .cpp file.  The way I had read to do that was to dump out a yaml file from python with your network’s ops specified in it, and then, when building pytorch, specifying that pre-made yaml file with “-DSELECTED_OP_LIST”, something like “-DSELECTED_OP_LIST:STRING=myNetOps.yaml”.  But again, I didn’t mess with that much since I read it wasn’t necessary unless you were trying to restrict things for space reasons or something…</p>
<p>I’m holding out hope that <a class="mention" href="/u/tom">@tom</a> will have some great insight on this. <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=10" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>

pav: <p>Hey <a class="mention" href="/u/daroo">@daroo</a> <a class="mention" href="/u/tom">@tom</a> did you manage to solve this? I tried like million different ways of registering those  operators without any luck. The docs are stating that all you have to do is to use “<span class="hashtag">#include</span> &lt;torchvision/vision.h&gt;” - just like <a class="mention" href="/u/daroo">@daroo</a> mentioned. But this simply doesn’t work… The only thing I was able to do in order to fix it short term was to clone the previous version (0.8.0) build it myself and link against it…</p>

daroo: <p><a class="mention" href="/u/pav">@pav</a> Just this morning, I think I made some progress.  I noticed that, despite the docs indicating <span class="hashtag">#include</span>’ing the vision.h file being all that is needed, my formed executable was not linked with torchvision, even though it was specified on the linker command line.  As this link: <a href="https://github.com/pytorch/vision/issues/2134" class="inline-onebox" rel="noopener nofollow ugc">About RegisterOperators in C++ · Issue #2134 · pytorch/vision · GitHub</a> discusses, the linker will recognize that a library isn’t needed and not link with it at all.  That’s what user bmanga’s suggested update at the bottom of that post is supposed to overcome.</p>
<p>That clever way of forcing torchvision to be linked in even if it doesn’t detect any use of the library either wasn’t implemented or else its just not working in my case I think.  So I used the big hammer approach by forcing the linker to use the “whole archive” for torchvision.  On the link line right before specification of the libtorchvision library, I added “-Wl,–whole-archive” and then right after specification of libtorchvision I added “-Wl,–no-whole-archive”.</p>
<p>Now, I can load the torchvision FRCNN without error!</p>
<p>When I try to load my own custom net I get some other errors, but I think its unrelated to this.  So, for now, I think forcing the linker to load the entire torchvision library even if it doesn’t think it is needed seems to get past the issue at hand.</p>

tom: <p>Hi,</p>
<p>as discussed in the bug, if your executable isn’t linked to libtorchvision, you are in trouble.<br>
I’m surprised that no-as-needed doesn’t work, but hey.<br>
An alternative could be to just dlopen libtorchvision manually.</p>
<p>Best regards</p>
<p>Thomas</p>

pav: <p><a class="mention" href="/u/daroo">@daroo</a> <a class="mention" href="/u/tom">@tom</a> Ahh I see! Yes you are both right - I’m on mac so don’t have access to “-Wl,–whole-archive”. However, what I ended up using is ‘-Wl,-needed-ltorchvision’ - now all the ops are being registered. Thanks Guys!</p>