基于TF的简易关键字语音识别

news2024/11/24 8:50:42

⚠申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计10182字,阅读大概需要10分钟
🌈更多学习内容, 欢迎👏关注👀【文末】我的个人微信公众号:不懂开发的程序猿
个人网站:https://jerry-jy.co/

❗❗❗知识付费,🈲止白嫖,有需要请后台私信或【文末】个人微信公众号联系我

基于TF的简易关键字语音识别

  • 基于TF的简易关键字语音识别
    • 一、任务需求
    • 二、任务目标
          • 1、掌握TensorFlow操作方法
          • 2、掌握频谱图
          • 3、掌握模型构建、训练、预测、评估方法
    • 三、任务环境
          • 1、jupyter开发环境
          • 2、python3.6
          • 3、tensorflow2.4
    • 四、任务实施过程
      • 1、导入工具
      • 2、导入语音命令数据集
      • 3、读取音频文件及其标签
      • 4、频谱图
      • 5、构建和训练模型
      • 6、评估测试集性能
      • 7、显示混淆矩阵
      • 8、对音频文件运行推理
    • 五、任务小结
  • 说明

基于TF的简易关键字语音识别


一、任务需求

本实验主要展示如何构建一个能够识别十个不同单词的基本语音识别网络。当然,真实的语音和音频识别系统要复杂得多,但就像图像的 MNIST 一样,本实验主要目的是对所语音识别涉及的技术有基本的了解。

完成本教程后,我们将拥有一个可以识别简单语音命令的模型,该模型尝试将一秒钟的音频剪辑分类为“向下”、“前进”、“向左”、“否”、“向右”、“停止”、“向上” “ 是的”。

要求:利用TensorFlow生成简易关键字语音识别模型

二、任务目标

1、掌握TensorFlow操作方法
2、掌握频谱图
3、掌握模型构建、训练、预测、评估方法

三、任务环境

1、jupyter开发环境
2、python3.6
3、tensorflow2.4

四、任务实施过程

1、导入工具

导入必要的模块和依赖项

import os
import pathlib

import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import tensorflow as tf

from tensorflow.keras.layers.experimental import preprocessing
from tensorflow.keras import layers
from tensorflow.keras import models
from IPython import display

import pickle

seed = 42
tf.random.set_seed(seed)
np.random.seed(seed)

2、导入语音命令数据集

原始数据集包含超过 105,000 个 WAV 音频文件,其中包含人们说 30 个不同单词的内容。

我们将只使用数据集的一部分,称为mini_speech_commands,来节省数据加载时间。

data_dir = pathlib.Path('/home/jovyan/datas/mini_speech_commands')

检查有关数据集的基本统计信息。

commands = np.array(tf.io.gfile.listdir(str(data_dir)))
commands = commands[commands != 'README.md']
print('Commands:', commands)

Commands: [‘left’ ‘no’ ‘yes’ ‘right’ ‘up’ ‘go’ ‘down’ ‘stop’]

将音频文件提取到列表中并随机打散。

filenames = tf.io.gfile.glob(str(data_dir) + '/*/*')
filenames = tf.random.shuffle(filenames)
num_samples = len(filenames)
print('Number of total examples:', num_samples)
print('Number of examples per label:',
      len(tf.io.gfile.listdir(str(data_dir/commands[0]))))
print('Example file tensor:', filenames[0])
Number of total examples: 8000
Number of examples per label: 1000
Example file tensor: tf.Tensor(b'/home/jovyan/datas/mini_speech_commands/up/692a88e6_nohash_4.wav', shape=(), dtype=string)

分别使用 80:10:10 的比例将文件拆分为训练集、验证集和测试集。

train_files = filenames[:6400]
val_files = filenames[6400: 6400 + 800]
test_files = filenames[-800:]

print('Training set size', len(train_files))
print('Validation set size', len(val_files))
print('Test set size', len(test_files))
Training set size 6400
Validation set size 800
Test set size 800

