Soft Clustering Anchors for Self-Supervised Speech Representation Learning in Joint Embedding Prediction Architectures

Paper: arXiv:2602.09040 Code: gioannides/clustering-anchored-jepa Code reference: main @ edb58af1 (2026-02-07)

1. Motivation (研究动机)

这篇论文处理的是一个很具体的失败模式:把 Joint Embedding Predictive Architecture (JEPA) 直接搬到 speech SSL 时,模型可能不是学到更抽象的 phonetic / acoustic representation,而是进入 representation collapse。论文中的 Pure JEPA 在 ASR 下游评测上达到 100.00% WER,接近完全不可用;聚类分析也显示它只有 45% normalized entropy、只使用 516/1024 个 cluster。这说明 EMA teacher + masked latent prediction 本身并没有给 speech encoder 足够稳定的声学参考系,teacher 会跟着 student 一起漂移,最后只能提供“自洽但无声学意义”的目标。

现有 speech SSL 的主流解法通常绕不开离线聚类或离散目标:HuBERT / WavLM 会先用 k-means 产生 pseudo-label,再用中间层表示重新聚类、重新训练。这个范式有效,但代价是多轮 re-clustering:每一轮都要先抽取全量特征,再拟合 clustering,再训练模型,工程和计算调度都很重。BEST-RQ 避免了迭代 re-clustering,但它用的是 frozen random-projection quantizer;目标稳定,却不是从输入声学分布拟合出来的,和 phoneme boundary / spectral ambiguity 的关系较弱。

作者想解决的问题不是刷新 speech SSL 的绝对 SOTA,而是验证一个更基础的命题:如果 JEPA 的预测目标来自 latent teacher,那么能否用一个不会随 encoder 一起 co-adapt 的外部 acoustic anchor 来阻止 speech representation collapse?如果可行,JEPA 就可以保留“预测 latent 而非重建 waveform / spectrogram、无需 contrastive negatives”的优点,同时不再依赖 HuBERT/WavLM 式的多轮硬聚类。

这个问题值得研究,因为 speech signal 的局部边界天然模糊:很多 frame 处在 phonetic transition 之间,hard k-means 会把它强行分配到某一个 cluster,丢掉边界不确定性。Gaussian Mixture Model (GMM) 的 soft posterior 则能表示“这个 frame 同时像多个声学成分”的概率结构。若这种 soft clustering target 足够稳定,它可以在 early training 给 encoder 一个低层声学坐标系;等 JEPA loss 逐渐接管后,模型仍可在 residual anchor 下学习更高层的时间上下文抽象。

2. Idea (核心思想)

核心洞察:speech JEPA collapse 的根因不是缺少更复杂的 predictor,而是 teacher-student 系统缺少一个不随网络更新而漂移的声学参照;因此,先在 log-mel feature space 上拟合一次 frozen GMM,再把它的 soft posterior 当作辅助目标,就能把 JEPA 的 latent prediction 绑回稳定的 acoustic structure。

本文的关键创新是 GMM-Anchored JEPA:Phase 1 只在输入 log-mel spectrogram 上拟合一次 diagonal-covariance GMM;Phase 2 中 student encoder 同时做 masked EMA-teacher latent prediction 和 GMM posterior matching,且 cluster loss 权重 从 1.0 线性衰减到 0.01。这个 residual weight 不是装饰项:ablation 显示把 衰减到 0 会让 WER 从 29.2% 恶化到 40.9%,cluster entropy 从 84.7% 掉到 57.7%。

和 HuBERT / WavLM 的根本差别在于:HuBERT / WavLM 用 hard k-means target,并且依赖迭代 re-clustering;GMM-Anchored JEPA 只聚类一次输入特征,使用 soft posterior,训练期间 GMM 冻结不更新。和 BEST-RQ 的差别在于:BEST-RQ 的目标是 random projection quantizer,稳定但随机;GMM anchor 是从真实 acoustic feature distribution 拟合出来的,保留了语音帧靠近多个 cluster boundary 时的不确定性。

3. Method (方法)

3.1 Overall framework:两阶段、一个 frozen acoustic anchor

