从波形到Log-Mel Fbank:原理、NumPy实现与特征对齐
语音算法工程师学习路线 · 音频基础 02问题背景语音模型的输入不仅由张量形状定义还由生成张量的完整前处理决定。两套实现都输出[T, 80]并不意味着它们生成了相同的 Fbank。本文面向需要理解音频前处理、核对训练与部署输入的工程师记录一套可复现的 Log-Mel Fbank 实现并重点讨论工程中容易产生差异的参数。处理流程WAV解码 → float32归一化 → 预加重 → 分帧 → 加窗 → RFFT → 功率谱 → Mel滤波器组 → 对数压缩 → Fbank实验使用 16 kHz、单声道、16-bit PCM WAV帧长 25 ms帧移 10 ms512 点 FFT80 个 Mel 滤波器。短时分析语音在较长时间范围内是非平稳信号但在几十毫秒内可以近似进行短时频谱分析。帧长影响频率分辨率和短时稳定假设帧移影响时间采样密度和计算量。本文不做中心填充对末尾不足一帧的部分补零。帧数计算为1 ceil((max(采样数, 400) - 400) / 160)。固定 1 秒、16000 个采样点得到 99 帧最后一帧补 80 个零如果丢弃不完整末帧则得到 98 帧。不同库采用丢弃、中心填充或其他边界规则时时间帧数可能不同。窗函数与功率谱有限长度截断会产生频谱泄漏。乘窗能降低边界突变但也会改变谱形。训练使用 Hamming部署就不能随意换成 Hann。功率谱计算为spectrum np.fft.rfft(frames, nn_fft) power np.abs(spectrum) ** 2 / n_fft这里的缩放方式也需要写进输入规范。Mel滤波器组与对数Mel 滤波器组在低频区域更密、高频区域更疏。三角滤波器对功率谱做频带汇聚后再使用对数压缩动态范围。mel_energy power filters.T fbank np.log(np.maximum(mel_energy, 1e-10))本实现覆盖 0–8000 Hz用 floor 将 Mel 边界离散到 FFT 频点构造简化三角滤波器不做滤波器面积归一化使用自然对数能量下限为 1e-10。Mel 公式、频率上下限、频点离散、面积归一化、对数底数和能量下限都会影响数值。可复现实验使用 Python 3.10 或以上版本依赖 NumPy。本次验证环境为 Python 3.12.14、NumPy 2.3.5、macOS arm64这是前处理脚本验证没有运行真实语音模型或目标设备。将下方完整代码保存为fbank_demo.py。自备非空的 16 kHz、单声道、16-bit PCM WAV命名为input.wav并放在同一目录脚本不会自动重采样或下混。python -m pip install numpy python -B fbank_demo.py input.wav --save input_fbank.npy以下是完整源码包含命令行入口、分帧和 Mel 滤波器组实现#!/usr/bin/env python3 Read a mono 16-bit PCM WAV and compute Log-Mel Fbank features. from __future__ import annotations import argparse import wave from pathlib import Path import numpy as np def read_wav(path: Path) - tuple[np.ndarray, int]: with wave.open(str(path), rb) as reader: channels reader.getnchannels() sample_width reader.getsampwidth() sample_rate reader.getframerate() frames reader.readframes(reader.getnframes()) if channels ! 1 or sample_width ! 2 or sample_rate ! 16000: raise ValueError(示例只接受 16 kHz / mono / 16-bit PCM WAV请先按模型约定转换) if not frames: raise ValueError(WAV 没有音频采样) samples np.frombuffer(frames, dtypei2).astype(np.float32) / 32768.0 return samples, sample_rate def hz_to_mel(hz: np.ndarray | float) - np.ndarray: return 2595.0 * np.log10(1.0 np.asarray(hz) / 700.0) def mel_to_hz(mel: np.ndarray | float) - np.ndarray: return 700.0 * (10.0 ** (np.asarray(mel) / 2595.0) - 1.0) def frame_signal(samples: np.ndarray, frame_length: int, frame_shift: int) - np.ndarray: if len(samples) frame_length: samples np.pad(samples, (0, frame_length - len(samples))) frame_count 1 int(np.ceil((len(samples) - frame_length) / frame_shift)) total_length (frame_count - 1) * frame_shift frame_length samples np.pad(samples, (0, total_length - len(samples))) starts np.arange(frame_count)[:, None] * frame_shift offsets np.arange(frame_length)[None, :] return samples[starts offsets] def mel_filterbank(sample_rate: int, n_fft: int, n_mels: int) - np.ndarray: mel_points np.linspace(hz_to_mel(0.0), hz_to_mel(sample_rate / 2), n_mels 2) hz_points mel_to_hz(mel_points) bins np.floor((n_fft 1) * hz_points / sample_rate).astype(int) bins np.clip(bins, 0, n_fft // 2) filters np.zeros((n_mels, n_fft // 2 1), dtypenp.float32) for index in range(n_mels): left, center, right bins[index : index 3] if center left: filters[index, left:center] ( np.arange(left, center) - left ) / (center - left) if right center: filters[index, center:right] ( right - np.arange(center, right) ) / (right - center) return filters def compute_fbank( samples: np.ndarray, sample_rate: int, frame_ms: float 25.0, shift_ms: float 10.0, n_fft: int 512, n_mels: int 80, pre_emphasis: float 0.97, ) - np.ndarray: if samples.ndim ! 1 or samples.size 0: raise ValueError(samples 必须是非空的一维采样序列) if sample_rate ! 16000 or frame_ms 0 or shift_ms 0 or n_mels 0: raise ValueError(本示例要求 16 kHz 输入帧长、帧移与 Mel 维数均为正) emphasized np.append(samples[0], samples[1:] - pre_emphasis * samples[:-1]) frame_length round(sample_rate * frame_ms / 1000.0) frame_shift round(sample_rate * shift_ms / 1000.0) if frame_length 1 or frame_shift 1 or n_fft frame_length: raise ValueError(帧长和帧移至少为 1 个采样点FFT 点数不能小于帧长) frames frame_signal(emphasized, frame_length, frame_shift) frames * np.hamming(frame_length).astype(np.float32) spectrum np.fft.rfft(frames, nn_fft) power (np.abs(spectrum) ** 2 / n_fft).astype(np.float32) mel_energy power mel_filterbank(sample_rate, n_fft, n_mels).T return np.log(np.maximum(mel_energy, 1e-10)).astype(np.float32) def main() - int: parser argparse.ArgumentParser(description__doc__) parser.add_argument(wav, typePath) parser.add_argument(--n-mels, typeint, default80) parser.add_argument(--save, typePath, help可选将特征保存为 .npy) args parser.parse_args() samples, sample_rate read_wav(args.wav) features compute_fbank(samples, sample_rate, n_melsargs.n_mels) print(fsample_rate{sample_rate}) print(fsamples{samples.shape}, duration{len(samples) / sample_rate:.3f}s) print(ffbank_shape{features.shape}, dtype{features.dtype}) print(fmin{features.min():.6f}, max{features.max():.6f}, mean{features.mean():.6f}) print(first_frame_first_8, np.array2string(features[0, :8], precision5)) if args.save: args.save.parent.mkdir(parentsTrue, exist_okTrue) np.save(args.save, features) print(fsaved{args.save.resolve()}) return 0 if __name__ __main__: raise SystemExit(main())项目内固定 1 秒、440 Hz、16 kHz、单声道、16-bit PCM 合成音频的基准统计为fbank_shape(99, 80), dtypefloat32 min-23.025850, max-3.616596, mean-16.075018自备语音会得到不同的帧数与统计量。上面数字仅描述固定输入不能拿它判断另一段音频是否正确。本文固定 16 kHz 输入以保持 400 点帧长不超过 512 点 FFT改变采样率时需要重新定义整套参数。对齐策略推荐把前处理拆成稳定接口并对固定 WAV 保存以下参考数据归一化后的采样值第一帧加窗结果第一帧功率谱第一帧 Mel 能量第一帧 Log-Mel完整特征的 shape 和统计量。逐层对齐能够判断问题发生在音频解码、帧边界、频谱、Mel 滤波还是后续归一化而不是把所有差异都归因于模型导出。局限本文实现用于解释链路和建立对齐基准没有实现去直流、dither 或 CMVN不声称与 Kaldi、torchaudio、librosa 或任意商用 SDK 默认配置完全一致。项目中应以训练配置和已有参考实现为准本文统计不代表模型效果、速度、内存或功耗。结论Fbank 并不是可替换的通用前处理名称而是一组必须明确版本和参数的计算规则。输入契约没有固定模型部署就没有可靠的数值基线。下一篇一个语音唤醒系统包含哪些模块原作者AI算法学习社。微信搜索公众号「AI算法学习社」回复「语音路线」领取公开免费版。

