activationscope 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. activationscope-0.1.0/MANIFEST.in +5 -0
  2. activationscope-0.1.0/PKG-INFO +94 -0
  3. activationscope-0.1.0/README.md +69 -0
  4. activationscope-0.1.0/activationscope/_C.pyi +46 -0
  5. activationscope-0.1.0/activationscope/__init__.py +12 -0
  6. activationscope-0.1.0/activationscope/_naive.py +105 -0
  7. activationscope-0.1.0/activationscope/policies.py +58 -0
  8. activationscope-0.1.0/activationscope/tracker.py +348 -0
  9. activationscope-0.1.0/activationscope/utils.py +202 -0
  10. activationscope-0.1.0/activationscope.egg-info/PKG-INFO +94 -0
  11. activationscope-0.1.0/activationscope.egg-info/SOURCES.txt +45 -0
  12. activationscope-0.1.0/activationscope.egg-info/dependency_links.txt +1 -0
  13. activationscope-0.1.0/activationscope.egg-info/requires.txt +8 -0
  14. activationscope-0.1.0/activationscope.egg-info/top_level.txt +1 -0
  15. activationscope-0.1.0/csrc/accumulator.hpp +65 -0
  16. activationscope-0.1.0/csrc/bindings.cpp +53 -0
  17. activationscope-0.1.0/csrc/callback.cpp +162 -0
  18. activationscope-0.1.0/csrc/callback.hpp +25 -0
  19. activationscope-0.1.0/csrc/capture_policy.cpp +38 -0
  20. activationscope-0.1.0/csrc/capture_policy.hpp +38 -0
  21. activationscope-0.1.0/csrc/datastructures.hpp +55 -0
  22. activationscope-0.1.0/csrc/hook_register.cpp +115 -0
  23. activationscope-0.1.0/csrc/hook_register.hpp +31 -0
  24. activationscope-0.1.0/csrc/reduction.cpp +27 -0
  25. activationscope-0.1.0/csrc/reduction.hpp +35 -0
  26. activationscope-0.1.0/csrc/session.cpp +259 -0
  27. activationscope-0.1.0/csrc/session.hpp +100 -0
  28. activationscope-0.1.0/csrc/utils.cpp +61 -0
  29. activationscope-0.1.0/csrc/utils.hpp +30 -0
  30. activationscope-0.1.0/pyproject.toml +47 -0
  31. activationscope-0.1.0/setup.cfg +4 -0
  32. activationscope-0.1.0/setup.py +39 -0
  33. activationscope-0.1.0/tests/test_capture_policy_edge_cases.py +338 -0
  34. activationscope-0.1.0/tests/test_e2e_models.py +288 -0
  35. activationscope-0.1.0/tests/test_integ_capture_policies.py +119 -0
  36. activationscope-0.1.0/tests/test_integ_lifecycle.py +225 -0
  37. activationscope-0.1.0/tests/test_integ_reduction_policies.py +124 -0
  38. activationscope-0.1.0/tests/test_integ_storage_policies.py +75 -0
  39. activationscope-0.1.0/tests/test_memory_assumptions.py +182 -0
  40. activationscope-0.1.0/tests/test_memory_leak_detection.py +336 -0
  41. activationscope-0.1.0/tests/test_model_complexity.py +388 -0
  42. activationscope-0.1.0/tests/test_parity.py +156 -0
  43. activationscope-0.1.0/tests/test_pinned_memory.py +286 -0
  44. activationscope-0.1.0/tests/test_smoke.py +143 -0
  45. activationscope-0.1.0/tests/test_svd_analysis.py +671 -0
  46. activationscope-0.1.0/tests/test_unit_layer_selection.py +200 -0
  47. activationscope-0.1.0/tests/test_unit_policies.py +207 -0