3、读取音频文件及其标签

音频文件最初将作为二进制文件读取,然后我们将其转换为tensor。

我们使用tf.audio.decode_wav加载音频文件,它将 WAV 编码的音频作为张量和采样率返回。

WAV 文件包含时间序列数据,每秒采样数设定。每个样本代表该特定时间音频信号的幅度。在 16 位系统中,如mini_speech_commands中的文件,取值范围从 -32768 到 32767。此数据集的采样率为 16kHz。需要注意的是,当我们使用tf.audio.decode_wav会将值标准化到范围 [-1.0, 1.0]。

def decode_audio(audio_binary):
    audio, _ = tf.audio.decode_wav(audio_binary)
    return tf.squeeze(audio, axis=-1)

每个 WAV 文件的标签是其父目录名。

def get_label(file_path):
    parts = tf.strings.split(file_path, os.path.sep)

  # 在此处使用索引而不是元组解包,以使其能够在 TensorFlow 图中工作。
    return parts[-2]

接下来定义一个方法,该方法将接受 WAV 文件的文件名并输出一个包含音频和标签的元组,用于监督训练。

def get_waveform_and_label(file_path):
    label = get_label(file_path)
    audio_binary = tf.io.read_file(file_path)
    waveform = decode_audio(audio_binary)
    return waveform, label

对数据集使用map方法,应用自定义的get_waveform_and_label函数。

AUTOTUNE = tf.data.AUTOTUNE
files_ds = tf.data.Dataset.from_tensor_slices(train_files)
waveform_ds = files_ds.map(get_waveform_and_label, num_parallel_calls=AUTOTUNE)

检查一些带有相应标签的音频波形。

rows = 3
cols = 3
n = rows*cols
fig, axes = plt.subplots(rows, cols, figsize=(10, 12))
for i, (audio, label) in enumerate(waveform_ds.take(n)):
    r = i // cols
    c = i % cols
    ax = axes[r][c]
    ax.plot(audio.numpy())
    ax.set_yticks(np.arange(-1.2, 1.2, 0.2))
    label = label.numpy().decode('utf-8')
    ax.set_title(label)

plt.show()

这是一些样本对应的发音波形图,从波形图上可以看出,不同语音的波形图形态差异很大,相同语音的波形图,也不尽相同。

4、频谱图

在这一部分,我们将波形转换为频谱图,该频谱图显示频率随时间的变化并可表示为 2D 图像。这通过应用短时傅立叶变换 (STFT) 将音频转换到时频域来实现。

傅立叶变换 ( tf.signal.fft) 将信号转换为其分量频率,但会丢失所有时间信息。STFT ( tf.signal.stft) 将信号分成时间窗口,并对每个窗口运行傅立叶变换,保留一些时间信息,并返回一个可以运行标准卷积的二维张量。

STFT 生成表示幅度和相位的复数数组。但是,本实验中只需要幅度,这可以通过在tf.signal.stft的输出上应用tf.abs来得到。

选择frame_lengthframe_step参数使得生成的频谱图“图像”几乎是方形的。

同时我们还希望波形具有相同的长度,以便将其转换为频谱图图像时,结果将具有相似的维度。这可以通过简单地对短于一秒的音频剪辑进行零填充来完成,所以你会看到有些音频频谱图存在空白。

def get_spectrogram(waveform):
    # 使用Padding填充小于16000个样本的文件
    zero_padding = tf.zeros([16000] - tf.shape(waveform), dtype=tf.float32)

    # 用填充连接音频,使所有音频剪辑的长度相同
    waveform = tf.cast(waveform, tf.float32)
    equal_length = tf.concat([waveform, zero_padding], 0)
    spectrogram = tf.signal.stft(
        equal_length, frame_length=255, frame_step=128)

    spectrogram = tf.abs(spectrogram)

    return spectrogram

接下来,我们将探索数据。比较数据集的一个示例样本的波形、频谱图和实际音频。

