TensorFlow models 中的 VGGish:AudioSet 128 维音频嵌入模型的完整使用指南
【免费下载链接】modelsModels and examples built with TensorFlow项目地址: https://gitcode.com/GitHub_Trending/mode/models
本文基于 TensorFlow models 仓库research/audioset/vggish目录中的官方实现,系统讲解 VGGish 模型的定义、安装验证、输入特征流水线与输出嵌入后处理的完整链路。读完后,你将能够复现 AudioSet 发布的 128 维音频嵌入:对任意音频做特征提取、加载预训练检查点做推理、按官方格式做 PCA 白化与量化,或在其之上叠加自定义层进行微调训练。
模型定位与代码布局
最初发布的 AudioSet 数据集中包含每个音频片段的 128 维嵌入向量,这些向量由一个在大型 YouTube 数据集上训练的 VGG 风格音频分类模型产生(即后来 YouTube-8M 项目的前身)。仓库提供了该模型的 TensorFlow 定义,称为VGGish,同时附带两部分的支撑代码:把音频波形转换为模型输入特征的前端,以及把模型嵌入输出后处理为与 AudioSet 发布格式一致的模块(见 README)。
VGGish 的两种典型用法:
- 作为特征提取器:VGGish 把音频输入特征转换为语义化的高层 128 维嵌入,可直接喂给下游分类模型。由于嵌入比原始音频特征语义更紧凑,下游模型通常可以做得更浅。例如,你可以用发布的嵌入训练一个覆盖 AudioSet 中 10 个类别的分类器,之后对任意音频运行本仓库提供的特征提取器与 VGGish 模型,把得到的嵌入喂给该分类器。vggish_inference_demo.py 演示了如何对任意音频产生 VGGish 嵌入。
- 作为更大模型的一部分:把 VGGish 当作接受音频特征输入的模型的底层"热启动"(warm start),在嵌入层之上再叠加更多层。如果你有与典型 YouTube 视频片段差异较大的大数据集,还可以借此微调 VGGish(或其部分层)。vggish_train_demo.py 演示了如何在 VGGish 之上加层并训练整个模型。
目录内各文件的职责如下(与 README 的 "About the Model" 一节对应):
| 文件 | 职责 |
|---|---|
| vggish_slim.py | 模型定义(TensorFlow Slim 表示) |
| vggish_params.py | 全部超参数 |
| vggish_input.py | 音频波形到输入示例的转换器 |
| mel_features.py | 音频特征提取辅助函数 |
| vggish_postprocess.py | 嵌入后处理(PCA + 量化) |
| vggish_inference_demo.py | 推理模式演示 |
| vggish_train_demo.py | 训练模式演示 |
| vggish_smoke_test.py | 安装冒烟测试 |
安装与依赖验证
VGGish 依赖以下 Python 包(requirements.txt 中已列出,版本要求宽松,较新的版本均可):
numpyresampy(重采样)tensorflowtf_slimsixsoundfile
除依赖包外,还需要下载两个数据文件,并放到与代码相同的目录(下载地址在原 README 中给出):
vggish_model.ckpt:TensorFlow checkpoint 格式的 VGGish 预训练模型检查点;vggish_pca_params.npz:NumPy 压缩归档格式的嵌入 PCA 参数。
下面是 README 给出的示例安装与测试会话(虚拟环境部分为可选,用于把依赖与系统隔离,虚拟环境内不要使用sudo):
# 可选:用 virtualenv 隔离环境 # $ virtualenv vggish # Python 2 # 或 # $ python3 -m venv vggish # Python 3 # 激活:$ source vggish/bin/activate # 以 bash 为例 # 退出:$ deactivate # 先升级 pip,并确认安装了 wheel $ sudo python -m pip install --upgrade pip wheel # 安装全部依赖 $ sudo pip install -r requirements.txt # 进入 models 仓库中的 VGGish 目录(本仓库已包含该代码) $ cd models/research/audioset/vggish # 下载两个数据文件到与代码相同的目录(地址见原 README) # vggish_model.ckpt # vggish_pca_params.npz # 安装就绪,运行冒烟测试 $ python vggish_smoke_test.py # 若输出 "Looks Good To Me" 即安装成功冒烟测试 vggish_smoke_test.py 的验证逻辑值得细看:它生成一段 3 秒、1 kHz、16 kHz 采样率的正弦波,依次执行重采样(验证resampy可用)、特征提取(断言输出形状为[num_secs, 96, 64])、模型前向、嵌入后处理,最后用 10% 的相对容差校验嵌入的均值/标准差(见 vggish_smoke_test.py#L101-L113):
- 原始嵌入:期望均值约
0.000657,标准差约0.343; - 后处理嵌入(uint8):期望均值约
126.0,标准差约89.3。
源文件注释标明这些期望值是在 NumPy 1.21.6 / TF 2.8.2 与 NumPy 1.24.3 / TF 2.13.0(Python 3.10,Linux)上实测的,两组配置结果一致——这为运行环境版本给出了可靠的参考基准。
模型架构:VGG 配置 A 的音频变体
架构相关代码见 vggish_slim.py 与 vggish_params.py。VGGish 是 VGG 模型的变体,具体是 11 个权重层的配置 A(Configuration A)。相对标准 VGG-A 做了三处改动:
- 输入尺寸改为 96×64 的对数梅尔频谱音频输入;
- 去掉最后一组卷积/最大池化层,卷积/池化从五组减为四组;
- 末尾不再是 1000 宽的全连接层,而是128 宽的全连接层,作为紧凑的嵌入层。
模型定义到嵌入层为止。注意:嵌入层不含最终的非线性激活,输出的是激活前的值(pre-activation)。在 VGGish 之上叠加层训练时,应先把嵌入送入你选择的非线性再进入后续层。
核心超参数(vggish_params.py#L22-L42):
| 参数 | 值 | 含义 |
|---|---|---|
NUM_FRAMES | 96 | 输入梅尔频谱补丁的帧数 |
NUM_BANDS/NUM_MEL_BINS | 64 | 输入梅尔频谱补丁的频率带数 |
EMBEDDING_SIZE | 128 | 嵌入层宽度 |
SAMPLE_RATE | 16000 | 目标采样率 |
STFT_WINDOW_LENGTH_SECONDS | 0.025 | STFT 窗长(25 ms) |
STFT_HOP_LENGTH_SECONDS | 0.010 | STFT 帧移(10 ms) |
MEL_MIN_HZ/MEL_MAX_HZ | 125 / 7500 | 梅尔带覆盖的频段 |
LOG_OFFSET | 0.01 | 稳定对数的偏移量 |
EXAMPLE_WINDOW_SECONDS/EXAMPLE_HOP_SECONDS | 0.96 / 0.96 | 每个示例 0.96 s,零重叠 |
QUANTIZE_MIN_VAL/QUANTIZE_MAX_VAL | -2.0 / +2.0 | 量化裁剪范围 |
INIT_STDDEV | 0.01 | 权重初始化标准差 |
LEARNING_RATE/ADAM_EPSILON | 1e-4 / 1e-8 | 内部训练用的 Adam 超参数 |
网络主体结构在 vggish_slim.py#L91-L106:输入按[batch_size, 96, 64]送入(reshape 为 4 维后卷积),经过
conv1(64) → pool1 → conv2(128) → pool2 → conv3(256×2) → pool3 → conv4(512×2) → pool4 → flatten → fc1(4096×2) → fc2(128, 无激活)
其中fc2通过activation_fn=None显式去除了默认 ReLU,输出张量命名为vggish/embedding。默认约定(vggish_slim.py#L64-L79 的 arg_scope 设置):权重初始化为 N(0, 0.01)、偏置为 0、激活为 ReLU、卷积为 3×3 stride 1 SAME 填充、最大池化为 2×2 stride 2 SAME 填充。
关于张量接口,vggish_params.py#L49-L53 定义了:
- 输入:
vggish/input_features:0,float32,形状[batch_size, 96, 64]; - 输出:
vggish/embedding:0; - 发布特征名:
audio_embedding(写 TFRecord 时使用)。
检查点加载函数load_vggish_slim_checkpoint(vggish_slim.py#L109-L136)可以在初始化所有变量之后调用(即 TensorFlow 文档中的 init_fn),它先在一个独立图里以推理模式列出 VGGish 的全部变量名,再用一个专用Saver只恢复名字匹配的 VGGish 变量——因此即使当前图中还有 VGGish 之上新增的层,加载检查点也不会报错,这正是"在 VGGish 上叠加层再训练"能成立的关键机制。
输入流水线:从波形到 96×64 示例
VGGish 训练时使用的音频特征计算流程(README "Input: Audio Features" 一节)为:
- 所有音频重采样为 16 kHz 单声道;
- 计算短时傅里叶变换幅度谱:窗长 25 ms、帧移 10 ms、周期性 Hann 窗;
- 通过梅尔滤波器组映射到 125–7500 Hz 范围的 64 个梅尔带;
- 计算稳定对数梅尔谱:
log(mel-spectrum + 0.01),偏移量避免对零取对数; - 将特征切成零重叠的 0.96 秒示例:每个示例覆盖 64 个梅尔带 × 96 帧(每帧 10 ms)。
仓库提供的是纯 NumPy 实现,产出的特征与 Google 内部生产代码非常接近。具体实现要点:
- 分帧mel_features.py#L21-L45:
frame()用np.lib.stride_tricks.as_strided实现零拷贝分帧,末尾不完整的帧直接丢弃(不做零填充); - 周期性 Hann 窗mel_features.py#L48-L68:
periodic_hann()实现的是周期为 N 的完整余弦周期,而非np.hanning()那种周期 N-1 的"对称"窗,更适合傅里叶分析; - STFT 幅度mel_features.py#L71-L92:
stft_magnitude()对加窗帧做np.fft.rfft取模,FFT 长度为不小于窗长的最小 2 的幂(见 mel_features.py#L214); - 赫兹→梅尔换算mel_features.py#L96-L111:采用 HTK 公式
mel = 1127 * log(1 + hz / 700); - 梅尔滤波器组mel_features.py#L114-L189:
spectrogram_to_mel_matrix()按梅尔域线性三角带构建权重矩阵,DC 频带系数恒为 0,最终梅尔谱即M = S · A的矩阵乘法; - 总入口mel_features.py#L192-L233:
log_mel_spectrogram()把以上步骤串起来,返回(num_frames, num_mel_bins)的对数梅尔谱。
在 vggish_input.py 中,waveform_to_examples()(L37-L81)完成最后的装配:多声道输入先在声道维取均值转单声道;采样率不是 16000 时用resampy.resample重采样;随后计算对数梅尔谱,并按1 / STFT_HOP_LENGTH_SECONDS = 100 Hz的特征采样率把 0.96 秒窗、0.96 秒步长(即example_window_length = example_hop_length = 96帧)再次分帧,输出形状[num_examples, 96, 64]的示例数组。
wavfile_to_examples()(L84-L97)是针对 WAV 的便捷封装:通过soundfile以int16读入,断言样本类型为 16 位 PCM,再除以 32768 归一到[-1.0, +1.0]后进入waveform_to_examples。若未安装soundfile,读 WAV 会抛出NotImplementedError,但直接在内存中传入 NumPy 波形仍可使用。
输出流水线:PCA 白化与 8-bit 量化
发布的 AudioSet 嵌入在对外发布前经过了后处理:应用 PCA 变换(实际上同时完成了 PCA 与白化),并把每个嵌入元素量化到 8 bit。这样做的目的是与 YouTube-8M 项目保持格式兼容——该项目以同样的 PCA/白化/量化格式发布了数百万 YouTube 视频的视频嵌入与音频嵌入(README "Output: Embeddings" 一节)。
vGGish_postprocess.py 的Postprocessor实现了同样的变换:
- 构造时从
.npz文件加载pca_eigen_vectors(128×128 矩阵)与pca_means(reshape 成 128×1 列向量),并对形状做了断言校验(vggish_postprocess.py#L42-L50); postprocess()对[batch_size, 128]的嵌入批处理:先减去均值再左乘 PCA 矩阵(L75-L76);然后裁剪到[-2.0, +2.0],线性映射到[0, 255]并转为uint8(L80-L89)。
README 同时给出一个重要提示:如果你不需要与发布的嵌入或 YouTube-8M 兼容,可以跳过后处理直接使用原始嵌入;此外原 README 还提供了一个 Colab 入口,演示如何下载模型并对自己的声音数据计算嵌入(链接见原文档)。
推理演示:对任意音频生成嵌入
vggish_inference_demo.py 展示了"特征提取 + 模型加载 + 后处理"三个构件在推理场景下的组合方式。命令行参数(L61-L78):
--wav_file:WAV 文件路径(16 位 PCM)。不指定时生成一段 5 秒、1 kHz、44.1 kHz 的正弦波合成输入;--checkpoint:模型检查点路径,默认vggish_model.ckpt;--pca_params:PCA 参数路径,默认vggish_pca_params.npz;--tfrecord_file:可选,把后处理后的嵌入以 AudioSet 同格式的 SequenceExample 写入 TFRecord。
三种典型调用方式(源自文件 docstring):
# 对 WAV 文件推理并打印嵌入(检查点与 PCA 参数从当前目录读取) $ python vggish_inference_demo.py --wav_file /path/to/a/wav/file # 推理并额外写 TFRecord,同时显式指定检查点与 PCA 参数路径 $ python vggish_inference_demo.py --wav_file /path/to/a/wav/file \ --tfrecord_file /path/to/tfrecord/file \ --checkpoint /path/to/model/checkpoint \ --pca_params /path/to/pca/params # 使用内置合成正弦输入,模型文件从当前目录读取 $ python vggish_inference_demo.py主流程(vggish_inference_demo.py#L98-L147):wavfile_to_examples()产出示例批 →define_vggish_slim(training=False)定义推理模型 →load_vggish_slim_checkpoint()加载检查点 → 按名字取出vggish/input_features:0与vggish/embedding:0→sess.run得到原始嵌入 →Postprocessor.postprocess()得到量化嵌入 → 若指定了--tfrecord_file,把批内每一行(约对应 1 秒音频,即 96 个 10 ms 帧)的 128 字节量化嵌入按bytes_list逐条写入特征名audio_embedding的SequenceExample。这正是 AudioSet 发布嵌入的存储格式。
训练演示:在 VGGish 之上加层与微调
vggish_train_demo.py 是一个玩具示例:训练一个区分正弦波、常量信号、白噪声三个类别的分类器。合成波形先转成打乱顺序的对数梅尔示例批并附带 one-hot 标签,再送入一个"VGGish 打底 + 顶部若干层"的模型。命令行参数(L60-L73):
--num_batches:喂入的批次数量(默认 30);--train_vggish:True 表示训练时允许 VGGish 参数变化(即微调 VGGish);False 表示 VGGish 参数冻结,仅作为固定特征提取器;--checkpoint:检查点路径,默认vggish_model.ckpt。
典型调用:
# 训练 100 步,使用默认位置的检查点,允许微调 VGGish $ python vggish_train_demo.py --num_batches 100 # 训练 50 步,冻结 VGGish(只做特征提取),检查点放在其他位置 $ python vggish_train_demo.py --num_batches 50 \ --train_vggish=False \ --checkpoint /path/to/model/checkpoint顶部模型的搭建细节(vggish_train_demo.py#L131-L171)值得注意:
- 由于嵌入是激活前的值,先
tf.nn.relu(embeddings)再过 100 单元的全连接层,最后接 3 单元 logits 层 + sigmoid(L139-L145); - 损失用
sigmoid_cross_entropy_with_logits求均值,支持多标签; - 优化器为 Adam,学习率与 epsilon 直接取
vggish_params.LEARNING_RATE(1e-4)与vggish_params.ADAM_EPSILON(1e-8),即与内部训练 VGGish 相同的超参数; - 训练循环前先
tf.global_variables_initializer()初始化全图变量(包括顶部新层),再调用load_vggish_slim_checkpoint()只恢复 VGGish 部分——这与define_vggish_slim(training=FLAGS.train_vggish)中trainable标志控制的可训练范围配合,实现了"微调"与"冻结"两种模式的一键切换。
小结
VGGish 在 TensorFlow models 仓库中提供了一条完整的音频嵌入生产链路:16 kHz 重采样 → 周期性 Hann 窗 STFT → 64 带梅尔滤波(125–7500 Hz)→ 稳定对数化 → 0.96 秒零重叠分帧(96×64),送入四组卷积 + 两层 4096 全连接 + 128 维嵌入层的 VGG-A 变体;输出可选择直接作为 pre-activation 嵌入使用,或经 PCA 白化与[-2, 2] → [0, 255]的 8-bit 量化后与 AudioSet/YouTube-8M 发布格式对齐。安装后先跑 vggish_smoke_test.py 验证环境,再用 vggish_inference_demo.py 与 vggish_train_demo.py 分别覆盖推理与微调两条路径。需要说明的是:本实现基于 TensorFlow 1.x API(以tensorflow.compat.v1方式运行)并依赖tf_slim,冒烟测试的期望值在 TF 2.8.2 与 2.13.0 上验证一致,选型与升级版本时应以此为准。
【免费下载链接】modelsModels and examples built with TensorFlow项目地址: https://gitcode.com/GitHub_Trending/mode/models
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考