Commit d6d67893 d6d67893f179fa1f4d61916a65b34f2851f1bba2 by cnb.bofCdSsphPA

Make data onboarding and long FMA transfer supervision easier to sustain

Constraint: The user needs detailed data-format guidance now, while the real FMA archive transfer still requires durable hands-off supervision across long sessions
Rejected: Treat documentation and download-watch work as separate later tasks | Would leave either user guidance or transfer resilience lagging behind active development
Confidence: high
Scope-risk: narrow
Directive: Keep the new training-data/pgvector guide aligned with actual manifest fields and use watch_fma_download.py as the first-line long-transfer watchdog
Tested: /usr/local/miniconda3/bin/python -m py_compile acr-engine/scripts/watch_fma_download.py; /usr/local/miniconda3/bin/python acr-engine/scripts/watch_fma_download.py --cycles 2 --interval 2; /usr/local/miniconda3/bin/python acr-engine/scripts/prepare_fma_archive.py inspect
Not-tested: Full archive completion, extraction, and real-data smoke remain pending
1 parent 83a3f89f
#!/usr/bin/env python3
"""Ensure the FMA archive download keeps running; restart if stalled or dead."""
from __future__ import annotations
import argparse
import json
import subprocess
import time
from pathlib import Path
PYTHON = "/usr/local/miniconda3/bin/python"
INSPECT = [PYTHON, "scripts/prepare_fma_archive.py", "inspect"]
BG = [PYTHON, "scripts/prepare_fma_archive.py", "bg-download"]
DEFAULT_LOG = Path("/tmp/fma_modelscope_watch.log")
def inspect() -> dict:
out = subprocess.check_output(INSPECT, text=True)
return json.loads(out)
def bg_download() -> dict:
out = subprocess.check_output(BG, text=True)
return json.loads(out)
def has_live_curl() -> bool:
proc = subprocess.run(
["bash", "-lc", "ps -ef | grep 'fma_small.zip' | grep -v grep >/dev/null"],
capture_output=True,
text=True,
)
return proc.returncode == 0
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--interval", type=float, default=5.0)
parser.add_argument("--cycles", type=int, default=3)
parser.add_argument("--log-path", default=str(DEFAULT_LOG))
args = parser.parse_args()
log_path = Path(args.log_path)
log_path.parent.mkdir(parents=True, exist_ok=True)
events = []
previous_size = None
for _ in range(args.cycles):
snapshot = inspect()
size = int(snapshot["archive_size"])
live = has_live_curl()
restarted = None
if (previous_size is not None and size <= previous_size) or not live:
restarted = bg_download()
time.sleep(2)
snapshot = inspect()
size = int(snapshot["archive_size"])
live = has_live_curl()
event = {
"snapshot": snapshot,
"live_curl": live,
"restarted": restarted,
}
events.append(event)
previous_size = size
time.sleep(args.interval)
text = json.dumps({"status": "ok", "events": events}, indent=2, ensure_ascii=False)
log_path.write_text(text)
print(text)
if __name__ == "__main__":
main()
......@@ -232,6 +232,54 @@
### Stage: FMA 下载自动守护
完成项:
- 新增 [acr-engine/scripts/watch_fma_download.py](../acr-engine/scripts/watch_fma_download.py)
- 支持周期性:
- inspect 当前下载进度
- 检查 curl 进程是否存活
- 如果停滞或进程消失则自动重新触发 `bg-download`
验证结果:
- `/usr/local/miniconda3/bin/python -m py_compile scripts/watch_fma_download.py` 成功
- `/usr/local/miniconda3/bin/python scripts/watch_fma_download.py --cycles 2 --interval 2` 成功
- 当前 watcher 观测到:
- 第 1 次 `archive_size=513179648`
- 第 2 次 `archive_size=516587520`
- 两次 `live_curl=true`
- 两次 `restarted=null`(说明下载健康推进,无需重启)
- 最新 inspect:
- `archive_size=522387456`
- `archive_progress_percent=6.8023`
结论:
- 现在真实 FMA 下载不只可手动恢复,也具备基础自动守护能力
- 长时间下载的持续性进一步增强
### Stage: 训练数据与 pgvector 专项文档
完成项:
- 新增 [docs/training-data-and-pgvector-guide.md](./training-data-and-pgvector-guide.md)
- 单独详细说明:
- 当前训练数据/输入数据应该是什么格式
- `reference` / `query` 的角色区分
- BGM / 手机录音 / 直播录屏如何转成训练数据
- `song_id` / `type` / `offset` / `segment_type` 等标签建议
- 未来接 `pgvector` 时的推荐表结构与字段设计
- 将该文档挂接到 [docs/README.md](./README.md)[docs/session-handoff.md](./session-handoff.md)
验证结果:
- 文档内容已对齐当前代码行为:
- `acr-engine/src/data/dataset.py`
- `acr-engine/src/data/manifest_tools.py`
- 已补充从“原始音频 -> 标准化资产 -> manifest -> pgvector”的完整分层说明
结论:
- 现在项目对“训练数据应该长什么样”与“以后怎么接 pgvector”已经有独立、可交接、可执行的文档说明
### Stage: FMA 后台续传恢复
完成项:
......
......@@ -275,6 +275,7 @@
- [docs/open-dataset-workflow.md](./open-dataset-workflow.md)
- [docs/session-handoff.md](./session-handoff.md)
- [docs/current-capability-map.md](./current-capability-map.md)
- [docs/training-data-and-pgvector-guide.md](./training-data-and-pgvector-guide.md)
- [acr-engine/FIRST_RUN_CHECKLIST.md](../acr-engine/FIRST_RUN_CHECKLIST.md)
- FMA 真实子集下载脚手架已存在:[acr-engine/scripts/fetch_fma_subset.py](../acr-engine/scripts/fetch_fma_subset.py);最近验证结果是旧直链 `403`、页面级历史 URL `404`;但 `https://modelscope.cn/datasets/pengzhendong/fma/resolve/master/fma_small.zip` 已验证 `200 OK` 且支持 range
- 运行 [acr-engine/scripts/status_snapshot.py](../acr-engine/scripts/status_snapshot.py)
......
# Training Data, Input Format, and pgvector Guide / 训练数据、输入格式与 pgvector 指南
> 更新:2026-06-02
## 一页结论
如果后面要把这个 ACR 项目做成稳定可持续的数据工程,建议把数据分成 4 层:
1. **原始音频层**:BGM、歌曲母带、录音、直播切片、手机录音
2. **标准音频资产层**:统一采样率、声道、文件命名后的可训练音频
3. **manifest 层**`catalog.json` / `train.json` / `test.json` / `val.json`
4. **向量索引层**:embedding、metadata、pgvector/ANN 检索库
当前项目真正训练时吃进去的,不是“任意音频文件夹”,而是:
- 音频文件
- 配套 manifest
- `song_id` 级别标签
- query/reference 角色划分
如果未来要接 `pgvector`,推荐做法不是直接把原始音频塞进数据库,而是:
- 音频留文件系统/对象存储
- metadata 落 PostgreSQL
- embedding 向量落 `pgvector`
- `song_id` / `segment_id` / `type` / `offset` 做结构化列
---
## 1. 分层结构图
```mermaid
flowchart LR
A[原始 BGM / 录音 / 歌曲] --> B[标准化音频资产]
B --> C[Reference Catalog]
B --> D[Query Segments]
C --> E[Manifest JSON]
D --> E
E --> F[训练 / 评测]
C --> G[Embedding / Fingerprint 索引]
G --> H[pgvector / 检索服务]
```
---
## 2. 当前代码实际接受什么数据
### 2.1 音频层要求
从当前代码看,核心读取逻辑是:
- `librosa.load(..., sr=16000, mono=True)`
- 默认训练片段长度:`5s`
- 外部数据 query 推荐长度:`8s`
- 频谱输入:Mel spectrogram
- 当前文档目标输入层:**128 Mel**
所以推荐原始资产先标准化为:
| 项目 | 推荐值 | 说明 |
|---|---|---|
| 文件类型 | `.wav` / `.mp3` / `.flac` / `.ogg` | 当前转换工具支持这些后缀 |
| 采样率 | `16k` | 训练/评测读取时会统一到 16k |
| 声道 | mono | 当前 pipeline 按 mono 读取 |
| reference 时长 | 尽量完整曲目 | 用于建索引 |
| query 时长 | `5s``8s` | 训练常用 5s,开放数据切 query 常用 8s |
### 2.2 manifest 层要求
当前项目的实际关键字段:
| 字段 | 必需 | 用途 |
|---|---|---|
| `song_id` | 是 | 主标签,同一首歌所有 query/reference 共享 |
| `audio_path` | 是 | 音频相对路径 |
| `duration` | 是 | 时长,控制切片与合法性 |
| `type` | 是 | `reference` / `clean` / `augmented` / `confused` / `humming_like` |
| `offset` | 建议 | query 在原曲中的起始偏移 |
| `segment_type` | 建议 | `intro` / `mid` / `outro` / `external_query` |
| `source_dataset` | 建议 | 数据来源标记 |
---
## 3. 训练数据到底应该长什么样
## 3.1 Reference 数据
Reference 表示“曲库真身”,用于建索引。
示例:
```json
{
"song_id": "song_0001",
"audio_path": "audio/song_0001.wav",
"duration": 183.4,
"type": "reference",
"source_dataset": "internal_bgm"
}
```
要求:
- 一首歌至少 1 条 reference
- reference 通常是整首歌或较长主版本
- 不建议把噪音重、混响重、环境录音直接当 reference 主资产
---
## 3.2 Query 数据
Query 表示“我要识别的输入样本”。
示例:
```json
{
"song_id": "song_0001",
"audio_path": "audio/song_0001_query_03.wav",
"duration": 5.0,
"type": "clean",
"offset": 63.2,
"segment_type": "mid",
"source_dataset": "internal_bgm"
}
```
Query 的内容应该是:
- 原曲中的 5s/8s 切片
- 或人工合成退化片段
- 或真实世界录音片段
### Query 常见类型
| type | 含义 | 适合来源 |
|---|---|---|
| `clean` | 原曲直接切片 | 标准训练/评测主力 |
| `augmented` | 加噪、混响、压缩、EQ 后的切片 | 提升鲁棒性 |
| `confused` | 与其他音乐更相似、容易误判的片段 | 难例强化 |
| `humming_like` | 旋律近似、弱配器、手机录音风格 | 旋律/哼唱类近似查询 |
---
## 4. 如果我们有 BGM、音乐录音,怎么转成训练数据
## 4.1 场景 A:你有一批“标准 BGM 母带/成品曲目”
这是最容易的情况。
### 推荐做法
1. 每首完整 BGM 作为 `reference`
2. 从同一首 BGM 中随机切出多个 query 片段
3. 给这些片段打上同一个 `song_id`
4. query 再按用途分到 `train/test/val`
### 结果结构
```mermaid
flowchart TD
A[完整 BGM] --> B[reference]
A --> C1[query 1]
A --> C2[query 2]
A --> C3[query 3]
C1 --> D[train/test]
C2 --> D
C3 --> D
```
### 关键点
- `song_id` 必须稳定
- 曲目版本要分清:主版本、纯伴奏版、短版、重编版不要误当同一首
- 如果版本差异大,建议拆成不同 `song_id`
---
## 4.2 场景 B:你有“手机录音/环境录音/直播录屏”
这类数据不应直接全部当 reference 主资产,而更适合作为 query 或 hard-case 样本。
### 推荐角色
| 资产 | 角色 |
|---|---|
| 清晰母带 / 官方音源 | reference |
| 手机录音 / 环境录音 | query |
| 直播采集片段 | query |
| 背景噪声下的短片段 | query |
### 标注建议
如果录音可以明确对应某首 reference:
```json
{
"song_id": "song_0001",
"audio_path": "queries/phone/song_0001_live_01.wav",
"duration": 5.0,
"type": "augmented",
"segment_type": "external_query",
"source_dataset": "phone_recording"
}
```
如果录音非常嘈杂、很接近旋律轮廓但音色严重失真:
- 可标 `type=humming_like`
- 或单独增加你们自己的扩展类型,如 `field_recording`
但要注意:
- 训练前最好先映射回当前系统已有类型,避免代码完全不认识
---
## 4.3 场景 C:你只有一堆音频文件夹,还没做精标注
先做“弱监督标准化”比不做强。
### 最低可行办法
- 一首文件 = 一个 `song_id`
- 整首文件 = `reference`
- 从整首里随机切 query = `clean`
- 后续再人工修正错标/重名/版本冲突
这也是当前:
- `manifest_tools.py audio-dir-to-splits`
- `external_adapters.py prepare-local`
在做的事情。
### 适用场景
- FMA
- MTG-Jamendo
- 你本地一批 BGM 文件夹
- 一批已知 song-level 但未做 segment 级标注的数据
---
## 5. label 应该怎么设计
## 5.1 主标签:`song_id`
这是最重要的标签。
同一首歌的:
- reference
- clean query
- 手机录音 query
- 混响/噪声 query
都应该共享同一个 `song_id`
### 推荐命名
| 场景 | 推荐形式 |
|---|---|
| 内部 BGM | `bgm_000001` |
| 商业曲库 | `catalog_000001` |
| 开源数据 | `fma_012345` |
| 多版本项目 | `song_0001_vocal` / `song_0001_inst` |
---
## 5.2 辅助标签
建议额外保留:
| 字段 | 作用 |
|---|---|
| `version_id` | 区分原版 / 伴奏版 / 重编版 |
| `segment_type` | 区分 intro / mid / outro |
| `recording_type` | 区分 studio / phone / live / screen_capture |
| `noise_level` | 区分 clean / mild / heavy |
| `source_dataset` | 保留来源审计 |
当前代码最少已经建议保留:
- `type`
- `offset`
- `segment_type`
- `source_dataset`
---
## 6. 如果以后接 pgvector,当前应该怎么处理
## 6.1 不要直接把原始音频塞进 pgvector
推荐分工:
| 层 | 存放位置 |
|---|---|
| 原始音频文件 | 对象存储 / 文件系统 |
| manifest / metadata | PostgreSQL 普通表 |
| embedding 向量 | `pgvector` 列 |
| 音频指纹 | PostgreSQL JSON / 独立索引 / 文件 |
---
## 6.2 推荐表结构
```mermaid
flowchart TD
A[songs] --> B[segments]
A --> C[references]
C --> D[reference_embeddings]
B --> E[query_embeddings]
```
### songs
| 列 | 说明 |
|---|---|
| `song_id` | 主键/唯一业务键 |
| `title` | 曲名 |
| `artist` | 作者/演出者 |
| `version_id` | 版本标识 |
| `source_dataset` | 来源 |
| `license` | 许可证 |
### references
| 列 | 说明 |
|---|---|
| `reference_id` | 主键 |
| `song_id` | 外键 |
| `audio_uri` | 文件路径/对象存储地址 |
| `duration_sec` | 时长 |
| `sample_rate` | 采样率 |
### segments
| 列 | 说明 |
|---|---|
| `segment_id` | 主键 |
| `song_id` | 外键 |
| `audio_uri` | query 文件路径 |
| `offset_sec` | 起始偏移 |
| `duration_sec` | 片段长度 |
| `type` | clean/augmented/confused/humming_like |
| `segment_type` | intro/mid/outro/external_query |
| `split` | train/test/val |
### reference_embeddings / query_embeddings
| 列 | 说明 |
|---|---|
| `id` | 主键 |
| `song_id` | 外键 |
| `segment_id` / `reference_id` | 外键 |
| `embedding` | `vector(n)` |
| `model_version` | 模型版本 |
| `created_at` | 生成时间 |
---
## 6.3 pgvector 推荐实践
如果当前 embedding 维度是 192,那么可以设计:
```sql
embedding vector(192)
```
### 推荐额外保留字段
- `model_version`
- `data_version`
- `index_version`
- `source_dataset`
- `type`
- `offset_sec`
- `segment_type`
这样以后你能做:
- 按模型版本重建 embedding
- 按数据集来源筛选候选
- 按 query 类型分析误判
-`segment_type` 做 intro/outro 针对性策略
---
## 6.4 当前项目要为 pgvector 提前准备什么
现在就该做的,不是先建数据库,而是先保证这些字段规范:
1. `song_id` 稳定且唯一
2. `audio_path` 可追踪
3. `type` 明确
4. `offset` 可回溯
5. `source_dataset` 清晰
6. 模型产出的 embedding 可以和 metadata 一一对应
换句话说:
> 先把 manifest 设计好,未来接 pgvector 才不会返工。
---
## 7. 推荐的落地目录与数据加工流程
## 7.1 原始层
```text
acr-engine/data/raw/
bgm_master/
phone_recordings/
live_captures/
fma_small_audio/
```
## 7.2 标准化层
```text
acr-engine/data/curated/
my_bgm_v1/
audio/
manifests/
```
## 7.3 训练/评测层
```text
catalog.json
train.json
test.json
val.json
```
---
## 8. 针对你当前项目的推荐操作方式
## 8.1 如果你现在手上有 BGM
最推荐:
1. 每首完整 BGM 先放入一个目录
2. 用统一命名整理
3. 运行:
```bash
/usr/local/miniconda3/bin/python src/data/external_adapters.py inspect-local fma <你的目录>
/usr/local/miniconda3/bin/python src/data/external_adapters.py prepare-local fma <你的目录> --output-root data/external_ingested
/usr/local/miniconda3/bin/python src/data/external_adapters.py validate-local fma data/external_ingested/fma/manifests
```
如果只是你自有 BGM,不一定非要叫 `fma`,后面可以再做一个内部 adapter。
---
## 8.2 如果你现在手上有录音/采集片段
建议:
- 不要把这批录音直接当 reference 主库
- 先把它们映射到已有 reference 的 `song_id`
- 作为 query / hard-case 数据进入训练或评测
如果当前无法人工全标:
- 先放到单独目录
- 先做 song-level 或 file-level 对齐
- 后续逐步补 segment-level 标注
---
## 9. 一张总表
| 你手上的数据 | 推荐转化方式 | 在系统里的角色 |
|---|---|---|
| 完整 BGM 母带 | 整首保留 + 随机切 query | reference + clean query |
| 官方歌曲文件 | 整首保留 + 切片 | reference + clean query |
| 手机录音 | 对齐到已有 `song_id` | augmented / humming_like query |
| 直播录屏 | 截出音乐段并对齐 `song_id` | external query |
| 背景噪声录音 | 作为 hard case | confused / augmented |
| 开源整包数据集 | 先 `inspect-local`/`prepare-local` | baseline train/eval corpus |
---
## 10. 你现在最值得立刻固化的字段
如果后面确定要上 `pgvector`,现在最少要保证每条样本都能追踪:
- `song_id`
- `audio_path`
- `duration`
- `type`
- `offset`
- `segment_type`
- `source_dataset`
- `split`
- `model_version`(生成 embedding 时记录)
---
## 11. 推荐文档跳转
- [dataset-spec.md](./dataset-spec.md)
- [open-dataset-workflow.md](./open-dataset-workflow.md)
- [dataset-sources-and-licensing.md](./dataset-sources-and-licensing.md)
- [session-handoff.md](./session-handoff.md)
## Sources
- Current code behavior from:
- [acr-engine/src/data/dataset.py](../acr-engine/src/data/dataset.py)
- [acr-engine/src/data/manifest_tools.py](../acr-engine/src/data/manifest_tools.py)
- Existing project docs:
- [dataset-spec.md](./dataset-spec.md)
- [open-dataset-workflow.md](./open-dataset-workflow.md)