for waveform, label in waveform_ds.take(1):
    label = label.numpy().decode('utf-8')
    spectrogram = get_spectrogram(waveform)

print('Label:', label)
print('Waveform shape:', waveform.shape)
print('Spectrogram shape:', spectrogram.shape)
print('Audio playback')
display.display(display.Audio(waveform, rate=16000))
Label: up
Waveform shape: (16000,)
Spectrogram shape: (124, 129)
Audio playback

自定义函数,用于绘制波形图和对数频谱图

def plot_spectrogram(spectrogram, ax):
    # 转换为频率的对数尺度和转置,使时间表示在x轴
    log_spec = np.log(spectrogram.T+1e-8)
    height = log_spec.shape[0]
    width = log_spec.shape[1]
    X = np.linspace(0, np.size(spectrogram), num=width, dtype=int)
    Y = range(height)
    ax.pcolormesh(X, Y, log_spec,shading='nearest')


fig, axes = plt.subplots(2, figsize=(12, 8))
timescale = np.arange(waveform.shape[0])
axes[0].plot(timescale, waveform.numpy())
axes[0].set_title('Waveform')
axes[0].set_xlim([0, 16000])
plot_spectrogram(spectrogram.numpy(), axes[1])
axes[1].set_title('Spectrogram')
plt.show()

在这里插入图片描述
第一幅图是波形图,第二幅图是对数频谱图,我们要做的,就是根据频谱图形,训练网络模型,识别语音命令。

现在将波形数据集转换为具有作为整数 ID 的频谱图图像及其相应标签。

def get_spectrogram_and_label_id(audio, label):
    spectrogram = get_spectrogram(audio)
    spectrogram = tf.expand_dims(spectrogram, -1)
    label_id = tf.argmax(label == commands)
    return spectrogram, label_id
spectrogram_ds = waveform_ds.map(
    get_spectrogram_and_label_id, num_parallel_calls=AUTOTUNE)

检查数据集不同样本的频谱图图像。

rows = 3
cols = 3
n = rows*cols
fig, axes = plt.subplots(rows, cols, figsize=(10, 10))
for i, (spectrogram, label_id) in enumerate(spectrogram_ds.take(n)):
    r = i // cols
    c = i % cols
    ax = axes[r][c]
    plot_spectrogram(np.squeeze(spectrogram.numpy()), ax)
    ax.set_title(commands[label_id.numpy()])
    ax.axis('off')

plt.show()

在这里插入图片描述
从图中可以看出,我们的代码能够正确运行,并且不同命令对应的频谱图差异较大,相同命令则比较相似。

5、构建和训练模型

现在我们可以构建和训练模型。但在此之前,还需要在验证集和测试集上重复训练集的预处理过程。

def preprocess_dataset(files):
    files_ds = tf.data.Dataset.from_tensor_slices(files)
    output_ds = files_ds.map(get_waveform_and_label, num_parallel_calls=AUTOTUNE)
    output_ds = output_ds.map(
        get_spectrogram_and_label_id,  num_parallel_calls=AUTOTUNE)
    return output_ds
train_ds = spectrogram_ds
val_ds = preprocess_dataset(val_files)
test_ds = preprocess_dataset(test_files)

对数据做batch处理,这里测试集不需要处理

batch_size = 64
train_ds = train_ds.batch(batch_size)
val_ds = val_ds.batch(batch_size)

添加数据集cache()和prefetch()操作以在训练模型时减少读取延迟。

train_ds = train_ds.cache().prefetch(AUTOTUNE)
val_ds = val_ds.cache().prefetch(AUTOTUNE)

对于模型,您将使用一个简单的卷积神经网络 (CNN),因为您已将音频文件转换为频谱图图像。该模型还有以下额外的预处理层:

  • Resizing层到下采样输入,以使模型训练速度更快。
  • Normalization根据平均值和标准偏差对图像中的每个像素进行归一化的层。

对于Normalization层,首先需要在训练数据上应用adapt,来计算聚合统计数据(即均值和标准差)。

