一种pytorch端到端中文语音识别项目思路-基于deepspeech.pytorch(一)

语音识别.jpg

小编这一段时间研究端到端的实现中文语音的识别,项目主体代码使用了https://github.com/SeanNaren/deepspeech.pytorch/
的方案,不同的是这个模型主要为英文设计,在中文识别上可能需要做出一些变化,不仅涉及到数据集语料库,还涉及到部分业务逻辑的修改,下面根据数据结构的变化为时间线详细说一下。
整理好了思路和模块之间耦合的细节,按照这个思路自己去实现一套语音识别项目也是可以的。
这个项目在github上面版本更新比较频繁,优化的地方也有很多,2020年初到现在配置方式从传统的

argparse.ArgumentParser()

变成了独立的配置模块,不得不说相当优雅了,松耦合使得代码的调试过程避免了很多bug。
说一下整体的思路吧:、
一般语音识别工作相当于是把“语音特征数据”和“文字、词”关联起来了,需要我们的模型认识每一个字,这些字组合起来还要像是一句话,第一种方案:声学模型+语言模型,声学模型做的关联是“特征数据”---“音素”,语言模型做的是“音素”---“句子”,语言模型让这些识别结果看起来更像人话;第二种方案:端到端,也就是“特征数据”---“一句话”,这一般需要编解码工具的辅助,本项目中涉及到的有greedyDecoder和beamDecoder,beamDecoder中可以加入语言模型的辅助,项目支持kenlm类型的语言模型(实际上他并不算是一种语言模型,他把N-gram语言模型进行了包装,只不过是添加了更强的搜索算法,可以提高项目的效率)
1、关于语音识别工作,数据始终是我们不能忽略的,这是我们设计模型结构和业务逻辑的重点。在语音识别的任务中,第一步就是提取特征,什么是提取特征呢?就是把原始的音频文件比如wav转换成矩阵数据,一般数据的size大小和音频的长度近乎成正比。常用的特征提取方法是:MFCC、FBANK、LPC,这个项目中使用的方法类似于fbank,具体的实现代码如下:

import librosa
import soundfile as sf
import numpy as np
def load_audio(path):
    sound, sample_rate = sf.read(path, dtype='int16')
    sound = sound.astype('float32') / 32767  # normalize audio
    if len(sound.shape) > 1:
        if sound.shape[1] == 1:
            sound = sound.squeeze()
        else:
            sound = sound.mean(axis=1)  # multiple channels, average
    return sound

audio_path = 'wav音频路径' 
y = load_audio(audio_path)
n_fft = int(16000*.02) # 16000是音频的频率,02是傅里叶变换的窗口
win_length = n_fft
hop_length = int(16000*.01)
# STFT
D = librosa.stft(y, n_fft=n_fft, hop_length=hop_length,
                    win_length=win_length, window='hamming')
spect, phase = librosa.magphase(D)
spect = np.log1p(spect)

spect 就是最后提取的数据特征了,可以直接作为一个item输入到model中。
我用一个3s的wav文件得到的数据size是(161,844),这里161是不会变的,更长的音频会使得844这个位置的维度变大,这和其他的特征提取方法可能不太一样,我们这里就使用161*1000来举栗子吧。
这里要提到的一点是一般训练的数据都会采用短音频控制在10s之内是最好,过长的音频会挑战机器的内存,有可能会爆掉。
2、看一下项目的配置参数,刚开始接触深度学习的项目,参数有很多,这个项目里面把参数分成了两种:训练参数、推理参数,其实我们并不知道这些参数是干嘛的,下面分析一下吧
训练参数:
(1)epochs,我们输入的数据集里面有很多条数据,一条(item)这里就是一个短音频对应的数据特征,一个epoch就相当于把所有的数据(按照一定的排列方式和采样方式)从模型里面训练过了一遍,有多少个epoch就过了多少遍,一般的可以设置成30,50,70,100,看数据量大小,也看模型自己收敛不收敛,这个可以在train的过程中判断,如果发现模型已经长时间没有准确率上的提高,就可以设置停止当前epoch的训练;
(2)seed,在训练的过程中数据的排列和生成涉及到很多随机的过程,如果先设置了seed,就会让每一次随机到的内容都一样,就会让每次训练的结果都一样,可以根据自己的需要选择,他真的影响的代码是下面这一句:

    # Set seeds for determinism
    torch.manual_seed(cfg.training.seed)
    torch.cuda.manual_seed_all(cfg.training.seed)
    np.random.seed(cfg.training.seed)
    random.seed(cfg.training.seed)