相关新闻

FurMark 1.6.5:GPU供电与显存稳定性深度验证工具

FurMark 1.6.5:GPU供电与显存稳定性深度验证工具

简介:FurMark 1.6.5 是一款专业级 OpenGL 显卡压力测试与稳定性检测工具,面向硬件工程师、超频爱好者及DIY用户,用于评估显卡在极限负载下的性能表现、温度控制与长期运行可靠性。资源包共20个文件,含5个批处理脚本(如…

2026/10/10 12:34:12 阅读更多 →
SpringBoot体检预约系统开发难点与并发控制实战

SpringBoot体检预约系统开发难点与并发控制实战

简介:面向中小医院体检预约管理场景的Spring Boot开发学习资料,以“人民医院体检预约系统”为完整案例,覆盖患者端与后台管理端两大模块:前者包含注册登录、体检列表/套餐查看预约、咨询与在线客服;后者包含用户管理、…

2026/10/10 12:34:12 阅读更多 →
MIMIC-IV数据库实操:从申请到PostgreSQL导入与SQL查询

MIMIC-IV数据库实操:从申请到PostgreSQL导入与SQL查询

简介:这是一份面向医学研究人员、数据科学从业者及临床AI学习者的MIMIC-IV数据库入门与使用笔记。资源为单个docx文档(约97KB),针对MIMIC-IV_V2.0版本,系统梳理了患者标识体系(subject_id唯一标识患者、had…

2026/10/10 12:34:12 阅读更多 →

最新新闻

Harness 工程安全基线:为 AI Agent 编写 SECURITY.md 安全策略文件

Harness 工程安全基线:为 AI Agent 编写 SECURITY.md 安全策略文件

【免费下载链接】learn-harness-engineering Harness engineering beginner tutorial, from 0 to 1 项目地址: https://gitcode.com/gh_mirrors/le/learn-harness-engineering 点击查看 免费下载 SECURITY.md 是面向 Agent 的仓库(agent-first reposito…

2026/10/10 14:07:49 阅读更多 →
ponyc 0.57.1 修复 x86 macOS 上 Xcode 15 链接 Pony 程序失败问题解析

ponyc 0.57.1 修复 x86 macOS 上 Xcode 15 链接 Pony 程序失败问题解析

编程语言编译器语言运行时 【免费下载链接】ponyc Pony is an open-source, actor-model, capabilities-secure, high performance programming language 项目地址: https://gitcode.com/gh_mirrors/po/ponyc 点击查看 免费下载 导读 ponyc 0.57.1 是一次聚焦单一…

2026/10/10 14:07:49 阅读更多 →
LeetCode 139 单词拆分全解析:动态规划、剪枝优化与 Trie 加速

LeetCode 139 单词拆分全解析:动态规划、剪枝优化与 Trie 加速

刷 LeetCode 的人,几乎都会被一道叫“单词拆分”的题拦住过。它排在热门 100 题的中段,题干看起来非常简单:给一个字符串和一个字典,问这个字符串能不能被字典里的单词完整拼出来。但第一次动手写的时候,很容易在贪心、…

2026/10/10 14:07:49 阅读更多 →
每日热门skill-半年228K星,ECC凭什么让AI编程Agent长出肌肉记忆:TaoToken统一Key接入Claude Code与Cursor的配置实录

每日热门skill-半年228K星,ECC凭什么让AI编程Agent长出肌肉记忆:TaoToken统一Key接入Claude Code与Cursor的配置实录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 14:07:49 阅读更多 →
GGUF 十四个量化版本翻车实录:0731 版本地部署炸显存/掉速的坑全在这

GGUF 十四个量化版本翻车实录:0731 版本地部署炸显存/掉速的坑全在这

GGUF 十四个量化版本翻车实录:0731 版本地部署炸显存/掉速的坑全在这 【免费下载链接】DeepSeek-V4-Flash-0731 项目地址: https://ai.gitcode.com/hf_mirrors/deepseek-ai/DeepSeek-V4-Flash-0731 DeepSeek-V4-Flash-0731 官方发布后,社区里最热…

2026/10/10 14:07:49 阅读更多 →
STM32F423RH与PJ85718DM的HVAC温度监测方案

STM32F423RH与PJ85718DM的HVAC温度监测方案

1. 项目背景与核心需求拆解温度监测这件事,看起来简单,真要做到“本地能看、远程能查、长期稳定”,里面门道不少。我最近在做一个嵌入式和 HVAC(暖通空调)场景下的温度采集方案,主控用的是 STM32F423RH&…

2026/10/10 14:06:48 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →