0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何利用Tensorflow编写一个基本的端到端自动语音识别

倩倩 来源:lq 作者:读芯术 2019-10-09 15:08 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

本文阐述了如何利用Tensorflow编写一个基本的端到端自动语音识别(Automatic Speech Recognition,ASR)系统,详细介绍了最小神经网络的各个组成部分以及可将音频转为可读文本的前缀束搜索解码器。

虽然当下关于如何搭建基础机器学习系统的文献或资料有很多,但是大部分都是围绕计算机视觉和自然语言处理展开的,极少有文章就语音识别展开介绍。本文旨在填补这一空缺,帮助初学者降低入门难度,提高学习自信。

前提

初学者需要熟练掌握:

· 神经网络的组成

· 如何训练神经网络

· 如何利用语言模型求得词序的概率

概述

· 音频预处理:将原始音频转换为可用作神经网络输入的数据

· 神经网络:搭建一个简单的神经网络,用于将音频特征转换为文本中可能出现的字符的概率分布

· CTC损失:计算不使用相应字符标注音频时间步长的损失

· 解码:利用前缀束搜索和语言模型,根据各个时间步长的概率分布生成文本

本文重点讲解了神经网络、CTC损失和解码。

音频预处理

搭建语音识别系统,首先需要将音频转换为特征矩阵,并输入到神经网络中。完成这一步的简单方法就是创建频谱图。

def create_spectrogram(signals):

stfts = tf.signal.stft(signals, fft_length=256)

spectrograms = tf.math.pow(tf.abs(stfts), 0.5)

return spectrograms

这一方法会计算出音频信号的短时傅里叶变换(Short-time Fourier Transform)以及功率谱,其最终输出可直接用作神经网络输入的频谱图矩阵。其他方法包括滤波器组和MFCC(Mel频率倒谱系数)等。

了解更多音频预处理知识:https://haythamfayek.com/2016/04/21/speech-processing-for-machine-learning.html

神经网络

下图展现了一个简单的神经网络结构。

语音识别基本结构

频谱图输入可以看作是每个时间步长的向量。1D卷积层从各个向量中提取出特征,形成特征向量序列,并输入LSTM层进一步处理。LSTM层(或双LSTM层)的输入则传递至全连接层。利用softmax激活函数,可得出每个时间步长的字符概率分布。整个网络将会用CTC损失函数进行训练(CTC即Connectionist Temporal Classification,是一种时序分类算法)。熟悉整个建模流程后可尝试使用更复杂的模型。

class ASR(tf.keras.Model):

def __init__(self, filters, kernel_size, conv_stride, conv_border, n_lstm_units, n_dense_units):

super(ASR, self).__init__()

self.conv_layer = tf.keras.layers.Conv1D(filters,

kernel_size,

strides=conv_stride,

padding=conv_border,

activation=‘relu’)

self.lstm_layer = tf.keras.layers.LSTM(n_lstm_units,

return_sequences=True,

activation=‘tanh’)

self.lstm_layer_back = tf.keras.layers.LSTM(n_lstm_units,

return_sequences=True,

go_backwards=True,

activation=‘tanh’)

self.blstm_layer = tf.keras.layers.Bidirectional(self.lstm_layer, backward_layer=self.lstm_layer_back)

self.dense_layer = tf.keras.layers.Dense(n_dense_units)

def call(self, x):

x = self.conv_layer(x)

x = self.blstm_layer(x)

x = self.dense_layer(x)

return x

为什么使用CTC呢?搭建神经网络旨在预测每个时间步长的字符。然而现有的标签并不是各个时间步长的字符,仅仅是音频的转换文本。而文本的各个字符可能横跨多个步长。如果对音频的各个时间步长进行标记,C-A-T就会变成C-C-C-A-A-T-T。而每隔一段时间,如10毫秒,对音频数据集进行标注,并不是一个切实可行的方法。CTC则解决上了上述问题。CTC并不需要标记每个时间步长。它忽略了文本中每个字符的位置和实际相位差,把神经网络的整个概率矩阵输入和相应的文本作为输入。

CTC 损失计算

输出矩阵示例

假设真实的数据标签为CAT,在四个时间步长中,有序列C-C-A-T,C-A-A-T,C-A-T-T,_-C-A-T,C-A-T-_与真实数据相对应。将这些序列的概率相加,可得到真实数据的概率。根据输出的概率矩阵,将序列的各个字符的概率相乘,可得到单个序列的概率。则上述序列的总概率为0.0288+0.0144+0.0036+0.0576+0.0012=0.1056。CTC损失则为该概率的负对数。Tensorflow自带损失函数文件。

