语音识别系列1--语音识别CTC之数据准备

语音识别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的数据准备过程,多卡并行的时候数据读取还是比较慢,如何快速有效读写文件也是需要解决的一个问题。

你可能感兴趣的:(tensorflow,ctc,kaldi,tensorflow,kaldi,数据准备,CTC,tfrecord)