Figure 1 解读:图中左侧 Phase 1 是一次性 GMM fitting:输入 waveform 先变为 log-mel features,然后拟合 个 diagonal Gaussian components。右侧 Phase 2 是真正的 JEPA pretraining:online encoder 看增强后的 audio,target encoder 是 EMA copy 并看 clean audio;predictor 只在 masked positions 上预测 target latent,同时 cluster head 预测 frozen GMM posterior。 从 1.0 衰减到 0.01,使 early stage 主要由 GMM anchor 建立声学结构,late stage 让 JEPA objective 学上下文抽象,但保留极小 anchor 防止 drift。

整个方法可以理解成给 JEPA 加了一个“不会被模型说服的老师”。EMA teacher 虽然平滑,但它的表示来自同一个训练过程;如果 student 开始把不同语音都映射到相似区域,teacher 也会逐步复制这种退化表示。GMM 则只看固定的 log-mel space,训练期间不再更新,因此它不能和 encoder 一起坍缩。早期强监督把 encoder 拉到声学可分的区域,后期残余监督则像 regularizer 一样阻止 JEPA loss 在 masked prediction 中走向无信息的自洽解。

3.2 Phase 1:在 log-mel 上拟合 diagonal-covariance GMM

给定 waveform ,先计算 论文使用 log-mel 而不是 raw waveform 或 learned feature,因为 log-mel 是稳定、低维且不随训练变化的 acoustic representation。GMM 有 个 component,使用 diagonal covariance 来控制大规模语音帧上的拟合成本。对单帧特征 ,soft posterior 为 Appendix 进一步写成 log-probability + softmax 的形式: released code 中 train/gmm/gmm.pyGradientGMM 与这个公式一致:meanslog_varslog_weights 是可学习参数,covariancesexp(log_vars) 得到并 clamp 到最小 soft_assign() 调用 log_prob() 后对 component 维度做 F.softmaxtrain/gmm/fit_gmm.py 用 Adam + cosine scheduler 拟合 GMM,默认 K=1024n_mels=80lr=1e-2sgd_batch_size=16384,并用 chunking 避免 一次性展开导致 OOM。

3.3 Phase 2:JEPA latent prediction + GMM posterior matching

student encoder 记为 ,EMA target encoder 记为 ,predictor 记为 。mask 后的 student latent 为 ,target latent 为 。JEPA loss 只在 masked positions 上计算: cluster head 把 encoder 输出映射到 个 logits,经 softmax 得到 。cluster loss 用 KL divergence 匹配 frozen GMM posterior: 总损失为 其中 。released code 的 train/jepa_conformer/train.pytrain/jepa_transformer/train.py 中,create_mask() 返回 1=keep、0=mask;训练时 m=(1-mask)*pad_mask,因此 MSE 和 KL 都只对 masked real-audio frames 计入损失。EMA 更新由 utils.ema_update(target, online, decay=args.ema_decay) 实现,默认 ema_decay=0.996

3.4 Denoising augmentation:online 看增强音频,target 看 clean audio

为了让表示对噪声和重叠语音更稳健,online encoder 输入 ,target encoder 输入 clean waveform。噪声混合按目标 SNR 缩放: utterance mixing 从另一个 utterance 抽取片段并按能量比混合: 论文和代码都给出相同的默认概率:noise probability 0.25,mix probability 0.25;noise SNR 从 dB 采样,speech mix ratio 从 dB 采样;augmentor 维护 64 条 recent utterances 的 CPU buffer 作为 noise/mix source。

3.5 表示分析图:collapse、cluster usage 和 temporal stability

Figure 2 解读:四个 panel 比较 frame-level embeddings 的 UMAP。Pure JEPA 聚成很小的密集区域,说明不同 acoustic frames 被压到相近 latent;WavLM-style 覆盖范围更大,但 cluster 边界 diffuse 且重叠;GMM-JEPA-T 和 GMM-JEPA 则形成更清楚的局部区域,说明 frozen soft anchor 不只是改善下游任务,也改变了 representation geometry。

Figure 3 解读:横轴是按频次排序后的 cluster rank,纵轴是 frame count 的 log scale。Pure JEPA 大约在 270 个 cluster 后快速衰减,后面大量 cluster 几乎不用;WavLM-style 虽然 used clusters 数量达到 978/1024,但频次非常不均匀,少数 cluster 占主导,因此 entropy 只有 31%。GMM-JEPA 两个变体曲线更平,说明 soft GMM anchor 让 1024 个 cluster 被更均匀地利用。

