【Day5】从频域到 wave 的转换,浅谈虚数可以拿来 Train Model 吗?

在频域里面遭遇虚数

经过前面 4 篇的介绍我们已经知道如何萃取出声音的特徵了,我们用来训练的资料,都是在频域里面的资料,所以之後的模型势必然预测出来的会是个频谱。

虽然也是有人直接用 wave 来训练,但目前我觉得比较成功的案例大概是 waveGan 系列

那我们该如何把频谱转回 wave 呢 ? 在那之前我们先看看我们之前做了什麽事。

不就是个傅立叶转换吗 ?

没错,我们确实是做了傅立叶转换,但出来的结果是一个复数,所以我们取了 abs。

我就是要反其道而行,我今天就是要用这个复数来训练 model 拉!

确实也有人在做这方面的研究,但这部分遇到的困难就是,即便你让 weight 跟 bias 也变成了复数的型态,在遇到激活函数的时候(为了让 Backpropagation 起作用),根据刘维定理 这些有界的激活函数在复数平面上是常数,也就是说它们是线性的。所以当他们在复数平面上的时候就会有奇异点(不可微分的点),造成函数往无穷大那头爆炸。

那我设计一个超屌激活函数让它不要爆炸不就好了?

是这样子没错,但首先你会先设计到疯掉,然後还是无法避免的让它在某些点爆炸,接着你会设定一些条件让它可以指定搜索某部分的空间就好,来防止它爆炸。

有人有做到上面的程度,但目前只在 FCN 上成功

最後,光是设计激活函数还有边界之外,还有很多眉角要注意(就不一一列举了,自己也不是很明白),结果就是付出的运算成本跟得到的不成比例,因此这方面的研究目前看起来还是挺绝望的 XD。

总而言之,还是有人在努力突破这个问题,可惜目前并没有成功,回归正题,我们再取了 abs 之後,其实也等於丢了一个很重要的相位讯息,举一个简单的例子,5 可以分解成 3+4i,也可以分解成 4+3i,但你不晓得哪个才是原本的讯号。

Griffin Lim 演算法

那如果我们看的信号不是只看一帧而已,而是看了连续的信号,那麽当左右的信号幅度和原本正常的幅度不同,是否就说明了这样的相位是错误的,假设信号的幅度变化是有规律的,那麽相位就必定也会满足某种规律,比方说一个三角波,从一帧来看,每个相位是都可以的,但是如果要保证相邻的波型幅度是正确的,那麽每帧的相位就会有规律,也因为有这样的假设,所以如果你的讯号像是在坐过山车那样,这种算法就会失灵。

讲那麽多,直接做一次比较快啦!

import librosa
import numpy as np
from IPython.display import Audio

sig,sr = librosa.load("test.wav")
sig = sig[:50000]
# 听听看原始声音
Audio(sig,rate=sr)

# 定义一些参数,之後再补充说明
window='han'
hop_length = 256
n_fft = 2048
spec = librosa.feature.melspectrogram(sig,n_fft=n_fft,hop_length=hop_length,window=window)
# 我们直接 call 它的涵式来转回 stft,注意这里是已经取过 abs 的後的值
spec = librosa.feature.inverse.mel_to_stft(spec)

定义自己的 griffin lim ,虽然主要还是参考 librosa 的写法

def grifflim(S,n_iter=100,hop_length=hop_length,window="hann"):
    n_fft = 2 * (S.shape[0] - 1)
    # 初始化角度
    angles = np.empty(S.shape, dtype=np.complex64)
    angles = np.exp(2j * np.pi * np.random.rand(*S.shape))
    # 纪录重建之後的结果
    rebuilt = 0.0
    for _ in range(n_iter):
        # 纪录前一个重建之後的结果
        tprev = rebuilt

        # 把现在的讯号直接转回去
        inverse = librosa.istft(
            S * angles,
            hop_length=hop_length,
        )

        #  再转回来
        rebuilt = librosa.stft(
            inverse,
            n_fft=n_fft,
            hop_length=hop_length,
        )

        # 更新角度
        angles = rebuilt -  tprev
        angles /= np.abs(angles) 

    return librosa.istft(
        S * angles,
        hop_length=hop_length,
        window=window,
    )
    

来听一下重建之後的水平

sig_after = grifflim(spec)
Audio(sig_after,rate=sr)

讲了那麽多, 其实 librosa 都帮我们做好了 XD

librosa.griffinlim(S)
# 你甚至可以省略转成 stft
librosa.feature.inverse.mel_to_audio(M)

自己写还是有好处的,像是在更新角度的地方就可以用别的做法 (取个 exp 之类的),你也可以更改 n_ter 的次数,像 librosa 只做了 32 次,那结果是有非常严重的机械音。

没有错,griffin lim 一个致命的缺点就是机器音

所以当 AI 大红大紫之时,也有人透过训练,将这部分的交给 Model 来处理,我们称呼这种 Model 为

~~ Vocoder ~~

但明天想先补充一些其他名词资讯,可能後天才会开始介绍这部分 XD

小结

今天我们实作 griffin lim ,它可以让 mel 重新回到 wave 的怀抱,即使它是一种传统的演算法但依然有效;我们也稍微讨论了一下虚数在机器学习下的状况,得到的结论是目前还是不太行,最後这几天的 code 已更新在 Github 上了,我尽量想让大家可以从网页上 copy 过去 notebook 直接运行就好,希望大家喜欢,再次感谢您耐心的阅读!

参考资料

What Would Happen if Neural Network States Were Complex Numbers?
Griffin-Lim演算法

/images/emoticon/emoticon09.gif/images/emoticon/emoticon13.gif/images/emoticon/emoticon14.gif/images/emoticon/emoticon22.gif/images/emoticon/emoticon28.gif


<<:  【Day5】不是八卦阵的有序集合:阵列

>>:  Leetcode 挑战 Day 02 [9. Palindrome Number]

Day23 切版笔记- 人员介绍卡片

运用到的观念 border搭配伪元素制作出三角形区块 绝对定位&相对定位 用:hover ...

5. 如何在快速发展的公司中生存

前言 这篇文章比较是条列式的列出讲者在slack快速发展中学到的几件事情,适合给快速成长的公司中的...

09 程序除错技巧指南

不管在哪个阶段,在写程序时总是会遇到大大小小的问题,不是程序不照着你的想法走,就是他连动都不想动。在...

CSS微动画 - Loading来了!九宫格可以很多变化

Q: 还是Loading吗? A: 一大系列!接下来的样式会比较不同~ 上两篇做完圆圈的Loadi...

AI ninja project [day 21] 自动编码器 Autoencoders

可以先看看Autoencoders的构造, 将原始的numpy array 或是tensor(可能是...