#简介
主要就是将一段音频转换成文字。下载一段音频和他的标签,使用库提取MFCC特征,得到这个特征输入到LSTM中,每一步输出所有标签的概率,如果使用Greedy Search就直接去最高概率的字符,如果使用Beam Search就。。。

第一步就是下载数据集了,作者使用的是LDC93S1数据集,一个wav的音频,一个txt的标签。其实只有一句话

0 1 She had your dark suit in greasy wash water all year.

前两个数字我也不知道啥意思,反正没有用到。
导入需要的包

from six.moves.urllib.request import urlretrieve
from six.moves import xrange as rangeimport os
import sys
import numpy as np

首先是下载的代码,输入下载的路径url + filename,保存的文件名filename,如果下载到文件大小和实际大小不一致就抛出异常。

url = 'https://catalog.ldc.upenn.edu/desc/addenda/'
last_percent_reported = Nonedef maybe_download(filename, expected_bytes, force=False):"""Download a file if not present, and make sure it's the right size."""if force or not os.path.exists(filename):print('Attempting to download:', filename)filename, _ = urlretrieve(url + filename, filename,reporthook=download_progress_hook)print('\nDownload Complete!')statinfo = os.stat(filename)if statinfo.st_size == expected_bytes: # 尺寸不一致,抛出异常print('Found and verified', filename)else:raise Exception('Failed to verify ' + filename +'. Can you get to it with a browser?')return filename

下面这个是钩子函数,就是下载的时候,打印一些东西,这个就是为了好看

def download_progress_hook(count, blockSize, totalSize):"""A hook to report the progress of a download. This is mostly intended forusers with slow internet connections. Reports every 1% change in downloadprogress."""global last_percent_reportedpercent = int(count * blockSize * 100 / totalSize)if last_percent_reported != percent:if percent % 5 == 0:sys.stdout.write("%s%%" % percent)sys.stdout.flush()else:sys.stdout.write(".")sys.stdout.flush()last_percent_reported = percent

上面两个都没啥用,我就是直接在网上找到这个数据集下载的。

然后需要将字符转换成数字(其实我觉得应该是单词,但是作者转的是字符,其实无所谓的,反正只要提取的特征长度大于转为数字后的标签长度ctc就可以运行,只不过解码出来的东西不一样而已)
ctc要求输入的每个标签中插入blank,例如标签是abc de,变成_a_b_c_d_e_。这里定义blank符号为<SPACE>,blank的编码为0。其他字符按照ASIC来表示。

# Constants
SPACE_TOKEN = '<space>'
SPACE_INDEX = 0
FIRST_INDEX = ord('a') - 1  # 字符进行编码# Some configs
num_features = 13 # 下面使用mfcc提取特征,特征维度固定为13
# Accounting the 0th indice +  space + blank label = 28 characters
num_classes = (ord('z') - ord('a') + 1) + 1 + 1 # 小写字符+空格+blank

接着读取标签,进行转换

# Readings targets
with open(target_filename, 'r') as f:# Only the last line is necessaryline = f.readlines()[-1] # 其实只有一行,假装好多行# Get only the words between [a-z] and replace period for noneoriginal = ' '.join(line.strip().lower().split(' ')[2:]).replace('.', '')targets = original.replace(' ', '  ')targets = targets.split(' ')

一开始怎么都看不懂作者的猴戏操作,

# 前两个是数字,我们不需要,分割的时候直接舍弃,保留后面的文本,
# 去除一行前后的空格换行等,都转成小写,空格分割,'.'舍弃
original = ' '.join(line.strip().lower().split(' ')[2:]).replace('.', '')

下面是猴戏操作

# 将空格换成空格空格,这是为了保留空格,上面说了空格也是标签,需要保存下来,但是以空格切分的时候,空格会被丢掉,
# 我们再添加一个空格使其保存下来
targets = original.replace(' ', '  ')
# 然后以空格切分
targets = targets.split(' ')

现在标签变成了这样

['she', '', 'had', '', 'your', '', 'dark', '', 'suit', '', 'in', '', 'greasy', '', 'wash', '', 'water', '', 'all', '', 'year']