Figure 4 解读:每个 panel 上半部是 frame-level cluster ID,下半部是 confidence。Pure JEPA 的 cluster ID 快速 flicker,confidence 接近零;WavLM-style confidence 在 0.4—0.6 左右但仍频繁切换;GMM-JEPA-T 维持约 0.7—0.9 的高 confidence,GMM-JEPA 也有更连贯的 temporal spans。这个图直接对应论文对 adjacent consistency 的讨论:有声学意义的 cluster 应该能持续 50—200ms,而不是每个 20ms frame 都随机跳变。

Figure 5 解读:这是 的 ablation 可视化。即使 early training 有 GMM grounding,只要后期完全移除 anchor,UMAP 仍会变得 diffuse,cluster distribution 再次出现陡峭 dropoff,utterance-level assignment 也更不稳定。它说明 GMM supervision 不是一次性 initialization trick,而是整个训练过程中的 residual regularizer。

3.6 Source-grounded pseudocode

下面伪代码按 released code 的结构写,而不是只复述论文算法。

import torch
import torch.nn.functional as F
 
class GradientGMM(torch.nn.Module):
    def __init__(self, K: int = 1024, dim: int = 80):
        super().__init__()
        self.means = torch.nn.Parameter(torch.randn(K, dim) * 0.1)
        self.log_vars = torch.nn.Parameter(torch.zeros(K, dim))
        self.log_weights = torch.nn.Parameter(torch.zeros(K))
 
    def log_prob(self, x: torch.Tensor) -> torch.Tensor:
        weights = F.softmax(self.log_weights, dim=0)
        vars = torch.exp(self.log_vars).clamp(min=1e-1)
        diff = x.unsqueeze(1) - self.means.unsqueeze(0)
        mahal = ((diff ** 2) / vars.unsqueeze(0)).sum(dim=-1)
        log_det = torch.log(vars).sum(dim=-1)
        log_prob = -0.5 * (x.shape[-1] * torch.log(torch.tensor(2.0 * 3.14159265)) + log_det + mahal)
        return log_prob + torch.log(weights + 1e-10).unsqueeze(0)
 
    @torch.no_grad()
    def soft_assign(self, x: torch.Tensor) -> torch.Tensor:
        if x.dim() == 3:  # [B, C, T] -> [B*T, C]
            x = x.permute(0, 2, 1).reshape(-1, x.shape[1])
        return F.softmax(self.log_prob(x.float()), dim=-1)