解码

由上文的神经网络,可输出一个CTC矩阵。这一矩阵给出了各个时间步长中每个字符在其字符集中的概率。利用前缀束搜索,可从CTC矩阵中得出所需的文本。

除了字母和空格符,CTC矩阵的字符集还包括两种特别的标记(token,也称为令牌)——空白标记和字符串结束标记。

空白标记的作用:CTC矩阵中的时间步长通常比较小,如10毫秒。因此,句子中的一个字符会横跨多个时间步长。如,C-A-T会变成C-C-C-A-A-T-T。所以,需要将CTC矩阵中出现该问题的字符串中的重复部分折叠,消除重复。那么像FUNNY这种本来就有两个重复字符(N)的词要怎么办呢?在这种情况下,就可以使用空白标记,将其插入两个N中间,就可以防止N被折叠。而这么做实际上并没有在文本中添加任何东西,也就不会影响其内容或形式。因此,F-F-U-N-[空白]-N-N-Y最终会变成FUNNY。

结束标记的作用:字符串的结束表示着一句话的结束。对字符串结束标记后的时间步长进行解码不会给候选字符串增加任何内容。

步骤

初始化

· 准备一个初始列表。列表包括多个候选字符串,一个空白字符串,以及各个字符串在不同时间步长以空白标记结束的概率,和以非空白标记结束的概率。在时刻0,空白字符串以空白标记结束的概率为1,以非空白标记结束的概率则为0。

迭代

· 选择一个候选字符串,将字符一个一个添加进去。计算拓展后的字符串在时刻1以空白标记和非空白标记结束的概率。将拓展字符串及其概率记录到列表中。将拓展字符串作为新的候选字符串,在下一时刻重复上述步骤。

· 情况A:如果添加的字符是空白标记,则保持候选字符串不变。

· 情况B:如果添加的字符是空格符,则根据语言模型将概率与和候选字符串的概率成比例的数字相乘。这一步可以防止错误拼写变成最佳候选字符串。如,避免COOL被拼成KUL输出。

· 情况C:如果添加的字符和候选字符串的最后一个字符相同,(以候选字符串FUN和字符N为例),则生成两个新的候选字符串,FUNN和FUN。生成FUN的概率取决于FUN以空白标记结束的概率。生成FUNN的概率则取决于FUN以非空白标记结束的概率。因此,如果FUN以非空白标记结束,则去除额外的字符N。

输出

经过所有时间步长迭代得出的最佳候选字符串就是输出。

为了加快这一过程,可作出如下两个修改。

1.在每一个时间步长,去除其他字符串,仅留下最佳的K个候选字符串。具体操作为:根据字符串以空白和非空白标记结束的概率之和,对候选字符串进行分类。

2.去除矩阵中概率之和低于某个阈值(如0.001)的字符。

具体操作细节可参考如下代码。

def prefix_beam_search(ctc,

alphabet,

blank_token,

end_token,

space_token,

lm,

k=25,

alpha=0.30,

beta=5,

prune=0.001):

‘’‘

function to perform prefix beam search on output ctc matrix and return the best string

:param ctc: output matrix

:param alphabet: list of strings in the order their probabilties are present in ctc output

:param blank_token: string representing blank token

:param end_token: string representing end token

:param space_token: string representing space token

:param lm: function to calculate language model probability of given string

:param k: threshold for selecting the k best prefixes at each timestep

:param alpha: language model weight (b/w 0 and 1)

:param beta: language model compensation (should be proportional to alpha)

:param prune: threshold on the output matrix probability of a character.

If the probability of a character is less than this threshold, we do not extend the prefix with it

:return: best string

’‘’

zero_pad = np.zeros((ctc.shape[0]+1,ctc.shape[1]))

zero_pad[1:,:] = ctc

ctc = zero_pad

total_timesteps = ctc.shape[0]

# #### Initialization ####

null_token = ‘’

Pb, Pnb = Cache(), Cache()

Pb.add(0,null_token,1)

Pnb.add(0,null_token,0)

prefix_list = [null_token]

# #### Iterations ####

for timestep in range(1, total_timesteps):

pruned_alphabet = [alphabet[i] for i in np.where(ctc[timestep] 》 prune)[0]]

for prefix in prefix_list:

if len(prefix) 》 0 and prefix[-1] == end_token:

Pb.add(timestep,prefix,Pb.get(timestep - 1,prefix))

Pnb.add(timestep,prefix,Pnb.get(timestep - 1,prefix))

continue