@@ -0,0 +1,5 @@
1
+ include README.md
2
+ include pyproject.toml
3
+ include setup.py
4
+ recursive-include csrc *.cpp *.hpp
5
+ recursive-include activationscope *.py *.pyi
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: activationscope
3
+ Version: 0.1.0
4
+ Summary: High-performance PyTorch plugin for tracking and analyzing intermediate neural network activations
5
+ Author: Jan Miksa
6
+ Project-URL: Homepage, https://github.com/OneAndZero24/ActivationScope
7
+ Project-URL: Repository, https://github.com/OneAndZero24/ActivationScope
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: C++
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: torch>=2.0.1
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest>=7.0; extra == "dev"
21
+ Requires-Dist: pytest-cov; extra == "dev"
22
+ Requires-Dist: pytest-benchmark; extra == "dev"
23
+ Requires-Dist: ruff; extra == "dev"
24
+ Requires-Dist: build; extra == "dev"
25
+
26
+ # ActivationScope
27
+
28
+ ***Jan Miksa @ IDEAS Research Institute***
29
+
30
+ **High-performance PyTorch activation tracker with online reduction functionality for efficient model analysis.**
31
+
32
+ Built on Python + C++ with native `libtorch` hooks and **TorchScript** (`torch.jit.script`) reductions compiled to `.pt` files.
33
+
34
+ **Key Benefits**
35
+ - Zero‑copy read‑back: activation tensors are shared between C++ and Python without extra copies.
36
+ - Native C++ hooks: no Python compute overhead per forward pass.
37
+ - Flexible policy knobs (`StoragePolicy`, `ReductionPolicy`, `CapturePolicy`, `CaptureMode`) let you balance memory, compute, and I/O.
38
+ - Direct-to-disk streaming (`StoragePolicy.DISK`) — activations are written directly from C++ to disk. Ideal for long-running training loops with very large models. Activations are read back on demand from `.dat` files.
39
+ - Works with large models (e.g., diffusion) and supports streaming statistics for online use cases.
40
+
41
+ ---
42
+
43
+ ## Quick Start
44
+
45
+ Every tracked layer stores full activations by default — no registration needed:
46
+
47
+ ```python
48
+ import activationscope
49
+
50
+ with activationscope.ActivationScope().track(model) as tracker:
51
+ for x, y in dataloader:
52
+ out = model(x)
53
+ loss.backward()
54
+
55
+ acts = tracker.activations # {layer_name: [Tensor, ...]} across all batches
56
+ ```
57
+
58
+ ## Performance
59
+
60
+ ### Toy model — 48 × Linear(256,256), batch=32, 200 forwards, CPU
61
+
62
+ | Approach | ms/forward | Overhead vs baseline | Data captured |
63
+ |---|---|---|---|
64
+ | No tracking | 2.05 | — | — |
65
+ | Naive Python hooks | 3.13 | +52.7% | 594 MiB |
66
+ | **ActivationScope** | **2.65** | **+29.2%** | **594 MiB** |
67
+
68
+ - **Peak VMS identical** — Scope 402,506 vs Naive 402,630 MiB (~0.03% diff, within ASLR noise)
69
+ - **1.18× faster** than naive Python hooks (3.13 → 2.65 ms/fwd)
70
+ - **95 layers tracked** (inputs + outputs across 48 linear layers)
71
+ - **Zero-copy readback**: 594 MiB in 2.4 ms
72
+
73
+ Run it yourself:
74
+ ```bash
75
+ # Toy model (fast, GPU or CPU)
76
+ PYTHONPATH=. python -m benchmark.runner
77
+
78
+ # Pretrained ResNet-18 (requires torchvision)
79
+ PYTHONPATH=. python -m benchmark.runner --model resnet18
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Usage Guide
85
+
86
+ For detailed usage instructions, see the [Usage Documentation](docs/usage/README.md).
87
+
88
+ ## Development Guide
89
+
90
+ Documentation and developer setup information is available in [Development Documentation](docs/development/README.md).
91
+
92
+ ## Design Documentation
93
+
94
+ The design document outlining the architecture and implementation details can be found in [Design Documentation](docs/DESIGN.md).
@@ -0,0 +1,69 @@
1
+ # ActivationScope
2
+
3
+ ***Jan Miksa @ IDEAS Research Institute***
4
+
5
+ **High-performance PyTorch activation tracker with online reduction functionality for efficient model analysis.**
6
+
7
+ Built on Python + C++ with native `libtorch` hooks and **TorchScript** (`torch.jit.script`) reductions compiled to `.pt` files.
8
+
9
+ **Key Benefits**
10
+ - Zero‑copy read‑back: activation tensors are shared between C++ and Python without extra copies.
11
+ - Native C++ hooks: no Python compute overhead per forward pass.
12
+ - Flexible policy knobs (`StoragePolicy`, `ReductionPolicy`, `CapturePolicy`, `CaptureMode`) let you balance memory, compute, and I/O.
13
+ - Direct-to-disk streaming (`StoragePolicy.DISK`) — activations are written directly from C++ to disk. Ideal for long-running training loops with very large models. Activations are read back on demand from `.dat` files.
14
+ - Works with large models (e.g., diffusion) and supports streaming statistics for online use cases.
15
+
16
+ ---
17
+
18
+ ## Quick Start
19
+
20
+ Every tracked layer stores full activations by default — no registration needed:
21
+
22
+ ```python
23
+ import activationscope
24
+
25
+ with activationscope.ActivationScope().track(model) as tracker:
26
+ for x, y in dataloader:
27
+ out = model(x)
28
+ loss.backward()
29
+
30
+ acts = tracker.activations # {layer_name: [Tensor, ...]} across all batches
31
+ ```
32
+
33
+ ## Performance
34
+
35
+ ### Toy model — 48 × Linear(256,256), batch=32, 200 forwards, CPU
36
+
37
+ | Approach | ms/forward | Overhead vs baseline | Data captured |
38
+ |---|---|---|---|
39
+ | No tracking | 2.05 | — | — |
40
+ | Naive Python hooks | 3.13 | +52.7% | 594 MiB |
41
+ | **ActivationScope** | **2.65** | **+29.2%** | **594 MiB** |
42
+
43
+ - **Peak VMS identical** — Scope 402,506 vs Naive 402,630 MiB (~0.03% diff, within ASLR noise)
44
+ - **1.18× faster** than naive Python hooks (3.13 → 2.65 ms/fwd)
45
+ - **95 layers tracked** (inputs + outputs across 48 linear layers)
46
+ - **Zero-copy readback**: 594 MiB in 2.4 ms
47
+
48
+ Run it yourself:
49
+ ```bash
50
+ # Toy model (fast, GPU or CPU)
51
+ PYTHONPATH=. python -m benchmark.runner
52
+
53
+ # Pretrained ResNet-18 (requires torchvision)
54
+ PYTHONPATH=. python -m benchmark.runner --model resnet18
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Usage Guide
60
+
61
+ For detailed usage instructions, see the [Usage Documentation](docs/usage/README.md).
62
+
63
+ ## Development Guide
64
+
65
+ Documentation and developer setup information is available in [Development Documentation](docs/development/README.md).
66
+
67
+ ## Design Documentation
68
+
69
+ The design document outlining the architecture and implementation details can be found in [Design Documentation](docs/DESIGN.md).
@@ -0,0 +1,46 @@
1
+ """Type stubs for the compiled C++ extension ``activationscope._C``.
2
+
3
+ TorchScript reduction path — reductions are compiled via torch.jit.script,
4
+ serialised to .pt files, and loaded by the C++ backend.
5
+ """
6
+
7
+ from typing import Any, Dict, List
8
+
9
+ import torch
10
+
11
+ # ── Session lifecycle ──────────────────────────────────────────────
12
+
13
+ def session_create(
14
+ storage: int,
15
+ reduction: int,
16
+ sample_every: int,
17
+ max_batches: int,
18
+ auto_cpu_threshold_bytes: int,
19
+ use_pinned: bool,
20
+ session_dir: str = "",
21
+ capture_mode: int = 0,
22
+ ) -> int: ...
23
+
24
+ def session_destroy(id: int) -> None: ...
25
+
26
+ def session_readback(
27
+ id: int,
28
+ ) -> Dict[str, List[torch.Tensor]]: ...
29
+
30
+ def session_readback_disk(
31
+ id: int,
32
+ ) -> Dict[str, List[str]]: ...
33
+
34
+ def session_clear(id: int) -> None: ...
35
+
36
+ def session_detach_hooks(id: int) -> None: ...
37
+
38
+ # ── Hook registration ──────────────────────────────────────────────
39
+
40
+ def session_register_hooks(
41
+ id: int,
42
+ module_ptr: Any,
43
+ layer_key: str,
44
+ capture_dir_int: int,
45
+ reduction_path: str = "",
46
+ ) -> None: ...
@@ -0,0 +1,12 @@
1
+ """Public API for ActivationScope — session-scoped, zero-copy, native hooks."""
2
+
3
+ from activationscope.policies import StoragePolicy, ReductionPolicy, CapturePolicy, CaptureMode
4
+ from activationscope.tracker import ActivationScope
5
+
6
+ __all__ = [
7
+ "ActivationScope",
8
+ "StoragePolicy",
9
+ "ReductionPolicy",
10
+ "CapturePolicy",
11
+ "CaptureMode",
12
+ ]
@@ -0,0 +1,105 @@
1
+ """Shared naive PyTorch forward-hook tracker for benchmarking and testing.
2
+
3
+ Provides a simple reference implementation of activation accumulation using
4
+ Python ``register_forward_hook``. Used by the benchmark runner to compare
5
+ memory/throughput against ActivationScope's C++ hooks, and by parity tests
6
+ to verify equivalent behavior.
7
+ """
8
+
9
+ import contextlib
10
+ from fnmatch import fnmatch
11
+
12
+ import torch
13
+
14
+ from activationscope.policies import CaptureMode
15
+
16
+
17
+ class NaiveHookTracker:
18
+ """Accumulate per-layer activations via Python forward hooks.
19
+
20
+ Mirrors the behavior of ``ActivationScope`` with ``STORE_ALL`` reduction
21
+ and ``EVERY`` capture policy, but uses pure-Python hooks. Not intended
22
+ for production use — exists solely as a reference for benchmarking and
23
+ correctness testing.
24
+
25
+ Parameters
26
+ ----------
27
+ capture_mode : CaptureMode
28
+ Controls copy behaviour of captured tensors:
29
+ * ``CaptureMode.REFERENCE`` (default) — ``.detach().cpu()``.
30
+ Tensors share storage with the autograd graph and may be
31
+ invalidated on subsequent forward passes.
32
+ * ``CaptureMode.SNAPSHOT`` — ``.detach().cpu().clone()``.
33
+ Completely independent copies, safe to keep across forwards.
34
+
35
+ Usage::
36
+
37
+ n = NaiveHookTracker(capture_mode=CaptureMode.SNAPSHOT)
38
+ with n.track(model):
39
+ for _ in range(n_batches):
40
+ _ = model(x)
41
+ acts = n.activations # dict[str, list[Tensor]]
42
+ """
43
+
44
+ def __init__(self, capture_mode: CaptureMode = CaptureMode.REFERENCE):
45
+ self._handles: list = []
46
+ self._activations: dict[str, list[torch.Tensor]] = {}
47
+ self._capture_mode = capture_mode
48
+
49
+ def track(self, model, layers=None):
50
+ """Context manager: attach → yield self → detach."""
51
+
52
+ @contextlib.contextmanager
53
+ def _ctx():
54
+ self._attach(model, layers=layers)
55
+ yield self
56
+ self._detach()
57
+
58
+ return _ctx()
59
+
60
+ def _attach(self, model, layers=None):
61
+ containers = (torch.nn.ModuleList, torch.nn.ModuleDict, torch.nn.Sequential)
62
+ all_modules = {
63
+ n: m
64
+ for n, m in model.named_modules()
65
+ if n != "" and not isinstance(m, containers)
66
+ }
67
+ targets = (
68
+ all_modules
69
+ if layers is None
70
+ else {
71
+ n: m
72
+ for n, m in all_modules.items()
73
+ if any(fnmatch(n, p) for p in layers)
74
+ }
75
+ )
76
+ self._activations = {name: [] for name in targets}
77
+ for name in targets:
78
+ handle = targets[name].register_forward_hook(self._make_hook(name))
79
+ self._handles.append(handle)
80
+
81
+ def _make_hook(self, layer_name):
82
+ capture_mode = self._capture_mode
83
+
84
+ def hook_fn(_module, _inp, out):
85
+ if isinstance(out, torch.Tensor):
86
+ t = out.detach().cpu()
87
+ self._activations[layer_name].append(
88
+ t.clone() if capture_mode == CaptureMode.SNAPSHOT else t
89
+ )
90
+ elif isinstance(out, (tuple, list)) and len(out) > 0:
91
+ t = out[0].detach().cpu()
92
+ self._activations[layer_name].append(
93
+ t.clone() if capture_mode == CaptureMode.SNAPSHOT else t
94
+ )
95
+
96
+ return hook_fn
97
+
98
+ def _detach(self):
99
+ for h in self._handles:
100
+ h.remove()
101
+ self._handles = []
102
+
103
+ @property
104
+ def activations(self):
105
+ return self._activations
@@ -0,0 +1,58 @@
1
+ """ActivationScope policy enumerations.
2
+
3
+ Three independently tunable policy knobs that govern every aspect of memory
4
+ and compute behaviour. Integer-valued to pass trivially to the C++ backend.
5
+ """
6
+
7
+
8
+ class _PolicyMeta(type):
9
+ """Metaclass that makes policy classes iterable and subscriptable."""
10
+
11
+ def __iter__(cls):
12
+ return iter(cls._members_)
13
+
14
+ def __class_getitem__(cls, item):
15
+ return cls._members_[item]
16
+
17
+
18
+ class StoragePolicy(int, metaclass=_PolicyMeta):
19
+ """Where tensor data lives after capture."""
20
+
21
+ AUTO = 0 # Heuristic: < threshold → CPU, >= threshold → GPU
22
+ CPU = 1 # Transfer to host memory
23
+ GPU = 2 # Stay on original device
24
+ DISK = 3 # Stream directly to disk; bypass in-memory accumulation
25
+
26
+ _members_ = (AUTO, CPU, GPU, DISK)
27
+
28
+
29
+ class ReductionPolicy(int):
30
+ """What gets kept vs. reduced across batches."""
31
+
32
+ STORE_ALL = 0 # Full tensor per batch appended
33
+ STREAMING = 1 # Per-batch reduction output replaces/accumulates in-place
34
+ FINAL_ONLY = 2 # Last-batch activation overwrites previous
35
+
36
+
37
+ class CapturePolicy(int, metaclass=_PolicyMeta):
38
+ """When and how often hooks fire."""
39
+
40
+ EVERY = 0 # Every forward fires hooks
41
+ SAMPLE_N = 1 # Captures every Nth forward
42
+ MAX_K = 2 # Captures exactly K batches then stops
43
+
44
+ _members_ = (EVERY, SAMPLE_N, MAX_K)
45
+
46
+
47
+ class CaptureMode(int, metaclass=_PolicyMeta):
48
+ """Whether to clone captured tensors for independent storage.
49
+
50
+ Controls the copy behaviour of both the native C++ tracker
51
+ (``activationscope.tracker.ActivationScope``) and the pure‑Python
52
+ fallback (``activationscope._naive.NaiveHookTracker``).
53
+ """
54
+
55
+ REFERENCE = 0 # detach() only — shares storage with autograd graph
56
+ SNAPSHOT = 1 # detach() + clone() — completely independent copy
57
+
58
+ _members_ = (REFERENCE, SNAPSHOT)