torch 中的 stft、torchaudio 中的 Spectrogram、Melscale、MelSpectrogram 的使用
目录
torch.stft
output = torch.stft(input, n_fft, hop_length=None, win_length=None, window=None, center=True, pad_mode='reflect', normalized=False, onesided=None, return_complex=None)
简介:短时傅里叶变换
其中:
- input(Tensor) 代表输入信号
- n_fft(int) 代表快速傅里叶变换的序列长度
- hop_length(int) 代表相邻两个滑动窗口帧之间的距离,也即是帧移
- win_length(int) 代表窗口大小,默认等于 n_fft
- window(Tensor) 代表窗口函数,默认是矩形窗口(全1)
- center(bool) 代表是否对输入tensor在两端进行padding,使得第 t t t 帧是以 t × h o p _ l e n g t h t \times hop\_length t×hop_length 为中心的,默认为 True,如果为 False,则第 t t t 帧以 t × h o p _ l e n g t h t \times hop\_length t×hop_length 开始。
- pad_mode(str) 代表padding的模式(当center为True才有效),默认为 reflect,即对边界的值进行reflect padding
- normalized(bool) 代表是否对返回的STFT结果进行归一化,默认为 False
- onesided(bool) 代表是否只返回一侧的STFT结果(当输入Tensor为实数时,输出为对称的),当输入Tensor和window都为实数时默认为 True,反之为 False
- return_complex(bool) 代表返回实部和虚部的STFT结果还是返回复数
输出:
- output(Tensor) 代表输出的STFT结果,维度为 ⋆ × N × T \star \times N \times T ⋆×N×T, ⋆ \star ⋆ 代表 batch size, N N N 代表频率域的维度, T T T 代表时间域的维度。
计算表达式为:
X [ ω , m ] = ∑ k = 0 win_length-1 window [ k ] input [ m × hop_length + k ] exp ( − j 2 π ⋅ ω k win_length ) X[\omega, m] = \sum_{k=0}^{\text {win\_length-1 }} \text { window }[k] \text { input }[m \times \text { hop\_length }+k] \exp \left(-j \frac{2 \pi \cdot \omega k}{\text { win\_length }}\right) X[ω,m]=k=0∑win_length-1 window [k] input [m× hop_length +k]exp(−j win_length 2π⋅ωk)
其中,
m
m
m 为滑动窗口的索引,
ω
\omega
ω 代表频率,当 onesided=False
时,
ω
\omega
ω 的取值范围为:
[
0
,
n_fft
]
[0, \text { n\_fft }]
[0, n_fft ],当 onesided=True
时,
ω
\omega
ω 的取值范围为:
[
0
,
⌊
n_fft
2
⌋
]
[0, \lfloor \frac{\text { n\_fft} }2 \rfloor ]
[0,⌊2 n_fft⌋]
注意:上面的表达式是加窗的短时傅里叶变换,但是在计算过程中,往往采用快速傅里叶变换来计算每个 m m m 对应的帧,因此 n_fft 和 win_length 可以不相等,计算时对加窗后的信号进行补零到 n_fft 的长度,所以需要满足: 0 < win_length <= n_fft
当参数center默认为True时,此时会对信号进行padding,第 0 0 0 帧以 x [ 0 ] x[0] x[0] 居中,且左右两边 padding 的长度为 ⌊ n_fft 2 ⌋ \lfloor \frac{\text { n\_fft} }2 \rfloor ⌊2 n_fft⌋,输出的帧数为 T = ⌊ seq_len hop_length ⌋ + 1 T = \lfloor \frac{\text{seq\_len}}{\text{hop\_length}}\rfloor + 1 T=⌊hop_lengthseq_len⌋+1 其中,seq_len 代表输入tensor的序列长度。
而当设置center参数为False时,第 0 0 0 帧以 x [ 0 ] x[0] x[0] 为开头,输出维度很奇怪,这里我也不太懂,欢迎讨论。
在频域维度,当
oneside=True
输出的 N = n_fft 2 + 1 N = \frac{\text { n\_fft} }2 + 1 N=2 n_fft+1 ,当oneside=False
输出的 N = n_fft N = \text { n\_fft} N= n_fft
最后,由于此时并不知道输入信号的采样频率, 所以输出的频率没有具体值。或者说,输出矩阵在频率轴的值对应的频率为 ω = 2 π N k \omega = \frac{2\pi}{N}k ω=N2πk,其中, k k k 为频率轴的索引,且这里的 ω \omega ω 为DSP中常说的数字角频率,他和模拟频率 f f f 的关系为: ω = 2 π f f s \omega = \frac{2\pi f}{f_s} ω=fs2πf,其中 f s f_s fs 为输入信号的采样频率,那么 k = n_fft 2 k=\frac{\text{n\_fft}}{2} k=2n_fft 为频率轴信号的最高频率(假定
oneside=True
),对应的具体值为采样率的 1 / 2 1/2 1/2,而这也符合奈奎斯特采样定理:为了不失真地恢复原始信号,采样率必须大于测量信号最大频率的两倍 f s > 2 × f N f_s > 2 \times f_N fs>2×fN
torchaudio.transforms.Spectrogram
output = torchaudio.transforms.Spectrogram(n_fft: int = 400, win_length: Optional[int] = None, hop_length: Optional[int] = None, pad: int = 0, window_fn: Callable[[...], torch.Tensor] = <built-in method hann_window of type object>, power: Optional[float] = 2.0, normalized: bool = False, wkwargs: Optional[dict] = None, center: bool = True, pad_mode: str = 'reflect', onesided: bool = True, return_complex: Optional[bool] = None)
简介:求输入信号的语谱图
其中:
- n_fft 为FFT的长度,默认为400,会生成 n_fft // 2 + 1 个 bins
- win_length 为窗口的长度,默认为 n_fft
- hop_length 为相邻两个滑动窗口帧之间的距离,即帧移,默认为 win_length // 2
- pad 为对输入信号两边的补零长度,默认为 0
- window_fn 为窗函数,默认为 torch.hann_window
- power 为语谱图的幂指数,默认为 2.0,值必须大于 0,取 1 代表能量,取 2 代表功率。
- normalized 为是否对语谱图进行归一化,默认为 False
- wkwargs 为窗函数的参数,默认为 None
- center 为是否对输入tensor在两端进行padding,使得第 t t t 帧是以 t × h o p _ l e n g t h t \times hop\_length t×hop_length 为中心的,默认为 True,如果为 False,则第 t t t 帧以 t × h o p _ l e n g t h t \times hop\_length t×hop_length 开始。
- pad_mode 为补零方式,默认为 reflect
- onesided 为是否只计算一侧的语谱图,默认为 True,即返回单侧的语谱图,如果为 False,则返回全部的语谱图。
- return_complex 不再使用了
求解Spectrogram的本质其实还是stft,因此当
power=1
时,输出的就是stft的能量谱。当power=None
时,就是直接输出了stft的结果,当power=2
时,输出就是power=1
的值的平方,更多细节见其他中的第二条。
torchaudio.transforms.MelScale
torchaudio.transforms.MelScale(n_mels: int = 128, sample_rate: int = 16000, f_min: float = 0.0, f_max: Optional[float] = None, n_stft: Optional[int] = None, norm: Optional[str] = None, mel_scale: str = 'htk')
简介:用来将stft的结果转换为mel域中的stft的结果。
其中:
- n_mels(int) 为mel滤波器的个数,默认为128
- sample_rate(int) 为音频采样率,默认为16000
- f_min(float) 为音频的最小频率,默认为0.0
- f_max(float) 为音频的最大频率,默认为采样率二分之一: sample_rate // 2
- n_stft(int) 为stft的bin的个数,不给的话默认为None,从第一个输入计算得到。
- norm(str) 如果为“slaney”,则将三角形mel权重除以mel bin的宽度(即进行面积归一化),默认为None
- mel_scale(str) 采用的mel尺度: htk 或者 slaney,默认 htk
输入:
- input(Tensor) Spectrogram 的输出,其维度为
⋆
×
n
_
s
t
f
t
×
T
\star \times n\_stft \times T
⋆×n_stft×T
输出: - output(Tensor) 返回大小为 ⋆ × n _ m e l s × T \star \times n\_mels \times T ⋆×n_mels×T 的Tensor
这一过程其实就是首先计算一个 ⋆ × n _ s t f t × n _ m e l s \star \times n\_stft \times n\_mels ⋆×n_stft×n_mels 的滤波器组矩阵,这个矩阵体现了信号频率 和 mel 域频率之间的转换关系,然后和输入的 Spectrogram 做矩阵乘法:
最后求得mel域的Spectrogram,即Mel谱。
注意:计算Spectrogram的时候不需要采样率,这是因为输出的频率是数字域角频率,而在计算MelScale的时候,需要具体的模拟频率和Mel域频率之间的转换关系,因此需要采样率来求出模拟频率的具体值,最后再转换到Mel域频率。
torchaudio.transforms.MelSpectrogram
torchaudio.transforms.MelSpectrogram(sample_rate: int = 16000, n_fft: int = 400, win_length: Optional[int] = None, hop_length: Optional[int] = None, f_min: float = 0.0, f_max: Optional[float] = None, pad: int = 0, n_mels: int = 128, window_fn: Callable[[...], torch.Tensor] = <built-in method hann_window of type object>, power: float = 2.0, normalized: bool = False, wkwargs: Optional[dict] = None, center: bool = True, pad_mode: str = 'reflect', onesided: bool = True, norm: Optional[str] = None, mel_scale: str = 'htk')
简介:求输入信号的Mel谱
其中:
- sample_rate(int) 为输入波形的采样率
- n_fft(int) 为FFT的长度,默认为400,会生成 n_fft // 2 + 1 个 bins
- win_length 为窗口的长度,默认为 n_fft
- hop_length 为相邻两个滑动窗口帧之间的距离,即帧移,默认为 win_length // 2
- f_min(float) 为音频的最小频率,默认为0.0
- f_max(float) 为音频的最大频率,默认为None
- pad 为对输入信号两边的补零长度,默认为 0
- n_mels(int) 为mel滤波器的个数,默认为128
- window_fn 为窗函数,默认为 torch.hann_window
- power(float) 为语谱图的幂指数,默认为 2.0,值必须大于 0,取 1 代表能量,取 2 代表功率。
- normalized(bool) 为是否对stft的输出进行归一化,默认为 False
- wkwargs 为窗函数的参数,默认为 None
- center(bool) 为是否对输入tensor在两端进行padding,使得第 t t t 帧是以 t × h o p _ l e n g t h t \times hop\_length t×hop_length 为中心的,默认为 True,如果为 False,则第 t t t 帧以 t × h o p _ l e n g t h t \times hop\_length t×hop_length 开始。
- pad_mode(str) 为补零方式,默认为 reflect
- onesided(bool) 为是否只计算一侧的谱图,默认为 True,即返回单侧的语谱图,如果为 False,则返回全部的谱图。
- norm(str) 如果为“slaney”,则将三角形mel权重除以mel bin的宽度(即进行面积归一化),默认为None
- mel_scale(str) 采用的mel尺度: htk 或者 slaney,默认 htk
输入:
- input(Tensor) 输入波形信号
输出:
- output(Tensor) 输出Mel谱
可以看出,MelSpectrogram的参数完全就是Spectrogram的参数+MelScale的参数。实际上,这个函数的计算过程就是在里面先计算spectrogram再计算mel_scale:
测试结果也验证了这一点:
用两种方法计算的mel谱结果完全一样。
其他
- 关于信号的能量谱和功率谱(参考):
- 能量谱:通常用来描述能量信号(能量有限的信号称为能量信号),表示的是信号在各个频点中的能量分布情况,如果对能量谱在频域进行积分,则可以求出信号的总能量。
- 功率谱:通常用来描述功率信号(功率有限的信号称为功率信号),是指用密度的概念表示信号功率在各频率点的分布情况,对功率谱在频域上积分就可以得到信号的功率。
- 维纳-辛钦定理:信号的功率谱和其自相关函数是一对傅里叶变换。在工程实际中,即便是功率信号,由于持续的时间有限,可以直接对信号进行傅里叶变换,然后对得到的幅度谱的模求平方,再除以持续时间来估计信号的功率谱。
- 根据上面的解释,
torchaudio.transforms.Spectrogram
求功率谱时并没有除以持续时间,而是直接输出了平方值。
- 关于Spectrogram和stft:
- 当
torchaudio.transforms.Spectrogram
网络中的power=1
时,输出的Spectrogram是能量图,在其他参数完全相同的情况下,其输出结果和torch.stft
函数中return_complex=True
的输出再求复数的模值之后的结果相同:
图中, x 1 x_1 x1 是stft函数求模之后的结果, x 2 x_2 x2 是直接求Spectrogram得到的结果。 - 当
torchaudio.transforms.Spectrogram
网络中的power=None
时,输出的Spectrogram是sftf的直接结果,在其他参数完全相同的情况下,其输出结果和torch.stft
函数中return_complex=True
完全相同:
图中, x 1 x_1 x1 是stft函数之后的结果, x 2 x_2 x2 是直接求Spectrogram得到的结果。
- 事实上,Spectrogram内部就是调用了stft的计算:
然后根据 power
取值计算最终的输出。
- reflect padding : 以矩阵的边界为对称轴,将内部的数据复制到外部: