中文语音识别_中文语音识别 发音词典 - CSDN
精华内容
参与话题
  • 本文搭建一个完整的中文语音识别系统,包括声学模型和语言模型,能够将输入的音频信号识别为汉字。 声学模型使用了应用较为广泛的递归循环网络中的GRU-CTC的组合,除此之外还引入了科大讯飞提出的DFCNN深度全序列...

    本文搭建一个完整的中文语音识别系统,包括声学模型和语言模型,能够将输入的音频信号识别为汉字。

    该系统实现了基于深度框架的语音识别中的声学模型和语言模型建模,其中声学模型包括CNN-CTC、GRU-CTC、CNN-RNN-CTC,语言模型包含transformerCBHG
    数据集采用了目前能找到的所有中文免费数据,包括:thchs-30、aishell、primewords、st-cmd四个数据集,训练集总计大约450个小时。

    项目地址:https://github.com/audier/DeepSpeechRecognition

    为了方便同学们自己做实验,写了实践版的tutorial:

    1. 声学模型

    声学模型目前开源了部分示例模型,后期将不定期更新一些模型。

    GRU-CTC

    我们使用 GRU-CTC的结构搭建了第一个声学模型,该模型在项目的gru_ctc.py文件中。
    利用循环神经网络可以利用语音上下文相关的信息,得到更加准确地信息,而GUR又能选择性的保留需要的长时信息,使用双向rnn又能够充分的利用上下文信号。
    但该方法缺点是一句话说完之后才能进行识别,且训练相对cnn较慢。该模型使用python/keras进行搭建,本文系统都使用python搭建。

    • 网络结构如下:
        def _model_init(self):
            self.inputs = Input(name='the_inputs', shape=(None, 200, 1))
            x = Reshape((-1, 200))(self.inputs)
            x = dense(512, x)
            x = dense(512, x)
            x = bi_gru(512, x)
            x = bi_gru(512, x)
            x = bi_gru(512, x)
            x = dense(512, x)
            self.outputs = dense(self.vocab_size, x, activation='softmax')
            self.model = Model(inputs=self.inputs, outputs=self.outputs)
            self.model.summary()
    

    DFCNN

    由于两个原因在使用GRU作为语音识别的时候我们会遇到问题。

    • 一方面是我们常常使用双向循环神经网络才能取得更好的识别效果,这样会影响解码实时性。
    • 另一方面随着网络结构复杂性增加,双向GRU的参数是相同节点数全连接层的6倍,这样会导致训练速度非常缓慢。

    科大讯飞提出了一种使用深度卷积神经网络来对时频图进行识别的方法,就是DFCNN
    论文地址:http://xueshu.baidu.com/usercenter/paper/show?paperid=be5348048dd263aced0f2bdc75a535e8&site=xueshu_se

    利用CNN参数共享机制,可以将参数数量下降几个数量级别,且深层次的卷积和池化层能够充分考虑语音信号的上下文信息,且可以在较短的时间内就可以得到识别结果,具有较好的实时性。
    该模型在cnn_ctc.py中,实验中是所有网络结果最好的模型,目前能够取得较好的泛化能力。其网络结构如下:
    DFCNN

    def cnn_cell(size, x, pool=True):
        x = norm(conv2d(size)(x))
        x = norm(conv2d(size)(x))
        if pool:
            x = maxpool(x)
        return x
    
    class Am():
        def _model_init(self):
            self.inputs = Input(name='the_inputs', shape=(None, 200, 1))
            self.h1 = cnn_cell(32, self.inputs)
            self.h2 = cnn_cell(64, self.h1)
            self.h3 = cnn_cell(128, self.h2)
            self.h4 = cnn_cell(128, self.h3, pool=False)
            self.h5 = cnn_cell(128, self.h4, pool=False)
            # 200 / 8 * 128 = 3200
            self.h6 = Reshape((-1, 3200))(self.h5)
            self.h7 = dense(256)(self.h6)
            self.outputs = dense(self.vocab_size, activation='softmax')(self.h7)
            self.model = Model(inputs=self.inputs, outputs=self.outputs)
            self.model.summary()
    

    DFSMN

    而前馈记忆神经网络也也解决了双向GRU的参数过多和实时性较差的缺点,它利用一个记忆模块,包含了上下几帧信息,能够得到不输于双向GRU-CTC的识别结果,阿里最新的开源系统就是基于DFSMN的声学模型,只不过在kaldi的框架上实现的。我们将考虑使用DFSMN+CTC的结构在python上实现。该网络实质上是用一个特殊的CNN就可以取得相同的效果,我们将CNN的宽设置为memory size,将高度设置为feature dim,将channel设置为hidden units,这样一个cnn的层就可以模仿fsmn的实现了。
    结构如下:
    DFSMN

    2. 语言模型

    n-gram

    n元语法是一个非常经典的语言模型,这里不过多介绍啦。
    如果想要通过语料来训练一个n-gram的模型,可以参考我的小实验的实现方法。

    CBHG

    该想法来自于一个大神搞输入法的项目,下面部分也引用此处:搜喵出入法
    他是利用该模型建立一个按键到汉字的作用,本文对其结构和数据处理部分稍作改动,作为语言模型。

    拼音输入的本质上就是一个序列到序列的模型:输入拼音序列,输出汉字序列。所以天然适合用在诸如机器翻译的seq2seq模型上。

    模型初始输入是一个随机采样的拼音字母的character embedding,经过一个CBHG的模型,输出是五千个汉字对应的label。
    这里使用的CBHG模块是state-of-art的seq2seq模型,用在Google的机器翻译和语音合成中,该模型放在cbhg.py中,结构如下:
    图片来自 Tacotron: Towards End-to-End Speech Synthesis
    CBHG

    该模型训练实验结果如下,实际上,后续和transformer的比较中,该模型无论是收敛速度还是识别效果上都是很难和transformer比较。

    请输入测试拼音:ta1 mei2 you3 duo1 shao3 hao2 yan2 zhuang4 yu3 dan4 ta1 que4 ba3 ai4 qin1 ren2 ai4 jia1 ting2 ai4 zu3 guo2 ai4 jun1 dui4 wan2 mei3 de tong3 yi1 le qi3 lai2
    她没有多少豪言壮语但她却把爱亲人爱家庭爱祖国爱军队完美地统一了起来
    
    请输入测试拼音:chu2 cai2 zheng4 bo1 gei3 liang3 qian1 san1 bai3 wan4 yuan2 jiao4 yu4 zi1 jin1 wai4 hai2 bo1 chu1 zhuan1 kuan3 si4 qian1 wu3 bai3 qi1 shi2 wan4 yuan2 xin1 jian4 zhong1 xiao3 xue2
    除财政拨给两千三百万元教太资金外还拨出专款四千五百七十万元新建中小学
    
    请输入测试拼音:ke3 shi4 chang2 chang2 you3 ren2 gao4 su4 yao2 xian1 sheng1 shuo1 kan4 jian4 er4 xiao3 jie3 zai4 ka1 fei1 guan3 li3 he2 wang2 jun4 ye4 wo4 zhe shou3 yi1 zuo4 zuo4 shang4 ji3 ge4 zhong1 tou2
    可是常常有人告诉姚先生说看见二小姐在咖啡馆里和王俊业握着族一坐坐上几个钟头
    

    transformer

    新增基于transformer结构的语言模型transformer.py,该模型已经被证明有强于其他框架的语言表达能力。

    建议使用transformer作为语言模型,该模型是自然语言处理这两年最火的模型,今年的bert就是使用的该结构。本文最近更新系统使用的语言模型就是transformer。

     the  0 th example.
    文本结果: lv4 shi4 yang2 chun1 yan1 jing3 da4 kuai4 wen2 zhang1 de di3 se4 si4 yue4 de lin2 luan2 geng4 shi4 lv4 de2 xian1 huo2 xiu4 mei4 shi1 yi4 ang4 ran2
    原文结果: lv4 shi4 yang2 chun1 yan1 jing3 da4 kuai4 wen2 zhang1 de di3 se4 si4 yue4 de lin2 luan2 geng4 shi4 lv4 de2 xian1 huo2 xiu4 mei4 shi1 yi4 ang4 ran2
    原文汉字: 绿是阳春烟景大块文章的底色四月的林峦更是绿得鲜活秀媚诗意盎然
    识别结果: 绿是阳春烟景大块文章的底色四月的林峦更是绿得鲜活秀媚诗意盎然
    
     the  1 th example.
    文本结果: ta1 jin3 ping2 yao1 bu4 de li4 liang4 zai4 yong3 dao4 shang4 xia4 fan1 teng2 yong3 dong4 she2 xing2 zhuang4 ru2 hai3 tun2 yi4 zhi2 yi3 yi1 tou2 de you1 shi4 ling3 xian1
    原文结果: ta1 jin3 ping2 yao1 bu4 de li4 liang4 zai4 yong3 dao4 shang4 xia4 fan1 teng2 yong3 dong4 she2 xing2 zhuang4 ru2 hai3 tun2 yi4 zhi2 yi3 yi1 tou2 de you1 shi4 ling3 xian1
    原文汉字: 他仅凭腰部的力量在泳道上下翻腾蛹动蛇行状如海豚一直以一头的优势领先
    识别结果: 他仅凭腰部的力量在泳道上下翻腾蛹动蛇行状如海豚一直以一头的优势领先
    
     the  2 th example.
    文本结果: pao4 yan3 da3 hao3 le zha4 yao4 zen3 me zhuang1 yue4 zheng4 cai2 yao3 le yao3 ya2 shu1 di4 tuo1 qu4 yi1 fu2 guang1 bang3 zi chong1 jin4 le shui3 cuan4 dong4
    原文结果: pao4 yan3 da3 hao3 le zha4 yao4 zen3 me zhuang1 yue4 zheng4 cai2 yao3 le yao3 ya2 shu1 di4 tuo1 qu4 yi1 fu2 guang1 bang3 zi chong1 jin4 le shui3 cuan4 dong4
    原文汉字: 炮眼打好了炸药怎么装岳正才咬了咬牙倏地脱去衣服光膀子冲进了水窜洞
    识别结果: 炮眼打好了炸药怎么装岳正才咬了咬牙倏地脱去衣服光膀子冲进了水窜洞
    

    3. 数据集

    数据集采用了目前我能找到的所有中文免费数据,包括:thchs-30、aishell、primewords、st-cmd四个数据集,训练集总计大约450个小时,在之前实验过程中,使用thchs-30+aishell+st-cmd数据集对DFCNN声学模型进行训练,以64batch_size训练。
    包括stc、primewords、Aishell、thchs30四个数据集,共计约430小时, 相关链接:http://www.openslr.org/resources.php

    Name train dev test
    aishell 120098 14326 7176
    primewords 40783 5046 5073
    thchs-30 10000 893 2495
    st-cmd 10000 600 2000

    数据标签整理在data路径下,其中primewords、st-cmd目前未区分训练集测试集。

    若需要使用所有数据集,只需解压到统一路径下,然后设置utils.py中datapath的路径即可。

    4. 配置

    本项目现已训练一个迷你的语音识别系统,将项目下载到本地上,下载thchs数据集并解压至data,运行test.py,不出意外能够进行识别,结果如下:

     the  0 th example.
    文本结果: lv4 shi4 yang2 chun1 yan1 jing3 da4 kuai4 wen2 zhang1 de di3 se4 si4 yue4 de lin2 luan2 geng4 shi4 lv4 de2 xian1 huo2 xiu4 mei4 shi1 yi4 ang4 ran2
    原文结果: lv4 shi4 yang2 chun1 yan1 jing3 da4 kuai4 wen2 zhang1 de di3 se4 si4 yue4 de lin2 luan2 geng4 shi4 lv4 de2 xian1 huo2 xiu4 mei4 shi1 yi4 ang4 ran2
    原文汉字: 绿是阳春烟景大块文章的底色四月的林峦更是绿得鲜活秀媚诗意盎然
    识别结果: 绿是阳春烟景大块文章的底色四月的林峦更是绿得鲜活秀媚诗意盎然
    

    若自己建立模型则需要删除现有模型,重新配置参数训练。

    与数据相关参数在utils.py中:

    • data_type: train, test, dev
    • data_path: 对应解压数据的路径
    • thchs30, aishell, prime, stcmd: 是否使用该数据集
    • batch_size: batch_size
    • data_length: 我自己做实验时写小一些看效果用的,正常使用设为None即可
    • shuffle:正常训练设为True,是否打乱训练顺序
    def data_hparams():
        params = tf.contrib.training.HParams(
            # vocab
            data_type = 'train',
            data_path = 'data/',
            thchs30 = True,
            aishell = True,
            prime = False,
            stcmd = False,
            batch_size = 1,
            data_length = None,
            shuffle = False)
          return params
    

    使用train.py文件进行模型的训练。

    声学模型可选cnn-ctc、gru-ctc,只需修改导入路径即可:

    from model_speech.cnn_ctc import Am, am_hparams

    from model_speech.gru_ctc import Am, am_hparams

    语言模型可选transformer和cbhg:

    from model_language.transformer import Lm, lm_hparams

    from model_language.cbhg import Lm, lm_hparams

    转载请注明出处:https://blog.csdn.net/chinatelecom08/article/details/82557715

    展开全文
  • tf15: 中文语音识别

    万次阅读 热门讨论 2019-12-09 15:16:59
    THCHS30是Dong Wang, Xuewei Zhang, Zhiyong Zhang这几位大神发布的开放语音数据集,可用于开发中文语音识别系统。 为了感谢这几位大神,我是跪在电脑前写的本帖代码。 可以参考这个,tql:...

    使用的数据集

    THCHS30是Dong Wang, Xuewei Zhang, Zhiyong Zhang这几位大神发布的开放语音数据集,可用于开发中文语音识别系统。

    为了感谢这几位大神,我是跪在电脑前写的本帖代码。

    可以参考这个,tql: https://github.com/xxbb1234021/speech_recognition 

    下载中文语音数据集(5G+):

    #coding: utf-8  
    import tensorflow as tf  
    import numpy as np  
    import os  
    from collections import Counter  
    import librosa  
      
    from joblib import Parallel, delayed  
      
    wav_path = 'data/wav/train'  
    label_file = 'data/doc/trans/train.word.txt'
      
    def get_wav_files(wav_path = wav_path):  
        wav_files = []  
        for (dirpath, dirnames, filenames) in os.walk(wav_path):  
            for filename in filenames:  
                if filename.endswith(".wav") or filename.endswith(".WAV"):  
                    filename_path = os.sep.join([dirpath, filename])  
                    if os.stat(filename_path).st_size < 240000:  
                        continue  
                    wav_files.append(filename_path)  
      
        return wav_files  
      
    wav_files = get_wav_files()  
      
    def get_wav_label(wav_files = wav_files, label_file = label_file):  
        labels_dict = {}  
        with open(label_file, "r", encoding='utf-8') as f:  
            for label in f:  
                label = label.strip("\n")  
                label_id, label_text = label.split(' ', 1)  
                labels_dict[label_id] = label_text  
      
        labels = []  
        new_wav_files = []  
        for wav_file in wav_files:  
            wav_id = os.path.basename(wav_file).split(".")[0]  
            if wav_id in labels_dict:  
                labels.append(labels_dict[wav_id])  
                new_wav_files.append(wav_file)  
      
        return new_wav_files, labels  
      
    def get_wav_length(wav):  
        import numpy as np  
        import librosa  
      
        print(wav)  
      
        wav, sr = librosa.load(wav)  
        mfcc = np.transpose(librosa.feature.mfcc(wav, sr), [1, 0])  
        return len(mfcc)  
      
    pointer = 0  
    def get_next_batches(batch_size, wav_max_len):  
        global pointer  
        batches_wavs = []  
        batches_labels = []  
        for i in range(batch_size):  
            wav, sr = librosa.load(wav_files[pointer])  
            mfcc = np.transpose(librosa.feature.mfcc(wav, sr), [1,0])  
            batches_wavs.append(mfcc.tolist())  
            batches_labels.append(labels_vector[pointer])  
            pointer += 1  
      
        # 取零补齐  
        # label append 0 , 0 对应的字符  
        # mfcc 默认的计算长度为20(n_mfcc of mfcc) 作为channel length  
        for mfcc in batches_wavs:  
            while len(mfcc) < wav_max_len:  
                mfcc.append([0]*20)  
        for label in batches_labels:  
            while len(label) < label_max_len:  
                label.append(0)  
      
        return batches_wavs, batches_labels  
      
    conv1d_index = 0  
    def conv1d_layer(input_tensor, size, dim, activation, scale, bias):  
        global conv1d_index  
        with tf.variable_scope("conv1d_" + str(conv1d_index)):  
            W = tf.get_variable('W', (size, input_tensor.get_shape().as_list()[-1], dim), dtype=tf.float32, initializer=tf.random_uniform_initializer(minval=-scale, maxval=scale))  
            if bias:  
                b = tf.get_variable('b', [dim], dtype = tf.float32, initializer=tf.constant_initializer(0))  
            out = tf.nn.conv1d(input_tensor, W, stride=1, padding='SAME') + (b if bias else 0)  
      
            if not bias:  
                beta = tf.get_variable('beta', dim, dtype=tf.float32, initializer=tf.constant_initializer(0))  
                gamma = tf.get_variable('gamma', dim, dtype=tf.float32, initializer=tf.constant_initializer(1))  
                mean_running = tf.get_variable('mean', dim, dtype=tf.float32, initializer=tf.constant_initializer(0))  
                variance_running = tf.get_variable('variance', dim, dtype=tf.float32, initializer=tf.constant_initializer(1))  
                mean, variance = tf.nn.moments(out, axes=list(range(len(out.get_shape()) - 1)))  
      
                def update_running_stat():  
                    decay = 0.99  
      
                    # 定义了均值方差指数衰减 见 http://blog.csdn.net/liyuan123zhouhui/article/details/70698264  
                    update_op = [mean_running.assign(mean_running * decay + mean * (1 - decay)), variance_running.assign(variance_running * decay + variance * (1 - decay))]  
      
                    # 指定先执行均值方差的更新运算 见 http://blog.csdn.net/u012436149/article/details/72084744  
                    with tf.control_dependencies(update_op):  
                        return tf.identity(mean), tf.identity(variance)  
      
                # 条件运算(https://applenob.github.io/tf_9.html) 按照作者这里的指定 是不进行指数衰减的  
                m, v = tf.cond(tf.Variable(False, trainable=False), update_running_stat,lambda: (mean_running, variance_running))  
                out = tf.nn.batch_normalization(out, m, v, beta, gamma, 1e-8)  
      
            if activation == 'tanh':  
                out = tf.nn.tanh(out)  
            elif activation == 'sigmoid':  
                out = tf.nn.sigmoid(out)  
      
            conv1d_index += 1  
            return out  
      
    # 极黑卷积层 https://www.zhihu.com/question/57414498  
    # 其输入参数中要包含一个大于 1 的rate 输出 channels与输入相同  
    aconv1d_index = 0  
    def aconv1d_layer(input_tensor, size, rate, activation, scale, bias):  
        global aconv1d_index  
        with tf.variable_scope('aconv1d_' + str(aconv1d_index)):  
            shape = input_tensor.get_shape().as_list()  
      
            # 利用 2 维极黑卷积函数计算相应 1 维卷积,expand_dims squeeze做了相应维度处理  
            # 实际 上一个 tf.nn.conv1d 在之前的tensorflow版本中是没有的,其的一个实现也是经过维度调整后调用 tf.nn.conv2d  
            W = tf.get_variable('W', (1, size, shape[-1], shape[-1]), dtype=tf.float32, initializer=tf.random_uniform_initializer(minval=-scale, maxval=scale))  
            if bias:  
                b = tf.get_variable('b', [shape[-1]], dtype=tf.float32, initializer=tf.constant_initializer(0))  
            out = tf.nn.atrous_conv2d(tf.expand_dims(input_tensor, dim=1), W, rate = rate, padding='SAME')  
            out = tf.squeeze(out, [1])  
      
            if not bias:  
                beta = tf.get_variable('beta', shape[-1], dtype=tf.float32, initializer=tf.constant_initializer(0))  
                gamma = tf.get_variable('gamma', shape[-1], dtype=tf.float32, initializer=tf.constant_initializer(1))  
                mean_running = tf.get_variable('mean', shape[-1], dtype=tf.float32, initializer=tf.constant_initializer(0))  
                variance_running = tf.get_variable('variance', shape[-1], dtype=tf.float32, initializer=tf.constant_initializer(1))  
                mean, variance = tf.nn.moments(out, axes=list(range(len(out.get_shape()) - 1)))  
      
                def update_running_stat():  
                    decay = 0.99  
                    update_op = [mean_running.assign(mean_running * decay + mean * (1 - decay)), variance_running.assign(variance_running * decay + variance * (1 - decay))]  
                    with tf.control_dependencies(update_op):  
                        return tf.identity(mean), tf.identity(variance)  
      
                m, v = tf.cond(tf.Variable(False, trainable=False), update_running_stat,lambda: (mean_running, variance_running))  
                out = tf.nn.batch_normalization(out, m, v, beta, gamma, 1e-8)  
      
            if activation == 'tanh':  
                out = tf.nn.tanh(out)  
            elif activation == 'sigmoid':  
                out = tf.nn.sigmoid(out)  
      
            aconv1d_index += 1  
            return out  
      
    def speech_to_text_network(n_dim = 128, n_blocks = 3):  
        out = conv1d_layer(input_tensor=X, size=1, dim = n_dim, activation='tanh', scale=0.14, bias=False)  
      
        def residual_block(input_sensor, size, rate):  
            conv_filter = aconv1d_layer(input_tensor=input_sensor, size=size, rate=rate, activation='tanh', scale=0.03, bias=False)  
            conv_gate = aconv1d_layer(input_tensor=input_sensor, size=size, rate=rate, activation='sigmoid', scale=0.03, bias=False)  
            out = conv_filter * conv_gate  
            out = conv1d_layer(out, size = 1, dim=n_dim, activation='tanh', scale=0.08, bias=False)  
            return out + input_sensor, out  
      
        skip = 0  
        for _ in range(n_blocks):  
            for r in [1, 2, 4, 8, 16]:  
                out, s = residual_block(out, size = 7, rate = r)  
                skip += s  
      
        logit = conv1d_layer(skip, size = 1, dim = skip.get_shape().as_list()[-1], activation='tanh', scale = 0.08, bias=False)  
      
        # 最后卷积层输出是词汇表大小  
        logit = conv1d_layer(logit, size = 1, dim = words_size, activation = None, scale = 0.04, bias = True)  
      
        return logit  
      
    # 作者自己定义了优化器  
    class MaxPropOptimizer(tf.train.Optimizer):  
        def __init__(self, learning_rate=0.001, beta2=0.999, use_locking=False, name="MaxProp"):  
            super(MaxPropOptimizer, self).__init__(use_locking, name)  
            self._lr = learning_rate  
            self._beta2 = beta2  
            self._lr_t = None  
            self._beta2_t = None  
        def _prepare(self):  
            self._lr_t = tf.convert_to_tensor(self._lr, name="learning_rate")  
            self._beta2_t = tf.convert_to_tensor(self._beta2, name="beta2")  
        def _create_slots(self, var_list):  
            for v in var_list:  
                self._zeros_slot(v, "m", self._name)  
        def _apply_dense(self, grad, var):  
            lr_t = tf.cast(self._lr_t, var.dtype.base_dtype)  
            beta2_t = tf.cast(self._beta2_t, var.dtype.base_dtype)  
            if var.dtype.base_dtype == tf.float16:  
                eps = 1e-7  
            else:  
                eps = 1e-8  
            m = self.get_slot(var, "m")  
            m_t = m.assign(tf.maximum(beta2_t * m + eps, tf.abs(grad)))  
            g_t = grad / m_t  
            var_update = tf.assign_sub(var, lr_t * g_t)  
            return tf.group(*[var_update, m_t])  
        def _apply_sparse(self, grad, var):  
            return self._apply_dense(grad, var)  
      
    def train_speech_to_text_network(wav_max_len):  
        logit = speech_to_text_network()  
      
        # CTC loss  
        indices = tf.where(tf.not_equal(tf.cast(Y, tf.float32), 0.))  
        target = tf.SparseTensor(indices=indices, values=tf.gather_nd(Y, indices) - 1, dense_shape=tf.cast(tf.shape(Y), tf.int64))  
        loss = tf.nn.ctc_loss(target, logit, sequence_len, time_major=False)  
        # optimizer  
        lr = tf.Variable(0.001, dtype=tf.float32, trainable=False)  
        optimizer = MaxPropOptimizer(learning_rate=lr, beta2=0.99)  
        var_list = [t for t in tf.trainable_variables()]  
        gradient = optimizer.compute_gradients(loss, var_list=var_list)  
        optimizer_op = optimizer.apply_gradients(gradient)  
      
        with tf.Session() as sess:  
            sess.run(tf.global_variables_initializer())  
      
            saver = tf.train.Saver(tf.global_variables())  
      
            for epoch in range(16):  
                sess.run(tf.assign(lr, 0.001 * (0.97 ** epoch)))  
      
                global pointer  
                pointer = 0  
                for batch in range(n_batch):  
                    batches_wavs, batches_labels = get_next_batches(batch_size, wav_max_len)  
                    train_loss, _ = sess.run([loss, optimizer_op], feed_dict={X: batches_wavs, Y: batches_labels})  
                    print(epoch, batch, train_loss)  
                if epoch % 1 == 0:   #之前是5
                    saver.save(sess, r'D:\\tensorflow\\Speech_Recognition\\speech.module', global_step=epoch)  
      
    # 训练  
    #train_speech_to_text_network()  
      
    # 语音识别  
    # 把 batch_size 改为1  
    def speech_to_text(wav_file):  
        wav, sr = librosa.load(wav_file, mono=True)  
        mfcc = np.transpose(np.expand_dims(librosa.feature.mfcc(wav, sr), axis=0), [0,2,1])  
      
        logit = speech_to_text_network()  
      
        saver = tf.train.Saver()  
        with tf.Session() as sess:  
            saver.restore(sess, tf.train.latest_checkpoint('.'))  
      
        decoded = tf.transpose(logit, perm=[1, 0, 2])
        decoded, _ = tf.nn.ctc_beam_search_decoder(decoded, sequence_len, merge_repeated=False)
        decoded = sess.run(decoded, feed_dict={X: mfcc})
        # predict = tf.sparse_to_dense(decoded[0].indices, decoded[0].shape, decoded[0].values) + 1
        print (decoded)
        predict = tf.sparse_to_dense(decoded[0].indices,decoded[0].dense_shape,decoded[0].values) + 1
        # predict = decode_sparse_tensor(decoded[0])
        predict = sess.run(predict)
        print(predict)
      
      
    if __name__ == "__main__":  
        wav_files = get_wav_files()  
        wav_files, labels = get_wav_label()  
        print(u"样本数 :", len(wav_files))  
      
        all_words = []  
        for label in labels:  
            # 字符分解  
            all_words += [word for word in label]  
      
        counter = Counter(all_words)  
        count_pairs = sorted(counter.items(), key=lambda x: -x[1])  
      
        words, _ = zip(*count_pairs)  
        words_size = len(words)  
        print(u"词汇表大小:", words_size)  
      
        word_num_map = dict(zip(words, range(len(words))))  
      
        # 当字符不在已经收集的words中时,赋予其应当的num,这是一个动态的结果  
        to_num = lambda word: word_num_map.get(word, len(words))  
      
        # 将单个file的标签映射为num 返回对应list,最终all file组成嵌套list  
        labels_vector = [list(map(to_num, label)) for label in labels]  
      
        label_max_len = np.max([len(label) for label in labels_vector])  
        print(u"最长句子的字数:" + str(label_max_len))  
      
        # 下面仅仅计算了语音特征相应的最长的长度。  
        # 如果仅仅是计算长度是否需要施加变换后计算长度?  
        parallel_read = False  
        if parallel_read:  
            wav_max_len = np.max(Parallel(n_jobs=7)(delayed(get_wav_length)(wav) for wav in wav_files))  
        else:  
            wav_max_len = 673  
        print("最长的语音", wav_max_len)  
      
        batch_size = 8  
        n_batch = len(wav_files) // batch_size  
      
        X = tf.placeholder(dtype=tf.float32, shape=[batch_size, None, 20])  
      
        # 实际mfcc中的元素并非同号,不严格的情况下如此得到序列长度也是可行的  
        sequence_len = tf.reduce_sum(tf.cast(tf.not_equal(tf.reduce_sum(X, reduction_indices=2), 0.), tf.int32), reduction_indices=1)  
      
        Y = tf.placeholder(dtype=tf.int32, shape=[batch_size, None])  
      
        train_speech_to_text_network(wav_max_len)  
    
    

     

     

    后续:从麦克风获得语音输入,使用上面的模型进行识别。

     

     

    然后预测可以用下边这个:

     

    if __name__ == "__main__":
        
        wav_files = get_wav_files()  
        wav_files, labels = get_wav_label()  
        print(u"样本数 :", len(wav_files))  
      
        all_words = []  
        for label in labels:  
            # 字符分解  
            all_words += [word for word in label]  
      
        counter = Counter(all_words)  
        count_pairs = sorted(counter.items(), key=lambda x: -x[1])  
      
        words, _ = zip(*count_pairs)  
        words_size = len(words)  
        print(u"词汇表大小:", words_size)  
      
        word_num_map = dict(zip(words, range(len(words))))  
      
        # 当字符不在已经收集的words中时,赋予其应当的num,这是一个动态的结果  
        to_num = lambda word: word_num_map.get(word, len(words))  
      
        # 将单个file的标签映射为num 返回对应list,最终all file组成嵌套list  
        labels_vector = [list(map(to_num, label)) for label in labels]  
      
        label_max_len = np.max([len(label) for label in labels_vector])  
        print(u"最长句子的字数:" + str(label_max_len))  
      
        # 下面仅仅计算了语音特征相应的最长的长度。  
        # 如果仅仅是计算长度是否需要施加变换后计算长度?  
        parallel_read = False  
        if parallel_read:  
            wav_max_len = np.max(Parallel(n_jobs=7)(delayed(get_wav_length)(wav) for wav in wav_files))  
        else:  
            wav_max_len = 673  
        print("最长的语音", wav_max_len)  
      
        batch_size = 1 
        n_batch = len(wav_files) // batch_size
      
        X = tf.placeholder(dtype=tf.float32, shape=[batch_size, None, 20])
      
        # 实际mfcc中的元素并非同号,不严格的情况下如此得到序列长度也是可行的  
        sequence_len = tf.reduce_sum(tf.cast(tf.not_equal(tf.reduce_sum(X, reduction_indices=2), 0.), tf.int32), reduction_indices=1)  
      
        Y = tf.placeholder(dtype=tf.int32, shape=[batch_size, None])
      
        #train_speech_to_text_network(wav_max_len)  #训练
        
        wav_file = "./D4_750.wav"
        print (wav_file)
        speech_to_text(wav_file)

     

     

    相关资源:

     

     

    Share the post "TensorFlow练习15: 中文语音识别"

    •  

     

    展开全文
  • 文章目录利用thchs30为例建立一个语音识别系统1. 特征提取2. 模型搭建搭建cnn+dnn+ctc的声学模型3. 训练准备下载数据3.1 生成音频文件和标签文件列表定义函数`source_get`,获取音频文件及标注文件列表确认相同id...

    利用thchs30为例建立一个语音识别系统

    • 数据处理
    • 搭建模型
      • DFCNN

    论文地址:http://xueshu.baidu.com/usercenter/paper/show?paperid=be5348048dd263aced0f2bdc75a535e8&site=xueshu_se
    代码地址:https://github.com/audier/my_ch_speech_recognition/tree/master/tutorial

    语言模型代码实践tutorial也有啦:

    基于CBHG结构:https://blog.csdn.net/chinatelecom08/article/details/85048019
    基于自注意力机制:https://blog.csdn.net/chinatelecom08/article/details/85051817

    1. 特征提取

    input为输入音频数据,需要转化为频谱图数据,然后通过cnn处理图片的能力进行识别。

    1. 读取音频文件

    import scipy.io.wavfile as wav
    import matplotlib.pyplot as plt
    import os
    
    # 随意搞个音频做实验
    filepath = 'test.wav'
    
    fs, wavsignal = wav.read(filepath)
    
    plt.plot(wavsignal)
    plt.show()
    

    png

    2. 构造汉明窗

    import numpy as np
    
    x=np.linspace(0, 400 - 1, 400, dtype = np.int64)
    w = 0.54 - 0.46 * np.cos(2 * np.pi * (x) / (400 - 1))
    plt.plot(w)
    plt.show()
    

    png

    3. 对数据分帧

    • 帧长: 25ms
    • 帧移: 10ms
    采样点(s) = fs
    采样点(ms)= fs / 1000
    采样点(帧)= fs / 1000 * 帧长
    
    time_window = 25
    window_length = fs // 1000 * time_window
    

    4. 分帧加窗

    # 分帧
    p_begin = 0
    p_end = p_begin + window_length
    frame = wavsignal[p_begin:p_end]
    plt.plot(frame)
    plt.show()
    # 加窗
    frame = frame * w
    plt.plot(frame)
    plt.show()
    

    png

    在这里插入图片描述

    5. 傅里叶变换

    所谓时频图就是将时域信息转换到频域上去,具体原理可百度。人耳感知声音是通过

    from scipy.fftpack import fft
    
    # 进行快速傅里叶变换
    frame_fft = np.abs(fft(frame))[:200]
    plt.plot(frame_fft)
    plt.show()
    
    # 取对数,求db
    frame_log = np.log(frame_fft)
    plt.plot(frame_log)
    plt.show()
    
    

    在这里插入图片描述

    在这里插入图片描述

    • 分帧
    • 加窗
    • 傅里叶变换
    import numpy as np
    import scipy.io.wavfile as wav
    from scipy.fftpack import fft
    
    
    # 获取信号的时频图
    def compute_fbank(file):
    	x=np.linspace(0, 400 - 1, 400, dtype = np.int64)
    	w = 0.54 - 0.46 * np.cos(2 * np.pi * (x) / (400 - 1) ) # 汉明窗
    	fs, wavsignal = wav.read(file)
    	# wav波形 加时间窗以及时移10ms
    	time_window = 25 # 单位ms
    	window_length = fs / 1000 * time_window # 计算窗长度的公式,目前全部为400固定值
    	wav_arr = np.array(wavsignal)
    	wav_length = len(wavsignal)
    	range0_end = int(len(wavsignal)/fs*1000 - time_window) // 10 # 计算循环终止的位置,也就是最终生成的窗数
    	data_input = np.zeros((range0_end, 200), dtype = np.float) # 用于存放最终的频率特征数据
    	data_line = np.zeros((1, 400), dtype = np.float)
    	for i in range(0, range0_end):
    		p_start = i * 160
    		p_end = p_start + 400
    		data_line = wav_arr[p_start:p_end]	
    		data_line = data_line * w # 加窗
    		data_line = np.abs(fft(data_line))
    		data_input[i]=data_line[0:200] # 设置为400除以2的值(即200)是取一半数据,因为是对称的
    	data_input = np.log(data_input + 1)
    	#data_input = data_input[::]
    	return data_input
    
    • 该函数提取音频文件的时频图
    import matplotlib.pyplot as plt
    filepath = 'test.wav'
    
    a = compute_fbank(filepath)
    plt.imshow(a.T, origin = 'lower')
    plt.show()
    

    在这里插入图片描述

    2. 数据处理

    下载数据

    thchs30: http://www.openslr.org/18/

    2.1 生成音频文件和标签文件列表

    考虑神经网络训练过程中接收的输入输出。首先需要batch_size内数据需要统一数据的shape。

    格式为:[batch_size, time_step, feature_dim]

    然而读取的每一个sample的时间轴长都不一样,所以需要对时间轴进行处理,选择batch内最长的那个时间为基准,进行padding。这样一个batch内的数据都相同,就能进行并行训练啦。

    source_file = 'data_thchs30'
    

    定义函数source_get,获取音频文件及标注文件列表

    形如:

    E:\Data\thchs30\data_thchs30\data\A11_0.wav.trn
    E:\Data\thchs30\data_thchs30\data\A11_1.wav.trn
    E:\Data\thchs30\data_thchs30\data\A11_10.wav.trn
    E:\Data\thchs30\data_thchs30\data\A11_100.wav.trn
    E:\Data\thchs30\data_thchs30\data\A11_102.wav.trn
    E:\Data\thchs30\data_thchs30\data\A11_103.wav.trn
    E:\Data\thchs30\data_thchs30\data\A11_104.wav.trn
    E:\Data\thchs30\data_thchs30\data\A11_105.wav.trn
    E:\Data\thchs30\data_thchs30\data\A11_106.wav.trn
    E:\Data\thchs30\data_thchs30\data\A11_107.wav.trn
    
    def source_get(source_file):
        train_file = source_file + '/data'
        label_lst = []
        wav_lst = []
        for root, dirs, files in os.walk(train_file):
            for file in files:
                if file.endswith('.wav') or file.endswith('.WAV'):
                    wav_file = os.sep.join([root, file])
                    label_file = wav_file + '.trn'
                    wav_lst.append(wav_file)
                    label_lst.append(label_file)
                
        return label_lst, wav_lst
    
    label_lst, wav_lst = source_get(source_file)
    
    print(label_lst[:10])
    print(wav_lst[:10])
    
    ['data_thchs30/data/A23_73.wav.trn', 'data_thchs30/data/C4_681.wav.trn', 'data_thchs30/data/D12_793.wav.trn', 'data_thchs30/data/A19_137.wav.trn', 'data_thchs30/data/D11_898.wav.trn', 'data_thchs30/data/B33_491.wav.trn', 'data_thchs30/data/C7_546.wav.trn', 'data_thchs30/data/C32_671.wav.trn', 'data_thchs30/data/D32_817.wav.trn', 'data_thchs30/data/A32_115.wav.trn']
    ['data_thchs30/data/A23_73.wav', 'data_thchs30/data/C4_681.wav', 'data_thchs30/data/D12_793.wav', 'data_thchs30/data/A19_137.wav', 'data_thchs30/data/D11_898.wav', 'data_thchs30/data/B33_491.wav', 'data_thchs30/data/C7_546.wav', 'data_thchs30/data/C32_671.wav', 'data_thchs30/data/D32_817.wav', 'data_thchs30/data/A32_115.wav']
    

    确认相同id对应的音频文件和标签文件相同

    for i in range(10000):
        wavname = (wav_lst[i].split('/')[-1]).split('.')[0]
        labelname = (label_lst[i].split('/')[-1]).split('.')[0]
        if wavname != labelname:
            print('error')
    

    2.2 label数据处理

    定义函数read_label读取音频文件对应的拼音label

    def read_label(label_file):
        with open(label_file, 'r', encoding='utf8') as f:
            data = f.readlines()
            return data[1]
    
    print(read_label(label_lst[0]))
    
    def gen_label_data(label_lst):
        label_data = []
        for label_file in label_lst:
            pny = read_label(label_file)
            label_data.append(pny.strip('\n'))
        return label_data
    
    label_data = gen_label_data(label_lst)
    print(len(label_data))
    
    zhe4 ci4 quan2 guo2 qing1 nian2 pai2 qiu2 lian2 sai4 gong4 she4 tian1 jin1 zhou1 shan1 wu3 han4 san1 ge5 sai4 qu1 mei3 ge5 sai4 qu1 de5 qian2 liang3 ming2 jiang4 can1 jia1 fu4 sai4
    
    13388
    

    为label建立拼音到id的映射,即词典

    def mk_vocab(label_data):
        vocab = []
        for line in label_data:
            line = line.split(' ')
            for pny in line:
                if pny not in vocab:
                    vocab.append(pny)
        vocab.append('_')
        return vocab
    
    vocab = mk_vocab(label_data)
    print(len(vocab))
    
    1209
    

    有了词典就能将读取到的label映射到对应的id

    def word2id(line, vocab):
        return [vocab.index(pny) for pny in line.split(' ')]
    
    label_id = word2id(label_data[0], vocab)
    print(label_data[0])
    print(label_id)
    
    zhe4 ci4 quan2 guo2 qing1 nian2 pai2 qiu2 lian2 sai4 gong4 she4 tian1 jin1 zhou1 shan1 wu3 han4 san1 ge5 sai4 qu1 mei3 ge5 sai4 qu1 de5 qian2 liang3 ming2 jiang4 can1 jia1 fu4 sai4
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 9, 20, 21, 19, 9, 20, 22, 23, 24, 25, 26, 27, 28, 29, 9]
    

    总结:

    我们提取出了每个音频文件对应的拼音标签label_data,通过索引就可以获得该索引的标签。

    也生成了对应的拼音词典.由此词典,我们可以映射拼音标签为id序列。

    输出:

    • vocab
    • label_data
    print(vocab[:15])
    print(label_data[10])
    print(word2id(label_data[10], vocab))
    
    ['zhe4', 'ci4', 'quan2', 'guo2', 'qing1', 'nian2', 'pai2', 'qiu2', 'lian2', 'sai4', 'gong4', 'she4', 'tian1', 'jin1', 'zhou1']
    can1 jin1 shi4 ca1 zui3 he2 shou2 zhi3 de5 bei1 bian1 qing3 yong4 can1 zhi3 ca1 shi4 lian3 shang4 de5 han4 huo4 zhan1 shang5 de5 shui3 zhi1 qing3 yong4 zi4 ji3 de5 shou3 juan4 ca1 diao4
    [27, 13, 199, 200, 201, 63, 202, 203, 22, 204, 205, 206, 207, 27, 203, 200, 199, 208, 120, 22, 17, 209, 210, 211, 22, 31, 212, 206, 207, 213, 214, 22, 215, 216, 200, 217]
    

    2.3 音频数据处理

    音频数据处理,只需要获得对应的音频文件名,然后提取所需时频图即可。

    其中compute_fbank时频转化的函数在前面已经定义好了。

    fbank = compute_fbank(wav_lst[0])
    print(fbank.shape)
    
    (1026, 200)
    
    plt.imshow(fbank.T, origin = 'lower')
    plt.show()
    

    在这里插入图片描述

    由于声学模型网络结构原因(3个maxpooling层),我们的音频数据的每个维度需要能够被8整除。

    fbank = fbank[:fbank.shape[0]//8*8, :]
    
    print(fbank.shape)
    
    (1024, 200)
    

    总结:

    • 对音频数据进行时频转换
    • 转换后的数据需要各个维度能够被8整除

    2.4 数据生成器

    确定batch_size和batch_num

    total_nums = 10000
    batch_size = 4
    batch_num = total_nums // batch_size
    

    shuffle

    打乱数据的顺序,我们通过查询乱序的索引值,来确定训练数据的顺序

    from random import shuffle
    shuffle_list = [i for i in range(10000)]
    shuffle(shuffle_list)
    

    generator

    batch_size的信号时频图和标签数据,存放到两个list中去

    def get_batch(batch_size, shuffle_list, wav_lst, label_data, vocab):
        for i in range(10000//batch_size):
            wav_data_lst = []
            label_data_lst = []
            begin = i * batch_size
            end = begin + batch_size
            sub_list = shuffle_list[begin:end]
            for index in sub_list:
                fbank = compute_fbank(wav_lst[index])
                fbank = fbank[:fbank.shape[0] // 8 * 8, :]
                label = word2id(label_data[index], vocab)
                wav_data_lst.append(fbank)
                label_data_lst.append(label)
            yield wav_data_lst, label_data_lst
    
    batch = get_batch(4, shuffle_list, wav_lst, label_data, vocab)
    
    
    wav_data_lst, label_data_lst = next(batch)
    for wav_data in wav_data_lst:
        print(wav_data.shape)
    for label_data in label_data_lst:
        print(label_data)
    
    (1272, 200)
    (792, 200)
    (872, 200)
    (928, 200)
    [27, 13, 199, 200, 201, 63, 202, 203, 22, 204, 205, 206, 207, 27, 203, 200, 199, 208, 120, 22, 17, 209, 210, 211, 22, 31, 212, 206, 207, 213, 214, 22, 215, 216, 200, 217]
    [731, 5, 353, 301, 344, 41, 36, 212, 250, 103, 246, 199, 22, 766, 16, 380, 243, 411, 420, 9, 206, 259, 435, 244, 249, 113, 245, 344, 41, 188, 70]
    [0, 674, 444, 316, 20, 22, 103, 117, 199, 392, 376, 512, 519, 118, 438, 22, 328, 308, 58, 63, 1065, 198, 624, 472, 232, 159, 163, 199, 392, 376, 512, 519, 173, 22]
    [39, 51, 393, 471, 537, 198, 58, 535, 632, 100, 655, 63, 226, 488, 69, 376, 190, 409, 8, 349, 242, 93, 305, 1012, 369, 172, 166, 58, 156, 305, 179, 274, 44, 435]
    
    lens = [len(wav) for wav in wav_data_lst]
    print(max(lens))
    print(lens)
    
    1272
    [1272, 792, 872, 928]
    

    padding

    然而,每一个batch_size内的数据有一个要求,就是需要构成成一个tensorflow块,这就要求每个样本数据形式是一样的。
    除此之外,ctc需要获得的信息还有输入序列的长度。
    这里输入序列经过卷积网络后,长度缩短了8倍,因此我们训练实际输入的数据为wav_len//8。

    • padding wav data
    • wav len // 8 (网络结构导致的)
    def wav_padding(wav_data_lst):
        wav_lens = [len(data) for data in wav_data_lst]
        wav_max_len = max(wav_lens)
        wav_lens = np.array([leng//8 for leng in wav_lens])
        new_wav_data_lst = np.zeros((len(wav_data_lst), wav_max_len, 200, 1))
        for i in range(len(wav_data_lst)):
            new_wav_data_lst[i, :wav_data_lst[i].shape[0], :, 0] = wav_data_lst[i]
        return new_wav_data_lst, wav_lens
    
    pad_wav_data_lst, wav_lens = wav_padding(wav_data_lst)
    print(pad_wav_data_lst.shape)
    print(wav_lens)
    
    (4, 1272, 200, 1)
    [159  99 109 116]
    

    同样也要对label进行padding和长度获取,不同的是数据维度不同,且label的长度就是输入给ctc的长度,不需要额外处理

    • label padding
    • label len
    def label_padding(label_data_lst):
        label_lens = np.array([len(label) for label in label_data_lst])
        max_label_len = max(label_lens)
        new_label_data_lst = np.zeros((len(label_data_lst), max_label_len))
        for i in range(len(label_data_lst)):
            new_label_data_lst[i][:len(label_data_lst[i])] = label_data_lst[i]
        return new_label_data_lst, label_lens
    
    pad_label_data_lst, label_lens = label_padding(label_data_lst)
    print(pad_label_data_lst.shape)
    print(label_lens)
    
    (4, 36)
    [36 31 34 34]
    
    • 用于训练格式的数据生成器
    def data_generator(batch_size, shuffle_list, wav_lst, label_data, vocab):
        for i in range(len(wav_lst)//batch_size):
            wav_data_lst = []
            label_data_lst = []
            begin = i * batch_size
            end = begin + batch_size
            sub_list = shuffle_list[begin:end]
            for index in sub_list:
                fbank = compute_fbank(wav_lst[index])
                pad_fbank = np.zeros((fbank.shape[0]//8*8+8, fbank.shape[1]))
                pad_fbank[:fbank.shape[0], :] = fbank
                label = word2id(label_data[index], vocab)
                wav_data_lst.append(pad_fbank)
                label_data_lst.append(label)
            pad_wav_data, input_length = wav_padding(wav_data_lst)
            pad_label_data, label_length = label_padding(label_data_lst)
            inputs = {'the_inputs': pad_wav_data,
                      'the_labels': pad_label_data,
                      'input_length': input_length,
                      'label_length': label_length,
                     }
            outputs = {'ctc': np.zeros(pad_wav_data.shape[0],)} 
            yield inputs, outputs
    
    
    

    3. 模型搭建

    训练输入为时频图,标签为对应的拼音标签,如下所示:

    搭建语音识别模型,采用了 CNN+CTC 的结构。

    在这里插入图片描述

    import keras
    from keras.layers import Input, Conv2D, BatchNormalization, MaxPooling2D
    from keras.layers import Reshape, Dense, Lambda
    from keras.optimizers import Adam
    from keras import backend as K
    from keras.models import Model
    from keras.utils import multi_gpu_model
    
    Using TensorFlow backend.
    

    3.1 构建模型组件

    • 定义3*3的卷积层
    def conv2d(size):
        return Conv2D(size, (3,3), use_bias=True, activation='relu',
            padding='same', kernel_initializer='he_normal')
    
    • 定义batch norm层
    def norm(x):
        return BatchNormalization(axis=-1)(x)
    
    • 定义最大池化层,数据的后两维维度都减半
    def maxpool(x):
        return MaxPooling2D(pool_size=(2,2), strides=None, padding="valid")(x)
    
    • dense层
    def dense(units, activation="relu"):
        return Dense(units, activation=activation, use_bias=True,
            kernel_initializer='he_normal')
    
    • 由cnn + cnn + maxpool构成的组合
    # x.shape=(none, none, none)
    # output.shape = (1/2, 1/2, 1/2)
    def cnn_cell(size, x, pool=True):
        x = norm(conv2d(size)(x))
        x = norm(conv2d(size)(x))
        if pool:
            x = maxpool(x)
        return x
    
    • 添加CTC损失函数,由backend引入

    注意:CTC_batch_cost输入为:

    • labels 标签:[batch_size, l]
    • y_pred cnn网络的输出:[batch_size, t, vocab_size]
    • input_length 网络输出的长度:[batch_size]
    • label_length 标签的长度:[batch_size]
    def ctc_lambda(args):
        labels, y_pred, input_length, label_length = args
        y_pred = y_pred[:, :, :]
        return K.ctc_batch_cost(labels, y_pred, input_length, label_length)
    

    3.2 搭建cnn+dnn+ctc的声学模型

    class Amodel():
        """docstring for Amodel."""
        def __init__(self, vocab_size):
            super(Amodel, self).__init__()
            self.vocab_size = vocab_size
            self._model_init()
            self._ctc_init()
            self.opt_init()
    
        def _model_init(self):
            self.inputs = Input(name='the_inputs', shape=(None, 200, 1))
            self.h1 = cnn_cell(32, self.inputs)
            self.h2 = cnn_cell(64, self.h1)
            self.h3 = cnn_cell(128, self.h2)
            self.h4 = cnn_cell(128, self.h3, pool=False)
            # 200 / 8 * 128 = 3200
            self.h6 = Reshape((-1, 3200))(self.h4)
            self.h7 = dense(256)(self.h6)
            self.outputs = dense(self.vocab_size, activation='softmax')(self.h7)
            self.model = Model(inputs=self.inputs, outputs=self.outputs)
    
        def _ctc_init(self):
            self.labels = Input(name='the_labels', shape=[None], dtype='float32')
            self.input_length = Input(name='input_length', shape=[1], dtype='int64')
            self.label_length = Input(name='label_length', shape=[1], dtype='int64')
            self.loss_out = Lambda(ctc_lambda, output_shape=(1,), name='ctc')\
                ([self.labels, self.outputs, self.input_length, self.label_length])
            self.ctc_model = Model(inputs=[self.labels, self.inputs,
                self.input_length, self.label_length], outputs=self.loss_out)
    
        def opt_init(self):
            opt = Adam(lr = 0.0008, beta_1 = 0.9, beta_2 = 0.999, decay = 0.01, epsilon = 10e-8)
            #self.ctc_model=multi_gpu_model(self.ctc_model,gpus=2)
            self.ctc_model.compile(loss={'ctc': lambda y_true, output: output}, optimizer=opt)
    
    
    am = Amodel(1176)
    am.ctc_model.summary()
    
    __________________________________________________________________________________________________
    Layer (type)                    Output Shape         Param #     Connected to                     
    ==================================================================================================
    the_inputs (InputLayer)         (None, None, 200, 1) 0                                            
    __________________________________________________________________________________________________
    conv2d_1 (Conv2D)               (None, None, 200, 32 320         the_inputs[0][0]                 
    __________________________________________________________________________________________________
    batch_normalization_1 (BatchNor (None, None, 200, 32 128         conv2d_1[0][0]                   
    __________________________________________________________________________________________________
    conv2d_2 (Conv2D)               (None, None, 200, 32 9248        batch_normalization_1[0][0]      
    __________________________________________________________________________________________________
    batch_normalization_2 (BatchNor (None, None, 200, 32 128         conv2d_2[0][0]                   
    __________________________________________________________________________________________________
    max_pooling2d_1 (MaxPooling2D)  (None, None, 100, 32 0           batch_normalization_2[0][0]      
    __________________________________________________________________________________________________
    conv2d_3 (Conv2D)               (None, None, 100, 64 18496       max_pooling2d_1[0][0]            
    __________________________________________________________________________________________________
    batch_normalization_3 (BatchNor (None, None, 100, 64 256         conv2d_3[0][0]                   
    __________________________________________________________________________________________________
    conv2d_4 (Conv2D)               (None, None, 100, 64 36928       batch_normalization_3[0][0]      
    __________________________________________________________________________________________________
    batch_normalization_4 (BatchNor (None, None, 100, 64 256         conv2d_4[0][0]                   
    __________________________________________________________________________________________________
    max_pooling2d_2 (MaxPooling2D)  (None, None, 50, 64) 0           batch_normalization_4[0][0]      
    __________________________________________________________________________________________________
    conv2d_5 (Conv2D)               (None, None, 50, 128 73856       max_pooling2d_2[0][0]            
    __________________________________________________________________________________________________
    batch_normalization_5 (BatchNor (None, None, 50, 128 512         conv2d_5[0][0]                   
    __________________________________________________________________________________________________
    conv2d_6 (Conv2D)               (None, None, 50, 128 147584      batch_normalization_5[0][0]      
    __________________________________________________________________________________________________
    batch_normalization_6 (BatchNor (None, None, 50, 128 512         conv2d_6[0][0]                   
    __________________________________________________________________________________________________
    max_pooling2d_3 (MaxPooling2D)  (None, None, 25, 128 0           batch_normalization_6[0][0]      
    __________________________________________________________________________________________________
    conv2d_7 (Conv2D)               (None, None, 25, 128 147584      max_pooling2d_3[0][0]            
    __________________________________________________________________________________________________
    batch_normalization_7 (BatchNor (None, None, 25, 128 512         conv2d_7[0][0]                   
    __________________________________________________________________________________________________
    conv2d_8 (Conv2D)               (None, None, 25, 128 147584      batch_normalization_7[0][0]      
    __________________________________________________________________________________________________
    batch_normalization_8 (BatchNor (None, None, 25, 128 512         conv2d_8[0][0]                   
    __________________________________________________________________________________________________
    reshape_1 (Reshape)             (None, None, 3200)   0           batch_normalization_8[0][0]      
    __________________________________________________________________________________________________
    dense_1 (Dense)                 (None, None, 256)    819456      reshape_1[0][0]                  
    __________________________________________________________________________________________________
    the_labels (InputLayer)         (None, None)         0                                            
    __________________________________________________________________________________________________
    dense_2 (Dense)                 (None, None, 1176)   302232      dense_1[0][0]                    
    __________________________________________________________________________________________________
    input_length (InputLayer)       (None, 1)            0                                            
    __________________________________________________________________________________________________
    label_length (InputLayer)       (None, 1)            0                                            
    __________________________________________________________________________________________________
    ctc (Lambda)                    (None, 1)            0           the_labels[0][0]                 
                                                                     dense_2[0][0]                    
                                                                     input_length[0][0]               
                                                                     label_length[0][0]               
    ==================================================================================================
    Total params: 1,706,104
    Trainable params: 1,704,696
    Non-trainable params: 1,408
    __________________________________________________________________________________________________
    

    4. 模型训练及推断

    4.1 模型训练

    这样训练所需的数据,就准备完毕了,接下来可以进行训练了。我们采用如下参数训练:

    • batch_size = 4

    • batch_num = 10000 // 4

    • epochs = 1

    • 准备训练数据,shuffle是为了打乱训练数据顺序

    total_nums = 100
    batch_size = 20
    batch_num = total_nums // batch_size
    epochs = 50
    
    source_file = 'data_thchs30'
    label_lst, wav_lst = source_get(source_file)
    label_data = gen_label_data(label_lst[:100])
    vocab = mk_vocab(label_data)
    vocab_size = len(vocab)
    
    print(vocab_size)
    
    shuffle_list = [i for i in range(100)]
    
    
    716
    
    • 使用fit_generator

    • 开始训练

    am = Amodel(vocab_size)
    
    for k in range(epochs):
        print('this is the', k+1, 'th epochs trainning !!!')
        #shuffle(shuffle_list)
        batch = data_generator(batch_size, shuffle_list, wav_lst, label_data, vocab)
        am.ctc_model.fit_generator(batch, steps_per_epoch=batch_num, epochs=1)
    
    this is the 1 th epochs trainning !!!
    Epoch 1/1
    5/5 [==============================] - 30s 6s/step - loss: 422.8893
    this is the 2 th epochs trainning !!!
    .....
    
    5/5 [==============================] - 7s 1s/step - loss: 0.4708
    this is the 50 th epochs trainning !!!
    Epoch 1/1
    5/5 [==============================] - 7s 1s/step - loss: 0.4580
    

    4.2 模型推断

    • 解码器
    def decode_ctc(num_result, num2word):
    	result = num_result[:, :, :]
    	in_len = np.zeros((1), dtype = np.int32)
    	in_len[0] = result.shape[1];
    	r = K.ctc_decode(result, in_len, greedy = True, beam_width=10, top_paths=1)
    	r1 = K.get_value(r[0][0])
    	r1 = r1[0]
    	text = []
    	for i in r1:
    		text.append(num2word[i])
    	return r1, text
    
    • 模型识别结果解码
    # 测试模型 predict(x, batch_size=None, verbose=0, steps=None)
    batch = data_generator(1, shuffle_list, wav_lst, label_data, vocab)
    for i in range(10):
      # 载入训练好的模型,并进行识别
      inputs, outputs = next(batch)
      x = inputs['the_inputs']
      y = inputs['the_labels'][0]
      result = am.model.predict(x, steps=1)
      # 将数字结果转化为文本结果
      result, text = decode_ctc(result, vocab)
      print('数字结果: ', result)
      print('文本结果:', text)
      print('原文结果:', [vocab[int(i)] for i in y])
    
    数字结果:  [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19  9 20 21 19
      9 20 22 23 24 25 26 27 28 29  9]
    文本结果: ['zhe4', 'ci4', 'quan2', 'guo2', 'qing1', 'nian2', 'pai2', 'qiu2', 'lian2', 'sai4', 'gong4', 'she4', 'tian1', 'jin1', 'zhou1', 'shan1', 'wu3', 'han4', 'san1', 'ge5', 'sai4', 'qu1', 'mei3', 'ge5', 'sai4', 'qu1', 'de5', 'qian2', 'liang3', 'ming2', 'jiang4', 'can1', 'jia1', 'fu4', 'sai4']
    原文结果: ['zhe4', 'ci4', 'quan2', 'guo2', 'qing1', 'nian2', 'pai2', 'qiu2', 'lian2', 'sai4', 'gong4', 'she4', 'tian1', 'jin1', 'zhou1', 'shan1', 'wu3', 'han4', 'san1', 'ge5', 'sai4', 'qu1', 'mei3', 'ge5', 'sai4', 'qu1', 'de5', 'qian2', 'liang3', 'ming2', 'jiang4', 'can1', 'jia1', 'fu4', 'sai4']
    数字结果:  [30 31 32 33 34 35 36 32 37 38 39 40 41 22 15 42 43 44 41 45 46 47 48  3
     39 49 50 51 52 42 53 54]
    文本结果: ['xian1', 'shui3', 'yan2', 'zhi4', 'fei1', 'ma2', 'zu3', 'yan2', 'chang2', 'da2', 'shi2', 'hua2', 'li3', 'de5', 'shan1', 'ya2', 'dong4', 'xue2', 'li3', 'you3', 'chun1', 'qiu1', 'zhan4', 'guo2', 'shi2', 'qi1', 'gu3', 'yue4', 'zu2', 'ya2', 'mu4', 'qun2']
    原文结果: ['xian1', 'shui3', 'yan2', 'zhi4', 'fei1', 'ma2', 'zu3', 'yan2', 'chang2', 'da2', 'shi2', 'hua2', 'li3', 'de5', 'shan1', 'ya2', 'dong4', 'xue2', 'li3', 'you3', 'chun1', 'qiu1', 'zhan4', 'guo2', 'shi2', 'qi1', 'gu3', 'yue4', 'zu2', 'ya2', 'mu4', 'qun2']
    数字结果:  [55 56 57 11 58 18 59 60 22 61 62 63 64 62 65 66 57 11 58 67 18 59 60 68
     32 69 70 22  0 71 72 32 73 74 22 76]
    文本结果: ['wo3', 'men5', 'pai1', 'she4', 'le5', 'san1', 'wang4', 'chong1', 'de5', 'yuan3', 'jing3', 'he2', 'jin4', 'jing3', 'te4', 'bie2', 'pai1', 'she4', 'le5', 'cong2', 'san1', 'wang4', 'chong1', 'shen1', 'yan2', 'er2', 'chu1', 'de5', 'zhe4', 'tiao2', 'wan1', 'yan2', 'ni2', 'ning4', 'de5', 'lu4']
    原文结果: ['wo3', 'men5', 'pai1', 'she4', 'le5', 'san1', 'wang4', 'chong1', 'de5', 'yuan3', 'jing3', 'he2', 'jin4', 'jing3', 'te4', 'bie2', 'pai1', 'she4', 'le5', 'cong2', 'san1', 'wang4', 'chong1', 'shen1', 'yan2', 'er2', 'chu1', 'de5', 'zhe4', 'tiao2', 'wan1', 'yan2', 'ni2', 'ning4', 'de5', 'xiao3', 'lu4']
    数字结果:  [77 78 79 80 81 82 83 82 84 79 83 79 84 82 82 80 85 79 79 82 10 86]
    文本结果: ['bu2', 'qi4', 'ya3', 'bu4', 'bi4', 'su2', 'hua4', 'su2', 'wei2', 'ya3', 'hua4', 'ya3', 'wei2', 'su2', 'su2', 'bu4', 'shang1', 'ya3', 'ya3', 'su2', 'gong4', 'shang3']
    原文结果: ['bu2', 'qi4', 'ya3', 'bu4', 'bi4', 'su2', 'hua4', 'su2', 'wei2', 'ya3', 'hua4', 'ya3', 'wei2', 'su2', 'su2', 'bu4', 'shang1', 'ya3', 'ya3', 'su2', 'gong4', 'shang3']
    数字结果:  [ 87  13  88  25  89  90  91  92  93  90  94  95  96  97  98  99  84 100
      89 101 102  96 103 104 104  77 105 106 107 108 109  28 110 111]
    文本结果: ['ru2', 'jin1', 'ta1', 'ming2', 'chuan2', 'si4', 'fang1', 'sheng1', 'bo1', 'si4', 'hai3', 'yang3', 'xie1', 'ji4', 'shu4', 'guang3', 'wei2', 'liu2', 'chuan2', 'you1', 'liang2', 'xie1', 'zhong3', 'yuan2', 'yuan2', 'bu2', 'duan4', 'shu1', 'song4', 'dao4', 'qian1', 'jia1', 'wan4', 'hu4']
    原文结果: ['ru2', 'jin1', 'ta1', 'ming2', 'chuan2', 'si4', 'fang1', 'sheng1', 'bo1', 'si4', 'hai3', 'yang3', 'xie1', 'ji4', 'shu4', 'guang3', 'wei2', 'liu2', 'chuan2', 'you1', 'liang2', 'xie1', 'zhong3', 'yuan2', 'yuan2', 'bu2', 'duan4', 'shu1', 'song4', 'dao4', 'qian1', 'jia1', 'wan4', 'hu4']
    数字结果:  [112 113 114  28  22 115 116 117 118 119 108  20 120 121 122 123  58 124
     125 126 127 128 129 130 130 131 132 133  88 134  11]
    文本结果: ['yang2', 'dui4', 'zhang3', 'jia1', 'de5', 'er4', 'wa2', 'zi5', 'fa1', 'shao1', 'dao4', 'qu1', 'shang4', 'zhen2', 'suo3', 'kan4', 'le5', 'bing4', 'dai4', 'hui2', 'yi1', 'bao1', 'zhen1', 'yao4', 'yao4', 'yi4', 'qiong2', 'gei3', 'ta1', 'zhu4', 'she4']
    原文结果: ['yang2', 'dui4', 'zhang3', 'jia1', 'de5', 'er4', 'wa2', 'zi5', 'fa1', 'shao1', 'dao4', 'qu1', 'shang4', 'zhen2', 'suo3', 'kan4', 'le5', 'bing4', 'dai4', 'hui2', 'yi1', 'bao1', 'zhen1', 'yao4', 'yao4', 'yi4', 'qiong2', 'gei3', 'ta1', 'zhu4', 'she4']
    数字结果:  [135  89 136 112 137 138 139 112  14 123 132 140 141 142 143 144  39 145
     146 143 144 147 148 149  37 119 150 151 152 118 153 154]
    文本结果: ['xiang1', 'chuan2', 'sui2', 'yang2', 'di4', 'nan2', 'xia4', 'yang2', 'zhou1', 'kan4', 'qiong2', 'hua1', 'tu2', 'jing1', 'huai2', 'yin1', 'shi2', 'wen2', 'de2', 'huai2', 'yin1', 'pao2', 'chu2', 'shan4', 'chang2', 'shao1', 'yu2', 'nai3', 'tu1', 'fa1', 'qi2', 'xiang3']
    原文结果: ['xiang1', 'chuan2', 'sui2', 'yang2', 'di4', 'nan2', 'xia4', 'yang2', 'zhou1', 'kan4', 'qiong2', 'hua1', 'tu2', 'jing1', 'huai2', 'yin1', 'shi2', 'wen2', 'de2', 'huai2', 'yin1', 'pao2', 'chu2', 'shan4', 'chang2', 'shao1', 'yu2', 'nai3', 'tu1', 'fa1', 'qi2', 'xiang3']
    数字结果:  [ 87  63 155 156 157  69 158 159  22  33 160 107 161 162 163 155  70  58
     164 165  51  22 166 167 168 169 170 171 172 173 171 106 174]
    文本结果: ['ru2', 'he2', 'ti2', 'gao1', 'shao4', 'er2', 'du2', 'wu4', 'de5', 'zhi4', 'liang4', 'song4', 'qing4', 'ling2', 'ye3', 'ti2', 'chu1', 'le5', 'hen3', 'zhuo2', 'yue4', 'de5', 'jian4', 'jie3', 'na4', 'jiu4', 'shi5', 'zhua1', 'chuang4', 'zuo4', 'zhua1', 'shu1', 'gao3']
    原文结果: ['ru2', 'he2', 'ti2', 'gao1', 'shao4', 'er2', 'du2', 'wu4', 'de5', 'zhi4', 'liang4', 'song4', 'qing4', 'ling2', 'ye3', 'ti2', 'chu1', 'le5', 'hen3', 'zhuo2', 'yue4', 'de5', 'jian4', 'jie3', 'na4', 'jiu4', 'shi5', 'zhua1', 'chuang4', 'zuo4', 'zhua1', 'shu1', 'gao3']
    数字结果:  [ 88 153 120 175 138 176  10 177 178 179 137  22 180 160   3 181  18 182
     183 184 185 127 185 118 186  22 187 188 189  43 179  78]
    文本结果: ['ta1', 'qi2', 'shang4', 'yun2', 'nan2', 'cheng2', 'gong4', 'xun4', 'lian4', 'ji1', 'di4', 'de5', 'yi2', 'liang4', 'guo2', 'chan3', 'san1', 'lun2', 'mo2', 'tuo2', 'rou2', 'yi1', 'rou2', 'fa1', 'hong2', 'de5', 'shuang1', 'yan3', 'qi3', 'dong4', 'ji1', 'qi4']
    原文结果: ['ta1', 'qi2', 'shang4', 'yun2', 'nan2', 'cheng2', 'gong4', 'xun4', 'lian4', 'ji1', 'di4', 'de5', 'yi2', 'liang4', 'guo2', 'chan3', 'san1', 'lun2', 'mo2', 'tuo2', 'rou2', 'yi1', 'rou2', 'fa1', 'hong2', 'de5', 'shuang1', 'yan3', 'qi3', 'dong4', 'ji1', 'qi4']
    数字结果:  [190  75   5 173  83  22  65 191 192 193 194 153  83 195  20  66 150 145
     196  83  63   5  83 197 198  79  82  10  86]
    文本结果: ['ma3', 'xiao3', 'nian2', 'zuo4', 'hua4', 'de5', 'te4', 'zheng1', 'yu3', 'shen2', 'yun4', 'qi2', 'hua4', 'feng1', 'qu1', 'bie2', 'yu2', 'wen2', 'ren2', 'hua4', 'he2', 'nian2', 'hua4', 'ke3', 'wei4', 'ya3', 'su2', 'gong4', 'shang3']
    原文结果: ['ma3', 'xiao3', 'nian2', 'zuo4', 'hua4', 'de5', 'te4', 'zheng1', 'yu3', 'shen2', 'yun4', 'qi2', 'hua4', 'feng1', 'qu1', 'bie2', 'yu2', 'wen2', 'ren2', 'hua4', 'he2', 'nian2', 'hua4', 'ke3', 'wei4', 'ya3', 'su2', 'gong4', 'shang3']
    

    转载请注明出处:https://blog.csdn.net/chinatelecom08

    展开全文
  • 深度神经网络——中文语音识别

    千次阅读 2019-08-29 22:32:11
    1. 背景介绍 ...这个过程主要采用了 3 种技术,即自动语音识别(automatic speech recognition, ASR)、自然语言处理(natural language processing, NLP)和语音合成(speech synthesis,SS)。...

    1. 背景介绍

    语音是人类最自然的交互方式。计算机发明之后让机器能够“听懂”人类的语言、理解语言含义,并能做出正确回答就成为了人们追求的目标。这个过程主要采用了 3 种技术,即自动语音识别(automatic speech recognition,
    ASR)、自然语言处理(natural language processing,
    NLP)和语音合成(speech synthesis,SS)。语音识别技术的目的是让机器能听懂人类的语音,是一个典型的交叉学科任务。

    2. 概述

    语音识别系统模型由声学模型和语言模型两个部分组成,声学模型对应于语音到音素的概率计算,语言模型对应于音素到文字的概率计算。

    一个连续语音识别系统大致可以由四个部分组成:特征提取,声学模型,语言模型和解码部分。具体过程是首先从语音数据中经过提取得到声学特征,然后经过模型训练统计得到一个声学模型,作为识别的模板,并结合语言模型经过解码处理得到一个识别结果。

    声学模型是语音识别系统中关键的部分,它的作用是对声学单元产生的特征序列进行描述,对语音信号进行了分类。我们可以用声学模型来计算一段观测到的特征矢量属于各个声学单元的概率并根据似然准则将特征序列转化为状态序列。
    本文数据集地址 清华大学THCHS30中文语音数据集
    详细代码教程 中文语音识别

    2.1 特征提取

    神经网络不能将音频作为输入进行训练,所以第一步我们要对音频数据进行特征提取。常见的特征提取都是基于人类的发声机理和听觉感知,从发声机理到听觉感知认识声音的本质
    常用的一些声学特征如下:

    (1) 线性预测系数(LPC),线性预测分析是模拟人类的发声原理,通过分析声道短管级联的模型得到的。假设系统的传递函数跟全极点的数字滤波器是相似的,通常用 12一16个极点就可以描述语音信号的特征。所以对于 n 时刻的语音信号,
    我们可以用之前时刻的信号的线性组合近似的模拟。然后计算语音信号的采样值和线性预测的采样值,并让这两者之间达到均方的误差(MSE)最小,就可以得到 LPC 。
    (2) 感知线性预测(PLP),PLP 是一种基于听觉模型的特征参数。该参数是一种等效于 LPC 的特征,也是全极点模型预测多项式的一组系数。不同之处是
    PLP 是基于入耳昕觉,通过计算应用到频谱分析中,将输入语音信号经过入耳听觉模型处理,替代 LPC 所用的时域信号,这样的优点是有利于抗噪语音特征的提取。

    (3)梅尔频率倒谱系数(MFCC), MFCC 也是基于入耳听觉特性,梅尔频
    率倒谱频带划分是在 Mel刻度上等距划的,Mel频率的尺度值与实际频率的对数分布关系更符合人耳的听觉特性,所以可以使得语音信号有着更好的表示。

    (5)基于滤波器组的特征 Fbank(Filter bank), Fbank 特征提取方法就是相当
    于 MFCC 去掉最后一步的离散余弦变换,跟 MFCC 特征, Fbank 特征保留了更多的原始语音数据。

    (6)语谱图(Spectrogram),语谱图就是语音频谱图,一般是通过处理接收的时域信号得到频谱图,因此只要有足够时间长度的时域信号就可。语谱图的特点是观察语音不同频段的信号强度,可以看出随时间的变化情况。
    本文就是通过将语谱图作为特征输入,利用 CNN 进行图像处理进行训练。语谱图可以理解为在一段时间内的频谱图叠加而成,因此提取语谱图的主要步骤分为:分帧、加窗、快速傅立叶变换(FFT)。

    2.1.1 读取音频

    第一步,我们需要找到利用 scipy 模块将音频转化为有用的信息, fs 为采样频率, wavsignal 为语音数据。我们的数据集的 fs 均为16khz 。

    import scipy.io.wavfile as wav
    filepath = 'test.wav'
    
    fs, wavsignal = wav.read(filepath)
    

    2.1.2 分帧、加窗

    语音信号在宏观上是不平稳的,在微观上是平稳的,具有短时平稳性(10—30ms内可以认为语音信号近似不变为一个音素的发音),一般情况下取 25ms 。
    为了处理语音信号,我们要对语音信号进行加窗,也就是一次仅处理窗中的数据。因为实际的语音信号是很长的,我们不能也不必对非常长的数据进行一次性处理。明智的解决办法就是每次取一段数据,进行分析,然后再取下一段数据,再进行分析。我们的加窗操作指的就是汉明窗操作,原理就是把一帧内的数据乘以一个函数  并得到新的一帧数据。公式下文已给出。
    怎么仅取一段数据呢?因为之后我们会对汉明窗中的数据进行快速傅立叶变换(FFT),它假设一个窗内的信号是代表一个周期的信号(也就是说窗的左端和右端大致可以连续),而通常一小段音频数据没有明显的周期性,加上汉明窗后,数据就比较接近周期函数了。
    由于加上汉明窗,只有中间的数据体现出来了,两边的数据信息丢失了,所以等会移窗的时候会有重叠的部分,当窗口取了 25ms 时,步长可以取 10ms (其中 a 的值一般取0.46)。
    公式为:
    ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4ubmxhcmsuY29tL3l1cXVlL19fbGF0ZXgvZDRjYmM0ZjEyY2JlZmM3YTU1MDY5ODM2NDJhYzVlZjAuc3Zn?x-oss-process=image/format,png#card=math&code=W(n,a)=(1-a)-a*cos[\frac{2n\pi}{N-1}],0\geq n \leq N-1&height=34.59770114942529&width=332.2988505747127)

    代码为:

    import numpy as np
    
    x=np.linspace(0, 400 - 1, 400, dtype = np.int64)#返回区间内的均匀数字
    w = 0.54 - 0.46 * np.cos(2 * np.pi * (x) / (400 - 1))
    
    time_window = 25
    window_length = fs // 1000 * time_window
    
    # 分帧
    p_begin = 0
    p_end = p_begin + window_length
    frame = wavsignal[p_begin:p_end]
    
    plt.figure(figsize=(15, 5))
    ax4 = plt.subplot(121)
    ax4.set_title('the original picture of one frame')
    ax4.plot(frame)
    
    # 加窗
    
    frame = frame * w
    ax5 = plt.subplot(122)
    ax5.set_title('after hanmming')
    ax5.plot(frame)
    plt.show()
    

    图片为效果为:
    image.png

    2.1.3 快速傅立叶变换 (FFT)

    语音信号在时域上比较难看出其特性,所以通常转换为频域上的能量分布,所以我们对每帧经过窗函数处理的信号做快速傅立叶变换将时域图转换成各帧的频谱,然后我们可以对每个窗口的频谱叠加得到语谱图。

    代码为:

    from scipy.fftpack import fft
    
    # 进行快速傅里叶变换
    frame_fft = np.abs(fft(frame))[:200]
    plt.plot(frame_fft)
    plt.show()
    
    # 取对数,求db
    frame_log = np.log(frame_fft)
    plt.plot(frame_log)
    plt.show()
    

    2.2 CTC(Connectionist Temporal Classification)

    谈及语音识别,如果这里有一个剪辑音频的数据集和对应的转录,而我们不知道怎么把转录中的字符和音频中的音素对齐,这会大大增加了训练语音识别器的难度。如果不对数据进行调整处理,那就意味着不能用一些简单方法进行训练。对此,我们可以选择的第一个方法是制定一项规则,如“一个字符对应十个音素输入”,但人们的语速千差万别,这种做法很容易出现纰漏。为了保证模型的可靠性,第二种方法,即手动对齐每个字符在音频中的位置,训练的模型性能效果更佳,因为我们能知道每个输入时间步长的真实信息。但它的缺点也很明显——即便是大小合适的数据集,这样的做法依然非常耗时。事实上,制定规则准确率不佳、手动调试用时过长不仅仅出现在语音识别领域,其它工作,如手写识别、在视频中添加动作标记,同样会面对这些问题。
    这种场景下,正是 CTC 用武之地。 CTC 是一种让网络自动学会对齐的好方法,十分适合语音识别和书写识别。为了描述地更形象一些,我们可以把输入序列(音频)映射为X=[x1,x2,…,xT],其相应的输出序列(转录)即为Y=[y1,y2,…,yU]。这之后,将字符与音素对齐的操作就相当于在X和Y之间建立一个准确的映射,详细内容可见CTC经典文章
    损失函数部分代码:

    def ctc_lambda(args):
        labels, y_pred, input_length, label_length = args
        y_pred = y_pred[:, :, :]
        return K.ctc_batch_cost(labels, y_pred, input_length, label_length)
    

    解码部分代码:

    #num_result为模型预测结果,num2word 对应拼音列表。
    def decode_ctc(num_result, num2word):
    	result = num_result[:, :, :]
    	in_len = np.zeros((1), dtype = np.int32)
    	in_len[0] = result.shape[1];
    	r = K.ctc_decode(result, in_len, greedy = True, beam_width=10, top_paths=1)
    	r1 = K.get_value(r[0][0])
    	r1 = r1[0]
    	text = []
    	for i in r1:
    		text.append(num2word[i])
    	return r1, text
    

    3. 声学模型

    模型主要利用 CNN 来处理图像并通过最大值池化来提取主要特征加入定义好的 CTC 损失函数来进行训练。当有了输入和标签的话,模型构造就可以自己进行设定,如果准确率得以提升,那么都是可取的。有兴趣也可以加入LSTM 等网络结构,关于 CNN 和池化操作网上资料很多,这里就不再赘述了。有兴趣的读者可以参考往期的卷积神经网络 AlexNet 。
    代码:

    class Amodel():
        """docstring for Amodel."""
        def __init__(self, vocab_size):
            super(Amodel, self).__init__()
            self.vocab_size = vocab_size
            self._model_init()
            self._ctc_init()
            self.opt_init()
    
        def _model_init(self):
            self.inputs = Input(name='the_inputs', shape=(None, 200, 1))
            self.h1 = cnn_cell(32, self.inputs)
            self.h2 = cnn_cell(64, self.h1)
            self.h3 = cnn_cell(128, self.h2)
            self.h4 = cnn_cell(128, self.h3, pool=False)
            # 200 / 8 * 128 = 3200
            self.h6 = Reshape((-1, 3200))(self.h4)
            self.h7 = dense(256)(self.h6)
            self.outputs = dense(self.vocab_size, activation='softmax')(self.h7)
            self.model = Model(inputs=self.inputs, outputs=self.outputs)
    
        def _ctc_init(self):
            self.labels = Input(name='the_labels', shape=[None], dtype='float32')
            self.input_length = Input(name='input_length', shape=[1], dtype='int64')
            self.label_length = Input(name='label_length', shape=[1], dtype='int64')
            self.loss_out = Lambda(ctc_lambda, output_shape=(1,), name='ctc')\
                ([self.labels, self.outputs, self.input_length, self.label_length])
            self.ctc_model = Model(inputs=[self.labels, self.inputs,
                self.input_length, self.label_length], outputs=self.loss_out)
    
        def opt_init(self):
            opt = Adam(lr = 0.0008, beta_1 = 0.9, beta_2 = 0.999, decay = 0.01, epsilon = 10e-8)
            self.ctc_model.compile(loss={'ctc': lambda y_true, output: output}, optimizer=opt)
    
    
    

    4. 语言模型

    4.1 介绍统计语言模型

    统计语言模型是自然语言处理的基础,它是一种具有一定上下文相关特性的数学模型,本质上也是概率图模型的一种,并且广泛应用于机器翻译、语音识别、拼音输入、图像文字识别、拼写纠错、查找错别字和搜索引擎等。在很多任务中,计算机需要知道一个文字序列是否能构成一个大家理解、无错别字且有意义的句子,比如这几句话:

    许多人可能不太清楚到底机器学习是什么,而它事实上已经成为我们日常生活中不可或缺的重要组成部分。
    不太清楚许多人可能机器学习是什么到底,而它成为已经日常我们生活中组成部分不可或缺的重要。
    不清太多人机可楚器学许能习是么到什底,而已常我它成经日为们组生中成活部不重可的或缺分要。
    

    第一个句子符合语法规范,词义清楚,第二个句子词义尚且还清楚,第三个连词义都模模糊糊了。这正是从基于规则角度去理解的,在上个世纪70年代以前,科学家们也是这样想的。而之后,贾里尼克使用了一个简单的统计模型就解决了这个问题。从统计角度来看,第一个句子的概率很大,比如是  ,而第二个其次,比如是 ,第三个最小,比如是 。按照这种模型,第一个句子出现的概率是第二个的 10 的 20 次方倍,更不用说第三个句子了,所以第一个句子最符合常理。

    4.2 模型建立

    假设 S 为生成的句子,有一连串的词w1,w2,…wn构成,则句子 S 出现的概率为:
    ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4ubmxhcmsuY29tL3l1cXVlL19fbGF0ZXgvNzk3Mjg5OGFlYmM0ZTdjZjYzMDAwY2NmZmEzNGU2MjEuc3Zn?x-oss-process=image/format,png#card=math&code=P(S) = P(w1,w2,…,wn) = P(w1)*P(w2|w1)*P(w3|w1,w2)…P(wn|w1,w2,…,wn-1)&height=18.50574712643678&width=603.448275862069)
    由于计算机内存空间和算力的限制,我们明显需要更加合理的运算方法。一般来说,仅考虑与前一个词有关,就可以有着相当不错的准确率,在实际使用中,通常考虑与前两个词有关就足够了,极少情况下才考虑与前三个有关,因此我们可以选择采取下列这个公式:
    ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4ubmxhcmsuY29tL3l1cXVlL19fbGF0ZXgvNjY5MzUyNDY5ZDIwNmJlMDhkN2EyMjViYzBiNjMwZWMuc3Zn?x-oss-process=image/format,png#card=math&code=P(S) = P(w1,w2,…,wn) = P(w1)*P(w2|w1)*P(w3| w2)…P(wn|wn-1)&height=18.50574712643678&width=502.0689655172414)
    而 P 我们可以通过爬取资料统计词频来进行计算概率。

    4.3 拼音到文本的实现

    拼音转汉字的算法是动态规划,跟寻找最短路径的算法基本相同。我们可以将汉语输入看成一个通信问题,每一个拼音可以对应多个汉字,而每个汉字一次只读一个音,把每一个拼音对应的字从左到有连起来,就成为了一张有向图。未命名图片.png

    5. 模型测试

    声学模型测试:
    image.png
    语言模型测试:
    image.png
    由于模型简单和数据集过少,模型效果并不是很好。
    项目源码地址:https://momodel.cn/explore/5d5b589e1afd9472fe093a9e?type=app

    6. 参考资料

    论文:语音识别技术的研究进展与展望
    博客:ASRT_SpeechRecognition
    博客:DeepSpeechRecognition

    关于我们

    Mo(网址:momodel.cn)是一个支持 Python 的人工智能在线建模平台,能帮助你快速开发、训练并部署模型。


    Mo 人工智能俱乐部 是由网站的研发与产品设计团队发起、致力于降低人工智能开发与使用门槛的俱乐部。团队具备大数据处理分析、可视化与数据建模经验,已承担多领域智能项目,具备从底层到前端的全线设计开发能力。主要研究方向为大数据管理分析与人工智能技术,并以此来促进数据驱动的科学研究。

    目前俱乐部每周六在杭州举办以机器学习为主题的线下技术沙龙活动,不定期进行论文分享与学术交流。希望能汇聚来自各行各业对人工智能感兴趣的朋友,不断交流共同成长,推动人工智能民主化、应用普及化。
    image.png

    展开全文
  • 开源中文语音识别项目介绍:ASRFrame

    千次阅读 多人点赞 2020-01-06 18:53:41
    一个完整的语音识别框架,包括从数据清洗接口,数据读取接口到语音模型、声学模型、到最后的模型整合和UI的一整套流程 目前声学部分拼音识别准确率已经比较高了,但语言模型仍然存在诸多问题需要解决,因此...
  • 广告关闭腾讯云双11爆品提前享,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高满返5000元!接口是 http restful 形式,在使用该接口前,需要在... 接口要求集成实时语音识别 api 时,需按照...
  • 中文语音识别

    千次阅读 2018-05-07 11:12:24
    转载:http://f.dataguru.cn/thread-857009-1-1.html https://blog.csdn.net/u014365862/article/details/53869701使用的数据集THCHS30是Dong Wang, Xuewei Zhang, Zhiyong Zhang这...,可用于开发中文语音识别系统...
  • kaldi上第一个免费的中文语音识别例子

    万次阅读 多人点赞 2016-02-04 12:46:09
    今天在清华大学cslt实验室王东老师的分享下,kaldi终于有了免费的中文语音识别的例子,网址为:https://github.com/kaldi-asr/kaldi/tree/master/egs/thchs30。各位可以根据这个来训练自己的模型。 再次谢谢王东...
  • 中文语音识别pytorch

    万次阅读 2020-04-11 17:35:26
    https://github.com/libai3/masr
  • 用python编写的基于tensorflow平台的中文语音识别以及
  • kaldi中文语音识别

    2020-06-12 14:30:35
    编译与安装kaldi用kaldi进行中文语音识别本文记录了我编译安装kaldi和两种用kaldi进行中文语音识别的过程,包括如何kaldi的简单介绍、环境的配置、相关模型的训练、导入,算是自己近期学习的一个总结,也希望可以...
  • 语音识别基础篇(三) - pocketsphinx在windows下的中文语音识别 如需转载请标明出处:http://blog.csdn.net/itas109 QQ技术交流群:12951803 相关文章 语音识别基础篇(一) - CMU Sphinx简介 语音...
  • TensorFlow练习15: 中文语音识别

    千次阅读 2018-05-10 21:25:55
    语音识别是前文《聊天机器人》必不可少的一个组件,本帖就使用TensorFlow做一个中文语音识别。使用的数据集THCHS30是Dong Wang, Xuewei Zhang, Zhiyong Zhang这几位大神发布的开放语音数据集,可用于开...
  • 基于tensorflow 的中文语音识别模型

    千次阅读 2018-08-03 14:47:38
    目前网上关于tensorflow 的中文语音识别实现较少,而且结构功能较为简单。而百度在PaddlePaddle上的 Deepspeech2 实现功能却很强大,因此就做了一次大自然的搬运工把框架转为tensorflow…. 简介 百度开源的基于...
  • 本文链接:基于Pytorch实现的MASR中文语音识别 MASR是一个基于端到端的深度神经网络的中文普通话语音识别项目,本项目是基于https://github.com/nobody132/masr 进行开发的。 本教程源码地址:...
  • 文章目录搭建基于CBHG的拼音到汉字语言模型1. 数据处理2.... 模型训练及推断3.1 参数设定3.2 模型训练3.3 模型推断 搭建基于CBHG的拼音到汉字语言模型 之前做了个中文识别的系统,很多同学提了些语言模型...
  • 包括【st-cmd、primewords、aishell 、thchs-30、magicdata、aidatatang】六个数据集,共计约【1385】小时 https://blog.csdn.net/qq_28385535/article/details/103644423
  • 1.模型标注数据 模型标注数据文件位于: 其文件中每行代表一条数据,其格式为: wav_data_path \t pinyin_list \t hanzi_list \n 其数据名称和数据量如下: Name total train dev ...thchs-3...
  • 本文搭建一个完整的中文语音识别系统,包括声学模型和语言模型,能够将输入的音频信号识别为汉字。 声学模型使用了应用较为广泛的递归循环网络中的GRU-CTC的组合,除此之外还引入了科大讯飞提出的DFCNN深度全序列...
  • A Deep-Learning-Based Chinese Speech Recognition System 基于深度学习的中文语音识别系统
1 2 3 4 5 ... 20
收藏数 26,510
精华内容 10,604
关键字:

中文语音识别