for spectrogram, _ in spectrogram_ds.take(1):
    input_shape = spectrogram.shape
print('Input shape:', input_shape)
num_labels = len(commands)

norm_layer = preprocessing.Normalization()
norm_layer.adapt(spectrogram_ds.map(lambda x, _: x))

model = models.Sequential([
    layers.Input(shape=input_shape),
    preprocessing.Resizing(32, 32), 
    norm_layer,
    layers.Conv2D(32, 3, activation='relu'),
    layers.Conv2D(64, 3, activation='relu'),
    layers.MaxPooling2D(),
    layers.Dropout(0.25),
    layers.Flatten(),
    layers.Dense(128, activation='relu'),
    layers.Dropout(0.5),
    layers.Dense(num_labels),
])

model.summary()

在这里插入图片描述

model.compile(
    optimizer=tf.keras.optimizers.Adam(),
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=['accuracy'],
)

接下来我们将使用训练集训练模型,并使用验证集验证。由于训练时间可能较长,因此我们把训练代码放在markdown代码中,然后将训练结果保存起来,这样你就不用花费大量时间重新训练模型,只需在使用时加载这些模型即可。如果你想体验模型训练过程,把下列代码复制进来就可以了。

[Input]

EPOCHS = 10
history = model.fit(
    train_ds, 
    validation_data=val_ds,  
    epochs=EPOCHS,
    callbacks=tf.keras.callbacks.EarlyStopping(verbose=1, patience=2),
)

[Output]

Epoch 1/10
100/100 [==============================] - 80s 791ms/step - loss: 1.9188 - accuracy: 0.2723 - val_loss: 1.2747 - val_accuracy: 0.5987
...
Epoch 10/10
100/100 [==============================] - 44s 442ms/step - loss: 0.3792 - accuracy: 0.8680 - val_loss: 0.4814 - val_accuracy: 0.8350

然后我们把模型权重和训练过程的产出物history保存起来。

[Input]

model.save_weights('weights.h5')

with open('./trainHistoryDict', 'wb') as file_pi:
    pickle.dump(history.history, file_pi)

这样模型权重就被保存到weights.h5中,history被保存到trainHistoryDict

model.load_weights('weights.h5')
history = pickle.load(open('./trainHistoryDict', "rb"))

检查训练集和验证集的损失曲线,看看模型在训练过程中是如何改进的。

history.keys()

dict_keys([‘loss’, ‘accuracy’, ‘val_loss’, ‘val_accuracy’])

plt.plot(range(10),history['loss'], history['val_loss'])
plt.legend(['loss', 'val_loss'])
plt.show()

可以看到,模型在训练集和验证集上的损失快速下降,并且训练集上下降的速度更快。

6、评估测试集性能

让我们在测试集上运行模型并检查性能。

test_audio = []
test_labels = []

for audio, label in test_ds:
    test_audio.append(audio.numpy())
    test_labels.append(label.numpy())

test_audio = np.array(test_audio)
test_labels = np.array(test_labels)
y_pred = np.argmax(model.predict(test_audio), axis=1)
y_true = test_labels

test_acc = sum(y_pred == y_true) / len(y_true)
print(f'Test set accuracy: {test_acc:.0%}')

Test set accuracy: 91%

7、显示混淆矩阵

混淆矩阵有助于查看模型在测试集中的每个命令上的表现如何。

confusion_mtx = tf.math.confusion_matrix(y_true, y_pred) 
plt.figure(figsize=(10, 8))
sns.heatmap(confusion_mtx, xticklabels=commands, yticklabels=commands, 
            annot=True, fmt='g')
plt.xlabel('Prediction')
plt.ylabel('Label')
plt.show()

最终语音预测结果还是比较准确的,大部分语音内容都能够正确识别。错误比较多的情况出现在“go”和“no”上,这也正常,毕竟人耳听起来也非常相似,很容易弄混。

8、对音频文件运行推理

