- 示例工程
【免费下载链接】examples
TensorFlow examples
语音指令识别(Speech Commands Recognition)是移动端 AI 的典型落地场景之一:设备通过麦克风持续监听,识别出用户说出的预设短词(如 "yes"、"no"、"stop"),从而支撑免提交互、语音唤醒等能力。本指南以本仓库lite/examples/speech_commands下的完整示例为主线,系统讲解基于 TensorFlow Speech Commands 数据集的端到端方案,涵盖数据集获取、音频预处理、1D 卷积模型训练、模型导出为 TFLite,以及在 iOS 设备上的推理与部署。读完本文,你将掌握一条可复现的"语音数据集 → Keras 模型 → TFLite → 移动端识别"完整链路。
示例概览与项目定位
本目录(lite/examples/speech_commands/README.md)提供了一个"端到端"的语音指令识别示例集合,目标是在移动设备上对语音指令进行识别并高亮显示说出的单词。按官方文档规划,它包含三个组成部分:
- 使用 TensorFlow Lite 模型识别语音指令的Android 应用;
- 使用 TensorFlow Lite 模型识别语音指令的iOS 应用(见 ios/);
- 生成语音指令 TFLite 模型的训练与导出指南(见 ml/)。
需要说明的是,从当前仓库快照来看,lite/examples/speech_commands下实际包含的是 ml/(训练与模型导出脚本)和 ios/(iOS 示例应用)两部分;同时,仓库中的 README.md 与 ios/README.md 均带有明确的Deprecated(已弃用)警告,官方建议迁移到技术栈更新的 Sound Classification 示例。因此,本文在讲解本示例的技术实现时,也会提醒读者关注这一演进方向。
数据集:TensorFlow Speech Commands Dataset
数据集规模与内容
训练脚本依赖 TensorFlow Speech Commands 数据集。该数据集包含65,000 条 1 秒钟的语音片段,由数千名不同说话人录制,覆盖30 个英文短词,例如 "yes"、"no"、"up"、"down"、"left"、"right"、"on"、"off"、"stop"、"go" 等,同时附带_silence_(静音)与_background_noise_(背景噪声)等特殊样本类别。
类别选择
移动端指令识别通常只需要少量指令词。本示例从 30 个词中挑选10 个类别用于 TensorFlow Lite 语音指令应用:
stop down off right up go on yes left no对应实现见 ml/classes.py:get_classes(wanted_only=True)返回这 10 个词的列表并断言长度为 10;get_classes(wanted_only=False)则返回完整的 30 词列表(用于构建未知词映射)。
值得注意的一个细节:在类别体系中,除了 10 个目标词,还有两个特殊类别参与训练与推理。从 ml/generator.py 可以看到,词表由prepare_words_list()统一构造,最终类别集合为:
_silence_ _unknown_ <10 个目标词>_silence_(索引 0):静音类别,用于让模型学会"不说话时不做任何触发";_unknown_(索引 1):未知词类别,把不在目标词表中的其余 20 个词归入其中,增强模型对无关语音的鲁棒性。
因此模型输出层共有12 个类别,这一点与 iOS 端模型标签文件 conv_actions_labels.txt 的内容一一对应:
_silence_ _unknown_ yes no up down left right on off stop go数据集下载
ml/download.py 提供了数据集的一键下载脚本:它从http://download.tensorflow.org/data/speech_commands_v0.01.tar.gz下载speech_commands_v0.01版本的数据集,解压到data/train目录后删除压缩包。运行方式:
python download.pyrequirements.txt中声明的依赖包括wget==3.2(用于下载)、Keras==2.2.0、pandas==0.22.0、pandas-ml==0.5.0(用于混淆矩阵统计)以及tensorflow>=1.14.0、tensorflow-gpu>=1.14.0,详见 ml/requirements.txt。
音频数据预处理
基础采样配置
语音片段在送入模型前,会被统一整理为固定采样率、固定长度的 PCM 波形数据。文档给出的配置如下:
| 采样点数(Samples) | 采样率(Sample Rate) | 片段时长(Clip Duration) |
|---|---|---|
| 16000 | 16000 Hz | 1000 ms |
即每个样本是 1 秒钟、16000 Hz、单声道、16000 个采样点的原始波形。该配置在 ml/model.py 的prepare_model_settings()中通过desired_samples = int(sample_rate * clip_duration_ms / 1000)计算得到,并且训练脚本 ml/train.py 固定使用clip_duration_ms=1000、window_size_ms=30.0、window_stride_ms=10.0、dct_coefficient_count=80、num_log_mel_features=60等超参数。
多种输入表示:raw / spec / mfcc
train.py的-output_representation参数控制模型输入特征的类型,支持四种取值,其维度计算见 ml/model.py:
| 表示方式 | 含义 | 特征维度计算 |
|---|---|---|
raw | 原始 PCM 波形(默认) | desired_samples(即 16000) |
spec | 频谱图(spectrogram) | spectrogram_frequencies × spectrogram_length(257 × 频谱帧数) |
mfcc | 梅尔频率倒谱系数 | num_log_mel_features × spectrogram_length |
mfcc_and_raw | MFCC 与原始波形拼接 | 以 MFCC 为主,同时输出原始波形 |
其中频谱与 MFCC 的生成逻辑在 ml/generator.py 的prepare_processing_graph()中实现:先对波形做短时傅里叶变换(tf.signal.stft,帧长window_size_samples、帧移window_stride_samples),取幅度谱得到频谱图;再通过 80~7600 Hz 的梅尔滤波器组(linear_to_mel_weight_matrix)得到 log-Mel 频谱,进而计算 MFCC。训练时默认使用raw表示,配合下文介绍的 1D 时序卷积模型直接消费原始波形。
训练期数据增强
训练阶段还会对音频施加一系列"失真"增强(见 ml/generator.py 与 ml/utils.py 的data_gen()默认参数):
- 背景噪声混入:从
_background_noise_目录随机截取一段噪声叠加到前景语音上,训练时背景出现频率background_frequency=0.3、音量范围background_volume_range=0.15; - 音量调节:前景音量以
foreground_frequency=0.3的概率在 ±0.15 范围内随机缩放; - 时间平移:以
time_shift_frequency=0.3的概率在[-500, 0]个采样点范围内滚动波形(tf_roll实现),模拟指令起始时间的变化; - 静音样本处理:
_silence_样本将前景音量置零、仅保留背景噪声,避免模型学会"以全零向量识别静音"的捷径。
验证集与测试集则关闭所有随机增强(data_gen()中对非 training 模式将增强频率全部置 0),保证评估的公平性。
模型架构:时间堆叠的 1D 卷积网络
本示例的模型(conv_1d_time_stacked)是一个面向音频波形这类时序数据的时间堆叠 VGG 风格网络,全部使用一维卷积(Conv1D),定义在 ml/model.py。核心结构可以拆解为三层设计:
输入整形与预处理:输入为 16000 个采样点的原始波形,先
Reshape成[800, 20](把 1 秒波形按时间与幅值维度重组),再经过PreprocessRaw保持原值(对应preprocess_raw,见 ml/model.py)。此外代码中还有一个preprocess()((x + 0.8) / 7.0后裁剪到 [-5, 5]),用于非 raw 输入的归一化场景。上下文卷积
context_conv:使用膨胀(dilated)1D 卷积(dilation_rate=1,即普通卷积;可通过参数调整感受野)来提取更宽时间范围的上下文特征。每个卷积块都遵循Conv1D → BatchNormalization → relu6 激活的顺序,并施加 L2 正则(kernel_regularizer=l2(0.00001))、use_bias=False。降维卷积
reduce_conv:在context_conv之后,用 1D MaxPooling(pool_size=3, strides=2)对特征做降维,减少传入后续层的参数量。
从 ml/model.py 可以完整看到网络的堆叠序列——context_conv与reduce_conv交替出现,卷积核宽度从 1 逐步增长到 3,通道数逐级扩张:
context_conv(32, k=1) → reduce_conv(48, k=3) → context_conv(48, k=3) → reduce_conv(96, k=3) → context_conv(96, k=3) → reduce_conv(128, k=3) → context_conv(128, k=3) → reduce_conv(160, k=3) → context_conv(160, k=3) → reduce_conv(192, k=3) → context_conv(192, k=3) → reduce_conv(256, k=3) → context_conv(256, k=3)网络尾部依次是Dropout(0.3)(防止过拟合)、输出 12 类的Conv1D(num_classes, 5, activation='softmax')以及Reshape([-1])。模型使用 Adam 优化器(学习率3e-4)、categorical_crossentropy损失函数和categorical_accuracy指标完成编译(ml/model.py)。该架构思想源于论文Convolutional Neural Networks for Small-footprint Keyword Spotting,是经典的轻量级关键词唤醒(Keyword Spotting)模型范式。
训练流程
命令行参数
训练入口为 ml/train.py,它通过 argparse 定义如下参数:
python train.py [-h] [-sample_rate SAMPLE_RATE] \ [-batch_size BATCH_SIZE] \ [-output_representation OUTPUT_REPRESENTATION] \ -data_dirs DATA_DIRS [DATA_DIRS ...]各参数含义(默认值来自 ml/train.py):
| 参数 | 默认值 | 说明 |
|---|---|---|
-sample_rate | 16000 | 音频采样率 |
-batch_size | 32 | 训练批次大小 |
-output_representation | raw | 输入特征表示,可选raw、spec、mfcc、mfcc_and_raw |
-data_dirs | 必填 | 数据集目录列表,例如data/train |
训练示例
文档给出的完整训练命令:
python train.py -sample_rate 16000 -batch_size 64 -output_representation raw -data_dirs data/train执行后脚本会按以下流程运行(ml/train.py):
- 初始化 TensorFlow Session 与 Keras 后端;
- 用
prepare_model_settings()计算模型设置(10 个目标词 +_silence_+_unknown_,共 12 类); - 构建
AudioProcessor加载并划分数据:silence_percentage=13.0(每个划分集中静音样本占 13%)、unknown_percentage=60.0(未知词样本占 60%)、validation_percentage=10.0、testing_percentage=0.0;其中训练/验证/测试集的划分基于文件名哈希(which_set(),见 ml/generator.py),保证同一音频始终划入同一子集; - 构建
conv_1d_time_stacked模型,输入维度按output_representation取fingerprint_size或desired_samples; - 训练 100 个 epoch,并在每个 epoch 结束时通过回调记录混淆矩阵、学习率衰减与模型检查点。
回调与监控
训练期间挂载了四个 Keras 回调(ml/train.py):
- ConfusionMatrixCallback(ml/callbacks.py):每个 epoch 结束后在验证集上计算混淆矩阵,并写入
confusion_matrix.txt与wanted_confusion_matrix.txt(后者把非目标词折叠为_unknown_);同时把val_loss、val_categorical_accuracy等指标回填到日志; - ReduceLROnPlateau:监控
val_categorical_accuracy,连续 4 个 epoch 无提升时将学习率减半(factor=0.5),下限1e-5; - TensorBoard:日志写入
logs/目录,便于可视化训练曲线; - ModelCheckpoint:以
val_categorical_accuracy为监控指标、save_best_only=True,把最优权重保存到checkpoints/conv_1d_time_stacked_model/下,文件名为ep-{epoch:03d}-vl-{val_loss:.4f}.hdf5。
训练结果
文档记录的训练结果为:训练 100 个 epoch 后,val_categorical_accuracy达到 0.94,并给出了用于评估分类性能的混淆矩阵:
[099]: val_categorical_accuracy: 0.94
| 预测值\实际值 | silence | down | go | left | no | off | on | right | stop | two | up | yes |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| silence | 322 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| down | 0 | 240 | 6 | 0 | 7 | 0 | 1 | 0 | 0 | 4 | 0 | 0 |
| go | 5 | 3 | 223 | 0 | 2 | 1 | 1 | 0 | 0 | 15 | 2 | 0 |
| left | 0 | 0 | 0 | 221 | 2 | 0 | 0 | 1 | 0 | 8 | 0 | 7 |
| no | 0 | 0 | 4 | 0 | 246 | 0 | 2 | 0 | 0 | 14 | 0 | 0 |
| off | 0 | 0 | 1 | 0 | 0 | 229 | 2 | 0 | 0 | 5 | 15 | 0 |
| on | 3 | 0 | 0 | 0 | 0 | 7 | 227 | 0 | 0 | 14 | 1 | 0 |
| right | 0 | 0 | 0 | 4 | 0 | 0 | 0 | 222 | 0 | 21 | 3 | 1 |
| stop | 2 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 224 | 8 | 3 | 0 |
| two | 6 | 4 | 5 | 7 | 6 | 2 | 6 | 3 | 0 | 1468 | 4 | 0 |
| up | 1 | 0 | 0 | 0 | 0 | 10 | 1 | 0 | 0 | 11 | 230 | 0 |
| yes | 2 | 1 | 0 | 2 | 4 | 0 | 1 | 0 | 0 | 6 | 0 | 240 |
其中two行/列来源于验证集中包含的完整 30 词样本(非目标词在评估时归入_unknown_,但完整混淆矩阵会按原始标签统计),其余 10 个目标词与_silence_均表现出很高的对角线准确率,说明该 1D 时序卷积模型在轻量前提下对指令词识别是有效的。
导出 TFLite 模型
训练完成后,需要把 Keras 模型转换为 TensorFlow Lite 格式,才能在移动端运行。ml/export/目录提供了三种导出方式:
1. 直接转换 Keras 模型
ml/export/convert_keras_lite.py 演示了用TFLiteConverter.from_keras_model_file()直接转换:脚本中指定了keras_model(示例指向../conv_1d_time_stacked_model/ep-084-vl-0.2595.hdf5)以及输入/输出张量名the_input/the_output,转换产物写为converted_speed_keras_model.tflite。
2. 冻结图 + 量化导出
ml/export/convert_keras_to_quantized.py 提供了更完整的导出管线:先把 Keras 权重加载进模型(示例加载ep-022-vl-0.2864.hdf5),用tf.identity给输出节点命名,再用convert_variables_to_constants冻结为常量图,最后通过tf.io.write_graph写出 .pb 文件。脚本支持以下参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
-input_fld | . | Keras 权重文件所在目录 |
-output_fld | 同 input | 冻结图输出目录 |
-input_model_file | model.h5 | 输入权重文件名 |
-output_model_file | <input>.pb | 输出文件名 |
-num_outputs | 1 | 输出分支数(多流网络时设为输出个数) |
-graph_def | False | 是否额外写出 ASCII 图定义 |
-output_node_prefix | output_node | 输出节点命名前缀 |
-quantize | False | 是否启用权重/节点量化(quantize_weights、quantize_nodes) |
-theano_backend | False | Theano 后端时使用 channels_first;与量化不兼容 |
3. 使用 tflite_convert 命令行
ml/export/convert_tensorflow_lite.sh 展示了命令行转换方式:
tflite_convert --output_file converted_speech_model.tflite \ --graph_def_file model.h5.pb \ --output_format TFLITE \ --inference_type FLOAT \ --inference_input_type FLOAT \ --input_arrays input_1 \ --output_arrays output_node0该命令以冻结图model.h5.pb为输入,指定输入张量input_1、输出张量output_node0,推理类型保持 FLOAT,生成converted_speech_model.tflite。
如果不想自行训练,也可以直接使用官方预训练模型:iOS 端的 RunScripts/download_models.sh 会在首次构建时从https://storage.googleapis.com/download.tensorflow.org/models/tflite/conv_actions_tflite.zip自动下载conv_actions_frozen.tflite与conv_actions_labels.txt到SpeechCommands/Model/目录。
iOS 端部署
环境要求
根据 ios/README.md,iOS 示例应用的要求如下:
- iOS 12.0 及以上,Xcode 10.0 及以上;
- 需要安装 Xcode 命令行工具:
xcode-select --install(新安装 Xcode 时需先运行一次以同意许可协议); - 应用需要麦克风权限,且必须在真实 iOS 设备上运行——模拟器可以编译运行,但会抛出"找不到麦克风(camera not found exception)"异常;
- 无需自行构建整个 TensorFlow 库,使用CocoaPods即可拉取 TensorFlow Lite 库。
构建步骤
- 安装 CocoaPods(如未安装):
sudo gem install cocoapods; - 进入示例目录并安装 Pod 依赖、生成工作区文件:
cd examples/speech_commands/ios/ pod install如果之前安装过该 Pod 而命令失效,可尝试pod update。完成后应生成SpeechCommands.xcworkspace。
- 用 Xcode 打开SpeechCommands.xcworkspace(注意不要打开 .xcodeproj);
- 在General -> Signing中把 Bundle Identifier 改为唯一标识,并选择自己的开发团队;
- 构建并运行应用,首次启动时授予麦克风权限,之后对着设备说出指令词即可看到识别结果。
模型文件无需手动下载——它们由 Xcode 构建时的 Run Script 阶段(RunScripts/download_models.sh)自动下载并放入SpeechCommands/Model/目录。
注意事项:克隆仓库后请勿删除项目中指向 .tflite 与 .txt 的"空引用"。这些引用会在首次构建运行、模型和标签文件下载后被自动修复;如果误删引用,模型文件仍会被下载到 Model 目录,但需要手动把引用加回工程才能运行。
推理逻辑:从 PCM 到指令识别
应用完全使用 Swift 编写,通过 TensorFlow Lite Swift 库执行推理(Objective-C 开发者可改用 Objective-C 库)。核心实现在 ModelDataHandler.swift:
- 使用
Interpreter加载conv_actions_frozen.tflite,线程数默认 1(上限 10); - 每个推理周期把
[Int16]麦克风缓冲区归一化(除以maxInt16AsFloat32 = 32767.0)后拷贝到第 0 个输入张量(音频缓冲),并把采样率 16000 作为第 1 个输入张量; - 调用
interpreter.invoke()后读取输出张量,得到 12 个类别的置信度分数; - 通过
labelOffset = 2跳过_silence_与_unknown_,只展示 10 个指令词的识别结果。
由于单帧推理结果噪声较大,应用还用 RecognizeCommands.swift 对连续结果做了滑窗平均与去重,文档说明"界面显示的百分比是 1000ms 窗口内的平均指令识别置信度"。其核心参数包括:
averageWindowDuration = 1000.0:平均窗口时长(ms),与训练时的 1 秒片段时长对应;detectionThreshold:识别阈值(初始化时传入 0.3);suppressionTime = 1500.0:抑制时间——同一指令被识别后 1.5 秒内不重复触发;minimumCount = 3:窗口内至少积累 3 帧结果才参与判定;minTimeBetweenSamples = 30.0:相邻两次推理的最小时间间隔。
process()的逻辑是:把最新推理结果按时间戳追加到滑动窗口,丢弃超出平均窗口时长的旧结果;对窗口内各帧的 12 类分数做平均、排序;只有当最高平均分超过阈值、且距上次触发超过抑制时间时,才判定为新指令(isNew = true)并展示给用户。这套"滑窗平均 + 阈值 + 抑制"机制有效避免了单帧误判和指令重复触发,是语音指令类应用的标准后处理范式。
小结与演进方向
本示例完整覆盖了一条可复现的语音指令识别链路:Speech Commands 数据集下载 → 原始波形/频谱特征生成与增强 → 时间堆叠 1D 卷积模型训练 → 混淆矩阵评估 → Keras 冻结图导出为 TFLite → iOS 端 Interpreter 推理与滑窗后处理。其中 ml/ 与 ios/ 的源码可直接作为二次开发的起点。
同时需要再次强调:官方已在本示例的 README.md 与 ios/README.md 中明确标注Deprecated,并推荐迁移到采用最新技术栈的 Sound Classification 示例(该目录同样提供了 iOS 端的声音分类示例应用)。在阅读本指南时,建议把 1D 时序卷积建模、滑窗识别后处理等思想作为核心收获,而在新项目中优先采用官方当前推荐的 Sound Classification 方案。
- 示例工程
【免费下载链接】examples
TensorFlow examples
相关推荐
终极指南:TensorFlow Lite语音命令识别实战,从训练到移动端部署
终极指南:TensorFlow Lite语音命令识别实战,从训练到移动端部署 TensorFlow Lite语音命令识别是一项强大的技术,能让你的移动应用轻松具
示例工程基于 Speech Embedding TF-Hub 模块的三类关键词识别训练实战:从 Speech Commands 到自录语音与合成数据
基于 Speech Embedding TF Hub 模块的三类关键词识别训练实战:从 Speech Commands 到自录语音与合成数据 导读 本文基于 g
人工智能深度学习NLP计算机视觉强化学习给 T5 微调出长文本摘要模型:基于 CNN/Daily Mail 数据集的完整实战指南
给 T5 微调出长文本摘要模型:基于 CNN/Daily Mail 数据集的完整实战指南 打开一份几千字的新闻稿,你只想用半分钟抓住核心:谁、发生了什么、结果如
示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考