[声纹识别基础] TDNN怎么用的一维卷积?

Contents

  • 单输入通道/单输出通道
    • 预期结果
    • 参考代码
    • 运行结果
  • 多输入通道/单输出通道
    • 预期结果
    • 参考代码
    • 运行结果
  • 多输入通道/多输出通道
    • 预期结果
    • 参考代码
    • 运行结果
  • 从二维卷积角度理解一维卷积
    • 预期结果
    • 参考代码
    • 运行结果

时延神经网络(TDNNs)常用于声纹识别领域,例如著名的X-VECTOR基础结构就是TDNNs;它可以视作一维卷积神经网络1(1-d CNNs),甚至有人认为TDNNs是CNNs/LeNet-5的早期版本。

计算机视觉盛行的如今,数字图像处理中二维卷积(空域滤波/模版运算/互相关运算2)对于我们而言也不再陌生,无非就是中心数值等于按元素相乘后再相加嘛。

可是,到底如何理解一维卷积?它和二维卷积又有什么联系?
不妨通过实验,一探究竟。

单输入通道/单输出通道

预期结果

首先,我们暂时忽略通道。假定输入为 [ 1 , 2 , − 1 , 1 , − 3 ] [1, 2, -1, 1, -3] [1,2,1,1,3],核函数为 [ 1 , 0 , − 1 ] [1, 0, -1] [1,0,1](卷积核是一维的,移动方向也自然是一维的),如果卷积核在输入向量上移动,请问结果是多少呢?可以看看我有没有算错。[声纹识别基础] TDNN怎么用的一维卷积?_第1张图片
顺带一提,这里输入是一行五列的矩阵。不过换一个角度,我们也可以认为每次输入的是3帧信息,并按照某种权重加和汇入1个新帧。

这里的3是卷积核尺寸( k e r n e l   s i z e kernel\ size kernel size),从共享参数的全连接输入的角度说,3也是上下文所涵盖的范围, ( n − 1 , n + 1 )   o r   { n − 1 , n , n + 1 } (n-1,n+1) \ or \ \{n-1, n, n+1\} (n1,n+1) or {n1,n,n+1}

参考代码

# 输入
import torch.nn as nn
from torch.nn import Parameter
import torch.nn.functional as F
import torch
import numpy as np
import math

input = torch.tensor([
    [[1, 2, -1, 1, -3], ]
], dtype=torch.float)

conv1d = nn.Conv1d(in_channels=1,
                   out_channels=1,
                   kernel_size=3,
                   stride=1,
                   padding=0,
                   dilation=1,
                   bias=False,
                   padding_mode='zeros')


# 卷积核
conv1d.weight = nn.Parameter(torch.tensor([
    [[1., 0, -1], ]
]))

# 输出
conv1d(input)

运行结果

 tensor([[[2., 1., 2.]]], grad_fn=<ConvolutionBackward0>)

多输入通道/单输出通道

但是,一般说来,TDNNs输入的声学特征并不是一个向量,而是一个矩阵,横轴(宽度)与时间有关【不固定】,纵轴(高度)和频率相关【固定】,所以它有时也被称为时频谱图(Spectrogram)。

[声纹识别基础] TDNN怎么用的一维卷积?_第2张图片
图片来自MathWorks。

你还可以在 CHROME MUSIC LAB:SPECTROGRAM 上,亲自动手玩玩可视化自己的或自然的声音。
[声纹识别基础] TDNN怎么用的一维卷积?_第3张图片

当然,上文所指的广义的时频谱也分很多种(常见的有MFCC、MFBank、MelSpec等),对应各种不同的处理方式(例如,模拟人耳对频率、声强的非线性,平稳信号等),不过它们一般都要经历短时傅里叶变换(分帧、加窗、离散傅里叶变换)。

预期结果

我们暂时忽略输出通道。从一维卷积的视角说,[频率,时间] 应当被视作 [通道数,输入长度],即每个通道各有一个卷积核或一组共享权重,每个通道独立计算再把各个通道的结果相加3

假定输入为双通道(例如表示高频和低频) [ [ 1 , 2 , − 1 , 1 , − 3 ] , [ 3 , 1 , 0 , − 1 , 2 ] ] [[1, 2, -1, 1, -3],[3, 1, 0, -1, 2]] [[1,2,1,1,3],[3,1,0,1,2]]各通道上的核函数分别为 [ 1 , 0 , − 1 ] [1, 0, -1] [1,0,1] [ 0.5 , 0 , 0.5 ] [0.5, 0, 0.5] [0.5,0,0.5],请问这次结果又是多少呢?相信你肯定比我算的快。

[声纹识别基础] TDNN怎么用的一维卷积?_第4张图片

参考代码

# 输入
input = torch.tensor([
    [[1, 2, -1, 1, -3],
     [3, 1, 0, -1, 2]]
], dtype=torch.float)

