Coming from torchvision's feature extractor¶
This page is for people who use torchvision's create_feature_extractor to read intermediate
activations and want the same activations from TorchLens. The examples were written against
the torchvision range TorchLens's test environment installs (torchvision>=0.18,<1).
Checked 2026-11-03.
The same task in both¶
Read the output of layer1 of a ResNet-18 as one named feature.
In torchvision¶
# migration-test: tool=torchvision expected=[1, 64, 8, 8]
import torch
from torchvision.models import resnet18
from torchvision.models.feature_extraction import create_feature_extractor
torch.manual_seed(0)
model = resnet18(weights=None).eval()
extractor = create_feature_extractor(model, return_nodes={"layer1": "feat1"})
with torch.no_grad():
features = extractor(torch.randn(1, 3, 32, 32))
RESULT = list(features["feat1"].shape)
In TorchLens¶
# migration-test: tool=torchlens expected=([1, 64, 8, 8], True)
import torch
from torchvision.models import resnet18
import torchlens as tl
torch.manual_seed(0)
model = resnet18(weights=None).eval()
x = torch.randn(1, 3, 32, 32)
features = tl.extract(model, x, {"feat1": "layer1"})
# The same tensor a forward hook on model.layer1 sees.
seen = {}
handle = model.layer1.register_forward_hook(lambda m, i, out: seen.setdefault("out", out))
with torch.no_grad():
model(x)
handle.remove()
RESULT = (list(features["feat1"].shape), torch.equal(features["feat1"], seen["out"]))
Translation table¶
| torchvision | TorchLens | Notes |
|---|---|---|
create_feature_extractor(model, return_nodes=["layer1"]) |
tl.extract(model, x, ["layer1"]) |
Module addresses are accepted as they are. |
return_nodes={"layer1": "feat1"} (node name to output key) |
tl.extract(model, x, {"feat1": "layer1"}) (output key to lookup) |
The mapping runs the other way round: key first, then the address. |
get_graph_node_names(model) |
tl.trace(model, x).layers and .modules |
TorchLens lists every executed op and module call, not a symbolic graph. |
FX node names such as "layer1.0.relu_1" |
Op labels read from a trace (trace.layers) |
Any op, including functional calls such as torch.flatten, is a lookup. |
Rebuilt GraphModule you call repeatedly |
One call per batch, or tl.extract_dataset for many batches |
Nothing is rewritten: the model you pass is the model that runs. |
| Fails on data-dependent control flow (FX symbolic tracing) | Runs the eager forward as written | Branches, loops and Python logic run normally. |
tl.extract returns detached tensors by default. Pass detach_saved_activations=False when
you want to backpropagate through the returned activations.
What TorchLens adds¶
- Any executed op is a lookup, not only the nodes FX names:
tl.trace(model, x).layerslists them, including functional calls and the arithmetic inside blocks. - Control flow stays as written. Data-dependent branches and loops run in the eager forward, and repeated calls of one module keep one pass each.
- The model you pass is the model that runs: no rebuilt
GraphModule, so its parameters, hooks andforwardstay yours. tl.extract_datasetruns the same lookup over a dataloader and writes the activations to disk with provenance; see Multi-GPU extraction for sharded runs.trace.run_transaction(inputs=x, until=...)stops a run at a site when you do not want the rest of the model to execute.
What torchvision's feature extractor does that TorchLens does not¶
- It returns a
torch.nn.Module(aGraphModule) that you can call, fine-tune with a training loop, script or export as a truncated network.tl.extractreturns tensors for one call; it does not return a smaller model. - It drops the nodes after the last requested one, so later layers are not computed.
tl.extractruns the whole model unless you stop it withtrace.run_transaction(inputs=x, until=...). - It works from the symbolic graph without any input, which is what
get_graph_node_namesuses. TorchLens needs a real input to list what ran.
For tools that live outside TorchLens's scope, see where to go instead.
Using both¶
torchvision feeds TorchLens¶
A torchvision model is an ordinary nn.Module, so there is nothing to convert: pass
resnet18(weights=...) to tl.extract or tl.trace as the first example does. TorchLens has
no bridge for the extractor's GraphModule itself; give it the original model.
TorchLens feeds torchvision¶
There is no bridge. The module addresses a trace lists (trace.modules) are the same strings
return_nodes takes for module outputs, so you can find a name with TorchLens and pass it to
create_feature_extractor. Functional ops have FX node names that TorchLens labels do not
reproduce.
Semantic differences¶
- Mapping direction. torchvision's
return_nodesmaps node name to output key;tl.extractmaps output key to lookup. - Detached by default.
tl.extractreturns detached copies. torchvision's extractor returns tensors that stay attached to autograd when it runs with gradients enabled. - Whole model runs. TorchLens runs the unmodified model; the tensor at
layer1is the tensor a forward hook onlayer1sees, as the second example checks. - Eager, not symbolic. TorchLens records what a real input executed; it does not need the model to be FX-traceable.