From 42c9c01c9b4a32f057b3db7eebb883192d067066 Mon Sep 17 00:00:00 2001 From: moto <855818+mthrok@users.noreply.github.com> Date: Fri, 2 Apr 2021 20:13:38 +0000 Subject: [PATCH 1/5] Add autograd test to T.TimeStretch (and F.phase_vocoder) --- .../transforms/autograd_test_impl.py | 58 ++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/test/torchaudio_unittest/transforms/autograd_test_impl.py b/test/torchaudio_unittest/transforms/autograd_test_impl.py index 8bf5829ffe..3581b33b90 100644 --- a/test/torchaudio_unittest/transforms/autograd_test_impl.py +++ b/test/torchaudio_unittest/transforms/autograd_test_impl.py @@ -1,4 +1,5 @@ from typing import List +import unittest from parameterized import parameterized import torch @@ -37,8 +38,12 @@ def assert_grad( inputs_ = [] for i in inputs: - i.requires_grad = True - inputs_.append(i.to(dtype=torch.float64, device=self.device)) + if torch.is_tensor(i): + i = i.to( + dtype=torch.cdouble if i.is_complex() else torch.double, + device=self.device) + i.requires_grad = True + inputs_.append(i) assert gradcheck(transform, inputs_) assert gradgradcheck(transform, inputs_, nondet_tol=nondet_tol) @@ -129,3 +134,52 @@ def test_amplitude_to_db(self): transform = T.AmplitudeToDB() waveform = get_whitenoise(sample_rate=sample_rate, duration=0.05, n_channels=2) self.assert_grad(transform, [waveform]) + + @unittest.expectedFailure + def test_timestretch_zeros_fail(self): + """Test that ``T.TimeStretch`` fails gradcheck at 0 + + This is because ``F.phase_vocoder`` converts data from cartesian to polar coordinate, + which performs ``atan2(img, real)``, and gradient is not defined at 0. + """ + n_fft = 16 + transform = T.TimeStretch(n_freq=n_fft // 2 + 1, fixed_rate=0.99) + waveform = torch.zeros(2, 40) + spectrogram = get_spectrogram(waveform, n_fft=n_fft, power=None) + self.assert_grad(transform, [spectrogram]) + + @nested_params( + [0.7, 0.8, 0.9, 1.0, 1.3], + [False, True], + ) + def test_timestretch(self, rate, test_pseudo_complex): + """Verify that ``T.TimeStretch`` does not fail if it's not too close to 0 + + ``T.TimeStrech`` is not differentiable around 0, so this test checks the differentiability + for cases where input is not zero, and different configurations of `TimeStretch`. + + Ideally, we should be testing on Spectrogram of random waveform but it is hard to control + the values around zeros. + """ + n_fft = 16 + transform = T.TimeStretch(n_freq=n_fft // 2 + 1, fixed_rate=rate) + waveform = torch.zeros(2, 40) + spectrogram = get_spectrogram(waveform, n_fft=n_fft, power=None) + + # Epsilon values tried + # + # Note: + # This is not experimental and comprehensive. + # The result also depends on ``n_fft``. + # + # CPU / CUDA + # * 1e-1 ok / ok + # * 1e-2 ok / ok + # * 1e-3 ok / ok + # * 1e-3 + 1e-3j ok / ok + # * 1e-4 ok / NG + + spectrogram += 1e-3 + if test_pseudo_complex: + spectrogram = torch.view_as_real(spectrogram) + self.assert_grad(transform, [spectrogram]) From df45ab8d0ac7a6224f2153fbb1e237fb57f42fdf Mon Sep 17 00:00:00 2001 From: moto <855818+mthrok@users.noreply.github.com> Date: Thu, 15 Apr 2021 14:48:27 +0000 Subject: [PATCH 2/5] Update test --- .../transforms/autograd_test_impl.py | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/test/torchaudio_unittest/transforms/autograd_test_impl.py b/test/torchaudio_unittest/transforms/autograd_test_impl.py index 3581b33b90..e3ba6d4e0b 100644 --- a/test/torchaudio_unittest/transforms/autograd_test_impl.py +++ b/test/torchaudio_unittest/transforms/autograd_test_impl.py @@ -153,33 +153,29 @@ def test_timestretch_zeros_fail(self): [False, True], ) def test_timestretch(self, rate, test_pseudo_complex): - """Verify that ``T.TimeStretch`` does not fail if it's not too close to 0 + """Verify that ``T.TimeStretch`` does not fail if it's not close to 0 ``T.TimeStrech`` is not differentiable around 0, so this test checks the differentiability - for cases where input is not zero, and different configurations of `TimeStretch`. + for cases where input is not zero. - Ideally, we should be testing on Spectrogram of random waveform but it is hard to control - the values around zeros. + As tested above, when spectrogram contains values close to zero, the gradients are unstable + and gradcheck fails. + + In this test, we generate spectrogram from random signal, then we push the points around + zero away from the origin. + + This process does not reflect the real use-case, and it is not practical for users, but + this helps us understand to what degree the function is differentiable and when not. """ n_fft = 16 transform = T.TimeStretch(n_freq=n_fft // 2 + 1, fixed_rate=rate) - waveform = torch.zeros(2, 40) + waveform = get_whitenoise(sample_rate=40, duration=1, n_channels=2) spectrogram = get_spectrogram(waveform, n_fft=n_fft, power=None) - # Epsilon values tried - # - # Note: - # This is not experimental and comprehensive. - # The result also depends on ``n_fft``. - # - # CPU / CUDA - # * 1e-1 ok / ok - # * 1e-2 ok / ok - # * 1e-3 ok / ok - # * 1e-3 + 1e-3j ok / ok - # * 1e-4 ok / NG - - spectrogram += 1e-3 + # 1e-3 is still too close on CPU + epsilon = 1e-2 + too_close = spectrogram.abs() < epsilon + spectrogram[too_close] = spectrogram[too_close] / spectrogram[too_close].abs() if test_pseudo_complex: spectrogram = torch.view_as_real(spectrogram) self.assert_grad(transform, [spectrogram]) From 4ee118cce1fcd4d91f8b359d415cb7d65583ddae Mon Sep 17 00:00:00 2001 From: moto <855818+mthrok@users.noreply.github.com> Date: Thu, 15 Apr 2021 16:53:00 +0000 Subject: [PATCH 3/5] Fix --- test/torchaudio_unittest/transforms/autograd_test_impl.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/torchaudio_unittest/transforms/autograd_test_impl.py b/test/torchaudio_unittest/transforms/autograd_test_impl.py index e3ba6d4e0b..def3cbd8ae 100644 --- a/test/torchaudio_unittest/transforms/autograd_test_impl.py +++ b/test/torchaudio_unittest/transforms/autograd_test_impl.py @@ -36,6 +36,8 @@ def assert_grad( ): transform = transform.to(dtype=torch.float64, device=self.device) + # For the autograd test, float32 and cfloat64 are not good enough + # we make sure that all the tensors are of float64 or cfloat128 inputs_ = [] for i in inputs: if torch.is_tensor(i): @@ -172,10 +174,10 @@ def test_timestretch(self, rate, test_pseudo_complex): waveform = get_whitenoise(sample_rate=40, duration=1, n_channels=2) spectrogram = get_spectrogram(waveform, n_fft=n_fft, power=None) - # 1e-3 is still too close on CPU + # 1e-3 is too small (on CPU) epsilon = 1e-2 too_close = spectrogram.abs() < epsilon - spectrogram[too_close] = spectrogram[too_close] / spectrogram[too_close].abs() + spectrogram[too_close] = epsilon * spectrogram[too_close] / spectrogram[too_close].abs() if test_pseudo_complex: spectrogram = torch.view_as_real(spectrogram) self.assert_grad(transform, [spectrogram]) From 9b8358163f042b2833708bdfd61cdbc66ae4ddf6 Mon Sep 17 00:00:00 2001 From: moto <855818+mthrok@users.noreply.github.com> Date: Thu, 15 Apr 2021 16:54:13 +0000 Subject: [PATCH 4/5] Fix --- test/torchaudio_unittest/transforms/autograd_test_impl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/torchaudio_unittest/transforms/autograd_test_impl.py b/test/torchaudio_unittest/transforms/autograd_test_impl.py index def3cbd8ae..27f6eaeb01 100644 --- a/test/torchaudio_unittest/transforms/autograd_test_impl.py +++ b/test/torchaudio_unittest/transforms/autograd_test_impl.py @@ -154,7 +154,7 @@ def test_timestretch_zeros_fail(self): [0.7, 0.8, 0.9, 1.0, 1.3], [False, True], ) - def test_timestretch(self, rate, test_pseudo_complex): + def test_timestretch_non_zero(self, rate, test_pseudo_complex): """Verify that ``T.TimeStretch`` does not fail if it's not close to 0 ``T.TimeStrech`` is not differentiable around 0, so this test checks the differentiability From 51adad1e3ba11049c9ee297f49a680c48cd9c728 Mon Sep 17 00:00:00 2001 From: moto <855818+mthrok@users.noreply.github.com> Date: Thu, 15 Apr 2021 14:25:44 -0400 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: anjali411 --- test/torchaudio_unittest/transforms/autograd_test_impl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/torchaudio_unittest/transforms/autograd_test_impl.py b/test/torchaudio_unittest/transforms/autograd_test_impl.py index 27f6eaeb01..18646134a5 100644 --- a/test/torchaudio_unittest/transforms/autograd_test_impl.py +++ b/test/torchaudio_unittest/transforms/autograd_test_impl.py @@ -36,8 +36,8 @@ def assert_grad( ): transform = transform.to(dtype=torch.float64, device=self.device) - # For the autograd test, float32 and cfloat64 are not good enough - # we make sure that all the tensors are of float64 or cfloat128 + # gradcheck and gradgradcheck only pass if the input tensors are of dtype `torch.double` or + # `torch.cdouble`, when the default eps and tolerance values are used. inputs_ = [] for i in inputs: if torch.is_tensor(i):