1.本次进展

本次围绕音频检测模块,我重点做了三件事:

1)将训练环境从 CPU 迁移到可支持 GPU 的 Python 环境路径,加速训练

2)对音频训练脚本进行优化,新增参数

3)增加阈值扫描工具,对 spoof_prob 的判定阈值进行系统化优化

2.训练环境迁移问题与处理

2.1 现象

此前检测结果为:

python -c "import torch; print('torch=', torch.__version__); print('cuda=', torch.cuda.is_available()); print('count=', torch.cuda.device_count()); print('name=', torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'N/A')"  

torch=2.9.0+cpu

cuda=False

count=0

说明训练仍运行在 CPU 环境,速度较慢。

2.2 原因分析

核心原因是 Python 版本(3.13)与包源组合不匹配,导致安装到的不是可用的 CUDA 训练组合。

同时,transformers 与 torch 版本也出现兼容约束(涉及 torch.load 安全限制)。

2.3 处理结论

统一改用 py -3.11 指定解释器进行安装与训练,避免系统中多个 Python 环境混用。

并通过版本对齐使 torch + transformers 可以正常加载模型并训练。

2.4训练影响

训练环境迁移至GPU后,训练速度大幅提升,时间从原本训练1epoch大约1小时左右缩短到10分钟

以内,训练结果并无过多波动。

3. 训练脚本优化更新(train_wav2vec2.py)

在原脚本基础上继续做了提速与可控性增强,新增参数主要包括:

num_workers:并行读取数据(DataLoader 多进程读数据,提速明显)

eval_strategy:评估频率策略,告诉Trainer每多少步在验证集上跑一次评估(多少步、每轮)

save_strategy:checkpoint 保存策略,何时保存checkpoint

logging_steps:日志输出频率

fp16:混合精度训练(GPU),把部分计算从 FP32 切到 FP16(半精度),通过 AMP 自动混合精度来保持稳定性,可以显著提速和降低显存占用,允许更大的batchsize。

同时保留并强化此前优化:

use_class_weight:类别不平衡加权损失

weight_decay、--warmup_ratio:训练稳定性

decision_threshold:分类阈值可配置

metric_for_best_model:最佳模型选择标准

4. 阈值扫描:为什么做、怎么做

4.1 为什么要做阈值扫描

模型输出的是 spoof_prob 概率,最终分类依赖阈值 t:

若spoof_prob >= t 判 spoof(伪造),否则判 bonafide(真实)

不同阈值会直接改变误报/漏检权衡:

阈值降低:spoof 召回上升、误报可能增加

阈值升高:误报下降、漏检可能增加

反诈场景中,通常更关注降低漏检 spoof(cm_fn)

4.2 新增脚本:threshold_scan.py

我新增了阈值扫描脚本,支持:

从批量推理 CSV 读取 spoof_prob 与 true_label

一次扫描多个阈值(如0.35、0.4、0.45、0.5等)

输出每个阈值下的完整指标,便于不同阈值下进行对比

自动给出最优阈值(按 f1_spoof / recall_spoof / accuracy)

4.3示例代码

主流程:遍历阈值、选最优、导出CSV:

    rows = load_rows(input_csv)
    if not rows:
        raise ValueError("CSV 中没有可用于评估的数据。请确认包含 true_label/status/spoof_prob。")

    thresholds = parse_thresholds(args.thresholds)
    results = [metrics_at_threshold(rows, t) for t in thresholds]

    best = max(results, key=lambda x: x[args.optimize_for])

    output_csv = Path(args.output_csv)
    output_csv.parent.mkdir(parents=True, exist_ok=True)
    fieldnames = list(results[0].keys())
    with output_csv.open("w", encoding="utf-8", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(results)

4.4 执行流程

先生成带真值标签的批量推理结果:

python infer_audio_batch.py --model_dir outputs/wav2vec2-mid1/best --audio_dir data/ASVspoof2019_LA_dev/flac --glob "*.flac" --protocol_path data/ASVspoof2019_LA_cm_protocols/ASVspoof2019.LA.cm.dev.trl.txt --output_csv outputs/dev_batch_results.csv

再扫描多个阈值:

python threshold_scan.py --input_csv outputs/dev_batch_results.csv --thresholds "0.35,0.4,0.45,0.5,0.55,0.6" --optimize_for f1_spoof --output_csv outputs/threshold_scan_results.csv

最后观察不同阈值下指标对比以及选出所选中的最优阈值。

5.下一步计划

将选定阈值固化到 app.py 服务配置,并于小组成员目前成果进行结合,完成项目初版功能程序

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