Introduction

这次的作业是利用MLP完成语音识别的任务,具体的流程大体可以分成三个部分,第一是数据处理,将原始的numpy数组处理成torch的标准输入格式,第二部分是网络搭建,第三部分是调参+网络结构优化。

输入数据代表特征的一些细节指导书上写的很细了,这里不会说太多。

Part 1

原始文件的格式是这样的:

1
2
3
4
5
6
7
8
9
10
11
Data folder
|--dev-clean
|--test-clean
|--train-clean-100
| |--mfcc
| | |--0019-000198-000.npy
| | |--0019-000198-001.npy
| | |-- ... more files
| |--transcript
| | |--0019-000198-000.npy
| | |--0019-000198-001.npy

具体可以看kaggle data,下面我将展示npy文件的具体内容。首先介绍如何逐一读取训练集和测试集下的文件:

1
2
3
datapath = 'dev-clean' # or 'train-clean-100'
mfcc_list = os.listdir(f'{datapath}/mfcc')
trans_list = os.listdir(f'{datapath}/transcript')

利用os.listdir方法就能得到该目录下的所有文件名,然后再去逐个读文件,这里演示单个文件:

1
2
3
idx = 0
mfcc = np.load(f'{datapath}/mfcc/{mfcc_list[idx]}')
trans = np.load(f'{datapath}/transcript/{mfcc_list[idx]}')[1:-1]

这里对于trans是把首尾两个元素去除了,因为对应的是<eos><sos>,并不表示音素,这样得到的mfcctrans如下所示:

1
2
3
4
5
6
7
8
# mfcc (565, 15)
array([[ 1.24845963e+01, -1.36812055e+00, -1.42250881e-01, ...,
-4.10560369e-02, -8.77164230e-02, -1.29315346e-01],
...,
[ 7.65058327e+00, -5.13446569e-01, -8.14266324e-01, ...,
-2.23895818e-01, -1.85610950e-01, -7.40890345e-03]], dtype=float32)
# trans (565, )
array(['SIL', .., 'S', 'T', 'T', 'T', 'T', 'T', 'OW', 'OW', ..., 'SIL'], dtype='<U5')

trans还需要进行编码处理,因为最后神经网络的输出是数字的形式,这里就直接利用已有的PHONEMES建立映射即可:

1
2
3
4
5
6
7
8
9
PHONEMES = [
'SIL', 'AA', 'AE', 'AH', 'AO', 'AW', 'AY',
'B', 'CH', 'D', 'DH', 'EH', 'ER', 'EY',
'F', 'G', 'HH', 'IH', 'IY', 'JH', 'K',
'L', 'M', 'N', 'NG', 'OW', 'OY', 'P',
'R', 'S', 'SH', 'T', 'TH', 'UH', 'UW',
'V', 'W', 'Y', 'Z', 'ZH', '<sos>', '<eos>']

phone_map = {lb:idx for idx, lb in enumerate(PHONEMES)}

最后的那两个标识符加上不加上都不影响,一共有效的音素是40个,对应0-39的映射,最后会被转换成one-hot encoding作为输出。

接下来就是完成本次作业需要的AudioDataset(torch.utils.data.Dataset),其实就是填写__init____len____getitem__函数。下面就用训练集做演示说明三个函数具体需要完成的内容。

init函数

需要完成数据的初始化,也就是把原始的npy文件全读到self变量里,再处理成MLP输入的格式。需要完成的是对输入mfcc的padding,也就是单个训练音频数据前后补零,这样做的原因指导书有介绍,此外就是把训练集中的标签转为数字格式,之前已经提到了。

len函数

getitem函数

Part 2

模型的设置,目前的模型是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# This architecture will make you cross the very low cutoff
# However, you need to run a lot of experiments to cross the medium or high cutoff
class Network(torch.nn.Module):

def __init__(self, context):

super(Network, self).__init__()

input_size = (2*context + 1) * 15 #Why is this the case? Because we flattened the entire MFCC
output_size = 40 #Why? One hot encoded

self.model = torch.nn.Sequential(
torch.nn.Linear(input_size, 2048),
torch.nn.ReLU(),
torch.nn.Linear(2048, 648),
torch.nn.ReLU(),
torch.nn.Linear(648, 128),
torch.nn.ReLU(),
torch.nn.Linear(128, output_size)
)

def forward(self, x):
out = self.model(x)

return out

Part 3