语音识别CTC之数据准备
一、简介
CTC是一种端到端的神经网络训练方法,在语音识别领域应用非常广泛,和传统的语音识别HMM相比,CTC省略了数据对齐,特征选取的过程。传统的HMM在训练神经网络之前需要选择特定的语音特征,比如FBANK、MFCC。而CTC则直接将语音转到频域即可使用,传统的HMM在训练之前需要对数据进行帧对齐,对齐的过程首先要进行HMM-GMM的聚类,并且较小的建模粒度需要做状态的绑定,因为较大的建模粒度效果往往不好。最后使用GMM或者DNN对齐的标签也不是很准确。而端到端的CTC技术正好解决了这个问题,建模粒度更大,HMM-DNN模型用一个DNN模型替代,不需要对齐标签,仅需要整句话的标签即可,语料充足的情况下效果比传统的HMM方法要好。
tensorflow原生支持CTC目标函数,网络结构搭建简单,是一个不错的深度学习工具,我们通过tensorflow训练CTC模型,在3000小时语音数据的基础上,效果达到了字准95%,句准91%的效果,下面我们首先介绍训练过程的数据准备阶段。
二、数据格式
输入为kaldi提取的ark文件对应的scp文件,标签文件,输出为准备好的tfrecord文件。
scp文件格式如下:
0000000000000000000001 3000h.ark:43
0000000000000000000002 3000h.ark:68421
0000000000000000000003 3000h.ark:140799
0000000000000000000004 3000h.ark:213177
0000000000000000000005 3000h.ark:269235
0000000000000000000006 3000h.ark:312973
标签文件格式如下:
0000000000000000000001 125 61 146 113 146 2 216 115 124 1
0000000000000000000002 26 126 56 19 123 61 26 7
0000000000000000000003 57 163 26 62 143 95 121 98 121 59
0000000000000000000004 26 126 56 19 58 37 26 7 124 38 57 180
0000000000000000000005 192 189 58 23 121 62 124 129
0000000000000000000006 28 19 146 139 57 31 26 0
三、代码
Python代码如下:
import numpy as np
from tqdm import tqdm
import sys
import struct
#统计空标签个数
null_label=0
def int64_feature(value):
return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
def bytes_feature(value):
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
def get_features(scp_file):
#找到特征存储位置并读取
scp_file = scp_file.split(':')
file_name = scp_file[0]
position = int(scp_file[1]) + 5
with open(file_name, 'rb') as f:
f.seek(position)
#获取特征帧数
var_byte = struct.unpack('B', f.read(1))[0]
seq_len = struct.unpack('i', f.read(var_byte))[0]
#获取特征维数
var_byte = struct.unpack('B', f.read(1))[0]
feat_width = struct.unpack('i', f.read(var_byte))[0]
#读取特征
feats = f.read(4 * seq_len * feat_width)
#返回特征及特征帧数
return feats, seq_len
def get_label(raw_label):
#标签转为int数组
label = np.asarray(raw_label).astype(np.int64)
#标签转字符串
label = label.tostring()
return label
def create_tfrecord(scp_file, text_file, tfrecord_file):
#声明全局变量
global null_label
#特征索引全部读入内存
with open(scp_file) as f:
scp_list = f.readlines()
#标签全部读入内存
with open(text_file) as f:
text_list = f.readlines()
#检查特征文件与标签文件语音个数是否相等
if (len(scp_list) != len(text_list)):
print("error, wav file and text lines num not equal")
return
print("Converting data into %s ..." % tfrecord_file)
#创建写文件句柄
writer = tf.python_io.TFRecordWriter(tfrecord_file)
#tqdm显示处理进度条
for i in tqdm(range(len(scp_list))):
scp_line = scp_list[i].split()
text_line = text_list[i].split()
#检查特征ID和标签ID是否相同
if (scp_line[0] != text_line[0]):
print("scp and label not equal")
break
#读取特征及特征帧数
feature, seq_len = get_features(scp_line[1])
#读取标签
label = get_label(text_line[1:])
#标签长度为0时略过此条音频
if (len(label)==0):
null_label+=1
continue
#TFrecord数据格式
tf_example = tf.train.Example(
features = tf.train.Features(
feature={
#序列化为字符型
'feature': bytes_feature(feature),
'label' : bytes_feature(label),
#序列化为整形
'seq_len': int64_feature(seq_len)
}
)
)
#写入文件句柄
writer.write(tf_example.SerializeToString())
#关闭文件句柄
writer.close()
if __name__ == "__main__":
#输入使用kaldi提取特征的ark文件对应的scp索引文件,每一句话对应的标签文件,输出为tfrecord文件
if (len(sys.argv) != 4):
print (sys.argv[0], ":please input scp file, label file and output file")
exit(0)
else:
#导入tensorflow
import tensorflow as tf
#输入
scpfile=sys.argv[1]
textfile=sys.argv[2]
#输出
outputfile=sys.argv[3]
#根据特征文件和标签文件生成tfrecord文件
create_tfrecord(scpfile, textfile, outputfile)
#统计输出空标签的文件个数
print("null label ", null_label)
四、总结
以上就是tensorflow的数据准备过程,多卡并行的时候数据读取还是比较慢,如何快速有效读写文件也是需要解决的一个问题。