def make_block_mask(batch_size, frames, frame_lengths, min_span=10, max_span=25, ratio_range=(0.4, 0.65)):
    # Code convention: 1 = keep, 0 = mask.
    mask = torch.ones(batch_size, frames)
    for b in range(batch_size):
        real_T = frame_lengths[b]
        to_mask = int(real_T * torch.empty(()).uniform_(*ratio_range).item())
        masked = 0
        while masked < to_mask:
            span = torch.randint(min_span, min(max_span, max(min_span, real_T // 3)) + 1, ()).item()
            start = torch.randint(0, max(1, real_T - span), ()).item()
            end = min(start + span, real_T)
            mask[b, start:end] = 0
            masked += end - start
        mask[b, real_T:] = 1
    return mask
def gmm_anchored_jepa_step(online, target, frozen_gmm, batch, optimizer, ema_decay=0.996):
    wav_clean = batch["wav"]
    wav_aug = augment(wav_clean)  # noise and utterance mixing; target still sees clean audio
 
    with torch.no_grad():
        z_target = target(wav_clean)
        log_mel = compute_log_mel(wav_clean)
        q_gmm = frozen_gmm.soft_assign(log_mel)  # [B*T, K]
 
    mask = make_block_mask(wav_clean.shape[0], z_target.shape[-1], batch["frame_lengths"]).to(wav_clean.device)
    pad_mask = make_padding_mask(batch["frame_lengths"], z_target.shape[-1]).to(wav_clean.device)
 
    z_online, z_pred, cluster_logits = online(wav_aug, mask)
    masked_real = (1.0 - mask).unsqueeze(1) * pad_mask.unsqueeze(1)
    jepa_loss = ((z_pred - z_target.detach()) ** 2 * masked_real).sum()
    jepa_loss = jepa_loss / (masked_real.sum() * z_pred.shape[1]).clamp_min(1)
 
    flat = ((1.0 - mask) * pad_mask).view(-1).bool()
    pred_log_probs = F.log_softmax(cluster_logits.view(-1, cluster_logits.shape[-1])[flat], dim=-1)
    cluster_loss = F.kl_div(pred_log_probs, q_gmm[flat], reduction="batchmean")
 
    progress = current_step() / max_steps()
    cluster_weight = 1.0 + (0.01 - 1.0) * progress
    loss = jepa_loss + cluster_weight * cluster_loss
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    ema_update(target, online, decay=ema_decay)
    return loss
class DenoiseAugmentor:
    def __init__(self, p_noise=0.25, p_mix=0.25, noise_snr=(-5, 20), speech_snr=(-5, 5), buffer_size=64):
        self.p_noise = p_noise
        self.p_mix = p_mix
        self.noise_snr = noise_snr
        self.speech_snr = speech_snr
        self.buffer = []
        self.buffer_size = buffer_size
 
    def scale_for_snr(self, clean, noise, snr_db):
        clean_energy = clean.pow(2).mean().clamp_min(1e-8)
        noise_energy = noise.pow(2).mean().clamp_min(1e-8)
        return torch.sqrt(clean_energy / (10 ** (snr_db / 10.0) * noise_energy))
 
    @torch.no_grad()
    def __call__(self, wav_batch):
        aug = wav_batch.clone()
        # released code samples noise/mix from a rolling CPU utterance buffer.
        # It returns augmented audio while the target branch keeps wav_batch clean.
        return aug, wav_batch

3.7 Code-to-paper mapping and reproducibility caveats

Code reference: main @ edb58af1 (2026-02-07) — pseudocode and mapping based on this commit

Paper ConceptSource FileKey Class/Function
one-time GMM fitting on log-mel featurestrain/gmm/fit_gmm.py, train/gmm/gmm.pyGMMFitter, GradientGMM, soft_assign()
frozen GMM posterior targettrain/jepa_conformer/model.py, train/jepa_transformer/model.pyFrozenGMM.load(), FrozenGMM.soft_assign()
Conformer GMM-JEPA online/target encodertrain/jepa_conformer/model.pyOnlineEncoder, TargetEncoder, ClusterHead
Transformer GMM-JEPA varianttrain/jepa_transformer/model.pyOnlineEncoder, TargetEncoder, ConvFeatureExtractor
masked JEPA + KL cluster loss training looptrain/jepa_conformer/train.py, train/jepa_transformer/train.pytrain(), create_mask(), F.kl_div, ema_update()
denoising augmentationtrain/jepa_conformer/augment.py, train/jepa_transformer/augment.pyDenoiseAugmentor
WavLM-style hard-clustering baselinetrain/kmeans/fit_kmeans.py, train/wavlm/train.pyMiniBatchKMeans, WavLM-style labeler/training
representation/cluster analysisevaluation/cluster_analysis/...evaluate.py, evaluate_wavlm.py

论文公式与 released code 实现差异:第一,论文 Table 10 报告 Transformer variant 为 latent dim 512、10 layers、8 heads、FFN 2048、41.3M 参数;但 released train/jepa_transformer/README.md 的 usage 示例使用 --code_dim 768 --num_heads 12 --ff_dim 3072 --num_layers 12,并把它列为 Base 约 95M 参数。README 的 Small 配置(512/10/8/2048)更接近论文,但 conv_dim 和参数量仍未完全等同。

第二,论文 Table 10 写 GMM fitting 使用 9.6B training frames;released train/gmm/fit_gmm.py 默认 target_frames=100_000_000,仓库没有公开对应 9.6B 的 launch config。第三,论文 WavLM-style baseline 使用 ,而 released train/kmeans/fit_kmeans.py 的 usage/default 示例是 --K 512,复现实验必须显式覆盖。

第四,code 支持 gmm.dim == args.code_dim 时用 latent features 做 soft assignment,也支持多个 GMM 加权;论文主方法描述的是 log-mel GMM anchor。换言之,仓库提供了核心机制实现,但没有完整公开论文实验的 exact launch scripts / DeepSpeed config。

4. Experimental Setup (实验设置)

预训练数据。 模型在约 50,000 小时 speech 上预训练,数据来自 LibriLight large subset 和 English Granary。论文说明 GMM fitting / k-means fitting 的 training frames 为 9.6B,GMM 和 k-means 都在 80-bin log-mel feature 上运行,cluster 数

模型与对比方法。 作者评估两个 anchored encoder:Conformer-based GMM-JEPA(31.1M 参数,3 个 CNN/downsampling stages,4 个 Conformer layers,32 heads,latent dim 512)和 transformer-based GMM-JEPA-T(41.3M 参数,7-layer CNN frontend,10 个 Transformer layers,8 heads,FFN dim 2048,latent dim 512)。主要 baselines 是 Pure JEPA(,无 GMM anchor)和 WavLM-style baseline(同一总体架构,但用 log-mel k-means hard labels)。直接和 published HuBERT/WavLM checkpoint 比较被作者认为不公平,因为 model size、training data、compute 都不同。

下游任务与数据。 ASR 使用 frozen encoder representation,在 LibriSpeech train-clean-100 上训练 2-layer BiLSTM + CTC,带 SpecAugment,decoding 使用 greedy decoding 且无 language model,报告 LibriSpeech dev-clean WER。Emotion recognition 使用 IEMOCAP,四类 emotion(angry、happy、sad、neutral),mean-pooled representation + linear classifier,5-fold cross-validation。Slot filling 使用 SNIPS,speaker-disjoint splits,报告 Slot Type F1 和 Edit F1。表示质量还通过 K-means clustering 后的 entropy、used clusters、adjacent consistency、NMI、UMAP 和 cluster-confidence plots 评估。

训练超参数。 论文 Table 10 报告:GMM 使用 K-means++ 初始化、Mini-batch SGD 优化、learning rate cosine、9.6B frames;WavLM-style k-means baseline 使用 K-means++、Lloyd’s algorithm、20 iterations、9.6B frames。JEPA 训练用 AdamW,learning rate ,10% linear warmup/decay,weight decay ,gradient clipping 1.0,8 B200,EMA 从 1.0 衰减到 0.01,mask ratio ,span length frames。micro batch size 为 Conformer 96、Transformer 192。Augmentation 使用 noise SNR dB、mix ratio dB、noise/mix probability 各 0.25。

指标定义。 WER 越低越好,用于 ASR;SER accuracy 越高越好,用于 IEMOCAP emotion recognition;Slot Type F1 / Edit F1 衡量 slot filling;cluster entropy 衡量 cluster usage 是否均匀;Used 是实际被使用的 cluster 数;Adjacent consistency 是 ,衡量相邻 frame 的 cluster 是否连续;NMI 定义为 ,衡量两个 clustering 的一致性。

5. Experimental Results (实验结果)

主任务结果。 ASR 上,Pure JEPA 完全 collapse,WER 为 100.00%、CER 为 93.11%;WavLM-style baseline WER 为 33.22%、CER 为 11.28%;GMM-JEPA WER 降到 29.18%、CER 9.62%,相对 WavLM-style WER 改善 12.2%;GMM-JEPA-T 最好,WER 28.68%、CER 9.44%,相对改善 13.7%。这说明 GMM anchor 不只是让表示“看起来更均匀”,也能直接转化到 frozen-feature ASR。

ModelWER ↓CER ↓Relative WER
Pure JEPA100.0093.11
WavLM-style33.2211.28baseline
GMM-JEPA29.189.62-12.2%
GMM-JEPA-T28.689.44-13.7%

Slot filling 上,Pure JEPA 只有 Type F1 5.0、Edit F1 2.2;WavLM-style 为 59.1 / 33.7;GMM-JEPA-T 为 59.2 / 32.8,基本持平;GMM-JEPA 达到 Type F1 64.7、Edit F1 36.0,相对 WavLM-style 在 Type F1 上提升 5.6 个点。Emotion recognition 上,WavLM-style 五折平均 65.46%,GMM-JEPA 为 67.30%(+1.8),GMM-JEPA-T 为 67.76%(+2.3);每一折都高于 WavLM-style。

ModelSER F1F2F3F4F5Avg
Pure JEPA48.8846.1249.8948.6746.9748.11
WavLM-style64.8364.1965.3067.1165.8565.46
GMM-JEPA66.0767.8566.6768.5667.3767.30
GMM-JEPA-T67.1967.5267.5868.1168.4167.76

Cluster quality。 Table 4 是本文最能支撑“anchor prevents collapse”的证据。Pure JEPA entropy 45%、used clusters 516、adjacent consistency 0.205;WavLM-style entropy 更低,只有 31%,但 used clusters 为 978,说明它不是少用 cluster,而是极端不均匀:很多 cluster 只被少量 frame 使用,少数 dominant clusters 吞掉大部分分配。GMM-JEPA entropy 85%、used 1007、adjacent consistency 0.395;GMM-JEPA-T entropy 98%、used 1013、adjacent consistency 0.299。作者认为 adjacent consistency 不是越高越好:WavLM-style 的 0.750 可能来自 dominant cluster 长时间占用,不一定是 acoustic boundary 更好。

ModelEntropy ↑Used ↑Adj. Cons.
Pure JEPA45%5160.205
WavLM-style31%9780.750
GMM-JEPA85%10070.395
GMM-JEPA-T98%10130.299

Probe 与 NMI。 Speaker/Gender linear probe 方面,Pure JEPA 只有 Speaker 18.7±1.8、Gender 69.2±1.5;WavLM-style 已经接近饱和(99.6±0.3 / 99.4±0.2),GMM-JEPA-T 和 GMM-JEPA 也都接近 99.7—99.8。Phoneme probe 更能区分模型:Pure JEPA linear 7.4±0.0、MLP 9.0±0.1;WavLM-style 47.6±0.2 / 57.5±0.2;GMM-JEPA-T 51.4±0.1 / 59.4±0.4;GMM-JEPA 53.0±0.1 / 64.2±0.2。NMI between embedding clusters 显示 Pure JEPA 与其他模型几乎无共同结构(和 WavLM 0.08、和 GMM-JEPA 0.11、和 GMM-JEPA-T 0.06);GMM-JEPA 与 WavLM-style 为 0.47,GMM-JEPA-T 与 WavLM-style 为 0.57,说明 anchored methods 学到的结构和 hard-clustering baseline 有部分重叠,但不是简单复制。

Residual anchor ablation。 从 0.01 改为 0.00 后,cluster entropy 从 84.7 掉到 57.7,used clusters 从 1011 掉到 530;WER 从 29.2 上升到 40.9;SER 从 67.3 掉到 63.8;Slot Type F1 从 64.7 掉到 62.3;Slot Edit F1 从 36.0 掉到 34.2。正文文字还给出更精确的 WER 29.18% 40.95%、SER 67.30% 63.79%。这组结果支持作者的关键主张:GMM anchor 不是只在 early training 初始化一下 latent space,而是必须以很小权重持续存在,防止 JEPA objective 后期重新主导并造成 representational drift。

Entropy ↑Used ↑WER ↓SER ↑SF-T ↑SF-E ↑
0.0057.753040.963.862.334.2
0.0184.7101129.267.364.736.0

限制与 caveats。 第一,作者明确说模型只在 50k hours 上 single pass 训练,而 HuBERT / WavLM 通过多轮 re-clustering 相当于使用更多 compute;因此本文的目标是 controlled comparison,不是绝对 SOTA。第二,论文没有和 published HuBERT/WavLM checkpoints 直接对比,避免数据、模型规模和 compute confounds。第三,作者没有做 soft GMM assignment vs hard GMM assignment 的直接 ablation,因此“soft posterior 比 hard label 更适合 acoustic boundary”的解释仍是 plausible hypothesis,而非被单独验证的结论。第四, 和 decay schedule 来自 preliminary experiments,最优 schedule 仍未系统搜索。第五,released code 没有公开 exact paper launch configs / DeepSpeed config,因此如果要复现 Table 10 的 9.6B-frame、8×B200 设置,需要额外恢复训练脚本参数而不能只跑 README 默认命令。

总体结论:GMM-Anchored JEPA 证明了一个简洁但有效的稳定化策略——用一次性拟合的 frozen soft acoustic clusters 给 JEPA 一个外部坐标系。它避免了 HuBERT/WavLM 的迭代 re-clustering,同时在 matched baseline 下改善 ASR、SER、slot filling 和 cluster utilization。最重要的技术启发是:对于 speech JEPA,collapse prevention 不能只依赖 EMA teacher 的自蒸馏结构;一个低权重、持续存在、不会 co-adapt 的 acoustic anchor 对训练后期同样关键。