02|单调对齐搜索:动态规划与可验证路径
MAS(Monotonic Alignment Search)不是 softmax 注意力。本实验输入已经计算好的“帧—音素”得分矩阵,输出最佳硬路径;网络如何提供得分属于上游模型。Glow-TTS 论文
递推先计算最优累计得分,回溯再恢复实际对齐路径。

图 1|单调路径的停留、前进与终点约束
1. 递推及边界
公式符号与约定
| 符号 | 含义、单位与约定 |
|---|---|
| 声学帧索引,教学代码从 0 开始。 | |
| 音素索引,教学代码从 0 开始。 | |
| 当前帧归属当前音素的局部得分;可来自条件对数似然。 | |
| 从起点到此单元的最高累计得分。 | |
| 上一帧留在同一音素的路径。 | |
| 上一帧位于前一个音素的路径。 | |
| 选择累计分更高的合法前驱;不合法位置设为负无穷。 |
起点只允许 (0,0),終点为 (T-1,N-1);帧数 T 至少等于音素数 N。本代码保证每个音素至少占一帧,对零时长特殊符号应在上游先处理。
2. 数据与代码地图
| 顺序 | 变量 | 作用 |
|---|---|---|
| 输入 | score[T,N] float | 局部得分 |
| 前向递推 | dp[T,N] float | 最优累计得分 |
| 前驱记录 | move[T,N] bool | 是否来自前一个音素 |
| 终点回溯 | path[T,N] | 每行一个 1 |
| 按帧求和 | path.sum(0) | 每个音素的帧数 |
python19 行
def monotonic_path(score):
"""[frames,phones]; stay or advance by one; every phone gets >=1 frame."""
frames, phones = score.shape
if frames < phones: raise ValueError('not enough frames')
dp = torch.full_like(score, -torch.inf)
move = torch.zeros_like(score, dtype=torch.bool)
dp[0,0] = score[0,0]
for t in range(1, frames):
for n in range(min(t+1, phones)):
stay = dp[t-1,n]
advance = dp[t-1,n-1] if n else score.new_tensor(-torch.inf)
move[t,n] = advance > stay
dp[t,n] = score[t,n] + torch.maximum(stay,advance)
n = phones-1
path = torch.zeros_like(score)
for t in range(frames-1,-1,-1):
path[t,n] = 1
n -= int(move[t,n])
return path, dp[-1,-1]
3. 实际检查与数值解释
实验得分矩阵在完整源码中固定;最佳路径为 [0,0,1,2,2],选中分数为 3+2+3+4+5=17,时长为 [2,1,2]。同时枚举五帧分给三个音素的全部正整数时长组合,独立确认 17 是最大值,避免只验证输出形状。
动态规划结果与穷举一致,且每帧恰好分给一个音素。
硬 argmax 路径本身不可按普通连续函数求导;网络是在当前路径下更新,而不是对搜索索引反向传播。
本教学实现用 Python 循环便于阅读,时间与内存复杂度均随 T×N 增长。生产实现通常使用编译后的搜索核并处理 batch mask;把教学版速度当成 Glow-TTS 的模型速度没有意义。运行入口见 机制实验源码,返回 Glow-TTS。
正在连接 GitHub 评论…