可以看到两个词之间多了一个空字符串,这个就是原来的空格,作者上面的操作真是让我头大啊,说白了就是把空格补成<space>

我们再将所有的字符包括空格变成数字,这个标签就编码好了

# Adding blank label,如果是''就填入<SPACE>
targets = np.hstack([SPACE_TOKEN if x == '' else list(x) for x in targets])# Transform char into index 转换成相应的编码
targets = np.asarray([SPACE_INDEX if x == SPACE_TOKEN else ord(x) - FIRST_INDEXfor x in targets])'''
[19  8  5  0  8  1  4  0 25 15 21 18  0  4  1 18 11  0 19 21  9 20  0  914  0  7 18  5  1 19 25  0 23  1 19  8  0 23  1 20  5 18  0  1 12 12  025  5  1 18]
'''

但是CTC要求输入时稀疏矩阵,因此我们还需要将这个序列变成稀疏矩阵(虽然只有一行数据)。简单说一下稀疏矩阵是什么,假如我们有一个矩阵,

[[0, 1, 0, 0, 2],[1, 4, 0, 0, 0]]

可以看到有很多个0,只有几个是有值的,我们可以将其表示为稀疏矩阵,分为两部分,一个是值的位置,一个值的大小,讲道理应该还有一个shape

indices=[(0,1),(0,4),(1,0),(1,1)]
values = [ 1,    2,    1,    4  ]
shape = (2,5)

然后作者就猴戏了一个矩阵转稀疏矩阵的函数

def sparse_tuple_from(sequences, dtype=np.int32):"""Create a sparse representention of x.Args:sequences: a list of lists of type dtype where each element is a sequenceReturns:A tuple with (indices, values, shape)"""indices = [] # 位置values = [] # 具体的值for n, seq in enumerate(sequences): # sequences是一个二维listindices.extend(zip([n]*len(seq), range(len(seq)))) # 生成所有值的坐标,不管是不是0,都存下来values.extend(seq)indices = np.asarray(indices, dtype=np.int64)values = np.asarray(values, dtype=dtype)shape = np.asarray([len(sequences), np.asarray(indices).max(0)[1]+1], dtype=np.int64) # shape的行就是seqs的个数,列就是最长的那个seq的长度return indices, values, shape

分析一下猴戏操作

indices.extend(zip([n]*len(seq), range(len(seq))))

假如有一个序列为

a = [[1,2,3],[4],[5,6,7],[9,8]]

那么[5,6,7]的位置如何存储呢?这个序列是第2个,所以n=2,所有的值的行都是2开头的,列的位置是依次增加的,所以

[n]*len(seq) = [2,2,2]
range(len(seq)) = [0,1,2]
zip([n]*len(seq), range(len(seq))) = (2,0),(2,1),(2,2)

就是这个意思。看了好久,发现作者把0也存下来了,一度觉得是错的,后来想想,其实稀疏矩阵就是存储形式不一样,没有说就不能存0,只要形式对了就是稀疏矩阵,就可以输入CTC。

标签总算是搞完了,现在可以看看数据是什么样子的,这里是提取mfcc特征

