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

⚠申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计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–

说明

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Scrcpy]数据线连接安卓手机投屏windows电脑[win控制安卓手机]比Samsung Dex好用

配置好,只需要两步即可完成安卓手机投屏windows 第一步:usb线连接windows电脑 第二步:cmd输入投屏命令srccpy 搞定 前言/背景 一些视频资料只能下载到手机,很不喜欢手机那么小屏幕播放,播放很不方便 在家的话可以投…

上位机图像处理和嵌入式模块部署(树莓派4b镜像烧录经验总结)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 陆陆续续也烧录了好多次树莓派的镜像了,这里面有的时候很快,有的时候很慢。特别是烧录慢的时候,也不知道是自己…

Unity EventSystem入门

概述 相信在学习Unity中,一定有被UI事件困扰的时候把,当添加UICanvas的时候,Unity会为我们自动添加EventSystem,这个是为什么呢,Unity的UI事件是如何处理的呢,在使用各个UI组件的时候,一定有不…

流量暴涨!抖音+快手+小红书获客攻略!

在数字营销的海洋中,抖音、快手和小红书无疑是三座巨大的灯塔,照亮了品牌和个人获取流量的道路。这些平台不仅拥有庞大的用户基础,而且其独特的算法和社交特性让获客变得更加高效而精准。接下来,让我们深入探讨如何通过这三个平台…

linux系统 虚拟机的安装详细步骤

window: (1) 个人:win7 win10 win11 winxp (2)服务器:windows server2003 2008 2013 linux: (1)centos7 5 6 8 (2)redhat (3)ubuntu (4)kali 什么是linux: 主要是基于命令来完成各种操作,类似于DO…

上位机图像处理和嵌入式模块部署(树莓派4b代码优化)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 我们把程序从pc端port到嵌入式开发板上面,好处是降低了部署成本,代价是牺牲了设备性能。所以等到程序真正在开发板子上面运…

《QT实用小工具·六十》Qt 多列时间轴控件

1、概述 源码放在文章末尾 Qt 多列时间轴控件。 可与多段字符串格式自由转换,也可手动添加列表项。 专门用来以时间轴作为事件线发展顺序的故事大纲。 特点 时间背包功能:记录所有物品或属性发生的变化,随时回溯 时间可输入任意内容&…

改进猫群算法丨多车场多车型路径问题求解复现

车间调度系列文章: 1、路径优化历史文章2、路径优化丨带时间窗和载重约束的CVRPTW问题-改进遗传算法:算例RC1083、路径优化丨带时间窗和载重约束的CVRPTW问题-改进和声搜索算法:算例RC1084、路径优化丨复现论文-网约拼车出行的乘客车辆匹配及…

DeepSeek发布全新开源大模型,GPT-4级别能力 价格仅百分之一

最新国产开源MoE大模型,刚刚亮相就火了。 DeepSeek-V2性能达GPT-4级别,但开源、可免费商用、API价格仅为GPT-4-Turbo的百分之一。 因此一经发布,立马引发不小讨论。 从公布的性能指标来看,DeepSeek-V2的中文综合能力超越一众开源…

嵌入式硬件中PCB走线与过孔的电流承载能力分析

简介 使用FR4敷铜板PCBA上各个器件之间的电气连接是通过其各层敷着的铜箔走线和过孔来实现的。 由于不同产品、不同模块电流大小不同,为实现各个功能,设计人员需要知道所设计的走线和过孔能否承载相应的电流,以实现产品的功能,防止过流时产品烧毁。 文中介绍设计和测试FR4敷…

有了这么多套件,为什么还需要APaaS

文/明道云创始人任向晖 在明道云的业务活动中,比较常见的一个问题是和套件应用的关系。一个有具体应用需求的客户为什么不从市场上购买现成的套件应用,而要选择APaaS来构建呢?反过来说似乎也成立,既然一个平台什么应用都能搭建&a…

《Video Mamba Suite》论文笔记(1)Mamba在时序建模中的作用

原文链接 https://arxiv.org/abs/2403.09626https://arxiv.org/abs/2403.09626 原文代码 https://github.com/OpenGVLab/video-mamba-suitehttps://github.com/OpenGVLab/video-mamba-suite 原文笔记 What 《Video Mamba Suite: State Space Model as a Versatile Altern…

灌溉机器人 状压dp

灌溉机器人 题目描述 农田灌溉是一项十分费体力的农活,特别是大型的农田。小明想为农民伯伯们减轻农作负担,最近在研究一款高科技——灌溉机器人。它可以在远程电脑控制下,给农田里的作物进行灌溉。 现在有一片 N 行 M 列的农田。农田的土…

力扣437. 路径总和 III

Problem: 437. 路径总和 III 文章目录 题目描述思路复杂度Code 题目描述 思路 1.定义int类型函数rootSum(root, targetSum),用于求取每一个节点等于目标函数的路径数: 1.1.易知rootSum(root, targetSum)求出的数量等于rootSum(root.left, targetSum - va…

系统如何做好安全加固?

一、Windows系统 Windows系统出厂时,微软为了兼容性,默认并未对系统安全做严格的限制,因此还需要做一些基本的安全加固,方可防止黑客入侵。 1、系统补丁更新 为什么要更新系统补丁?很多人感觉漏洞更新没必要&#x…

软件开发者如何保护自己的知识产权?

最近一个关于开源软件的知识产权纠纷的案例,非常有代表性, 其中涉及到的平台openwrt,一口君十几年前曾玩过, 通过这个案例,我们可以学习如何在今后工作中保护自己的知识产权, 以及如何合理直接或者间接利…

《设计一款蓝牙热敏打印机》

主控芯片用易兆威蓝牙ic,通讯接口:蓝牙、串口、usb 安卓apk用java kotlin编写、上位机用Qt编写。

一文读懂Python的`__init__`,`__init__`方法的终极指南

大家好,今天给大家介绍一个Python中一个特殊的函数__init__。 在Python中,__init__方法是一个特殊的函数,它在创建类的新实例时自动调用。它的作用类似于其他编程语言中的构造函数,用于初始化对象的状态。这篇文章将带你深入了解…

论文复现和点评《基于随机森林模型的个人信用风险评估研究》

作者Toby,来源公众号:Python风控模型,论文复现和点评《基于随机森林模型的个人信用风险评估研究》 最近Toby老师看到一篇论文热度比较高,下载量有665次,论文标题是《基于随机森林模型的 个人信用风险评估研究》 论文篇…

我的256天之创作纪念日

目录 时光 数据的一些变化 开心的事 憧憬 时光 自上次CSDN的消息推送,又一个128天过去了,整天的工作和生活都在忙忙碌碌中度过,每到能静下来片刻,都倍感珍惜。因为一些原因,能够陪伴家人的时间越来越少&#xff…