-
Notifications
You must be signed in to change notification settings - Fork 732
feat: lfilter with filterbanks support #1587
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
5c239cb to
e889232
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, it looks good. The semantic around filter bank looks straightforward though documentation can be improved.
If I understand correctly, the implementation cannot handle the case where one wants to apply different filters per channel. (though I am not sure if that's valid use-case.) Is that correct?
torchaudio/functional/filtering.py
Outdated
| Returns: | ||
| Tensor: Waveform with dimension of ``(..., time)``. | ||
| Tensor: Waveform with dimension of ``(..., *, time)``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that, things about shape and filter bank (noted as * in the current form) needs more clarification.
Specifically, the distinction between the cases of 1D coeffs and 2D coeffs should be explained.
(even though I think the use of * here is very sophisticated.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable. How about
| Tensor: Waveform with dimension of ``(..., *, time)``. | |
| Tensor: Waveform with dimension of either ``(..., *, time)`` if ``a_coeffs`` and ``b_coeffs`` are 2D Tensors, | |
| or ``(..., time)`` otherwise. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good, but can we simply replace the * with something like num_filters?
(And in the docstring of a_coeffs, b_coeffs, can we explicitly say either 1D with shape (num_order + 1) or 2D with shape (num_filters, num_order + 1)?)
The thing is, ellipsis is used in many docstrings, but asterisk is very unique to this function, so I think that explicitly mentioning the difference of 1D and 2D are easier to understand.
No, it cannot apply different filters per channel. All signals are applied with same set of filters, regardless. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @yoyololicon
Sorry for not getting back sooner.
I think this PR is good, but if you can consider the comments, that would be great.
Thanks!
torchaudio/functional/filtering.py
Outdated
| waveform = waveform.reshape(-1, 1, shape[-1]) | ||
| if a_coeffs.ndim > 1: | ||
| shape = shape[:-1] + (a_coeffs.shape[0], shape[-1]) | ||
| waveform = waveform.repeat(1, a_coeffs.shape[0], 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of repeating the waveform manually and computing the shape manually, how about overwriting shape after repeating? I think that will be slightly easier to understand.
waveform = waveform.repeat(...)
shape = waveform.size()
torchaudio/functional/filtering.py
Outdated
| Returns: | ||
| Tensor: Waveform with dimension of ``(..., time)``. | ||
| Tensor: Waveform with dimension of ``(..., *, time)``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good, but can we simply replace the * with something like num_filters?
(And in the docstring of a_coeffs, b_coeffs, can we explicitly say either 1D with shape (num_order + 1) or 2D with shape (num_filters, num_order + 1)?)
The thing is, ellipsis is used in many docstrings, but asterisk is very unique to this function, so I think that explicitly mentioning the difference of 1D and 2D are easier to understand.
BenchmarksI have managed to build the sources with cuda support using docker container. BeforeCPUCUDAAfterCPUCUDA |
|
Hi @yoyololicon Thanks for the benchmark. What do you think is the feasibility of splitting CPU/CUDA code? BTW, I found an attempt to use RNN in #226. Although it is also pointed out that RNN without activation would not improve the performance as much. https://discuss.pytorch.org/t/vanillarnn-without-activation-function/26507/2. I wonder if this could help us in custom CUDA |
|
Hi @mthrok ,
Yes, they are the size of input. I can do the multiple filters case later.
The overhead concern is technically true, although the actual implementation is done by replacing
Hmm, I can't see much potentials in this direction of using RNN, in our case for now, cuz RNN is strictly a case of first-order IIR, and cannot generalize to arbitrary order. |
|
@yoyololicon Sorry for not getting back to you. I am very busy these days and I should be able to get back to this at the end of this week. |
|
@yoyololicon Again, really sorry for taking a long time. Let's continue to the next step. |
| assert shape == waveform.size() == output_waveform.size() | ||
| assert input_shape == waveform.size() | ||
| assert target_shape == output_waveform.size() | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Follow-up: We should add tests to verify that the resulting signals are same whether multiple filters are applied individually or as a bank.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, will add batch consistency tests and also benchmarks of multiple filters in later commits.
resolves #1528 , relates to #1561
This merge add the ability to apply multiple filters in
F.lfilter.When the input shape is
(..., time), if the shape of filter coefficients is(number_of_filters, filter_order), the shape of output will be(...., number_of_filters, time), which is the input shape plus one additional dimension from the filters.Before feeding to the cpp backend, the python frontend first duplicate the input
number_of_filterstimes and stacking them to form a shape of(...., number_of_filters, time).The cpp backend assume the shape of
waveformand*_coeffsare(batch, number_of_filters, time)and(number_of_filters, filter_order)(before are just(batch, time)and(filter_order,)). Different filters is applied parallelly at the second dimension ofwaveform.cc @mthrok