conv1d = nn.Conv1d(in_channels=2,
                   out_channels=1,
                   kernel_size=3,
                   stride=1,
                   padding=0,
                   dilation=1,
                   bias=False,
                   padding_mode='zeros')

# 卷积核
conv1d.weight = nn.Parameter(torch.tensor([
    [[1., 0, -1],
     [0.5, 0, 0.5]],
]))

# 输出
conv1d(input)

运行结果

 tensor([[[3.5000, 1.0000, 3.0000]]], grad_fn=<ConvolutionBackward0>)

多输入通道/多输出通道

预期结果

最后,该如何理解输出通道呢?其实无非就是把单输出通道的那一份卷积核,扩增几倍而已。例如,仍然假定输入为双通道 [ [ 1 , 2 , − 1 , 1 , − 3 ] , [ 3 , 1 , 0 , − 1 , 2 ] ] [[1, 2, -1, 1, -3],[3, 1, 0, -1, 2]] [[1,2,1,1,3],[3,1,0,1,2]]不过此时我们考虑简化的双输出通道的情况:

第一份输出:各输入通道上的核函数分别为 [ 1 , 0 , − 1 ] [1, 0, -1] [1,0,1] [ 0.5 , 0 , 0.5 ] [0.5, 0, 0.5] [0.5,0,0.5]

第二份输出:各输入通道上的核函数仍然是 [ 1 , 0 , − 1 ] [1, 0, -1] [1,0,1] [ 0.5 , 0 , 0.5 ] [0.5, 0, 0.5] [0.5,0,0.5]

那么,我们就可以得到双倍的输出。

参考代码

# 输入
input = torch.tensor([
    [[1, 2, -1, 1, -3],
     [3, 1, 0, -1, 2]]
], dtype=torch.float)

conv1d = nn.Conv1d(in_channels=2,
                   out_channels=2,
                   kernel_size=3,
                   stride=1,
                   padding=0,
                   dilation=1,
                   bias=False,
                   padding_mode='zeros')

# 卷积核
conv1d.weight = nn.Parameter(torch.tensor([
    [[1., 0, -1],
     [0.5, 0, 0.5]],
    
    [[1., 0, -1],
     [0.5, 0, 0.5]],
]))

# 输出
conv1d(input)

运行结果

tensor([[[3.5000, 1.0000, 3.0000],
         [3.5000, 1.0000, 3.0000]]], grad_fn=<ConvolutionBackward0>)

从二维卷积角度理解一维卷积

预期结果

其实,如果将各个通道的一维卷积核拼接起来:所谓的一维卷积在各通道上的加和,完全可以用二维卷积核 [ c h a n n e l s , k e r n e l _ s i z e ] [channels, kernel\_ size] [channels,kernel_size]按元素相乘后再相加理解。
[声纹识别基础] TDNN怎么用的一维卷积?_第5张图片

所以,许多博主所认为的一维卷积不代表卷积核是一维的,只代表卷积核的移动方向是一维的这种说法,从某种角度说,也有一定的道理。
[声纹识别基础] TDNN怎么用的一维卷积?_第6张图片

不过值得注意的是,Pytorch中Conv1d和Conv2d输入维度不一样。

例如,
一维卷积 [ b a t c h , c h a n n e l s , l e n g t h ] [batch, channels, length] [batch,channels,length]
二维卷积 [ b a t c h , c h a n n e l s , h e i g h t , w i d t h ] [batch, channels, height, width] [batch,channels,height,width]

参考代码

# 输入
input = torch.tensor([
    [[1, 2, -1, 1, -3],
     [3, 1, 0, -1, 2]]
], dtype=torch.float)

input=input.unsqueeze(dim=1)

conv2d = nn.Conv2d(in_channels=1,
                   out_channels=2,
                   kernel_size=(input.shape[2],3),
                   stride=1,
                   padding=0,
                   dilation=1,
                   bias=False,
                   padding_mode='zeros')

# 卷积核
conv2d.weight = nn.Parameter(torch.tensor([
    [[[1., 0, -1],
     [0.5, 0, 0.5]]],
    
    [[[1., 0, -1],
     [0.5, 0, 0.5]]],
]))

# 输出
conv2d(input)

运行结果

tensor([[[[3.5000, 1.0000, 3.0000]],
         [[3.5000, 1.0000, 3.0000]]]], grad_fn=<ConvolutionBackward0>)

  1. Daniel Povey et al. “Semi-Orthogonal Low-Rank Matrix Factorization for Deep Neural Networks…” conference of the international speech communication association(2018): n. pag. ↩︎

  2. http://zh.d2l.ai/chapter_convolutional-neural-networks/conv-layer.html ↩︎

  3. http://zh.d2l.ai/chapter_convolutional-neural-networks/channels.html#id2 ↩︎

你可能感兴趣的:(大三,声纹识别,深度学习,神经网络,pytorch,cnn,卷积神经网络)