最后,使用某个“不”的输入音频文件验证模型的预测输出。

sample_file = data_dir/'no/01bb6a2a_nohash_0.wav'

sample_ds = preprocess_dataset([str(sample_file)])

for spectrogram, label in sample_ds.batch(1):
    prediction = model(spectrogram)
    plt.bar(commands, tf.nn.softmax(prediction[0]))
    plt.title(f'Predictions for "{commands[label[0]]}"')
    plt.show()

可以看到我们的模型非常清楚地将音频命令识别为“no”。

五、任务小结

本实验操作完成一个能够识别十个不同单词的基本语音识别网络,该模型将一秒钟的音频剪辑分类为“向下”、“前进”、“向左”、“否”、“向右”、“停止”、“向上” “ 是的”。

通过本实验我们学习到了语音识别与命令控制相关知识,需要掌握以下知识点:

  • tensorflow的使用
  • 频谱图数据的构建
  • 模型构建与训练
  • 模型性能评估

–end–

说明

本实验(项目)/论文若有需要,请后台私信或【文末】个人微信公众号联系我

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1649345.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

淤地坝安全监测预警系统解决方案

一、方案背景 淤地坝是黄土高原地区人民群众长期同水土流失斗争实践中创造的一种行之有效的水土保持工程措施,在拦泥保土、减少入黄泥沙、防洪减灾、淤地造田、巩固退耕还林(草)、保障生态安全、促进粮食生产和水资源合理利用及经济社会稳定发…

自动驾驶学习1-超声波雷达

1、简介 超声波雷达:利用超声波测算距离的雷达传感器装置,通过发射、接收 40kHz、48kHz或 58kHz 频率的超声波,根据时间差测算出障碍物距离,当距离过近时触发报警装置发出警报声以提醒司机。 超声波:人耳所不能听到的…

HG-KN73J-S100 三菱伺服电机(750W型)

HG-KN73J-S100属于三菱MR-JE系列伺服系统,可以与伺服驱动器MR-JE-70A、MR-JE-70B、MR-JE-70C配套使用。HG-KN73J-S100完全替换HF-KN73J-S100。HG-KN73J-S100规格、HG-KN73J-S100参数。 HG-KN73J-S100参数说明:MR-JE低惯性/小容量、0.75Kw三菱伺服电机HG-…

Web实操(6),基础知识学习(24~)