for character in pruned_alphabet:

character_index = alphabet.index(character)

# #### Iterations : Case A ####

if character == blank_token:

value = Pb.get(timestep,prefix) + ctc[timestep][character_index] * (Pb.get(timestep - 1,prefix) + Pnb.get(timestep - 1,prefix))

Pb.add(timestep,prefix,value)

else:

prefix_extended = prefix + character

# #### Iterations : Case C ####

if len(prefix) 》 0 and character == prefix[-1]:

value = Pnb.get(timestep,prefix_extended) + ctc[timestep][character_index] * Pb.get(timestep-1,prefix)

Pnb.add(timestep,prefix_extended,value)

value = Pnb.get(timestep,prefix) + ctc[timestep][character_index] * Pnb.get(timestep-1,prefix)

Pnb.add(timestep,prefix,value)

# #### Iterations : Case B ####

elif len(prefix.replace(space_token, ‘’)) 》 0 and character in (space_token, end_token):

lm_prob = lm(prefix_extended.strip(space_token + end_token)) ** alpha

value = Pnb.get(timestep,prefix_extended) + lm_prob * ctc[timestep][character_index] * (Pb.get(timestep-1,prefix) + Pnb.get(timestep-1,prefix))

Pnb.add(timestep,prefix_extended,value)

else:

value = Pnb.get(timestep,prefix_extended) + ctc[timestep][character_index] * (Pb.get(timestep-1,prefix) + Pnb.get(timestep-1,prefix))

Pnb.add(timestep,prefix_extended,value)

if prefix_extended not in prefix_list:

value = Pb.get(timestep,prefix_extended) + ctc[timestep][-1] * (Pb.get(timestep-1,prefix_extended) + Pnb.get(timestep-1,prefix_extended))

Pb.add(timestep,prefix_extended,value)

value = Pnb.get(timestep,prefix_extended) + ctc[timestep][character_index] * Pnb.get(timestep-1,prefix_extended)

Pnb.add(timestep,prefix_extended,value)

prefix_list = get_k_most_probable_prefixes(Pb,Pnb,timestep,k,beta)

# #### Output ####

return prefix_list[0].strip(end_token)

这样,一个基础的语音识别系统就完成了。对上述步骤进行复杂化,可以得到更优的结果,如,搭建更大的神经网络和利用音频预处理技巧。

完整代码:https://github.com/apoorvnandan/speech-recognition-primer

注意事项:

