acids-cc 3.0__py3-none-any.whl

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.
@@ -0,0 +1,220 @@
1
+ Metadata-Version: 2.4
2
+ Name: acids-cc
3
+ Version: 3.0
4
+ Summary: Cached convolution layers for real-time streaming inference with PyTorch (upgrade of Caillon's cached_conv 2021 package)
5
+ Author: Axel Chemla--Romeu-Santos, Nils Demerlé
6
+ Author-email: ACIDS <acids.paris@gmail.com>
7
+ License: MIT
8
+ Project-URL: Documentation, https://cached-conv.readthedocs.io
9
+ Project-URL: Repository, https://github.com/acids-ircam/acids-cc
10
+ Keywords: deep learning,pytorch,convolution,streaming,real-time
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
14
+ Requires-Python: >=3.9
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: torch>1.8
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest; extra == "dev"
19
+ Requires-Dist: pytest-cov; extra == "dev"
20
+ Provides-Extra: docs
21
+ Requires-Dist: sphinx>=7; extra == "docs"
22
+ Requires-Dist: furo; extra == "docs"
23
+ Requires-Dist: myst-parser; extra == "docs"
24
+ Requires-Dist: sphinx-copybutton; extra == "docs"
25
+
26
+ # cached_conv
27
+
28
+ [![Documentation Status](https://readthedocs.org/projects/cached-conv/badge/?version=latest)](https://acids-cc.readthedocs.io/en/latest/)
29
+ [![docs](https://github.com/acids-ircam/acids-cc/actions/workflows/docs.yml/badge.svg)](https://github.com/acids-ircam/acids-cc/actions/workflows/docs.yml)
30
+
31
+ **Cached convolutions for real-time streaming inference with PyTorch.**
32
+
33
+ `cached_conv` provides drop-in replacements for PyTorch convolutional layers
34
+ that process a signal **block by block** and give the same result as
35
+ processing it all at once. Each layer keeps the end of the previous block in
36
+ an internal cache, used instead of zero padding at block boundaries.
37
+
38
+ The same model definition can be built in two modes:
39
+
40
+ - **eager** (default): regular layers, for training on full signals;
41
+ - **cached**: streaming layers, for real-time inference, e.g. once exported
42
+ with TorchScript.
43
+
44
+ ## Features
45
+
46
+ - 1D, 2D and 3D convolutions and transposed convolutions. Time is the last
47
+ axis, the only one that is streamed.
48
+ - `"same"`, `"causal"` and `"anticausal"` padding, computed from the kernel
49
+ size and dilation.
50
+ - Strides, dilation and groups.
51
+ - Latency tracking: every layer exposes its `cumulative_delay`, which
52
+ accumulates across a whole model.
53
+ - Windowed convolutions: the kernel is multiplied by a window before
54
+ convolving.
55
+ - StyleGAN 2-modulated convolutions. The style can change from one block to
56
+ the next.
57
+ - Streamable upsampling (nearest and linear), group normalization and
58
+ alignment of parallel branches.
59
+ - TorchScript export, on CPU, CUDA and MPS.
60
+
61
+ ## Installation
62
+
63
+ ```bash
64
+ pip install git+https://github.com/acids-ircam/acids-cc.git
65
+ ```
66
+
67
+ Requires Python ≥ 3.9 and PyTorch ≥ 1.9.
68
+
69
+ ## Quick start
70
+
71
+ ```python
72
+ import torch
73
+ import cached_conv as cc
74
+
75
+
76
+ def build_model():
77
+ conv1 = cc.Conv1d(1, 16, 5, padding="same")
78
+ conv2 = cc.Conv1d(16, 1, 5, padding="same",
79
+ cumulative_delay=conv1.cumulative_delay)
80
+ return cc.CachedSequential(conv1, conv2)
81
+
82
+
83
+ # 1. train in eager mode (the default)
84
+ model = build_model()
85
+
86
+ # 2. build the same model with cached layers, and load the trained weights
87
+ cc.use_cached_conv(True)
88
+ streaming_model = build_model()
89
+ streaming_model.load_state_dict(model.state_dict())
90
+
91
+ # 3. process a stream block by block
92
+ stream = torch.randn(1, 1, 8192)
93
+ y = torch.cat([streaming_model(block) for block in stream.split(512, -1)], -1)
94
+
95
+ # the output is delayed by the model's latency, in samples. The edges differ:
96
+ # before the signal starts, eager layers see zero padding, cached layers see
97
+ # the previous layer's response to silence
98
+ delay = streaming_model.cumulative_delay
99
+ offline = model(stream)
100
+ assert torch.allclose(y[..., 2 * delay:], offline[..., delay:-delay],
101
+ atol=1e-5)
102
+ ```
103
+
104
+ Cached layers can't look into the future: centered convolutions delay their
105
+ output instead. Pass each layer's `cumulative_delay` to the next one so that
106
+ the total latency is tracked correctly. With `padding="causal"` everywhere,
107
+ the latency only comes from strides.
108
+
109
+ ## Layers
110
+
111
+ The layers exposed by the package (`cc.Conv1d`, `cc.Upsample2d`...) are
112
+ factories: they return a regular or a cached layer, depending on
113
+ `cc.use_cached_conv` at construction time.
114
+
115
+ | | 1D | 2D | 3D |
116
+ | --- | --- | --- | --- |
117
+ | Convolution | `Conv1d` | `Conv2d` | `Conv3d` |
118
+ | Transposed convolution | `ConvTranspose1d` | `ConvTranspose2d` | `ConvTranspose3d` |
119
+ | Windowed convolution | `WindowedConv1d` | `WindowedConv2d` | `WindowedConv3d` |
120
+ | Modulated convolution | `ModulatedConv1d` | `ModulatedConv2d` | `ModulatedConv3d` |
121
+ | Upsampling | `Upsample1d` | `Upsample2d` | `Upsample3d` |
122
+ | Branch alignment | `AlignBranches1d` | `AlignBranches2d` | `AlignBranches3d` |
123
+ | Group normalization | `CachedGroupNorm1d` | `CachedGroupNorm2d` | `CachedGroupNorm3d` |
124
+
125
+ Inputs are shaped `(batch, channels, time)`,
126
+ `(batch, channels, height, time)` and `(batch, channels, depth, height, time)`.
127
+ In 2D and 3D, `padding` sets the mode along time; the other axes are always
128
+ padded "same".
129
+
130
+ ```python
131
+ # encoder / decoder; the transposed kernel size along time must be
132
+ # stride + 2 * padding
133
+ down = cc.Conv1d(16, 16, 4, stride=2, padding="same")
134
+ up = cc.ConvTranspose1d(16, 16, 4, stride=2, padding=1,
135
+ cumulative_delay=down.cumulative_delay)
136
+
137
+ # spectrogram convolution (frequency, time), windowed along time only
138
+ conv = cc.WindowedConv2d(1, 16, (3, 33), window=(None, "hann"))
139
+
140
+ # StyleGAN 2 modulation
141
+ mod = cc.ModulatedConv1d(16, 32, 3, padding="same", style_dim=64)
142
+ y = mod(torch.randn(2, 16, 512), torch.randn(2, 64))
143
+
144
+ # upsampling by an integer factor along time
145
+ upsample = cc.Upsample1d(2, mode="linear",
146
+ cumulative_delay=down.cumulative_delay)
147
+
148
+ # residual connection: delays the identity branch to match the convolution
149
+ conv = cc.Conv1d(16, 16, 3, padding="same")
150
+ residual = cc.AlignBranches1d(conv, torch.nn.Identity(),
151
+ delays=[conv.cumulative_delay, 0])
152
+ y_conv, y_identity = residual(torch.randn(1, 16, 512))
153
+ ```
154
+
155
+ ## Exporting with TorchScript
156
+
157
+ Caches are allocated on the first call. Run the cached model once before
158
+ scripting it:
159
+
160
+ ```python
161
+ cc.use_cached_conv(True)
162
+ model = build_model().eval()
163
+
164
+ with torch.no_grad():
165
+ model(torch.zeros(1, 1, 512))
166
+
167
+ torch.jit.script(model).save("model.ts")
168
+ ```
169
+
170
+ Caches have room for `max_batch_size` streams (a layer argument). Block
171
+ lengths must be divisible by the model's total stride.
172
+
173
+ ## Testing
174
+
175
+ `cc.test_equal` checks that a model gives the same output when streamed as
176
+ when processed offline:
177
+
178
+ ```python
179
+ assert cc.test_equal(build_model, torch.randn(1, 1, 4096))
180
+ ```
181
+
182
+ To run the library's test suite, on all available devices:
183
+
184
+ ```bash
185
+ pip install -e ".[dev]"
186
+ pytest tests
187
+ ```
188
+
189
+ To run it against several Python / PyTorch versions with
190
+ [uv](https://docs.astral.sh/uv/):
191
+
192
+ ```bash
193
+ ./test_versions.sh -p "3.9 3.12" -t "1.13 2.0 latest"
194
+ ./test_versions.sh --lowest # oldest torch for each Python version
195
+ ```
196
+
197
+ `tests/test_audio.py` also writes audio examples to `tests/audio_outputs/`
198
+ (waveforms in 1D, spectrograms in 2D / 3D), in eager and streaming modes.
199
+
200
+ ## Documentation
201
+
202
+ The full documentation is on
203
+ [cached-conv.readthedocs.io](https://cached-conv.readthedocs.io). To build it
204
+ locally:
205
+
206
+ ```bash
207
+ pip install -r docs/requirements.txt
208
+ sphinx-build -b html docs docs/_build/html
209
+ ```
210
+
211
+ ## Credits
212
+
213
+ Developed at [ACIDS](https://acids.ircam.fr) (IRCAM) by Axel
214
+ Chemla--Romeu-Santos and Nils Demerlé. Upgrade of the original
215
+ [cached_conv](https://github.com/acids-ircam/cached_conv) package by Antoine
216
+ Caillon (2021).
217
+
218
+ ## License
219
+
220
+ MIT
@@ -0,0 +1,11 @@
1
+ cached_conv/__init__.py,sha256=ipXUyiMTOkIFXC1WZzAag2PDHM79hayQbW58ueiguR8,5548
2
+ cached_conv/conv_1d.py,sha256=cD1crJmtqDoJEiG3kR6_5naah8GUX22VTuAbyoAyGYg,9670
3
+ cached_conv/conv_2d.py,sha256=E5-7imoOXdGYMd_gDNaH3NycL_lA6g5_TWCh7k4kaDQ,6960
4
+ cached_conv/conv_3d.py,sha256=qJyfYpOyQGDpT1C5BkEGPBQClCNC-5rmSHZn0P4qQs8,6662
5
+ cached_conv/upsample.py,sha256=Wh6Ici3Rl16HdDq9rfW7Iw9ZVOBCR4Sh2N7JPpGkBdQ,5913
6
+ cached_conv/utils.py,sha256=IF_8yLADQJUem4609sr6DlQqbTeRxLGmwlfW25B0Vqk,3696
7
+ cached_conv/variants.py,sha256=13hhgctHuv7JLfABQZoZewcVGBSG5cu4VJQ-gt0KqQQ,6126
8
+ acids_cc-3.0.dist-info/METADATA,sha256=sT3H5X7sj2MCzdL1RwDR2Uzl0NtrtfXiEHSus1jD06Q,7641
9
+ acids_cc-3.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
10
+ acids_cc-3.0.dist-info/top_level.txt,sha256=eL4WC83WC3L5w7KL96_vqaOwAKWeoW3RgUgi7UhlRos,12
11
+ acids_cc-3.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ cached_conv
@@ -0,0 +1,200 @@
1
+ import torch
2
+
3
+ USE_CACHED_CONV = False
4
+ MAX_BATCH_SIZE = 64 # Warning! added for retro-compatibility
5
+
6
+ def use_cached_conv(state: bool):
7
+ global USE_CACHED_CONV
8
+ USE_CACHED_CONV = state
9
+
10
+
11
+ def is_cached():
12
+ global USE_CACHED_CONV
13
+ return USE_CACHED_CONV
14
+
15
+
16
+ from .utils import Branches
17
+ from .conv_1d import (get_padding, CachedSequential, Sequential,
18
+ CachedPadding, CachedConv1d, CachedConvTranspose1d,
19
+ CachedGroupNorm, CachedGroupNorm1d)
20
+ from .conv_1d import Conv1d as _Conv1d
21
+ from .conv_1d import ConvTranspose1d as _ConvTranspose1d
22
+ from .conv_1d import AlignBranches1d as _AlignBranches1d
23
+ from .conv_2d import CachedConv2d, CachedConvTranspose2d, CachedGroupNorm2d
24
+ from .conv_2d import Conv2d as _Conv2d
25
+ from .conv_2d import ConvTranspose2d as _ConvTranspose2d
26
+ from .conv_2d import AlignBranches2d as _AlignBranches2d
27
+ from .conv_3d import CachedConv3d, CachedConvTranspose3d, CachedGroupNorm3d
28
+ from .conv_3d import Conv3d as _Conv3d
29
+ from .conv_3d import ConvTranspose3d as _ConvTranspose3d
30
+ from .conv_3d import AlignBranches3d as _AlignBranches3d
31
+ from .variants import (get_window, CachedWindowedConv1d,
32
+ CachedWindowedConv2d, CachedWindowedConv3d,
33
+ CachedModulatedConv1d, CachedModulatedConv2d,
34
+ CachedModulatedConv3d)
35
+ from . import variants as _variants
36
+ from .upsample import CachedUpsample1d, CachedUpsample2d, CachedUpsample3d
37
+ from . import upsample as _upsample
38
+
39
+
40
+ def chunk_process(f, x, N):
41
+ x = torch.split(x, x.shape[-1] // N, -1)
42
+ y = torch.cat([f(_x) for _x in x], -1)
43
+ return y
44
+
45
+
46
+ def test_equal(model_constructor, input_tensor, crop=True):
47
+ initial_state = USE_CACHED_CONV
48
+
49
+ use_cached_conv(False)
50
+ model = model_constructor().to(input_tensor.device)
51
+ use_cached_conv(True)
52
+ cmodel = model_constructor().to(input_tensor.device)
53
+
54
+ for p1, p2 in zip(model.parameters(), cmodel.parameters()):
55
+ p2.data.copy_(p1.data)
56
+
57
+ cd = cmodel.cumulative_delay
58
+ if cd > 0:
59
+ y = model(input_tensor)[..., :-cd]
60
+ else:
61
+ y = model(input_tensor)
62
+ cy = chunk_process(lambda x: cmodel(x), input_tensor, 4)[..., cd:]
63
+
64
+ if crop and cd > 0:
65
+ y = y[..., cd:-cd]
66
+ cy = cy[..., cd:-cd]
67
+
68
+ use_cached_conv(initial_state)
69
+ return torch.allclose(y, cy, 1e-4, 1e-4)
70
+
71
+
72
+ def Conv1d(*args, **kwargs):
73
+ if USE_CACHED_CONV:
74
+ return CachedConv1d(*args, **kwargs)
75
+ else:
76
+ return _Conv1d(*args, **kwargs)
77
+
78
+
79
+ def ConvTranspose1d(*args, **kwargs):
80
+ if USE_CACHED_CONV:
81
+ return CachedConvTranspose1d(*args, **kwargs)
82
+ else:
83
+ return _ConvTranspose1d(*args, **kwargs)
84
+
85
+
86
+ def AlignBranches1d(*args, **kwargs):
87
+ if USE_CACHED_CONV:
88
+ return _AlignBranches1d(*args, **kwargs)
89
+ else:
90
+ return Branches(*args, **kwargs)
91
+
92
+
93
+ # backward-compatible alias
94
+ def AlignBranches(*args, **kwargs):
95
+ return AlignBranches1d(*args, **kwargs)
96
+
97
+
98
+ def Conv2d(*args, **kwargs):
99
+ if USE_CACHED_CONV:
100
+ return CachedConv2d(*args, **kwargs)
101
+ else:
102
+ return _Conv2d(*args, **kwargs)
103
+
104
+
105
+ def ConvTranspose2d(*args, **kwargs):
106
+ if USE_CACHED_CONV:
107
+ return CachedConvTranspose2d(*args, **kwargs)
108
+ else:
109
+ return _ConvTranspose2d(*args, **kwargs)
110
+
111
+
112
+ def AlignBranches2d(*args, **kwargs):
113
+ if USE_CACHED_CONV:
114
+ return _AlignBranches2d(*args, **kwargs)
115
+ else:
116
+ return Branches(*args, **kwargs)
117
+
118
+
119
+ def Conv3d(*args, **kwargs):
120
+ if USE_CACHED_CONV:
121
+ return CachedConv3d(*args, **kwargs)
122
+ else:
123
+ return _Conv3d(*args, **kwargs)
124
+
125
+
126
+ def ConvTranspose3d(*args, **kwargs):
127
+ if USE_CACHED_CONV:
128
+ return CachedConvTranspose3d(*args, **kwargs)
129
+ else:
130
+ return _ConvTranspose3d(*args, **kwargs)
131
+
132
+
133
+ def AlignBranches3d(*args, **kwargs):
134
+ if USE_CACHED_CONV:
135
+ return _AlignBranches3d(*args, **kwargs)
136
+ else:
137
+ return Branches(*args, **kwargs)
138
+
139
+
140
+ def WindowedConv1d(*args, **kwargs):
141
+ if USE_CACHED_CONV:
142
+ return CachedWindowedConv1d(*args, **kwargs)
143
+ else:
144
+ return _variants.WindowedConv1d(*args, **kwargs)
145
+
146
+
147
+ def WindowedConv2d(*args, **kwargs):
148
+ if USE_CACHED_CONV:
149
+ return CachedWindowedConv2d(*args, **kwargs)
150
+ else:
151
+ return _variants.WindowedConv2d(*args, **kwargs)
152
+
153
+
154
+ def WindowedConv3d(*args, **kwargs):
155
+ if USE_CACHED_CONV:
156
+ return CachedWindowedConv3d(*args, **kwargs)
157
+ else:
158
+ return _variants.WindowedConv3d(*args, **kwargs)
159
+
160
+
161
+ def ModulatedConv1d(*args, **kwargs):
162
+ if USE_CACHED_CONV:
163
+ return CachedModulatedConv1d(*args, **kwargs)
164
+ else:
165
+ return _variants.ModulatedConv1d(*args, **kwargs)
166
+
167
+
168
+ def ModulatedConv2d(*args, **kwargs):
169
+ if USE_CACHED_CONV:
170
+ return CachedModulatedConv2d(*args, **kwargs)
171
+ else:
172
+ return _variants.ModulatedConv2d(*args, **kwargs)
173
+
174
+
175
+ def ModulatedConv3d(*args, **kwargs):
176
+ if USE_CACHED_CONV:
177
+ return CachedModulatedConv3d(*args, **kwargs)
178
+ else:
179
+ return _variants.ModulatedConv3d(*args, **kwargs)
180
+
181
+
182
+ def Upsample1d(*args, **kwargs):
183
+ if USE_CACHED_CONV:
184
+ return CachedUpsample1d(*args, **kwargs)
185
+ else:
186
+ return _upsample.Upsample1d(*args, **kwargs)
187
+
188
+
189
+ def Upsample2d(*args, **kwargs):
190
+ if USE_CACHED_CONV:
191
+ return CachedUpsample2d(*args, **kwargs)
192
+ else:
193
+ return _upsample.Upsample2d(*args, **kwargs)
194
+
195
+
196
+ def Upsample3d(*args, **kwargs):
197
+ if USE_CACHED_CONV:
198
+ return CachedUpsample3d(*args, **kwargs)
199
+ else:
200
+ return _upsample.Upsample3d(*args, **kwargs)