1.[ZJCTF 2019]NiZhuanSiWei1 (1)进入环境后看到一篇php代码,开始我简单的以为是一题常规的php伪协议,多次试错后发现它并没有那么简单,它包含了基础的文件包含,伪协议还有反序列化 (2&#x…

uniapp文本框上下滚动问题

一个基本需求,textarea标签没有办法通过手拖动的方式进行滚动,当文字超出其容量后,想要编辑上面被遮挡部分的文字这边难以点到,电脑可以鼠标滚轮,但手机需要拖动但无效: 下面提供了我的解决思路&#xff1a…

IP规划案例

整个OSPF环境IP基于172.16.0.0/16划分 172.16.0.0/16 先分成2个网段(OSPF RIP),借1位172.16.0.0/17 ---OSPF 再按区域划分(5个区域),借3位 172.16.0.0/20 ---Area 0 三个环回 MGRE 4个网…

【Vue】pinia

pinia 官网:https://pinia.vuejs.org/zh/ 搭建 pinia 环境 第一步:npm install pinia --save 第二步:操作src/main.ts import { createApp } from vue import App from ./App.vue/* 引入createPinia,用于创建pinia */ import { createP…

VALSE 2024 Tutorial内容总结--开放词汇视觉感知

视觉与学习青年学者研讨会(VALSE)旨在为从事计算机视觉、图像处理、模式识别与机器学习研究的中国青年学者提供一个广泛而深入的学术交流平台。该平台旨在促进国内青年学者的思想交流和学术合作,以期在相关领域做出显著的学术贡献&#xff0c…

技术速递|使用 .NET 为 Microsoft AI 构建可扩展网关

作者:Kara Saucerman 排版:Alan Wang Microsoft AI 团队构建了全面的内容、服务、平台和技术,以便消费者在任何设备上、任何地方获取他们想要的信息,并为企业改善客户和员工的体验。我们的团队支持多种体验,包括 Bing、…

RVM(相关向量机)、CNN_RVM(卷积神经网络结合相关向量机)、RVM-Adaboost(相关向量机结合Adaboost)

当我们谈到RVM(Relevance Vector Machine,相关向量机)、CNN_RVM(卷积神经网络结合相关向量机)以及RVM-Adaboost(相关向量机结合AdaBoost算法)时,每种模型都有其独特的原理和结构。以…

call, apply , bind 区别详解 及 实现购物车业务开发实例

call 方法: 原理 call 方法允许一个对象借用另一个对象的方法。通过 call,你可以指定某个函数运行时 this 指向的上下文。本质上,call 改变了函数运行时的作用域,它可以让我们借用一个已存 在的函数,而将函数体内的 th…

关于执行CLAM的代码的一些需要记录的点

文章链接:[2004.09666] Data Efficient and Weakly Supervised Computational Pathology on Whole Slide Images (arxiv.org) 代码链接:GitHub - mahmoodlab/CLAM: Data-efficient and weakly supervised computational pathology on whole slide images…

VALSE 2024 Workshop报告分享┆Open-Sora Plan视频生成开源计划——进展与不足

2024年视觉与学习青年学者研讨会(VALSE 2024)于5月5日到7日在重庆悦来国际会议中心举行。本公众号将全方位地对会议的热点进行报道,方便广大读者跟踪和了解人工智能的前沿理论和技术。欢迎广大读者对文章进行关注、阅读和转发。文章是对报告人…

新手做抖音小店多久能出单?新手抖音小店出单秘籍!出单教程必看

大家好,我是电商花花。 现阶段还是有很多朋友加入到抖音电商行业,因为抖音小店上还隐藏很多的红利和市场,很多新手开店后第一个问题就是,店铺开通后,一般多久能出单? 多久能出单,其实更看重的…

高等数学笔记(下中)

曲线积分 第一类曲线积分:对弧长的积分计算方法 定理:设 f ( x , y ) f(x,y) f(x,y)在曲线弧 L L L上有定义且连续, L L L的参数方程是 { x φ ( t ) y ψ ( t ) ( α ≤ t ≤ β ) \begin{cases} x\varphi(t)\\ y\psi(t) \end{cases}(\a…

国内如何下载TikTOK,手机刷机教程

最近很多玩家都来问怎么刷机?手机环境怎么搭建?这里给大家整理了苹果IOS刷机教程 1.iOS下载教程 : 步骤一:手机调试 苹果手机系统配置推荐:iPhone6S以上,16G。 注意:如果是选择购入二手手机…

某东抢购某台脚本——高版本

某东抢购某台脚本——高调 小白操作-学习参考 说明 这个脚本用于自动化京东的秒杀过程,特别是对于高需求商品如茅台。它展示了通过自动化工具模拟用户行为的能力,但同时也涉及到了使用自动化脚本可能违反网站使用条款的问题。使用此类脚本前应确保合…

软件设计师-应用技术-UML建模题3

基础知识及技巧: 1. 用例图: 1.1 考点: 题干里面有关项目的详细描述,完整用例图中的某些参与者和某些用来扣掉,根据题干内容和已有用例图补充。根据题干,分析用例图之间的关系。 1.2 基础知识&#xff…

速览Coinbase 2024Q1 财报重点:业务全面开花,净利润达11.8亿美元

作者:范佳宝,Odaily 星球日报 近期,Coinbase 发布了其 2024 年第一季度财报。 报告显示,Coinbase 第一季度营收为 16.4 亿美元,高于分析师平均预期的 13.4 亿美元;净利润为 11.8 亿美元,合每股…