1. 文中代码使用的是TensorFlow2.0系统,举例使用的音频文件选自LibriSpeech数据库(http://www.openslr.org/12)。

2. 文中代码并不包括训练音频数据集的批量处理生成器。读者需要自己编写。

3. 读者亦需自己编写解码部分的语言模型函数。最简单的方法就是基于语料库生成一部二元语法字典并计算字符概率。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 语音识别
    +关注

    关注

    39

    文章

    1804

    浏览量

    115579
  • 机器学习
    +关注

    关注

    66

    文章

    8541

    浏览量

    136236
  • tensorflow
    +关注

    关注

    13

    文章

    332

    浏览量

    61858
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    如何训练好自动驾驶模型?

    [首发于智驾最前沿微信公众号]最近有位小伙伴在后台留言提问:算法是怎样训练的?是模仿学习、强化学习和离线强化学习这三类吗?其实
    的头像 发表于 12-08 16:31 955次阅读
    如何训练好<b class='flag-5'>自动</b>驾驶<b class='flag-5'>端</b><b class='flag-5'>到</b><b class='flag-5'>端</b>模型?

    自动驾驶中仿真与基于规则的仿真有什么区别?

    自动驾驶领域,“仿真”指的是将感知控制的整个决策链条视为
    的头像 发表于 11-02 11:33 1441次阅读

    自动驾驶中“段式”和“二段式”有什么区别?

    [首发于智驾最前沿微信公众号]最近有小伙伴提问,段式和二段式
    的头像 发表于 10-24 09:03 641次阅读
    <b class='flag-5'>自动</b>驾驶中“<b class='flag-5'>一</b>段式<b class='flag-5'>端</b><b class='flag-5'>到</b><b class='flag-5'>端</b>”和“二段式<b class='flag-5'>端</b><b class='flag-5'>到</b><b class='flag-5'>端</b>”有什么区别?

    自动驾驶中常提的段式(单段)啥?

    自动驾驶技术的发展,催生出技术的应用,段式
    的头像 发表于 10-18 10:16 1392次阅读

    文读懂特斯拉自动驾驶FSD从辅助的演进

    [首发于智驾最前沿微信公众号]自动驾驶行业发展至今,特斯拉直被很多企业对标,其FSD系统的每次更新,都会获得非常多人的关注。早期自动驾驶是
    的头像 发表于 10-11 09:13 397次阅读
    <b class='flag-5'>一</b>文读懂特斯拉<b class='flag-5'>自动</b>驾驶FSD从辅助<b class='flag-5'>到</b><b class='flag-5'>端</b><b class='flag-5'>到</b><b class='flag-5'>端</b>的演进

    自动驾驶大模型为什么会有不确定性?

    。为了能让自动驾驶汽车做出正确、安全且符合逻辑的行驶动作,大模型被提了出来。
    的头像 发表于 09-28 09:20 557次阅读
    <b class='flag-5'>自动</b>驾驶<b class='flag-5'>端</b><b class='flag-5'>到</b><b class='flag-5'>端</b>大模型为什么会有不确定性?

    语音交互数据 精准赋能语音大模型进阶

    模型在多轮对话、噪声环境及语义理解方面表现不佳。 相较于传统数据集仅关注语音-文本的单点转写,语音交互数据集强调在真实多轮对话场景下的
    的头像 发表于 09-11 17:17 581次阅读

    广和通发布自研语音识别大模型FiboASR

    7月,全球领先的无线通信模组及AI解决方案提供商广和通,发布其自主研发的语音识别大模型FiboASR。该模型专为侧设备上面临的面对面实时对话及多人会议场景深度优化,在低延迟语音交互、
    的头像 发表于 08-04 11:43 1381次阅读

    Nullmax自动驾驶最新研究成果入选ICCV 2025

    Nullmax 在段式核心技术上的深厚积累与创新实力,尤其在
    的头像 发表于 07-05 15:40 1571次阅读
    Nullmax<b class='flag-5'>端</b><b class='flag-5'>到</b><b class='flag-5'>端</b><b class='flag-5'>自动</b>驾驶最新研究成果入选ICCV 2025

    为什么自动驾驶大模型有黑盒特性?

    、激光雷达数据)映射到控制输出(如方向盘转角、加速度、制动等),以深度神经网络为核心,打通了从视觉驾驶行为的完整链条。它也代表了自动驾驶从“规则驱动”向“数据驱动”的跃迁,展现了极高的潜力。当然这种结构也天然地带来了
    的头像 发表于 07-04 16:50 599次阅读
    为什么<b class='flag-5'>自动</b>驾驶<b class='flag-5'>端</b><b class='flag-5'>到</b><b class='flag-5'>端</b>大模型有黑盒特性?

    文带你厘清自动驾驶架构差异

    [首发于智驾最前沿微信公众号]随着自动驾驶技术飞速发展,智能驾驶系统的设计思路也经历了从传统模块化架构大模型转变。传统模块化架构将感
    的头像 发表于 05-08 09:07 794次阅读
    <b class='flag-5'>一</b>文带你厘清<b class='flag-5'>自动</b>驾驶<b class='flag-5'>端</b><b class='flag-5'>到</b><b class='flag-5'>端</b>架构差异

    自动驾驶技术研究与分析

    编者语:「智驾最前沿」微信公众号后台回复:C-0450,获取本文参考报告:《自动驾驶行业研究报告》pdf下载方式。 自动驾驶进入202
    的头像 发表于 12-19 13:07 1524次阅读

    自动泊车的应用

    要做到15Hz以上。这样就对存储和算力需求降低很多。 上海交通大学的五位学生发表了自动泊车的论文:《ParkingE2E: Cam
    的头像 发表于 12-18 11:38 1495次阅读
    <b class='flag-5'>端</b><b class='flag-5'>到</b><b class='flag-5'>端</b>在<b class='flag-5'>自动</b>泊车的应用

    阶跃星辰发布国内首个千亿参数语音大模型

    近日,阶跃星辰在官方公众号上宣布了项重大突破——推出Step-1o千亿参数语音大模型。该模型被誉为“国内首个千亿参数
    的头像 发表于 12-17 13:43 1066次阅读

    准确性超Moshi和GLM-4-Voice,语音双工模型Freeze-Omni

    GPT-4o 提供的全双工语音对话带来了股研究热潮,目前诸多工作开始研究如何利用 LLM 来实现
    的头像 发表于 12-17 10:21 1327次阅读
    准确性超Moshi和GLM-4-Voice,<b class='flag-5'>端</b><b class='flag-5'>到</b><b class='flag-5'>端</b><b class='flag-5'>语音</b>双工模型Freeze-Omni