这些在训练的最开始就要设置好
(3)batch_size模型的训练是在一个循环中,代码大概是下面这样的

for epoch in range(epoch):
    for batch_data in train_dataset:
        output = model(batch_data)

看代码的时候可以先抓住这几句重要的逻辑,其他的代码多数是在处理数据的格式和形状,在模型应用(也可以叫推理)的过程中一般是一个item为单位进行预测的,但是训练的过程中并不是(这么多数据要循环到什么时候去),这里输入数据的形状是batch_size(这里选16吧,比较常见,太大也会爆炸)×1×161×1000,这里的第二维度加了1(一般的彩色图像是三维的,也就是三层二维矩阵,相当于一层图像数据,因为后面要做CNN卷积,需要一个平面的多通道的数据)
(4)train_manifest val_manifest
这些叫做清单文件,也可以说成数据集的一种形式,是csv文件,每一行有两项数据,第一个是wav文件的地址,第二项是对应语音内容的地址一般为txt或者trn格式
(5)window_size window_stride
这里说的是我们1、中提到的语音特征提取步骤的窗口大小,按照默认的.02就挺好,一般在数据处理的时候窗口尺寸和窗口重叠是成对出现的,因为两个窗口关联性也就是重叠的区域大小一般可以为窗口建立关联性,可以提高数据分析的连续性,避免窗口边缘的数据和下一个窗口的数据产生关联,而我们设置的窗口割裂了他们的关系。
(6)no_cuda,不适用cuda也就是GPU啦,这一般是False,GPU又好又快,有条件的还是用一下了,不过在数据输入到模型之前会需要把数据和定义好的model也转化成gpu类型的,比如在torch里面这样:

device = torch.device("cuda")
inputs = inputs.to(device)
model = model.to(device)

(7)hidden_layers,hidden_size,这两个参数都是跟模型的形状相关的,不过不影响,不直接影响我们的数据,设置得合理一点就可以了,先介绍一下模型的内部构造吧:
CNN(卷积层)--RNN(lstm循环神经网络)--LINEAR(全连接层,也叫做线性层)--SOFTMAX
我们这个hidden_size出现在RNN和LINEAR之间,最后会被乘积的操作消掉,这里设置的1024,我觉得还可以。hideen_layer指的是RNN有几层,一般层数多一点5,8,10会让RNN的表现更好。
(8)learning_anneal在训练的过程中,每一个epoch都会调整自己的参数以及学习率,预测结果和目标结果(也就是label)发生同样的偏差的时候,学习率越高,网络参数的调整幅度就越大。这个是在模型发现自己矫枉过正的时候,就会衰减一下自己的学习率

新的学习率=旧的学习率/learning_anneal
lr = lr/ learning_anneal

(9)optim优化器的类型,是可以自己选的,我这里选择了Adam,优化器综合学习率、网络模型参数、反向传播(修正网络模型参数的方法)权重衰减的数据对网络模型的参数进行优化
优化器等的使用过程一般是这样的(pytorch中)

# 训练前
optim = torch.optim.Adam(model.paramters(), lr=lr)
# 训练中 epoch内
output = model(input)
loss = f(output, target)
optim.zero_grad()
loss.backward()
optim.step()

(10)损失函数的定义,CTCLoss
顺便说一下损失函数,这里也涉及到数据尺寸的分析。损失函数使用了CTCLoss,在将训练数据输入到模型中时,仅把label数据(也就是文本内容的变体)处理到字符的index形式即可,同时,每一个句子文本的长度都不一样,所以一个batch中的数据shape可能不一致,比如在deepspeech项目train.py中,下面这个具体的情况:

inputs = inputs.to(device) 
out, output_sizes = model(inputs, input_sizes)
out = out.transpose(0, 1)  # TxNxH
float_out = out.float()  # ensure float32 for loss
            
loss = criterion(float_out, targets, output_sizes, target_sizes).to(device)
loss = loss / inputs.size(0)  # average the loss by minibatch

float_out: torch.Size([426, 16, 8679]), '426'不定,每句话都不一样
targets: torch.Size([528]), '528'不定,每句话都不一样
训练数据的targets并没有转化成[0,0,0,1,0,0,0...,0]这样的形式,仍然是字符索引的int
targets是把一个batch的索引都拼在了一起,形成一个长的List
targets格式形如: [s1c1,s1c2,s1c3,...,s1c10,s2c1,s2c2,...,s16c13],s是句子c是句子中字符
在数据shape不同的情况下直接交给 CTCLoss() 损失函数处理

这篇先分析到这里,下一篇继续吧❥(^_-)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335