|
|
|
|
|
import os |
|
import torch |
|
|
|
from detectron2.utils.file_io import PathManager |
|
|
|
from .torchscript_patch import freeze_training_mode, patch_instances |
|
|
|
__all__ = ["scripting_with_instances", "dump_torchscript_IR"] |
|
|
|
|
|
def scripting_with_instances(model, fields): |
|
""" |
|
Run :func:`torch.jit.script` on a model that uses the :class:`Instances` class. Since |
|
attributes of :class:`Instances` are "dynamically" added in eager mode,it is difficult |
|
for scripting to support it out of the box. This function is made to support scripting |
|
a model that uses :class:`Instances`. It does the following: |
|
|
|
1. Create a scriptable ``new_Instances`` class which behaves similarly to ``Instances``, |
|
but with all attributes been "static". |
|
The attributes need to be statically declared in the ``fields`` argument. |
|
2. Register ``new_Instances``, and force scripting compiler to |
|
use it when trying to compile ``Instances``. |
|
|
|
After this function, the process will be reverted. User should be able to script another model |
|
using different fields. |
|
|
|
Example: |
|
Assume that ``Instances`` in the model consist of two attributes named |
|
``proposal_boxes`` and ``objectness_logits`` with type :class:`Boxes` and |
|
:class:`Tensor` respectively during inference. You can call this function like: |
|
:: |
|
fields = {"proposal_boxes": Boxes, "objectness_logits": torch.Tensor} |
|
torchscipt_model = scripting_with_instances(model, fields) |
|
|
|
Note: |
|
It only support models in evaluation mode. |
|
|
|
Args: |
|
model (nn.Module): The input model to be exported by scripting. |
|
fields (Dict[str, type]): Attribute names and corresponding type that |
|
``Instances`` will use in the model. Note that all attributes used in ``Instances`` |
|
need to be added, regardless of whether they are inputs/outputs of the model. |
|
Data type not defined in detectron2 is not supported for now. |
|
|
|
Returns: |
|
torch.jit.ScriptModule: the model in torchscript format |
|
""" |
|
assert ( |
|
not model.training |
|
), "Currently we only support exporting models in evaluation mode to torchscript" |
|
|
|
with freeze_training_mode(model), patch_instances(fields): |
|
scripted_model = torch.jit.script(model) |
|
return scripted_model |
|
|
|
|
|
|
|
export_torchscript_with_instances = scripting_with_instances |
|
|
|
|
|
def dump_torchscript_IR(model, dir): |
|
""" |
|
Dump IR of a TracedModule/ScriptModule/Function in various format (code, graph, |
|
inlined graph). Useful for debugging. |
|
|
|
Args: |
|
model (TracedModule/ScriptModule/ScriptFUnction): traced or scripted module |
|
dir (str): output directory to dump files. |
|
""" |
|
dir = os.path.expanduser(dir) |
|
PathManager.mkdirs(dir) |
|
|
|
def _get_script_mod(mod): |
|
if isinstance(mod, torch.jit.TracedModule): |
|
return mod._actual_script_module |
|
return mod |
|
|
|
|
|
with PathManager.open(os.path.join(dir, "model_ts_code.txt"), "w") as f: |
|
|
|
def get_code(mod): |
|
|
|
try: |
|
|
|
return _get_script_mod(mod)._c.code |
|
except AttributeError: |
|
pass |
|
try: |
|
return mod.code |
|
except AttributeError: |
|
return None |
|
|
|
def dump_code(prefix, mod): |
|
code = get_code(mod) |
|
name = prefix or "root model" |
|
if code is None: |
|
f.write(f"Could not found code for {name} (type={mod.original_name})\n") |
|
f.write("\n") |
|
else: |
|
f.write(f"\nCode for {name}, type={mod.original_name}:\n") |
|
f.write(code) |
|
f.write("\n") |
|
f.write("-" * 80) |
|
|
|
for name, m in mod.named_children(): |
|
dump_code(prefix + "." + name, m) |
|
|
|
if isinstance(model, torch.jit.ScriptFunction): |
|
f.write(get_code(model)) |
|
else: |
|
dump_code("", model) |
|
|
|
def _get_graph(model): |
|
try: |
|
|
|
return _get_script_mod(model)._c.dump_to_str(True, False, False) |
|
except AttributeError: |
|
return model.graph.str() |
|
|
|
with PathManager.open(os.path.join(dir, "model_ts_IR.txt"), "w") as f: |
|
f.write(_get_graph(model)) |
|
|
|
|
|
with PathManager.open(os.path.join(dir, "model_ts_IR_inlined.txt"), "w") as f: |
|
f.write(str(model.inlined_graph)) |
|
|
|
if not isinstance(model, torch.jit.ScriptFunction): |
|
|
|
with PathManager.open(os.path.join(dir, "model.txt"), "w") as f: |
|
f.write(str(model)) |
|
|