fs, audio = wav.read(audio_filename)inputs = mfcc(audio, samplerate=fs) #[291, 13]
# Tranform in 3D array
train_inputs = np.asarray(inputs[np.newaxis, :]) # 添加一个batch的维度,其实只有一个样本
train_inputs = (train_inputs - np.mean(train_inputs))/np.std(train_inputs) # 标准化
train_seq_len = [train_inputs.shape[1]] # 序列长度'''
shape = (1, 291, 13) # [batch,max_steps,n_features]
'''

接着就是让人喜欢的神经网络搭建了。

graph = tf.Graph()
with graph.as_default():# e.g: log filter bank or MFCC features# Has size [batch_size, max_stepsize, num_features], but the# batch_size and max_stepsize can vary along each stepinputs = tf.placeholder(tf.float32, [None, None, num_features]) # [batch,max_steps,n_features]# Here we use sparse_placeholder that will generate a# SparseTensor required by ctc_loss op.targets = tf.sparse_placeholder(tf.int32) # ctc要求标签是稀疏矩阵# 1d array of size [batch_size]seq_len = tf.placeholder(tf.int32, [None]) # CTC要求输入序列长度,但是dynamic_rnn其实不需要的# Defining the cell# Can be:#   tf.nn.rnn_cell.RNNCell#   tf.nn.rnn_cell.GRUCellcells = []for _ in range(num_layers): # stack lstmcell = tf.nn.rnn_cell.LSTMCell(num_units)cells.append(cell)stack = tf.nn.rnn_cell.MultiRNNCell(cells)# The second output is the last state and we will no use thatoutputs, _ = tf.nn.dynamic_rnn(stack, inputs, seq_len, dtype=tf.float32) # 其实这个seq_len是不需要的,tf会自动补成batch中最长的序列,其实有点奇怪,一个batch中所有的学列长度应该都是一样的,不知道这个参数有什么用shape = tf.shape(inputs)batch_s, max_timesteps = shape[0], shape[1]# Reshaping to apply the same weights over the timestepsoutputs = tf.reshape(outputs, [-1, num_hidden]) # 为了求logits,需要reshape [batch,max_steps,num_hidden]->[batch*max_steps,num_hidden]# Truncated normal with mean 0 and stdev=0.1# Tip: Try another initializationW = tf.Variable(tf.truncated_normal([num_hidden,num_classes],stddev=0.1))# Zero initialization# Tip: Is tf.zeros_initializer the same?b = tf.Variable(tf.constant(0., shape=[num_classes]))# Doing the affine projectionlogits = tf.matmul(outputs, W) + b # logits.shape = [batch*max_step, num_classes]# Reshaping back to the original shapelogits = tf.reshape(logits, [batch_s, -1, num_classes])# logits.shape = [batch, max_step, num_classes]# Time majorlogits = tf.transpose(logits, (1, 0, 2)) #CTC要求logits.shape = [max_step, batch, num_classes]loss = tf.nn.ctc_loss(targets, logits, seq_len)cost = tf.reduce_mean(loss)optimizer = tf.train.MomentumOptimizer(initial_learning_rate,0.9).minimize(cost)# Option 2: tf.nn.ctc_beam_search_decoder# (it's slower but you'll get better results)decoded, log_prob = tf.nn.ctc_greedy_decoder(logits, seq_len)# Inaccuracy: label error rateler = tf.reduce_mean(tf.edit_distance(tf.cast(decoded[0], tf.int32),targets))

上面的结构转换让我困惑了好久,后来我发现了简单的一个方法

graph = tf.Graph()
with graph.as_default():# e.g: log filter bank or MFCC features# Has size [batch_size, max_stepsize, num_features], but the# batch_size and max_stepsize can vary along each stepinputs = tf.placeholder(tf.float32, [None, None, num_features]) # [batch,max_steps,n_features]# Here we use sparse_placeholder that will generate a# SparseTensor required by ctc_loss op.targets = tf.sparse_placeholder(tf.int32) # ctc要求标签是稀疏矩阵# 1d array of size [batch_size]seq_len = tf.placeholder(tf.int32, [None]) # CTC要求输入序列长度,但是dynamic_rnn其实不需要的# Defining the cell# Can be:#   tf.nn.rnn_cell.RNNCell#   tf.nn.rnn_cell.GRUCellcells = []for _ in range(num_layers): # stack lstmcell = tf.nn.rnn_cell.LSTMCell(num_units)cells.append(cell)stack = tf.nn.rnn_cell.MultiRNNCell(cells)# The second output is the last state and we will no use thatoutputs, _ = tf.nn.dynamic_rnn(stack, inputs, seq_len, dtype=tf.float32) # 其实这个seq_len是不需要的,tf会自动补成batch中最长的序列,其实有点奇怪,一个batch中所有的学列长度应该都是一样的,不知道这个参数有什么用logits = tf.layers.dense(outputs, num_classes)    # 这里是修改的地方,作者说这总方法虽然方便但是更慢,我是不知道为什么可以运行,outputs竟然不需要flatten。。。。。。。,反正我不用这种方法# Time majorlogits = tf.transpose(logits, (1, 0, 2)) #CTC要求logits.shape = [max_step, batch, num_classes]loss = tf.nn.ctc_loss(targets, logits, seq_len)cost = tf.reduce_mean(loss)optimizer = tf.train.MomentumOptimizer(initial_learning_rate,0.9).minimize(cost)# Option 2: tf.nn.ctc_beam_search_decoder# (it's slower but you'll get better results) 但是我测试的这个例子greedy比beam要好decoded, log_prob = tf.nn.ctc_greedy_decoder(logits, seq_len)# Inaccuracy: label error rateler = tf.reduce_mean(tf.edit_distance(tf.cast(decoded[0], tf.int32),targets))

下面是输入

with tf.Session(graph=graph) as session:# Initializate the weights and biasestf.global_variables_initializer().run()for curr_epoch in range(num_epochs):train_cost = train_ler = 0start = time.time()for batch in range(num_batches_per_epoch):feed = {inputs: train_inputs,targets: train_targets,seq_len: train_seq_len}batch_cost, _ = session.run([cost, optimizer], feed)train_cost += batch_cost*batch_sizetrain_ler += session.run(ler, feed_dict=feed)*batch_sizetrain_cost /= num_examplestrain_ler /= num_examplesval_feed = {inputs: val_inputs,targets: val_targets,seq_len: val_seq_len}val_cost, val_ler = session.run([cost, ler], feed_dict=val_feed)log = "Epoch {}/{}, train_cost = {:.3f}, train_ler = {:.3f}, val_cost = {:.3f}, val_ler = {:.3f}, time = {:.3f}"print(log.format(curr_epoch+1, num_epochs, train_cost, train_ler,val_cost, val_ler, time.time() - start))# Decodingd = session.run(decoded[0], feed_dict=feed)str_decoded = ''.join([chr(x) for x in np.asarray(d[1]) + FIRST_INDEX])# Replacing blank label to nonestr_decoded = str_decoded.replace(chr(ord('z') + 1), '')# Replacing space label to spacestr_decoded = str_decoded.replace(chr(ord('a') - 1), ' ')print('Original:\n%s' % original)print('Decoded:\n%s' % str_decoded)

CTC函数的介绍

tf.nn.ctc_loss(
labels,
inputs,
sequence_length,
preprocess_collapse_repeated=False,
ctc_merge_repeated=True,
ignore_longer_outputs_than_inputs=False,
time_major=True
)

labels:这是一个SparseTensor,所以我们需要先将我们的标签改为稀疏张量。tfrecords返回的就是SparseTensor,我们不需要改变,如果我们直接使用标签,需要自己转换。

inputs:这个就是logits(不是概率)。time_major == False; Tensor shaped: [batch_size, max_time, num_classes]. If time_major == True (default): [max_time, batch_size, num_classes].

sequence_length:这是一个长度为batch_size的向量,向量里的每一个值代表batch中每个序列的长度。

time_major:注意默认是True,一定要好好修改。


ctc_tensorflow_example

tensorflow代码学习:CTC 代码解析相关推荐

  1. 【神经网络】(19) ConvNeXt 代码复现,网络解析,附Tensorflow完整代码

    各位同学好,今天和大家分享一下如何使用 Tensorflow 构建 ConvNeXt 卷积神经网络模型. 论文地址:https://arxiv.org/pdf/2201.03545.pdf 完整代码在 ...

  2. 超详细解读:神经语义解析的结构化表示学习 | 附代码分析

    在碎片化阅读充斥眼球的时代,越来越少的人会去关注每篇论文背后的探索和思考. 在这个栏目里,你会快速 get 每篇精选论文的亮点和痛点,时刻紧跟 AI 前沿成果. 点击本文底部的「阅读原文」即刻加入社区 ...

  3. Selenium学习 - 库代码解析

    Selenium学习 - 库代码解析 一.selenium/common exceptions.py 定义了一个继承自Exception类的WebDriverException基础异常类,然后通过它扩 ...

  4. tensorflow 语义slam_研究《视觉SLAM十四讲从理论到实践第2版》PDF代码+《OpenCV+TensorFlow深度学习与计算机视觉实战》PDF代码笔记...

    我们知道随着人工神经网络和深度学习的发展,通过模拟视觉所构建的卷积神经网络模型在图像识别和分类上取得了非常好的效果,借助于深度学习技术的发展,使用人工智能去处理常规劳动,理解语音语义,帮助医学诊断和支 ...

  5. slim php dd model,第二十四节,TensorFlow下slim库函数的使用以及使用VGG网络进行预训练、迁移学习(附代码)...

    在介绍这一节之前,需要你对slim模型库有一些基本了解,具体可以参考第二十二节,TensorFlow中的图片分类模型库slim的使用.数据集处理,这一节我们会详细介绍slim模型库下面的一些函数的使用 ...

  6. 【神经网络】(13) ShuffleNetV2 代码复现,网络解析,附Tensorflow完整代码

    各位同学好,今天和大家分享一下如何使用 Tensorflow 复现轻量化神经网络 ShuffleNetV2. 为了能将神经网络模型用于移动端(手机)和终端(安防监控.无人驾驶)的实时计算,通常这些设备 ...

  7. beego学习与代码示例WebIM解析-Ali0th

    Author : Ali0th Date : 2019-4-26 安装 go get github.com/astaxie/beego # beego go install # 在beego目录下安装 ...

  8. 大创学习记录(四)之yolov3代码学习

    PyTorch-YOLOv3项目训练与代码学习 借助从零开始的PyTorch项目理解YOLOv3目标检测的实现 PyTorch 对于PyTorch就不用多说了,目前最灵活.最容易掌握的深度学习库,它有 ...

  9. 超好用的自信学习:1行代码查找标签错误,3行代码学习噪声标签

    十三 发自 凹非寺 量子位 报道 | 公众号 QbitAI 你知道吗?就连ImageNet中也可能至少存在10万个标签问题. 在大量的数据集中去描述或查找标签错误本身就是挑战性超高的任务,多少英雄豪杰 ...

  10. 第一行代码学习笔记第九章——使用网络技术

    知识点目录 9.1 WebView的用法 9.2 使用HTTP协议访问网络 * 9.2.1 使用HttpURLConnection * 9.2.2 使用OkHttp 9.3 解析XML格式数据 * 9 ...

最新文章

  1. javascript基础拾遗——词法作用域
  2. Java中最早期的集合Vector
  3. Boost:自动索引允许的选项
  4. leetcode-13-罗马数字转整数
  5. python实时连接oracle_Python连接Oracle
  6. 百度首席科学家 Andrew Ng谈深度学习的挑战和未来
  7. 以TikTok为切入的海外流量打法?
  8. Ref_cursor
  9. springmvc网页找不到404_eclipse新建WebProject访问主页404错误的解决
  10. Nginx怎么打开目录浏览功能
  11. NYOJ954--N的阶乘的二进制表示最低位的1的位置
  12. 从东南亚到中东,为什么社交类产品成为游戏出海的突破口?
  13. Aspose Word模板使用总结
  14. centos7更换yum仓库、静态IP、SSH密钥登陆
  15. html页面 添加搜索关键词,如何为自己的网站添加关键字与描述详解
  16. Java修行——DAY12
  17. 留美CS学习的第一学期总结
  18. 一级建造师可以跨省考试吗?
  19. 对TS流的一些理解TS流的结构
  20. springboot整合redis缓存报错

热门文章

  1. unity C#脚本介绍
  2. 上皮细胞膜纳米囊泡包裹药物如紫杉醇,喜树碱,阿霉素
  3. Thunder团队第三周 - Scrum会议1
  4. MDK_EventRecorder
  5. 柠檬桉叶油和deet_关于驱蚊防蚊知识及方式手段选择,你想知道的这里都有
  6. ccsp2018游记
  7. 2021-11-23 WPF上位机 96-Modbus通信代码的封装
  8. v380云存储怎么查看_v380的云存储
  9. go-micro 在linux下安装出现service auth not found
  10. WindowsAPI 窗口