搓一个语音唤醒


(非原创,代某位卷王发送)


(哈哈 这个排版 稀烂咯)

目标

搓一个语音唤醒(keyword spotting),用于上课时摸鱼,老师点名时可以做出提醒,并且保留点名前后的一部分音频(毕竟还得知道为啥点你)。只需要识别名字,因此输入长度为1秒左右,约为两到四个音节,输出为两个类别的概率。计划采用简单的CNN+线性,将图输入进来卷积+池化然后激活,过两遍,采用适应性池化压缩为$图数\times1\times1$的向量,进两层线性+激活,最后输出为二维向量。

首先做出图像来

取一个音轨,使用快速傅里叶变换来将音频变成频谱图。首先,用torchaudio.load来load一个.wav,返回第一个参数是波形,第二个是采样率,用torchaudio.transforms.MelSpectrogram(这里填上面解析出来的波形)(梅尔图)。其实最好输入前做个滤波之类的预处理,我是因为训练的时候发现有大量的极大值(其他都是零点几,有几个10几的)所以用了些奇怪的函数来(就是log的一个变形),然后进行一个归一化(用的是最大最小值归一化)

然后丢给神经网络,先组一个网络

由于没有经验,加上torch本身的教程和中文教程都大同小异地看不懂,所以一边摸索一边做(但是笔记是写完才做的。。。所以基本上忘记踩了什么坑了 悲)
aF采用elu函数(变形的泄露relu),卷积层1为1层入4层出7核格宽,经过(2,2)的最大池化,卷积层2为4层入6层出5核格宽,经过AdaptiveMaxPool2d变为$6\times1\times1$向量,线性层1输入6输出8,线性层2输入8输出2,net本身结构为

def forward(x):
    x=maxPool(aF(cov1(x)))
    x=adaptivePool(aF(cov2(x)))
    x=x.view(1,-1)#这个是压缩为二维向量
    x=aF(linear1(x))
    x=sigmoid(linear2(x))#主要是考虑到这是个概率,所以用个sigmoid变换到0~1
    return x

前向和反向

首先先将输入的东西变成tensor,丢进去forward输出用个概率,和标准比对,开始是反向更新。首先先重置优化器的梯度,optimizer用的是optim.SGD(net.parameters(),lr),其中net.parameters()是需要更新的参数,lr是learningRate(具体参数自己找pytorch,笑死,毫无指引)。loss采用交叉熵nn.CrossEntropyLoss,参数为两个float,我是把两个tensor给.float再导进去。然后对loss进行一个.backward()来取梯度,再optimizer.step()来进行一个步进更新。我最后面返回了一个loss.item(),问题是这个是啥我还没看懂,说是loss但是很不平滑。

将音频流切片丢进去判断有没有激活

就是验证部分,取了数据集里面的另一部分来进行检验,我做出来是0.93的准确率。

从外部输入音频流

使用pyaudio库。首先是用audio=pyaudio.PyAudio()初始化一个类变量,调用函数stream=audio.open(参数)进行初始化设置,调用data=stream.read(参数)来获取音频流,音频流是记为两位十六进制数的二进制字符串,我是用np.frombuffer(stream.read(这里是frames_per_buffer),dtype=np.float32)来变换的,记得因为是实时音频流所以写while里面,至于这堆二进制怎么变成波形图就靠np罢(称赞一下写综述的大大,该方法经过一系列采坑后通过综述找到的。
采集设备也比较重要,好的麦克风可以先处理掉一部分噪声,收音也更集中。一开始用耳麦做的时候噪点非常多,回来换了舍友的麦之后梅尔图干净了不少。但是还是有个问题,我是按处理.wav输入的方式处理np.frombuffer解析的数据,既把它的返回直接当成时域的信号,清洗后输入转换成梅尔图,再与先前获取的底噪的梅尔图相减。结果上看,结果归一化后,较大值比较多,较小值比较少,与从.wav中获取的梅尔图恰相反。

遇到的问题(只能想起一些li)

比如说最开始的梅尔图,会导致输入的图大部分数在0~1少部分在10以上或者-10以下,归一化之后就会导致大部分数挤在中间(当然,你不归一化就没有这个烦恼了 大概 我懒得优化了 反正也是堆屎山),所有没用原本的log2函数,而且有负数,所以考虑了用有负数部分的泄露relu函数变形。
然后还有就是如果发现自己训练的RC比例和样本比例相同那可能就是你样本有问题(别问我怎么知道的),我训练目标用的是北京希尔贝壳的HIMIA数据集(吐槽一下,我发现为什么英文只有女声,是我查找的问题吗?),训练的非目标样本是随便截的音频。
我使用nn.AdaptiveMaxPool2d是考虑了输入的音频长度可能有一定范围的差别,所以加的,你当然可以不加,你喜欢的话awa 一开始做那个外部音频流的时候就只找到pyaudio能采集,然后一开始用np.fromstring来处理二进制发现经过很奇怪,没法转换到tensor中进行MelSpectrogram,所以又试了wave库,本来要试ffmpeg的,结果发现Python上的ffmpeg库是只能处理视频的半成品,然后就放弃了去查综述。 MelSpectrogram的语法是MelSpectrogram(参数)(tensor形式的输入),如果报了int is not callable之类的(具体忘了x)那应该是MelSpectrogram的输入你搞错了,请检查一下输入是否为tensor,以及维度(?这个不确定会不会是)


文章作者: LouisZ
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 LouisZ !
  目录