Capture the current ACR delivery state so the next engineer can resume immediately
Constraint: Keep the live song-centric mainline and selected20 regression baseline aligned in docs and artifacts Rejected: Deferring README/handoff consolidation | Would force the next session to reconstruct context again Confidence: high Scope-risk: narrow Directive: Update CHANGELOG, roadmap checklist, and handoff docs together whenever onboarding or delivery facts change Tested: /usr/local/miniconda3/bin/python /workspace/scripts/check_markdown_links.py --root /workspace/docs; selected20 baseline vs fresh report equality check; git diff --check; architect review APPROVED Not-tested: No new runtime pipeline execution in this commit; relies on already-generated selected20 artifacts
Showing
14 changed files
with
1701 additions
and
12 deletions
README.md
0 → 100644
| 1 | # ACR Workspace Handoff / 快速接手入口 | ||
| 2 | |||
| 3 | > 这份 README 面向新接手的开发人员:先告诉你**项目现在是什么、先看什么、先跑什么**,避免从零重新梳理。 | ||
| 4 | |||
| 5 | --- | ||
| 6 | |||
| 7 | ## 1. 这个仓库现在在做什么 | ||
| 8 | |||
| 9 | 当前主线是一个 **song-centric 音乐 ACR 系统**,重点有两条: | ||
| 10 | |||
| 11 | 1. **live PostgreSQL 主链** | ||
| 12 | - 把歌曲目录转换为 `song -> asset -> window -> fingerprint / embedding` | ||
| 13 | - 落到当前 4 表 schema: | ||
| 14 | |||
| 15 | ```text | ||
| 16 | media_entity -> audio_object -> feature_fact -> set_membership | ||
| 17 | ``` | ||
| 18 | |||
| 19 | 2. **selected20 小样本实战评测链** | ||
| 20 | - 用 20 首歌的专题数据评估当前方案在真实 query 条件下的 `song_id` 命中率 | ||
| 21 | - 当前专题重点关注: | ||
| 22 | - `type_7` | ||
| 23 | - `type_16` | ||
| 24 | |||
| 25 | 一句话理解: | ||
| 26 | |||
| 27 | > **数据库宿主链已经打通,当前要在不破坏主链的前提下继续优化 semantic lane,并用 selected20 做回归基线。** | ||
| 28 | |||
| 29 | --- | ||
| 30 | |||
| 31 | ## 2. 新同学先看哪些文档 | ||
| 32 | |||
| 33 | 建议按这个顺序读: | ||
| 34 | |||
| 35 | 1. [docs/start-here.md](./docs/start-here.md) | ||
| 36 | 2. [docs/delivery-onepager.md](./docs/delivery-onepager.md) | ||
| 37 | 3. [docs/session-handoff.md](./docs/session-handoff.md) | ||
| 38 | 4. [docs/song-ingest-query-delivery.md](./docs/song-ingest-query-delivery.md) | ||
| 39 | 5. [docs/research-delivery-roadmap.md](./docs/research-delivery-roadmap.md) | ||
| 40 | 6. [docs/selected20_songid_eval.md](./docs/selected20_songid_eval.md) | ||
| 41 | 7. [docs/postgresql-data-model.md](./docs/postgresql-data-model.md) | ||
| 42 | 8. [docs/postgres_db_schema_samples.md](./docs/postgres_db_schema_samples.md) | ||
| 43 | 9. [docs/CHANGELOG.md](./docs/CHANGELOG.md) | ||
| 44 | |||
| 45 | 如果只想快速进入状态,至少看这 4 份: | ||
| 46 | |||
| 47 | - [docs/start-here.md](./docs/start-here.md) | ||
| 48 | - [docs/session-handoff.md](./docs/session-handoff.md) | ||
| 49 | - [docs/research-delivery-roadmap.md](./docs/research-delivery-roadmap.md) | ||
| 50 | - [docs/selected20_songid_eval.md](./docs/selected20_songid_eval.md) | ||
| 51 | |||
| 52 | --- | ||
| 53 | |||
| 54 | ## 3. 先跑什么 | ||
| 55 | |||
| 56 | ### 3.1 live PostgreSQL 主链验证 | ||
| 57 | |||
| 58 | ```bash | ||
| 59 | cd /workspace | ||
| 60 | /usr/local/miniconda3/bin/python acr-engine/scripts/run_songcentric_directory_pipeline_live.py \ | ||
| 61 | --dsn 'postgres://d2:d2pass@127.0.0.1:5432/d2' \ | ||
| 62 | --schema acr_songcentric_test \ | ||
| 63 | --input-root acr-engine/data/songcentric_builder_smoke \ | ||
| 64 | --output-dir acr-engine/data/pgvector_eval/music20 | ||
| 65 | ``` | ||
| 66 | |||
| 67 | 或: | ||
| 68 | |||
| 69 | ```bash | ||
| 70 | acr-engine/scripts/start_songcentric_shortest_path.sh 'postgres://d2:d2pass@127.0.0.1:5432/d2' | ||
| 71 | ``` | ||
| 72 | |||
| 73 | ### 3.2 selected20 回归评测 | ||
| 74 | |||
| 75 | ```bash | ||
| 76 | cd /workspace/acr-engine | ||
| 77 | /usr/local/miniconda3/bin/python scripts/evaluate_selected20_songid_retrieval.py \ | ||
| 78 | --downloads-dir /root/hikoon_song_files/output/selected_20_songs/downloads \ | ||
| 79 | --reference-type 11 \ | ||
| 80 | --query-types 1 7 12 16 \ | ||
| 81 | --duration 8.0 \ | ||
| 82 | --topk 3 \ | ||
| 83 | --exact-weight 0.6 \ | ||
| 84 | --semantic-weight 0.4 \ | ||
| 85 | --output-json /workspace/acr-engine/data/local_eval/selected20_songid_eval_report.json \ | ||
| 86 | --output-md /workspace/docs/selected20_songid_eval.md | ||
| 87 | ``` | ||
| 88 | |||
| 89 | --- | ||
| 90 | |||
| 91 | ## 4. 当前你需要知道的核心事实 | ||
| 92 | |||
| 93 | ### 主链事实 | ||
| 94 | |||
| 95 | - 4 表 song-centric schema 已作为当前默认口径 | ||
| 96 | - exact lane 已接入 `chromaprint_matcher` | ||
| 97 | - semantic lane 已接入 `mert-v1-95m` | ||
| 98 | - 当前 live 主链已有 fresh evidence | ||
| 99 | |||
| 100 | ### selected20 事实 | ||
| 101 | |||
| 102 | - baseline 报告已存在 | ||
| 103 | - fresh 重跑报告已存在 | ||
| 104 | - baseline 与 fresh 结果一致,当前专题已可作为回归基线 | ||
| 105 | |||
| 106 | 当前 overall: | ||
| 107 | |||
| 108 | | lane | count | top1 | top3 | | ||
| 109 | |---|---:|---:|---:| | ||
| 110 | | exact | 123 | 0.6016 | 0.8130 | | ||
| 111 | | semantic | 123 | 0.4715 | 0.6016 | | ||
| 112 | | fused | 123 | 0.6341 | 0.8537 | | ||
| 113 | |||
| 114 | 当前关键结论: | ||
| 115 | |||
| 116 | - `type_1` 不是主要问题 | ||
| 117 | - `type_12` 表现已经较好 | ||
| 118 | - `type_7 / type_16` 仍是主要短板 | ||
| 119 | |||
| 120 | ### 当前 blocker | ||
| 121 | |||
| 122 | - MuQ 还没接通 | ||
| 123 | - 当前已知 blocker: | ||
| 124 | - `RuntimeError: operator torchvision::nms does not exist` | ||
| 125 | |||
| 126 | --- | ||
| 127 | |||
| 128 | ## 5. 建议下一步怎么做 | ||
| 129 | |||
| 130 | 优先顺序: | ||
| 131 | |||
| 132 | 1. **守住当前主链与 selected20 基线** | ||
| 133 | 2. **先整理 hard-case 白名单** | ||
| 134 | 3. **再解锁 MuQ runtime** | ||
| 135 | 4. **最后把有效策略回灌到 PostgreSQL 在线主链** | ||
| 136 | |||
| 137 | 详细 roadmap 和 checklist 见: | ||
| 138 | |||
| 139 | - [docs/research-delivery-roadmap.md](./docs/research-delivery-roadmap.md) | ||
| 140 | |||
| 141 | --- | ||
| 142 | |||
| 143 | ## 6. 代码和文档主要位置 | ||
| 144 | |||
| 145 | - 主代码: | ||
| 146 | - [acr-engine/](./acr-engine/) | ||
| 147 | - 核心文档: | ||
| 148 | - [docs/](./docs/) | ||
| 149 | - 运行脚本: | ||
| 150 | - [acr-engine/scripts/](./acr-engine/scripts/) | ||
| 151 | - 会话/交付状态: | ||
| 152 | - [.omx/](./.omx/) | ||
| 153 | |||
| 154 | --- | ||
| 155 | |||
| 156 | ## 7. 如果你是新接手开发,最短路径 | ||
| 157 | |||
| 158 | 只做这几步: | ||
| 159 | |||
| 160 | 1. 读 [docs/start-here.md](./docs/start-here.md) | ||
| 161 | 2. 读 [docs/session-handoff.md](./docs/session-handoff.md) | ||
| 162 | 3. 读 [docs/research-delivery-roadmap.md](./docs/research-delivery-roadmap.md) | ||
| 163 | 4. 跑一次主链验证 | ||
| 164 | 5. 跑一次 selected20 回归 | ||
| 165 | |||
| 166 | 这样基本就能在 10~20 分钟内进入当前上下文。 |
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
| 1 | # Selected 20 Songs 实战检索评测 | ||
| 2 | |||
| 3 | - 数据目录:`/root/hikoon_song_files/output/selected_20_songs/downloads` | ||
| 4 | - reference:`type_11` | ||
| 5 | - queries:`type_1, type_7, type_12, type_16` | ||
| 6 | - 当前方案:`chromaprint_matcher + mert-v1-95m` | ||
| 7 | - 语义截断时长:`8.0s` | ||
| 8 | - reference 文件数:`24` | ||
| 9 | - query 文件数:`123` | ||
| 10 | |||
| 11 | ## 1. 总体结果 | ||
| 12 | |||
| 13 | | lane | count | top1 | top3 | | ||
| 14 | |---|---:|---:|---:| | ||
| 15 | | exact | 123 | 0.6016 | 0.8130 | | ||
| 16 | | semantic | 123 | 0.4715 | 0.6016 | | ||
| 17 | | fused | 123 | 0.6341 | 0.8537 | | ||
| 18 | |||
| 19 | ## 2. 分 query type 结果 | ||
| 20 | |||
| 21 | | query_type | lane | count | top1 | top3 | | ||
| 22 | |---|---|---:|---:|---:| | ||
| 23 | | type_1 | exact | 21 | 1.0000 | 1.0000 | | ||
| 24 | | type_1 | semantic | 21 | 1.0000 | 1.0000 | | ||
| 25 | | type_1 | fused | 21 | 1.0000 | 1.0000 | | ||
| 26 | | type_7 | exact | 41 | 0.4146 | 0.7073 | | ||
| 27 | | type_7 | semantic | 41 | 0.1707 | 0.3902 | | ||
| 28 | | type_7 | fused | 41 | 0.4390 | 0.7561 | | ||
| 29 | | type_12 | exact | 23 | 0.8261 | 0.9130 | | ||
| 30 | | type_12 | semantic | 23 | 1.0000 | 1.0000 | | ||
| 31 | | type_12 | fused | 23 | 0.9130 | 1.0000 | | ||
| 32 | | type_16 | exact | 38 | 0.4474 | 0.7632 | | ||
| 33 | | type_16 | semantic | 38 | 0.1842 | 0.3684 | | ||
| 34 | | type_16 | fused | 38 | 0.4737 | 0.7895 | | ||
| 35 | |||
| 36 | ## 3. 失败样例(fused rank != 1) | ||
| 37 | |||
| 38 | - `type_7` / true=`572816` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/572816/type_7/三军令(副歌版).mp3` / top1=`614397` | ||
| 39 | - `type_7` / true=`604814` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/604814/type_7/偏差(剪辑版2)-吉鹏泽.wav` / top1=`614397` | ||
| 40 | - `type_7` / true=`604814` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/604814/type_7/偏差(剪辑版3)-吉鹏泽.wav` / top1=`614397` | ||
| 41 | - `type_7` / true=`643015` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/643015/type_7/置念 抖音推广音频.wav` / top1=`637559` | ||
| 42 | - `type_7` / true=`656394` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/656394/type_7/把你宠坏-推广片段.wav` / top1=`614740` | ||
| 43 | - `type_7` / true=`657848` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/657848/type_7/小神仙 主歌剪-1.mp3` / top1=`614397` | ||
| 44 | - `type_7` / true=`659300` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/659300/type_7/推广片段2.WAV` / top1=`614397` | ||
| 45 | - `type_7` / true=`675118` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/675118/type_7/敲冰求火片段1.1x.wav` / top1=`614397` | ||
| 46 | - `type_7` / true=`675118` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/675118/type_7/敲冰求火片段_2.wav` / top1=`614397` | ||
| 47 | - `type_7` / true=`675406` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/675406/type_7/2.mp3` / top1=`614397` | ||
| 48 | |||
| 49 | ## 4. 结论 | ||
| 50 | |||
| 51 | - `exact` 代表当前指纹链路单独表现。 | ||
| 52 | - `semantic` 代表当前 MERT 单独表现。 | ||
| 53 | - `fused` 代表当前版权保护场景里更接近实战的合并结果。 |
| 1 | #!/usr/bin/env /usr/local/miniconda3/bin/python | ||
| 2 | from __future__ import annotations | ||
| 3 | |||
| 4 | import argparse | ||
| 5 | import json | ||
| 6 | from collections import defaultdict | ||
| 7 | from pathlib import Path | ||
| 8 | from typing import Dict, List, Tuple | ||
| 9 | |||
| 10 | import librosa | ||
| 11 | import numpy as np | ||
| 12 | |||
| 13 | ROOT = Path(__file__).resolve().parents[1] | ||
| 14 | import sys | ||
| 15 | if str(ROOT) not in sys.path: | ||
| 16 | sys.path.insert(0, str(ROOT)) | ||
| 17 | |||
| 18 | from src.engines.chromaprint_matcher import ChromaprintMatcher, load_audio_mono | ||
| 19 | from scripts.enrich_songcentric_manifest_with_local_features import load_mert_runtime | ||
| 20 | |||
| 21 | |||
| 22 | def parse_args() -> argparse.Namespace: | ||
| 23 | ap = argparse.ArgumentParser() | ||
| 24 | ap.add_argument('--downloads-dir', required=True) | ||
| 25 | ap.add_argument('--reference-type', type=int, default=11) | ||
| 26 | ap.add_argument('--query-types', nargs='+', type=int, default=[1, 7, 12, 16]) | ||
| 27 | ap.add_argument('--duration', type=float, default=8.0) | ||
| 28 | ap.add_argument('--topk', type=int, default=3) | ||
| 29 | ap.add_argument('--exact-weight', type=float, default=0.6) | ||
| 30 | ap.add_argument('--semantic-weight', type=float, default=0.4) | ||
| 31 | ap.add_argument('--output-json', required=True) | ||
| 32 | ap.add_argument('--output-md', required=True) | ||
| 33 | return ap.parse_args() | ||
| 34 | |||
| 35 | |||
| 36 | def audio_files(path: Path) -> List[Path]: | ||
| 37 | if not path.exists(): | ||
| 38 | return [] | ||
| 39 | return sorted([p for p in path.iterdir() if p.is_file()]) | ||
| 40 | |||
| 41 | |||
| 42 | def collect_dataset(downloads_dir: Path, reference_type: int, query_types: List[int]) -> tuple[list[dict], list[dict]]: | ||
| 43 | references: list[dict] = [] | ||
| 44 | queries: list[dict] = [] | ||
| 45 | for song_dir in sorted([p for p in downloads_dir.iterdir() if p.is_dir()]): | ||
| 46 | song_id = song_dir.name | ||
| 47 | for ref in audio_files(song_dir / f'type_{reference_type}'): | ||
| 48 | references.append({'song_id': song_id, 'type': reference_type, 'path': str(ref)}) | ||
| 49 | for qtype in query_types: | ||
| 50 | for q in audio_files(song_dir / f'type_{qtype}'): | ||
| 51 | queries.append({'song_id': song_id, 'type': qtype, 'path': str(q)}) | ||
| 52 | return references, queries | ||
| 53 | |||
| 54 | |||
| 55 | def load_semantic_embedding(path: str, duration: float) -> np.ndarray: | ||
| 56 | rt = load_mert_runtime() | ||
| 57 | torch = rt['torch'] | ||
| 58 | sr = int(rt['sample_rate']) | ||
| 59 | y, _ = librosa.load(path, sr=sr, mono=True, duration=duration) | ||
| 60 | if y.size == 0: | ||
| 61 | raise ValueError(f'empty audio: {path}') | ||
| 62 | inputs = rt['feature_extractor'](y.astype(np.float32), sampling_rate=sr, return_tensors='pt') | ||
| 63 | with torch.no_grad(): | ||
| 64 | outputs = rt['model'](**inputs) | ||
| 65 | emb = outputs.last_hidden_state.mean(dim=1).squeeze(0).cpu().numpy().astype(np.float32) | ||
| 66 | norm = np.linalg.norm(emb) | ||
| 67 | if norm > 0: | ||
| 68 | emb = emb / norm | ||
| 69 | return emb | ||
| 70 | |||
| 71 | |||
| 72 | def normalize_score_pairs(score_pairs: List[Tuple[str, float]]) -> Dict[str, float]: | ||
| 73 | if not score_pairs: | ||
| 74 | return {} | ||
| 75 | vals = np.asarray([float(x[1]) for x in score_pairs], dtype=np.float32) | ||
| 76 | ids = [x[0] for x in score_pairs] | ||
| 77 | if vals.size == 1: | ||
| 78 | return {ids[0]: 1.0} | ||
| 79 | vmin = float(vals.min()) | ||
| 80 | vmax = float(vals.max()) | ||
| 81 | if abs(vmax - vmin) < 1e-12: | ||
| 82 | return {sid: 1.0 for sid in ids} | ||
| 83 | norm = (vals - vmin) / (vmax - vmin) | ||
| 84 | return {sid: float(v) for sid, v in zip(ids, norm)} | ||
| 85 | |||
| 86 | |||
| 87 | def topk_from_scores(score_map: Dict[str, float], topk: int) -> List[Dict]: | ||
| 88 | items = sorted(score_map.items(), key=lambda kv: kv[1], reverse=True)[:topk] | ||
| 89 | return [{'rank': idx + 1, 'song_id': sid, 'score': float(score)} for idx, (sid, score) in enumerate(items)] | ||
| 90 | |||
| 91 | |||
| 92 | def rank_of(song_id: str, ranked: List[Dict], default_rank: int) -> int: | ||
| 93 | for item in ranked: | ||
| 94 | if item['song_id'] == song_id: | ||
| 95 | return int(item['rank']) | ||
| 96 | return default_rank | ||
| 97 | |||
| 98 | |||
| 99 | def build_reference_assets(references: list[dict], duration: float): | ||
| 100 | matcher = ChromaprintMatcher(sr=16000) | ||
| 101 | ref_song_to_embeddings: dict[str, list[np.ndarray]] = defaultdict(list) | ||
| 102 | for ref in references: | ||
| 103 | y, _ = load_audio_mono(ref['path'], sr=matcher.sr) | ||
| 104 | matcher.index_song(ref['song_id'], y) | ||
| 105 | emb = load_semantic_embedding(ref['path'], duration=duration) | ||
| 106 | ref_song_to_embeddings[ref['song_id']].append(emb) | ||
| 107 | ref_song_embeddings: dict[str, np.ndarray] = {} | ||
| 108 | for song_id, embs in ref_song_to_embeddings.items(): | ||
| 109 | mat = np.vstack(embs) | ||
| 110 | mean_emb = mat.mean(axis=0) | ||
| 111 | norm = np.linalg.norm(mean_emb) | ||
| 112 | if norm > 0: | ||
| 113 | mean_emb = mean_emb / norm | ||
| 114 | ref_song_embeddings[song_id] = mean_emb.astype(np.float32) | ||
| 115 | return matcher, ref_song_embeddings | ||
| 116 | |||
| 117 | |||
| 118 | def evaluate(references: list[dict], queries: list[dict], duration: float, topk: int, exact_weight: float, semantic_weight: float) -> dict: | ||
| 119 | matcher, ref_song_embeddings = build_reference_assets(references, duration=duration) | ||
| 120 | ref_song_ids = sorted(ref_song_embeddings.keys()) | ||
| 121 | ref_matrix = np.vstack([ref_song_embeddings[sid] for sid in ref_song_ids]).astype(np.float32) | ||
| 122 | |||
| 123 | results = [] | ||
| 124 | by_type: dict[int, list[dict]] = defaultdict(list) | ||
| 125 | lane_rank_lists = { | ||
| 126 | 'exact': [], | ||
| 127 | 'semantic': [], | ||
| 128 | 'fused': [], | ||
| 129 | } | ||
| 130 | for q in queries: | ||
| 131 | qy, _ = load_audio_mono(q['path'], sr=matcher.sr) | ||
| 132 | exact_pairs = matcher.match(qy, top_k=max(topk * 5, 20)) | ||
| 133 | exact_norm = normalize_score_pairs(exact_pairs) | ||
| 134 | |||
| 135 | qemb = load_semantic_embedding(q['path'], duration=duration) | ||
| 136 | sims = ref_matrix @ qemb | ||
| 137 | semantic_pairs = [(sid, float(score)) for sid, score in zip(ref_song_ids, sims.tolist())] | ||
| 138 | semantic_norm = normalize_score_pairs(semantic_pairs) | ||
| 139 | |||
| 140 | fused_scores = {} | ||
| 141 | for sid in set(list(exact_norm.keys()) + list(semantic_norm.keys()) + ref_song_ids): | ||
| 142 | fused_scores[sid] = exact_weight * exact_norm.get(sid, 0.0) + semantic_weight * semantic_norm.get(sid, 0.0) | ||
| 143 | |||
| 144 | exact_ranked = topk_from_scores(exact_norm, topk) | ||
| 145 | semantic_ranked = topk_from_scores(semantic_norm, topk) | ||
| 146 | fused_ranked = topk_from_scores(fused_scores, topk) | ||
| 147 | |||
| 148 | default_rank = topk + 1 | ||
| 149 | exact_rank = rank_of(q['song_id'], exact_ranked, default_rank) | ||
| 150 | semantic_rank = rank_of(q['song_id'], semantic_ranked, default_rank) | ||
| 151 | fused_rank = rank_of(q['song_id'], fused_ranked, default_rank) | ||
| 152 | lane_rank_lists['exact'].append(exact_rank) | ||
| 153 | lane_rank_lists['semantic'].append(semantic_rank) | ||
| 154 | lane_rank_lists['fused'].append(fused_rank) | ||
| 155 | |||
| 156 | item = { | ||
| 157 | 'song_id': q['song_id'], | ||
| 158 | 'query_type': q['type'], | ||
| 159 | 'query_path': q['path'], | ||
| 160 | 'exact_rank': exact_rank, | ||
| 161 | 'semantic_rank': semantic_rank, | ||
| 162 | 'fused_rank': fused_rank, | ||
| 163 | 'exact_topk': exact_ranked, | ||
| 164 | 'semantic_topk': semantic_ranked, | ||
| 165 | 'fused_topk': fused_ranked, | ||
| 166 | } | ||
| 167 | results.append(item) | ||
| 168 | by_type[q['type']].append(item) | ||
| 169 | |||
| 170 | def metric_block(rank_list: list[int]) -> dict: | ||
| 171 | n = len(rank_list) | ||
| 172 | if n == 0: | ||
| 173 | return {'count': 0, 'top1': 0.0, 'top3': 0.0} | ||
| 174 | return { | ||
| 175 | 'count': n, | ||
| 176 | 'top1': sum(1 for r in rank_list if r == 1) / n, | ||
| 177 | 'top3': sum(1 for r in rank_list if r <= 3) / n, | ||
| 178 | } | ||
| 179 | |||
| 180 | overall = {lane: metric_block(ranks) for lane, ranks in lane_rank_lists.items()} | ||
| 181 | per_type = {} | ||
| 182 | for qtype, items in sorted(by_type.items()): | ||
| 183 | per_type[qtype] = { | ||
| 184 | 'exact': metric_block([x['exact_rank'] for x in items]), | ||
| 185 | 'semantic': metric_block([x['semantic_rank'] for x in items]), | ||
| 186 | 'fused': metric_block([x['fused_rank'] for x in items]), | ||
| 187 | } | ||
| 188 | |||
| 189 | failed_fused = [x for x in results if x['fused_rank'] != 1] | ||
| 190 | failed_fused.sort(key=lambda x: (x['query_type'], x['fused_rank'], x['song_id'], x['query_path'])) | ||
| 191 | |||
| 192 | return { | ||
| 193 | 'reference_count': len(references), | ||
| 194 | 'query_count': len(queries), | ||
| 195 | 'reference_song_count': len(ref_song_ids), | ||
| 196 | 'query_type_counts': {str(k): len(v) for k, v in sorted(by_type.items())}, | ||
| 197 | 'weights': {'exact': exact_weight, 'semantic': semantic_weight}, | ||
| 198 | 'overall': overall, | ||
| 199 | 'per_type': per_type, | ||
| 200 | 'failed_fused_examples': failed_fused[:20], | ||
| 201 | 'results': results, | ||
| 202 | } | ||
| 203 | |||
| 204 | |||
| 205 | def render_md(report: dict, downloads_dir: Path, reference_type: int, query_types: List[int], duration: float) -> str: | ||
| 206 | lines = [] | ||
| 207 | lines.append('# Selected 20 Songs 实战检索评测') | ||
| 208 | lines.append('') | ||
| 209 | lines.append(f'- 数据目录:`{downloads_dir}`') | ||
| 210 | lines.append(f'- reference:`type_{reference_type}`') | ||
| 211 | lines.append(f'- queries:`{", ".join(f"type_{x}" for x in query_types)}`') | ||
| 212 | lines.append('- 当前方案:`chromaprint_matcher + mert-v1-95m`') | ||
| 213 | lines.append(f'- 语义截断时长:`{duration:.1f}s`') | ||
| 214 | lines.append(f"- reference 文件数:`{report['reference_count']}`") | ||
| 215 | lines.append(f"- query 文件数:`{report['query_count']}`") | ||
| 216 | lines.append('') | ||
| 217 | lines.append('## 1. 总体结果') | ||
| 218 | lines.append('') | ||
| 219 | lines.append('| lane | count | top1 | top3 |') | ||
| 220 | lines.append('|---|---:|---:|---:|') | ||
| 221 | for lane in ['exact', 'semantic', 'fused']: | ||
| 222 | m = report['overall'][lane] | ||
| 223 | lines.append(f"| {lane} | {m['count']} | {m['top1']:.4f} | {m['top3']:.4f} |") | ||
| 224 | lines.append('') | ||
| 225 | lines.append('## 2. 分 query type 结果') | ||
| 226 | lines.append('') | ||
| 227 | lines.append('| query_type | lane | count | top1 | top3 |') | ||
| 228 | lines.append('|---|---|---:|---:|---:|') | ||
| 229 | for qtype, block in report['per_type'].items(): | ||
| 230 | for lane in ['exact', 'semantic', 'fused']: | ||
| 231 | m = block[lane] | ||
| 232 | lines.append(f"| type_{qtype} | {lane} | {m['count']} | {m['top1']:.4f} | {m['top3']:.4f} |") | ||
| 233 | lines.append('') | ||
| 234 | lines.append('## 3. 失败样例(fused rank != 1)') | ||
| 235 | lines.append('') | ||
| 236 | if not report['failed_fused_examples']: | ||
| 237 | lines.append('- 无,fused 全部 top1 正确。') | ||
| 238 | else: | ||
| 239 | for item in report['failed_fused_examples'][:10]: | ||
| 240 | lines.append( | ||
| 241 | f"- `type_{item['query_type']}` / true=`{item['song_id']}` / fused_rank=`{item['fused_rank']}` / file=`{item['query_path']}` / top1=`{item['fused_topk'][0]['song_id'] if item['fused_topk'] else 'NA'}`" | ||
| 242 | ) | ||
| 243 | lines.append('') | ||
| 244 | lines.append('## 4. 结论') | ||
| 245 | lines.append('') | ||
| 246 | lines.append('- `exact` 代表当前指纹链路单独表现。') | ||
| 247 | lines.append('- `semantic` 代表当前 MERT 单独表现。') | ||
| 248 | lines.append('- `fused` 代表当前版权保护场景里更接近实战的合并结果。') | ||
| 249 | return '\n'.join(lines) + '\n' | ||
| 250 | |||
| 251 | |||
| 252 | def main() -> int: | ||
| 253 | args = parse_args() | ||
| 254 | downloads_dir = Path(args.downloads_dir).resolve() | ||
| 255 | refs, queries = collect_dataset(downloads_dir, args.reference_type, args.query_types) | ||
| 256 | report = evaluate( | ||
| 257 | references=refs, | ||
| 258 | queries=queries, | ||
| 259 | duration=args.duration, | ||
| 260 | topk=args.topk, | ||
| 261 | exact_weight=args.exact_weight, | ||
| 262 | semantic_weight=args.semantic_weight, | ||
| 263 | ) | ||
| 264 | out_json = Path(args.output_json) | ||
| 265 | out_md = Path(args.output_md) | ||
| 266 | out_json.parent.mkdir(parents=True, exist_ok=True) | ||
| 267 | out_md.parent.mkdir(parents=True, exist_ok=True) | ||
| 268 | report.update({ | ||
| 269 | 'downloads_dir': str(downloads_dir), | ||
| 270 | 'reference_type': args.reference_type, | ||
| 271 | 'query_types': args.query_types, | ||
| 272 | 'duration_sec': args.duration, | ||
| 273 | 'topk': args.topk, | ||
| 274 | 'solution': 'chromaprint_matcher + mert-v1-95m', | ||
| 275 | }) | ||
| 276 | out_json.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding='utf-8') | ||
| 277 | out_md.write_text(render_md(report, downloads_dir, args.reference_type, args.query_types, args.duration), encoding='utf-8') | ||
| 278 | print(json.dumps({ | ||
| 279 | 'output_json': str(out_json), | ||
| 280 | 'output_md': str(out_md), | ||
| 281 | 'overall': report['overall'], | ||
| 282 | 'per_type': report['per_type'], | ||
| 283 | 'query_count': report['query_count'], | ||
| 284 | 'reference_count': report['reference_count'], | ||
| 285 | }, ensure_ascii=False, indent=2)) | ||
| 286 | return 0 | ||
| 287 | |||
| 288 | |||
| 289 | if __name__ == '__main__': | ||
| 290 | raise SystemExit(main()) |
| 1 | # Changelog | 1 | # Changelog |
| 2 | 2 | ||
| 3 | ## 2026-06-04 | 3 | ## 2026-06-04 |
| 4 | - 继续收紧 `docs/README.md` 的默认接手路径表达:除了保留 `delivery-onepager.md` 在阅读顺序中的位置外,又显式增加 `start-here -> delivery-onepager -> session-handoff -> song-ingest-query-delivery` 的主路径说明,避免入口顺序仍被误读。 | ||
| 5 | - 新增 `docs/delivery-onepager.md`,把当前 PostgreSQL 主链、selected20 基线、最短启动命令、fresh evidence 与下一步动作再压缩成一页交付摘要,方便外部同事或新 session 先快速接手。 | ||
| 6 | - 更新 `docs/README.md`、`docs/start-here.md` 与 `docs/session-handoff.md`,把 one-pager 接入默认阅读顺序,形成 “one-pager -> handoff -> runbook” 的精炼阅读路径。 | ||
| 7 | |||
| 8 | ## 2026-06-04 | ||
| 9 | - 新增 `docs/song-ingest-query-delivery.md`,把当前仓库里“歌曲目录 -> manifest -> window/features -> PostgreSQL import -> selected20 查询评测 -> 交付产物”的完整链路、操作命令、产物位置、验证点收敛成一份端到端操作手册。 | ||
| 10 | - 更新 `docs/README.md`、`docs/start-here.md` 与 `docs/session-handoff.md`,把这份端到端手册接入默认阅读路径,方便新 session 或交付接手时直接定位完整链路说明。 | ||
| 11 | |||
| 12 | ## 2026-06-04 | ||
| 13 | - 新增仓库根 `README.md`,作为新接手开发人员的快速入口:集中说明“项目当前在做什么、先看哪些核心文档、先跑哪些命令、当前已知事实与 blocker、下一步建议顺序”,并把核心文档链路全部串起来。 | ||
| 14 | - 根据后续 architect 复核继续修正 `docs/start-here.md` 的入口计数错误:当前实际列出 `8` 份文档,因此把“只读这 7 份文档”更正为“只读这 8 份文档”,避免交付入口文案继续误导。 | ||
| 15 | - 更新 `docs/research-delivery-roadmap.md` 的 checklist 表述,把“docs 已形成可唤起交付链路”明确扩展到 `root README + docs README + handoff/roadmap/selected20` 整体入口链。 | ||
| 16 | - 根据 architect 复核修正 `docs/start-here.md` 的入口计数错误:文中实际列出 `7` 份文档,因此把“只读这 6 份文档”更正为“只读这 7 份文档”,避免交付入口文案与真实列表不一致。 | ||
| 17 | - 在 `docs/research-delivery-roadmap.md` 增加“当前进度快照(2026-06-04)”,把 Phase A~E 当前状态显式化,避免后续 session 只看到 roadmap 而不知道已经推进到哪里。 | ||
| 18 | - 更新 roadmap checklist:把“docs 已形成可唤起交付链路”和 selected20 的固定入口 / `json+markdown` 输出 / 结果解读文档化标记为已完成,同时保留 hard-case 白名单、MuQ 接入、在线主链回灌为后续待办。 | ||
| 19 | - 新增 [research-delivery-roadmap.md](./research-delivery-roadmap.md),把当前研究成果、后续 roadmap、阶段 checklist 和建议任务包集中固化成一份可直接唤起的交付文档。 | ||
| 20 | - 更新 `docs/README.md`、`docs/start-here.md` 与 `docs/session-handoff.md` 的入口链路,加入 `research-delivery-roadmap.md` 与 `selected20_songid_eval.md`,让后续 session 能从“当前成果 + 后续任务构想”直接接续。 | ||
| 21 | - 在 `docs/session-handoff.md` 补充 `selected20_songid_eval_report_fresh.json` 与 fresh 重跑一致性的事实,明确 selected20 当前已具备可重复的小样本回归基线属性。 | ||
| 22 | - 基于现有 `.omx` 记录与产物,补齐 `selected20` 实战评测交付:确认 `acr-engine/data/local_eval/selected20_songid_eval_report.json` 与 `docs/selected20_songid_eval.md` 对应同一轮 20 首歌 song-level 评测,口径为 `type_11` 作为 reference、`type_1/7/12/16` 作为 query、`chromaprint_matcher + mert-v1-95m` 双 lane 融合。 | ||
| 23 | - 固化 `selected20` 当前实测结论:总计 `123` 条 query,`fused top1=0.6341 / top3=0.8537`,优于单独 `exact top1=0.6016 / top3=0.8130` 与 `semantic top1=0.4715 / top3=0.6016`;说明当前版权保护小样本场景下,融合优于单 lane。 | ||
| 24 | - 固化 `selected20` 的分类型判断:`type_1` 已满分;`type_12` 由 MERT 单 lane 最强(`top1=1.0`),但融合后更稳;当前主要短板仍集中在 `type_7` 与 `type_16`,其中 `type_7 fused top1=0.4390 / top3=0.7561`、`type_16 fused top1=0.4737 / top3=0.7895`。 | ||
| 25 | - 补充交付物说明:`docs/selected20_songid_eval_repro.md` 已给出复现命令与输出物位置,当前 20 首歌专题可作为后续接入 MuQ 或迁移 PostgreSQL song-centric 主链前的小样本回归基线。 | ||
| 26 | |||
| 27 | ## 2026-06-04 | ||
| 4 | - 继续收敛文档到当前 live 主链口径:补齐 `feature_fact.object_id -> audio_object(window)`、`window.parent_object_id -> asset`、`feature_fact.song_id -> media_entity(song)` 的绑定说明,并新增 manifest/SQL 双样例,专门回答 Phase-1 开源模型集合应该如何落地存储以及 feature 与 audio object 如何关联。 | 28 | - 继续收敛文档到当前 live 主链口径:补齐 `feature_fact.object_id -> audio_object(window)`、`window.parent_object_id -> asset`、`feature_fact.song_id -> media_entity(song)` 的绑定说明,并新增 manifest/SQL 双样例,专门回答 Phase-1 开源模型集合应该如何落地存储以及 feature 与 audio object 如何关联。 |
| 5 | - 修正 `docs/session-handoff.md` 中关于 semantic lane 的旧状态残留,统一到当前真实事实:live 默认已落 `chromaprint_matcher + mert-v1-95m`,MuQ 仍是下一阶段 challenger。 | 29 | - 修正 `docs/session-handoff.md` 中关于 semantic lane 的旧状态残留,统一到当前真实事实:live 默认已落 `chromaprint_matcher + mert-v1-95m`,MuQ 仍是下一阶段 challenger。 |
| 6 | - 继续补充可复核的 live 样例:把 `feature_id = 34 -> window_id = 22 -> asset_id = 20 -> song_beta` 的真实 PostgreSQL 回溯结果写入 handoff 与 schema sample 文档,方便下次 session 直接人工复核绑定链路。 | 30 | - 继续补充可复核的 live 样例:把 `feature_id = 34 -> window_id = 22 -> asset_id = 20 -> song_beta` 的真实 PostgreSQL 回溯结果写入 handoff 与 schema sample 文档,方便下次 session 直接人工复核绑定链路。 | ... | ... |
| ... | @@ -9,10 +9,20 @@ | ... | @@ -9,10 +9,20 @@ |
| 9 | 新同学接手顺序: | 9 | 新同学接手顺序: |
| 10 | 10 | ||
| 11 | 1. [start-here.md](./start-here.md) | 11 | 1. [start-here.md](./start-here.md) |
| 12 | 2. [session-handoff.md](./session-handoff.md) | 12 | 2. [delivery-onepager.md](./delivery-onepager.md) |
| 13 | 3. [postgresql-data-model.md](./postgresql-data-model.md) | 13 | 3. [session-handoff.md](./session-handoff.md) |
| 14 | 4. [postgres_db_schema_samples.md](./postgres_db_schema_samples.md) | 14 | 4. [song-ingest-query-delivery.md](./song-ingest-query-delivery.md) |
| 15 | 5. [CHANGELOG.md](./CHANGELOG.md) | 15 | 5. [research-delivery-roadmap.md](./research-delivery-roadmap.md) |
| 16 | 6. [selected20_songid_eval.md](./selected20_songid_eval.md) | ||
| 17 | 7. [postgresql-data-model.md](./postgresql-data-model.md) | ||
| 18 | 8. [postgres_db_schema_samples.md](./postgres_db_schema_samples.md) | ||
| 19 | 9. [CHANGELOG.md](./CHANGELOG.md) | ||
| 20 | |||
| 21 | 推荐把前 4 份当作默认接手主路径: | ||
| 22 | |||
| 23 | ```text | ||
| 24 | start-here -> delivery-onepager -> session-handoff -> song-ingest-query-delivery | ||
| 25 | ``` | ||
| 16 | 26 | ||
| 17 | --- | 27 | --- |
| 18 | 28 | ||
| ... | @@ -65,14 +75,18 @@ acr-engine/scripts/start_songcentric_shortest_path.sh 'postgres://d2:d2pass@127. | ... | @@ -65,14 +75,18 @@ acr-engine/scripts/start_songcentric_shortest_path.sh 'postgres://d2:d2pass@127. |
| 65 | - `semantic_runtime_available = true` | 75 | - `semantic_runtime_available = true` |
| 66 | - `semantic_runtime_ready_count = 5` | 76 | - `semantic_runtime_ready_count = 5` |
| 67 | - `semantic_fallback_count = 0` | 77 | - `semantic_fallback_count = 0` |
| 68 | - `import_counts.feature_fact = 29 | 78 | - `import_counts.feature_fact = 34` |
| 69 | 79 | ||
| 70 | --- | 80 | --- |
| 71 | 81 | ||
| 72 | ## 4. 当前保留文档分别解决什么 | 82 | ## 4. 当前保留文档分别解决什么 |
| 73 | 83 | ||
| 74 | - [start-here.md](./start-here.md):新同学 10 分钟接手入口 | 84 | - [start-here.md](./start-here.md):新同学 10 分钟接手入口 |
| 85 | - [delivery-onepager.md](./delivery-onepager.md):对外/跨团队交付时先发这一页 | ||
| 75 | - [session-handoff.md](./session-handoff.md):下次启动从哪里继续 | 86 | - [session-handoff.md](./session-handoff.md):下次启动从哪里继续 |
| 87 | - [song-ingest-query-delivery.md](./song-ingest-query-delivery.md):歌曲从目录入库到查询/评测/交付的完整操作手册 | ||
| 88 | - [research-delivery-roadmap.md](./research-delivery-roadmap.md):当前研究交付、roadmap、checklist | ||
| 89 | - [selected20_songid_eval.md](./selected20_songid_eval.md):20 首专题评测摘要 | ||
| 76 | - [postgresql-data-model.md](./postgresql-data-model.md):表设计、字段语义、feature 与 audio_object 的绑定关系、Phase-1 模型落库口径 | 90 | - [postgresql-data-model.md](./postgresql-data-model.md):表设计、字段语义、feature 与 audio_object 的绑定关系、Phase-1 模型落库口径 |
| 77 | - [postgres_db_schema_samples.md](./postgres_db_schema_samples.md):DDL、manifest/SQL 样例、典型查询链路、真实存储示例 | 91 | - [postgres_db_schema_samples.md](./postgres_db_schema_samples.md):DDL、manifest/SQL 样例、典型查询链路、真实存储示例 |
| 78 | - [CHANGELOG.md](./CHANGELOG.md):变更历史 | 92 | - [CHANGELOG.md](./CHANGELOG.md):变更历史 | ... | ... |
docs/delivery-onepager.md
0 → 100644
| 1 | # ACR 交付一页纸 | ||
| 2 | |||
| 3 | > 给新接手同事的最短摘要:先看这页,再决定要不要展开到完整 runbook。 | ||
| 4 | |||
| 5 | --- | ||
| 6 | |||
| 7 | ## 1. 现在已经交付了什么 | ||
| 8 | |||
| 9 | 当前已经有两条可用主线: | ||
| 10 | |||
| 11 | 1. **PostgreSQL song-centric 主链** | ||
| 12 | - 歌曲目录 -> manifest -> window -> fingerprint/embedding -> PostgreSQL | ||
| 13 | 2. **selected20 小样本评测主线** | ||
| 14 | - 20 首歌专题 -> exact / semantic / fused song-level 命中率评测 | ||
| 15 | |||
| 16 | 一句话: | ||
| 17 | |||
| 18 | > **数据库宿主链已经打通,20 首歌回归基线已经固定;下一步重点不是重做方案,而是在当前基线上继续补 semantic challenger。** | ||
| 19 | |||
| 20 | --- | ||
| 21 | |||
| 22 | ## 2. 当前默认模型 | ||
| 23 | |||
| 24 | 逻辑语义: | ||
| 25 | |||
| 26 | ```text | ||
| 27 | song -> asset -> window -> fingerprint / embedding | ||
| 28 | ``` | ||
| 29 | |||
| 30 | 物理落表: | ||
| 31 | |||
| 32 | ```text | ||
| 33 | media_entity -> audio_object -> feature_fact -> set_membership | ||
| 34 | ``` | ||
| 35 | |||
| 36 | --- | ||
| 37 | |||
| 38 | ## 3. 最短启动命令 | ||
| 39 | |||
| 40 | ```bash | ||
| 41 | cd /workspace | ||
| 42 | /usr/local/miniconda3/bin/python acr-engine/scripts/run_songcentric_directory_pipeline_live.py \ | ||
| 43 | --dsn 'postgres://d2:d2pass@127.0.0.1:5432/d2' \ | ||
| 44 | --schema acr_songcentric_test \ | ||
| 45 | --input-root acr-engine/data/songcentric_builder_smoke \ | ||
| 46 | --output-dir acr-engine/data/pgvector_eval/music20 | ||
| 47 | ``` | ||
| 48 | |||
| 49 | --- | ||
| 50 | |||
| 51 | ## 4. 当前 fresh evidence | ||
| 52 | |||
| 53 | 主链当前结果: | ||
| 54 | - `song_count = 2` | ||
| 55 | - `asset_count = 2` | ||
| 56 | - `window_count = 5` | ||
| 57 | - `matcher_fingerprint_count = 5` | ||
| 58 | - `semantic_runtime_ready_count = 5` | ||
| 59 | - `import_counts = media_entity:9 / audio_object:22 / feature_fact:34 / set_membership:9` | ||
| 60 | |||
| 61 | 说明: | ||
| 62 | - exact lane 已接 `chromaprint_matcher` | ||
| 63 | - semantic lane 已接 `mert-v1-95m` | ||
| 64 | - 当前 5 个 window 没走 fallback | ||
| 65 | |||
| 66 | --- | ||
| 67 | |||
| 68 | ## 5. selected20 当前结论 | ||
| 69 | |||
| 70 | - query 总数:`123` | ||
| 71 | - exact:`top1=0.6016 / top3=0.8130` | ||
| 72 | - semantic:`top1=0.4715 / top3=0.6016` | ||
| 73 | - fused:`top1=0.6341 / top3=0.8537` | ||
| 74 | |||
| 75 | 关键判断: | ||
| 76 | - `type_1` 已打满 | ||
| 77 | - `type_12` 很强 | ||
| 78 | - 主要短板仍是 `type_7 / type_16` | ||
| 79 | |||
| 80 | --- | ||
| 81 | |||
| 82 | ## 6. 你应该先看哪份文档 | ||
| 83 | |||
| 84 | ### 如果你只想快速接手 | ||
| 85 | 1. [start-here.md](./start-here.md) | ||
| 86 | 2. [delivery-onepager.md](./delivery-onepager.md) | ||
| 87 | 3. [session-handoff.md](./session-handoff.md) | ||
| 88 | |||
| 89 | ### 如果你要看完整操作链路 | ||
| 90 | - [song-ingest-query-delivery.md](./song-ingest-query-delivery.md) | ||
| 91 | |||
| 92 | ### 如果你要看专题评测 | ||
| 93 | - [selected20_songid_eval.md](./selected20_songid_eval.md) | ||
| 94 | - [selected20_songid_eval_repro.md](./selected20_songid_eval_repro.md) | ||
| 95 | |||
| 96 | --- | ||
| 97 | |||
| 98 | ## 7. 现在最重要的下一步 | ||
| 99 | |||
| 100 | 1. 保住当前 4 表主链不回退 | ||
| 101 | 2. 接入 MuQ challenger | ||
| 102 | 3. 每次变更后复跑 selected20 | ||
| 103 | 4. 重点盯 `type_7 / type_16` | ||
| 104 | |||
| 105 | --- | ||
| 106 | |||
| 107 | ## 8. 相关文档 | ||
| 108 | |||
| 109 | - [README.md](./README.md) | ||
| 110 | - [start-here.md](./start-here.md) | ||
| 111 | - [session-handoff.md](./session-handoff.md) | ||
| 112 | - [song-ingest-query-delivery.md](./song-ingest-query-delivery.md) | ||
| 113 | - [research-delivery-roadmap.md](./research-delivery-roadmap.md) | ||
| 114 | - [CHANGELOG.md](./CHANGELOG.md) |
docs/research-delivery-roadmap.md
0 → 100644
| 1 | # Research Delivery / 当前研究交付与 Roadmap | ||
| 2 | |||
| 3 | > 目标:把当前已经验证过的研究成果、下一阶段待办、以及可执行 checklist 固化成一份可直接唤起的交付文档。 | ||
| 4 | |||
| 5 | --- | ||
| 6 | |||
| 7 | ## 1. 当前交付了什么 | ||
| 8 | |||
| 9 | 当前已经形成两条可复用主线: | ||
| 10 | |||
| 11 | 1. **song-centric + 4 表 PostgreSQL 主链** | ||
| 12 | - 完整操作手册:[song-ingest-query-delivery.md](./song-ingest-query-delivery.md) | ||
| 13 | 2. **selected20(20 首歌)文件级小样本实战评测主线** | ||
| 14 | |||
| 15 | 一句话总结: | ||
| 16 | |||
| 17 | > **数据库宿主链已经打通,20 首专题评测基线也已经建立;下一阶段不是重新争论总方案,而是在当前基线上做 challenger 扩展与 hard-case 改善。** | ||
| 18 | |||
| 19 | --- | ||
| 20 | |||
| 21 | ## 2. 当前研究成果摘要 | ||
| 22 | |||
| 23 | ### 2.1 主数据 / 宿主链成果 | ||
| 24 | |||
| 25 | 当前默认物理模型: | ||
| 26 | |||
| 27 | ```text | ||
| 28 | media_entity -> audio_object -> feature_fact -> set_membership | ||
| 29 | ``` | ||
| 30 | |||
| 31 | 当前默认逻辑语义: | ||
| 32 | |||
| 33 | ```text | ||
| 34 | song -> asset -> window -> fingerprint / embedding | ||
| 35 | ``` | ||
| 36 | |||
| 37 | 已真实验证: | ||
| 38 | |||
| 39 | - live PostgreSQL schema 可建表 | ||
| 40 | - 真实目录 -> manifest -> import 可跑通 | ||
| 41 | - `windows[].features[]` 可直接导入 `feature_fact` | ||
| 42 | - exact lane 已接 `chromaprint_matcher` | ||
| 43 | - semantic lane 已接 `mert-v1-95m` | ||
| 44 | - 当前主链在 live PostgreSQL 上已有 fresh evidence | ||
| 45 | |||
| 46 | ### 2.2 selected20(20 首歌)专题成果 | ||
| 47 | |||
| 48 | 专题数据目录: | ||
| 49 | |||
| 50 | - `/root/hikoon_song_files/output/selected_20_songs/downloads` | ||
| 51 | |||
| 52 | 当前专题方案: | ||
| 53 | |||
| 54 | - exact:`chromaprint_matcher` | ||
| 55 | - semantic:`mert-v1-95m` | ||
| 56 | - fused:`0.6 * exact + 0.4 * semantic` | ||
| 57 | |||
| 58 | 当前 fresh 结果: | ||
| 59 | |||
| 60 | | lane | count | top1 | top3 | | ||
| 61 | |---|---:|---:|---:| | ||
| 62 | | exact | 123 | 0.6016 | 0.8130 | | ||
| 63 | | semantic | 123 | 0.4715 | 0.6016 | | ||
| 64 | | fused | 123 | 0.6341 | 0.8537 | | ||
| 65 | |||
| 66 | 分类型结论: | ||
| 67 | |||
| 68 | | query_type | count | 当前判断 | | ||
| 69 | |---|---:|---| | ||
| 70 | | `type_1` | 21 | 已打满,当前不是主要矛盾 | | ||
| 71 | | `type_7` | 41 | 主要短板之一,混淆明显 | | ||
| 72 | | `type_12` | 23 | 当前表现很好,semantic 单 lane 已打满 | | ||
| 73 | | `type_16` | 38 | 另一主要短板,仍需重点优化 | | ||
| 74 | |||
| 75 | 一句话判断: | ||
| 76 | |||
| 77 | > **当前融合结果优于单独 semantic,也略优于单独 exact;实战瓶颈主要在 `type_7 / type_16`。** | ||
| 78 | |||
| 79 | --- | ||
| 80 | |||
| 81 | ## 3. 当前 blocker | ||
| 82 | |||
| 83 | ### 3.1 语义 challenger blocker | ||
| 84 | |||
| 85 | MuQ 当前还没接入成功。 | ||
| 86 | |||
| 87 | 已确认事实: | ||
| 88 | |||
| 89 | - 候选:`OpenMuQ/MuQ-large-msd-iter` | ||
| 90 | - `muq` 包已安装 | ||
| 91 | - 当前 host 上 `import muq` 仍被 `RuntimeError: operator torchvision::nms does not exist` 阻塞 | ||
| 92 | |||
| 93 | 所以当前 blocker 不是“还没想到做什么”,而是: | ||
| 94 | |||
| 95 | > **MERT baseline 已可用,但 MuQ challenger 受 `torchvision` 兼容问题阻塞。** | ||
| 96 | |||
| 97 | ### 3.2 效果 blocker | ||
| 98 | |||
| 99 | 从 selected20 看,当前真正的效果短板不是: | ||
| 100 | |||
| 101 | - `type_1` | ||
| 102 | - `type_12` | ||
| 103 | |||
| 104 | 而是: | ||
| 105 | |||
| 106 | - `type_7` | ||
| 107 | - `type_16` | ||
| 108 | |||
| 109 | 这意味着下一阶段不能只盯 overall,必须盯 hard-case 的分类型提升。 | ||
| 110 | |||
| 111 | --- | ||
| 112 | |||
| 113 | ## 4. 后续 Roadmap(建议版) | ||
| 114 | |||
| 115 | ### 当前进度快照(2026-06-04) | ||
| 116 | |||
| 117 | | Phase | 状态 | 当前判断 | | ||
| 118 | |---|---|---| | ||
| 119 | | Phase A:冻结当前基线 | 进行中 | 主链与 selected20 基线都已形成,fresh 重跑一致性也已确认 | | ||
| 120 | | Phase B:接入 MuQ challenger | 未开始 | 仍被 `torchvision::nms` runtime blocker 卡住 | | ||
| 121 | | Phase C:selected20 回归闸门 | 进行中 | 已有脚本、JSON、Markdown 摘要,但还未完全制度化成固定门禁 | | ||
| 122 | | Phase D:专题策略回灌在线主链 | 未开始 | 还没有把 selected20 的 hard-case 分析映射回 PostgreSQL 查询侧 | | ||
| 123 | | Phase E:更大规模评测与工程化 | 未开始 | 当前仍以 selected20 为核心小样本基线 | | ||
| 124 | |||
| 125 | ### Phase A:冻结当前基线 | ||
| 126 | |||
| 127 | 目标: | ||
| 128 | |||
| 129 | - 守住当前 song-centric 4 表主链 | ||
| 130 | - 守住 selected20 小样本回归基线 | ||
| 131 | |||
| 132 | 完成标准: | ||
| 133 | |||
| 134 | - `run_songcentric_directory_pipeline_live.py` 继续稳定可跑 | ||
| 135 | - `selected20_songid_eval_report.json` 继续作为对照基线 | ||
| 136 | |||
| 137 | ### Phase B:接入 MuQ challenger | ||
| 138 | |||
| 139 | 目标: | ||
| 140 | |||
| 141 | - 在不破坏现有 MERT baseline 的前提下,加一条 MuQ semantic challenger lane | ||
| 142 | |||
| 143 | 完成标准: | ||
| 144 | |||
| 145 | - `import muq` / `from muq import MuQ` 可成功 | ||
| 146 | - runner/评测脚本能产出 MuQ embedding | ||
| 147 | - PostgreSQL 或文件级评测里能看到独立 MuQ 指标 | ||
| 148 | |||
| 149 | 关键评估: | ||
| 150 | |||
| 151 | - MuQ 单 lane 是否优于 MERT | ||
| 152 | - MuQ + exact 的 fused 是否改善 `type_7 / type_16` | ||
| 153 | |||
| 154 | ### Phase C:以 selected20 作为回归闸门 | ||
| 155 | |||
| 156 | 目标: | ||
| 157 | |||
| 158 | - 把 selected20 固化成每次 encoder / fusion 变更后的回归集 | ||
| 159 | |||
| 160 | 完成标准: | ||
| 161 | |||
| 162 | - 每次变更后都能直接复跑 selected20 | ||
| 163 | - 报告中必须比较: | ||
| 164 | - overall | ||
| 165 | - per-type | ||
| 166 | - 失败样例 | ||
| 167 | |||
| 168 | ### Phase D:把小样本专题映射回在线主链 | ||
| 169 | |||
| 170 | 目标: | ||
| 171 | |||
| 172 | - 把 selected20 的有效策略回灌到 PostgreSQL / song-centric 主链 | ||
| 173 | - 交付/接手时优先参考 [song-ingest-query-delivery.md](./song-ingest-query-delivery.md) | ||
| 174 | |||
| 175 | 完成标准: | ||
| 176 | |||
| 177 | - `feature_fact` 中能区分不同 semantic challenger | ||
| 178 | - 可以对同一批 query 比较 exact / MERT / MuQ / fused | ||
| 179 | |||
| 180 | ### Phase E:进入更大规模评测与工程化 | ||
| 181 | |||
| 182 | 目标: | ||
| 183 | |||
| 184 | - 从 selected20 扩展到更大的 reference / query 集 | ||
| 185 | - 明确生产级回归、巡检、落库与索引策略 | ||
| 186 | |||
| 187 | 完成标准: | ||
| 188 | |||
| 189 | - 有更大评测集 | ||
| 190 | - 有固定报表结构 | ||
| 191 | - 有稳定的主链/专题双回归机制 | ||
| 192 | |||
| 193 | --- | ||
| 194 | |||
| 195 | ## 5. Checklist(下一阶段执行清单) | ||
| 196 | |||
| 197 | ### 5.1 基线保护 checklist | ||
| 198 | |||
| 199 | - [x] 4 表 song-centric schema 已作为当前默认口径 | ||
| 200 | - [x] `chromaprint_matcher` 已接入 exact lane | ||
| 201 | - [x] `mert-v1-95m` 已接入 semantic baseline | ||
| 202 | - [x] selected20 已形成基线报告 | ||
| 203 | - [x] selected20 fresh 重跑结果与当前基线一致 | ||
| 204 | - [x] docs 已形成可唤起交付链路(root README / docs README / start-here / handoff / roadmap / selected20) | ||
| 205 | - [ ] 把 selected20 失败样例沉淀成 hard-case 白名单 | ||
| 206 | |||
| 207 | ### 5.2 MuQ 接入 checklist | ||
| 208 | |||
| 209 | - [ ] 审计 `torch / torchvision / muq` 兼容矩阵 | ||
| 210 | - [ ] 解决 `torchvision::nms does not exist` | ||
| 211 | - [ ] 在本机成功 `from muq import MuQ` | ||
| 212 | - [ ] 在评测脚本中增加 MuQ lane | ||
| 213 | - [ ] 产出 `MERT vs MuQ vs fused` 对比报告 | ||
| 214 | |||
| 215 | ### 5.3 效果优化 checklist | ||
| 216 | |||
| 217 | - [ ] 按 `type_7 / type_16` 汇总失败歌曲对 | ||
| 218 | - [ ] 统计最常见误判 top1 歌曲 | ||
| 219 | - [ ] 评估 fusion 权重是否仍应固定在 `0.6 / 0.4` | ||
| 220 | - [ ] 尝试基于 hard-case 调整融合策略 | ||
| 221 | - [ ] 验证优化是否真正改善 `type_7 / type_16` | ||
| 222 | |||
| 223 | ### 5.4 工程化 checklist | ||
| 224 | |||
| 225 | - [x] 让 selected20 评测成为固定入口命令 | ||
| 226 | - [x] 报告输出统一到 `json + markdown` | ||
| 227 | - [x] 在 docs 里固定“怎么读结果 / 怎么继续” | ||
| 228 | - [ ] 后续如果切 online path,保留该文件级评测作为快速回归集 | ||
| 229 | |||
| 230 | --- | ||
| 231 | |||
| 232 | ## 6. 推荐下次启动顺序 | ||
| 233 | |||
| 234 | 1. 先看 [session-handoff.md](./session-handoff.md) | ||
| 235 | 2. 再看 [selected20_songid_eval.md](./selected20_songid_eval.md) | ||
| 236 | 3. 再看本文件的 roadmap + checklist | ||
| 237 | 4. 决定是走: | ||
| 238 | - **MuQ 接入线** | ||
| 239 | - **selected20 hard-case 优化线** | ||
| 240 | - **PostgreSQL 在线回灌线** | ||
| 241 | |||
| 242 | ### 当前建议的下一步(按收益排序) | ||
| 243 | |||
| 244 | 1. **先做 hard-case 白名单** | ||
| 245 | - 把 `type_7 / type_16` 的失败样例整理成固定榜单 | ||
| 246 | 2. **再做 MuQ runtime 解锁** | ||
| 247 | - 先解决 `torchvision::nms`,再做 challenger 对比 | ||
| 248 | 3. **最后做主链回灌** | ||
| 249 | - 把 selected20 上确认有效的策略映射回 PostgreSQL 查询路径 | ||
| 250 | |||
| 251 | --- | ||
| 252 | |||
| 253 | ## 7. 当前建议任务构想 | ||
| 254 | |||
| 255 | ### 任务包 A:MuQ 解锁 | ||
| 256 | |||
| 257 | 目标: | ||
| 258 | - 打通 MuQ runtime | ||
| 259 | |||
| 260 | 产出: | ||
| 261 | - 环境兼容记录 | ||
| 262 | - 可运行 MuQ smoke | ||
| 263 | - 可落盘 MuQ embedding | ||
| 264 | |||
| 265 | ### 任务包 B:selected20 hard-case 分析 | ||
| 266 | |||
| 267 | 目标: | ||
| 268 | - 只盯 `type_7 / type_16` | ||
| 269 | |||
| 270 | 产出: | ||
| 271 | - 失败歌曲对榜单 | ||
| 272 | - 常见误判榜单 | ||
| 273 | - 是否需要调整 fusion 的结论 | ||
| 274 | |||
| 275 | ### 任务包 C:回归制度化 | ||
| 276 | |||
| 277 | 目标: | ||
| 278 | - 让 selected20 成为固定回归门禁 | ||
| 279 | |||
| 280 | 产出: | ||
| 281 | - 固定命令 | ||
| 282 | - 固定报告路径 | ||
| 283 | - 固定结果解读模板 | ||
| 284 | |||
| 285 | ### 任务包 D:在线主链映射 | ||
| 286 | |||
| 287 | 目标: | ||
| 288 | - 把有效的专题结论映射回 PostgreSQL 主链 | ||
| 289 | |||
| 290 | 产出: | ||
| 291 | - 主链中的 challenger 标识 | ||
| 292 | - 可对比的落库数据 | ||
| 293 | - 在线/离线一致的评估视图 | ||
| 294 | |||
| 295 | --- | ||
| 296 | |||
| 297 | ## 一句话交付结论 | ||
| 298 | |||
| 299 | > 当前最值得保护的研究资产不是“某个临时实验数字”,而是 **已经跑通的 4 表 song-centric 主链 + 已经成型的 selected20 小样本回归基线 + 明确的 MuQ / hard-case 后续路线图**。 |
docs/selected20_songid_eval.md
0 → 100644
| 1 | # Selected 20 Songs 实战检索评测 | ||
| 2 | |||
| 3 | > 完整入库/查询主链操作手册见 [song-ingest-query-delivery.md](./song-ingest-query-delivery.md);本页只负责 `selected20` 文件级实战评测摘要。 | ||
| 4 | |||
| 5 | - 数据目录:`/root/hikoon_song_files/output/selected_20_songs/downloads` | ||
| 6 | - reference:`type_11` | ||
| 7 | - queries:`type_1, type_7, type_12, type_16` | ||
| 8 | - 当前方案:`chromaprint_matcher + mert-v1-95m` | ||
| 9 | - 语义截断时长:`8.0s` | ||
| 10 | - reference 文件数:`24` | ||
| 11 | - query 文件数:`123` | ||
| 12 | |||
| 13 | ## 1. 总体结果 | ||
| 14 | |||
| 15 | | lane | count | top1 | top3 | | ||
| 16 | |---|---:|---:|---:| | ||
| 17 | | exact | 123 | 0.6016 | 0.8130 | | ||
| 18 | | semantic | 123 | 0.4715 | 0.6016 | | ||
| 19 | | fused | 123 | 0.6341 | 0.8537 | | ||
| 20 | |||
| 21 | ## 2. 分 query type 结果 | ||
| 22 | |||
| 23 | | query_type | lane | count | top1 | top3 | | ||
| 24 | |---|---|---:|---:|---:| | ||
| 25 | | type_1 | exact | 21 | 1.0000 | 1.0000 | | ||
| 26 | | type_1 | semantic | 21 | 1.0000 | 1.0000 | | ||
| 27 | | type_1 | fused | 21 | 1.0000 | 1.0000 | | ||
| 28 | | type_7 | exact | 41 | 0.4146 | 0.7073 | | ||
| 29 | | type_7 | semantic | 41 | 0.1707 | 0.3902 | | ||
| 30 | | type_7 | fused | 41 | 0.4390 | 0.7561 | | ||
| 31 | | type_12 | exact | 23 | 0.8261 | 0.9130 | | ||
| 32 | | type_12 | semantic | 23 | 1.0000 | 1.0000 | | ||
| 33 | | type_12 | fused | 23 | 0.9130 | 1.0000 | | ||
| 34 | | type_16 | exact | 38 | 0.4474 | 0.7632 | | ||
| 35 | | type_16 | semantic | 38 | 0.1842 | 0.3684 | | ||
| 36 | | type_16 | fused | 38 | 0.4737 | 0.7895 | | ||
| 37 | |||
| 38 | ## 3. 失败样例(fused rank != 1) | ||
| 39 | |||
| 40 | - `type_7` / true=`572816` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/572816/type_7/三军令(副歌版).mp3` / top1=`614397` | ||
| 41 | - `type_7` / true=`604814` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/604814/type_7/偏差(剪辑版2)-吉鹏泽.wav` / top1=`614397` | ||
| 42 | - `type_7` / true=`604814` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/604814/type_7/偏差(剪辑版3)-吉鹏泽.wav` / top1=`614397` | ||
| 43 | - `type_7` / true=`643015` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/643015/type_7/置念 抖音推广音频.wav` / top1=`637559` | ||
| 44 | - `type_7` / true=`656394` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/656394/type_7/把你宠坏-推广片段.wav` / top1=`614740` | ||
| 45 | - `type_7` / true=`657848` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/657848/type_7/小神仙 主歌剪-1.mp3` / top1=`614397` | ||
| 46 | - `type_7` / true=`659300` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/659300/type_7/推广片段2.WAV` / top1=`614397` | ||
| 47 | - `type_7` / true=`675118` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/675118/type_7/敲冰求火片段1.1x.wav` / top1=`614397` | ||
| 48 | - `type_7` / true=`675118` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/675118/type_7/敲冰求火片段_2.wav` / top1=`614397` | ||
| 49 | - `type_7` / true=`675406` / fused_rank=`2` / file=`/root/hikoon_song_files/output/selected_20_songs/downloads/675406/type_7/2.mp3` / top1=`614397` | ||
| 50 | |||
| 51 | ## 4. 结论 | ||
| 52 | |||
| 53 | - `exact` 代表当前指纹链路单独表现。 | ||
| 54 | - `semantic` 代表当前 MERT 单独表现。 | ||
| 55 | - `fused` 代表当前版权保护场景里更接近实战的合并结果。 |
docs/selected20_songid_eval_repro.md
0 → 100644
| 1 | # Selected20 SongID 检索评测复现手册 | ||
| 2 | |||
| 3 | > 这份文档只覆盖 `selected20` 文件级回归评测;完整数据库主链与“歌曲入库 -> 查询 -> 交付”见 [song-ingest-query-delivery.md](./song-ingest-query-delivery.md)。 | ||
| 4 | |||
| 5 | ## 1. 目标 | ||
| 6 | |||
| 7 | 验证目录: | ||
| 8 | |||
| 9 | - `/root/hikoon_song_files/output/selected_20_songs/downloads` | ||
| 10 | |||
| 11 | 在当前方案下: | ||
| 12 | |||
| 13 | - `reference = type_11` | ||
| 14 | - `query = type_1 / type_7 / type_12 / type_16` | ||
| 15 | - 目录名即 `song_id` | ||
| 16 | |||
| 17 | 检查实战里是否能够正确识别回对应 `song_id`。 | ||
| 18 | |||
| 19 | --- | ||
| 20 | |||
| 21 | ## 2. 当前方案口径 | ||
| 22 | |||
| 23 | 本专题默认评测的方案是: | ||
| 24 | |||
| 25 | - exact lane:`chromaprint_matcher` | ||
| 26 | - semantic lane:`mert-v1-95m` | ||
| 27 | - fused:`0.6 * exact + 0.4 * semantic` | ||
| 28 | |||
| 29 | 说明: | ||
| 30 | - `type_11` 视为无损 reference | ||
| 31 | - `type_1/7/12/16` 视为 query | ||
| 32 | - 最终评测目标是 song-level `song_id` 命中率 | ||
| 33 | |||
| 34 | --- | ||
| 35 | |||
| 36 | ## 3. 目录结构假设 | ||
| 37 | |||
| 38 | 每首歌结构类似: | ||
| 39 | |||
| 40 | ```text | ||
| 41 | <downloads>/<song_id>/type_1/* | ||
| 42 | <downloads>/<song_id>/type_7/* | ||
| 43 | <downloads>/<song_id>/type_11/* | ||
| 44 | <downloads>/<song_id>/type_12/* | ||
| 45 | <downloads>/<song_id>/type_16/* | ||
| 46 | ``` | ||
| 47 | |||
| 48 | 其中: | ||
| 49 | - 目录名 `<song_id>` 就是 ground truth | ||
| 50 | - `type_11` 至少 1 个文件 | ||
| 51 | - query type 可以多文件 | ||
| 52 | |||
| 53 | --- | ||
| 54 | |||
| 55 | ## 4. 复现命令 | ||
| 56 | |||
| 57 | ```bash | ||
| 58 | cd /workspace/acr-engine | ||
| 59 | /usr/local/miniconda3/bin/python scripts/evaluate_selected20_songid_retrieval.py \ | ||
| 60 | --downloads-dir /root/hikoon_song_files/output/selected_20_songs/downloads \ | ||
| 61 | --reference-type 11 \ | ||
| 62 | --query-types 1 7 12 16 \ | ||
| 63 | --duration 8.0 \ | ||
| 64 | --topk 3 \ | ||
| 65 | --exact-weight 0.6 \ | ||
| 66 | --semantic-weight 0.4 \ | ||
| 67 | --output-json /workspace/acr-engine/data/local_eval/selected20_songid_eval_report.json \ | ||
| 68 | --output-md /workspace/docs/selected20_songid_eval.md | ||
| 69 | ``` | ||
| 70 | |||
| 71 | --- | ||
| 72 | |||
| 73 | ## 5. 输出物 | ||
| 74 | |||
| 75 | ### JSON 报告 | ||
| 76 | |||
| 77 | - `acr-engine/data/local_eval/selected20_songid_eval_report.json` | ||
| 78 | |||
| 79 | 包含: | ||
| 80 | - overall top1/top3 | ||
| 81 | - per-type top1/top3 | ||
| 82 | - 每条 query 的 exact / semantic / fused 排名 | ||
| 83 | - 失败样例 | ||
| 84 | |||
| 85 | ### Markdown 摘要 | ||
| 86 | |||
| 87 | - `docs/selected20_songid_eval.md` | ||
| 88 | |||
| 89 | 适合给研发、产品、算法同学直接阅读。 | ||
| 90 | |||
| 91 | --- | ||
| 92 | |||
| 93 | ## 6. 评测逻辑 | ||
| 94 | |||
| 95 | ### 6.1 reference 构建 | ||
| 96 | |||
| 97 | - 遍历每个 `song_id` | ||
| 98 | - 读取 `type_11` 文件作为 reference | ||
| 99 | - 同时建立: | ||
| 100 | - `chromaprint_matcher` 指纹索引 | ||
| 101 | - `mert-v1-95m` song embedding | ||
| 102 | |||
| 103 | ### 6.2 query 评估 | ||
| 104 | |||
| 105 | 对每个 query 文件: | ||
| 106 | |||
| 107 | 1. 走 exact:`chromaprint_matcher.match(...)` | ||
| 108 | 2. 走 semantic:MERT embedding 和 reference song embedding 相似度 | ||
| 109 | 3. 融合: | ||
| 110 | |||
| 111 | ```text | ||
| 112 | fused_score = 0.6 * exact_norm + 0.4 * semantic_norm | ||
| 113 | ``` | ||
| 114 | |||
| 115 | 4. 输出 topk 候选 | ||
| 116 | 5. 检查 true `song_id` 的 rank | ||
| 117 | |||
| 118 | ### 6.3 指标 | ||
| 119 | |||
| 120 | - `top1`:正确 song_id 是否排第 1 | ||
| 121 | - `top3`:正确 song_id 是否排前 3 | ||
| 122 | |||
| 123 | --- | ||
| 124 | |||
| 125 | ## 7. 如何看结果 | ||
| 126 | |||
| 127 | 优先看: | ||
| 128 | |||
| 129 | 1. overall | ||
| 130 | 2. per query type | ||
| 131 | 3. failed_fused_examples | ||
| 132 | |||
| 133 | 建议解释顺序: | ||
| 134 | - exact 单独效果 | ||
| 135 | - semantic 单独效果 | ||
| 136 | - fused 是否优于单 lane | ||
| 137 | - 哪些 type 是当前主要短板 | ||
| 138 | |||
| 139 | --- | ||
| 140 | |||
| 141 | ## 8. 后续扩展 | ||
| 142 | |||
| 143 | 如果后续接入 MuQ: | ||
| 144 | - 可以在该脚本里增加 `muq` lane | ||
| 145 | - 输出:`exact / mert / muq / fused` | ||
| 146 | - 比较 `type_7 / type_12 / type_16` 是否改善 | ||
| 147 | |||
| 148 | 如果后续切到 PostgreSQL: | ||
| 149 | - 可以把当前文件级 query/reference 逻辑迁移为 `song -> asset -> window -> feature_fact` | ||
| 150 | - 但这个专题脚本仍然保留,作为**小样本回归评测**基线 |
| ... | @@ -2,6 +2,8 @@ | ... | @@ -2,6 +2,8 @@ |
| 2 | 2 | ||
| 3 | > 目标:让下次启动的新 session 在 **3~10 分钟内** 知道从哪里开始。 | 3 | > 目标:让下次启动的新 session 在 **3~10 分钟内** 知道从哪里开始。 |
| 4 | 4 | ||
| 5 | 快速对外转交时,先发: [delivery-onepager.md](./delivery-onepager.md) | ||
| 6 | |||
| 5 | --- | 7 | --- |
| 6 | 8 | ||
| 7 | ## 1. 下次启动先做什么 | 9 | ## 1. 下次启动先做什么 |
| ... | @@ -140,9 +142,11 @@ flowchart TD | ... | @@ -140,9 +142,11 @@ flowchart TD |
| 140 | 142 | ||
| 141 | 1. [README.md](./README.md) | 143 | 1. [README.md](./README.md) |
| 142 | 2. [start-here.md](./start-here.md) | 144 | 2. [start-here.md](./start-here.md) |
| 143 | 3. [postgresql-data-model.md](./postgresql-data-model.md) | 145 | 3. [research-delivery-roadmap.md](./research-delivery-roadmap.md) |
| 144 | 4. [postgres_db_schema_samples.md](./postgres_db_schema_samples.md) | 146 | 4. [selected20_songid_eval.md](./selected20_songid_eval.md) |
| 145 | 5. [CHANGELOG.md](./CHANGELOG.md) | 147 | 5. [postgresql-data-model.md](./postgresql-data-model.md) |
| 148 | 6. [postgres_db_schema_samples.md](./postgres_db_schema_samples.md) | ||
| 149 | 7. [CHANGELOG.md](./CHANGELOG.md) | ||
| 146 | 150 | ||
| 147 | 关键代码: | 151 | 关键代码: |
| 148 | - `acr-engine/sql/acr_pg_schema_songcentric_v1.sql` | 152 | - `acr-engine/sql/acr_pg_schema_songcentric_v1.sql` |
| ... | @@ -152,6 +156,9 @@ flowchart TD | ... | @@ -152,6 +156,9 @@ flowchart TD |
| 152 | - `acr-engine/scripts/import_songcentric_manifest_live.py` | 156 | - `acr-engine/scripts/import_songcentric_manifest_live.py` |
| 153 | - `acr-engine/scripts/start_songcentric_shortest_path.sh` | 157 | - `acr-engine/scripts/start_songcentric_shortest_path.sh` |
| 154 | 158 | ||
| 159 | 如果要完整看“歌曲怎么入库、怎么查询/评测、交付时要带哪些产物”,直接读: | ||
| 160 | - [song-ingest-query-delivery.md](./song-ingest-query-delivery.md) | ||
| 161 | |||
| 155 | --- | 162 | --- |
| 156 | 163 | ||
| 157 | ## 9. 下一步优先顺序 | 164 | ## 9. 下一步优先顺序 |
| ... | @@ -161,6 +168,58 @@ flowchart TD | ... | @@ -161,6 +168,58 @@ flowchart TD |
| 161 | 3. 保留 fallback 分支,不破坏当前 host 的可运行性 | 168 | 3. 保留 fallback 分支,不破坏当前 host 的可运行性 |
| 162 | 4. 重新跑主链 runner,确认 semantic lane 有 fresh 证据 | 169 | 4. 重新跑主链 runner,确认 semantic lane 有 fresh 证据 |
| 163 | 170 | ||
| 171 | ## 9.1 Selected20(20 首歌)当前评估结论 | ||
| 172 | |||
| 173 | 当前 `.omx` 记录对应的 20 首歌专题产物已经落在: | ||
| 174 | |||
| 175 | - `acr-engine/data/local_eval/selected20_songid_eval_report.json` | ||
| 176 | - `acr-engine/data/local_eval/selected20_songid_eval_report_fresh.json` | ||
| 177 | - `docs/selected20_songid_eval.md` | ||
| 178 | - `docs/selected20_songid_eval_repro.md` | ||
| 179 | - `docs/research-delivery-roadmap.md` | ||
| 180 | |||
| 181 | 评测口径: | ||
| 182 | - 数据目录:`/root/hikoon_song_files/output/selected_20_songs/downloads` | ||
| 183 | - reference:`type_11` | ||
| 184 | - query:`type_1 / type_7 / type_12 / type_16` | ||
| 185 | - 评测目标:song-level `song_id` 命中率 | ||
| 186 | - 当前方案:`chromaprint_matcher + mert-v1-95m` | ||
| 187 | - 融合口径:`0.6 * exact + 0.4 * semantic` | ||
| 188 | |||
| 189 | 当前实测汇总: | ||
| 190 | |||
| 191 | | lane | count | top1 | top3 | | ||
| 192 | |---|---:|---:|---:| | ||
| 193 | | exact | 123 | 0.6016 | 0.8130 | | ||
| 194 | | semantic | 123 | 0.4715 | 0.6016 | | ||
| 195 | | fused | 123 | 0.6341 | 0.8537 | | ||
| 196 | |||
| 197 | 补充说明: | ||
| 198 | |||
| 199 | - 2026-06-04 已做 fresh 重跑 | ||
| 200 | - `selected20_songid_eval_report_fresh.json` 与当前基线结果一致 | ||
| 201 | - 说明这份 20 首专题基线已具备可重复性,可直接作为后续 MuQ / fusion 变更的回归对照 | ||
| 202 | |||
| 203 | 按 query type 看: | ||
| 204 | |||
| 205 | | query_type | count | 结论 | | ||
| 206 | |---|---:|---| | ||
| 207 | | `type_1` | 21 | exact / semantic / fused 全部 `top1=1.0`,当前已打满 | | ||
| 208 | | `type_7` | 41 | 当前最弱短板之一;`fused top1=0.4390 / top3=0.7561`,比单 semantic 好,但仍不够稳 | | ||
| 209 | | `type_12` | 23 | 当前最好的一类之一;semantic 单 lane 已 `top1=1.0`,融合后 `top1=0.9130 / top3=1.0` | | ||
| 210 | | `type_16` | 38 | 当前另一主要短板;`fused top1=0.4737 / top3=0.7895`,仅小幅优于 exact | | ||
| 211 | |||
| 212 | 一句话判断: | ||
| 213 | |||
| 214 | > **这 20 首歌专题里,当前融合链路已经明显优于单独 semantic,也略优于单独 exact;但真正限制实战效果的仍是 `type_7 / type_16` 这类混淆/改写 query,而不是 `type_1` 或 `type_12`。** | ||
| 215 | |||
| 216 | 因此下次如果继续推进这个专题,优先顺序应是: | ||
| 217 | 1. 先把 `selected20_songid_eval_report.json` 当作小样本回归基线 | ||
| 218 | 2. 接入 MuQ challenger 后直接复跑这份专题 | ||
| 219 | 3. 重点比较 `type_7 / type_16` 是否提升,而不是只看 overall | ||
| 220 | 4. 如果后续切 PostgreSQL online path,也保留这份文件级评测作为快速回归基线 | ||
| 221 | 5. 继续按 [research-delivery-roadmap.md](./research-delivery-roadmap.md) 的 roadmap + checklist 拆下一阶段任务 | ||
| 222 | |||
| 164 | --- | 223 | --- |
| 165 | 224 | ||
| 166 | ## 一句话 handoff | 225 | ## 一句话 handoff | ... | ... |
docs/song-ingest-query-delivery.md
0 → 100644
| 1 | # Song Ingest → Query → Delivery 完整操作手册 | ||
| 2 | |||
| 3 | > 这份文档专门回答:**歌曲是怎么从目录/文件进入当前系统,再怎么被查询/评估,最后交付哪些产物与验证证据。** | ||
| 4 | |||
| 5 | --- | ||
| 6 | |||
| 7 | ## 1. 适用范围 | ||
| 8 | |||
| 9 | 当前仓库里有两条你必须区分的链路: | ||
| 10 | |||
| 11 | 1. **live PostgreSQL song-centric 主链** | ||
| 12 | - 目标:把真实歌曲目录导入当前 4 表 schema,并把 `window + fingerprint + embedding` 落到 PostgreSQL | ||
| 13 | - 入口脚本: | ||
| 14 | - `acr-engine/scripts/run_songcentric_directory_pipeline_live.py` | ||
| 15 | - `acr-engine/scripts/build_songcentric_manifest_from_directory.py` | ||
| 16 | - `acr-engine/scripts/enrich_songcentric_manifest_with_local_features.py` | ||
| 17 | - `acr-engine/scripts/import_songcentric_manifest_live.py` | ||
| 18 | |||
| 19 | 2. **selected20 文件级实战评测链** | ||
| 20 | - 目标:在 20 首歌专题上评估当前 `exact / semantic / fused` 的 song-level 命中率 | ||
| 21 | - 入口脚本: | ||
| 22 | - `acr-engine/scripts/evaluate_selected20_songid_retrieval.py` | ||
| 23 | |||
| 24 | 这两条链路都重要: | ||
| 25 | - **PostgreSQL 主链**负责“怎么入库、怎么绑定、怎么回查 song_id” | ||
| 26 | - **selected20 评测链**负责“当前方案实战到底效果如何” | ||
| 27 | |||
| 28 | --- | ||
| 29 | |||
| 30 | ## 2. 当前默认数据模型 | ||
| 31 | |||
| 32 | 逻辑语义: | ||
| 33 | |||
| 34 | ```text | ||
| 35 | song -> asset -> window -> fingerprint / embedding | ||
| 36 | ``` | ||
| 37 | |||
| 38 | 物理落表: | ||
| 39 | |||
| 40 | ```text | ||
| 41 | media_entity -> audio_object -> feature_fact -> set_membership | ||
| 42 | ``` | ||
| 43 | |||
| 44 | 绑定规则: | ||
| 45 | - `media_entity`:song 主体 | ||
| 46 | - `audio_object(object_type='asset')`:原始音频文件 | ||
| 47 | - `audio_object(object_type='window')`:切片窗口 | ||
| 48 | - `feature_fact.object_id -> audio_object.object_id`:feature 绑定 window | ||
| 49 | - `audio_object.parent_object_id`:window 回到 asset | ||
| 50 | - `feature_fact.song_id -> media_entity.entity_id`:song-level 回查与聚合 | ||
| 51 | - `set_membership`:reference/eval/hot set 路由 | ||
| 52 | |||
| 53 | 如果要看字段级解释,配套文档: | ||
| 54 | - [postgresql-data-model.md](./postgresql-data-model.md) | ||
| 55 | - [postgres_db_schema_samples.md](./postgres_db_schema_samples.md) | ||
| 56 | |||
| 57 | --- | ||
| 58 | |||
| 59 | ## 3. live PostgreSQL song-centric 主链:从歌曲目录到入库 | ||
| 60 | |||
| 61 | ### 3.1 一条命令跑完整主链 | ||
| 62 | |||
| 63 | ```bash | ||
| 64 | cd /workspace | ||
| 65 | /usr/local/miniconda3/bin/python acr-engine/scripts/run_songcentric_directory_pipeline_live.py \ | ||
| 66 | --dsn 'postgres://d2:d2pass@127.0.0.1:5432/d2' \ | ||
| 67 | --schema acr_songcentric_test \ | ||
| 68 | --input-root acr-engine/data/songcentric_builder_smoke \ | ||
| 69 | --output-dir acr-engine/data/pgvector_eval/music20 | ||
| 70 | ``` | ||
| 71 | |||
| 72 | 或: | ||
| 73 | |||
| 74 | ```bash | ||
| 75 | acr-engine/scripts/start_songcentric_shortest_path.sh 'postgres://d2:d2pass@127.0.0.1:5432/d2' | ||
| 76 | ``` | ||
| 77 | |||
| 78 | ### 3.2 这个 runner 实际做了什么 | ||
| 79 | |||
| 80 | `acr-engine/scripts/run_songcentric_directory_pipeline_live.py` 顺序执行 3 步: | ||
| 81 | |||
| 82 | 1. `build_manifest` | ||
| 83 | - 脚本:`acr-engine/scripts/build_songcentric_manifest_from_directory.py` | ||
| 84 | - 输入:歌曲目录 | ||
| 85 | - 输出:`songcentric_pipeline_manifest.jsonl` | ||
| 86 | |||
| 87 | 2. `enrich_features` | ||
| 88 | - 脚本:`acr-engine/scripts/enrich_songcentric_manifest_with_local_features.py` | ||
| 89 | - 输入:manifest | ||
| 90 | - 输出:`songcentric_pipeline_manifest_with_features.jsonl` | ||
| 91 | |||
| 92 | 3. `import_manifest` | ||
| 93 | - 脚本:`acr-engine/scripts/import_songcentric_manifest_live.py` | ||
| 94 | - 输入:带 feature 的 manifest | ||
| 95 | - 输出:`songcentric_pipeline_import_report.json` | ||
| 96 | |||
| 97 | 最终还会生成聚合报告: | ||
| 98 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_runner_report.json` | ||
| 99 | |||
| 100 | --- | ||
| 101 | |||
| 102 | ## 4. 第一步:build manifest(先把歌曲目录变成结构化 song/asset/window) | ||
| 103 | |||
| 104 | 脚本: | ||
| 105 | - `acr-engine/scripts/build_songcentric_manifest_from_directory.py` | ||
| 106 | |||
| 107 | ### 4.1 输入目录假设 | ||
| 108 | |||
| 109 | 脚本会递归扫描: | ||
| 110 | - `.wav` | ||
| 111 | - `.mp3` | ||
| 112 | - `.flac` | ||
| 113 | - `.ogg` | ||
| 114 | - `.m4a` | ||
| 115 | |||
| 116 | 并把目录结构解析为: | ||
| 117 | - 第一级目录:`song_key` | ||
| 118 | - 第二级目录:`artist` | ||
| 119 | - 文件本身:asset | ||
| 120 | |||
| 121 | ### 4.2 它做的关键事 | ||
| 122 | |||
| 123 | 1. 推断:`song.biz_key / title / artist_name` | ||
| 124 | 2. 为每个音频文件生成 1 条 asset | ||
| 125 | 3. 按 `window_ms=5000`、`stride_ms=2500` 默认切 windows | ||
| 126 | 4. 给每条记录附上 membership: | ||
| 127 | - `set_type=reference_set` | ||
| 128 | - `set_name=phase1_hot_reference_v1` | ||
| 129 | |||
| 130 | ### 4.3 输出物 | ||
| 131 | |||
| 132 | - manifest: | ||
| 133 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_manifest.jsonl` | ||
| 134 | - build report: | ||
| 135 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_build_report.json` | ||
| 136 | |||
| 137 | ### 4.4 当前 fresh evidence | ||
| 138 | |||
| 139 | 来自 `songcentric_pipeline_runner_report.json`: | ||
| 140 | - `song_count = 2` | ||
| 141 | - `asset_count = 2` | ||
| 142 | - `window_count = 5` | ||
| 143 | - `window_ms = 5000` | ||
| 144 | - `stride_ms = 2500` | ||
| 145 | |||
| 146 | --- | ||
| 147 | |||
| 148 | ## 5. 第二步:enrich features(给每个 window 补 exact / semantic 特征) | ||
| 149 | |||
| 150 | 脚本: | ||
| 151 | - `acr-engine/scripts/enrich_songcentric_manifest_with_local_features.py` | ||
| 152 | |||
| 153 | ### 5.1 它做的 exact lane | ||
| 154 | |||
| 155 | 对每个 `wav window`: | ||
| 156 | - 优先走仓库内 `ChromaprintMatcher` | ||
| 157 | - 生成: | ||
| 158 | - `feature_type='fingerprint'` | ||
| 159 | - `model_name='chromaprint_matcher'` | ||
| 160 | - `model_version='phase1_local'` | ||
| 161 | - `feature_set_name='chromaprint_matcher_5s'` | ||
| 162 | |||
| 163 | 如果 matcher 提取失败,才回退: | ||
| 164 | - `model_name='local_wavehash'` | ||
| 165 | |||
| 166 | ### 5.2 它做的 semantic lane | ||
| 167 | |||
| 168 | 语义路径先检查 runtime: | ||
| 169 | - `torch` | ||
| 170 | - `torchaudio` | ||
| 171 | - `transformers` | ||
| 172 | |||
| 173 | 当前 runtime 可用时: | ||
| 174 | - 走 `MERT-v1-95M` | ||
| 175 | - 写成: | ||
| 176 | - `feature_type='embedding'` | ||
| 177 | - `model_name='mert-v1-95m'` | ||
| 178 | - `model_version='hf-main'` | ||
| 179 | - `feature_set_name='mert_5s_hop2.5_v1'` | ||
| 180 | |||
| 181 | 如果 runtime 不可用: | ||
| 182 | - 回退到 `local_wavehash_embed` | ||
| 183 | |||
| 184 | ### 5.3 输出物 | ||
| 185 | |||
| 186 | - enriched manifest: | ||
| 187 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_manifest_with_features.jsonl` | ||
| 188 | - enrich report: | ||
| 189 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_enrich_report.json` | ||
| 190 | |||
| 191 | ### 5.4 当前 fresh evidence | ||
| 192 | |||
| 193 | 来自 `songcentric_pipeline_enrich_report.json`: | ||
| 194 | - `wav_windows_seen = 5` | ||
| 195 | - `features_added = 10` | ||
| 196 | - `matcher_fingerprint_count = 5` | ||
| 197 | - `fallback_fingerprint_count = 0` | ||
| 198 | - `semantic_runtime_available = true` | ||
| 199 | - `semantic_runtime_missing = []` | ||
| 200 | - `semantic_runtime_ready_count = 5` | ||
| 201 | - `semantic_fallback_count = 0` | ||
| 202 | |||
| 203 | 一句话解释: | ||
| 204 | |||
| 205 | > 当前 5 个 window 都已经真实拿到了 `chromaprint_matcher + mert-v1-95m`,没有走 fallback。 | ||
| 206 | |||
| 207 | --- | ||
| 208 | |||
| 209 | ## 6. 第三步:import manifest(把 song / asset / window / feature 真正落到 PostgreSQL) | ||
| 210 | |||
| 211 | 脚本: | ||
| 212 | - `acr-engine/scripts/import_songcentric_manifest_live.py` | ||
| 213 | |||
| 214 | ### 6.1 它的写入顺序 | ||
| 215 | |||
| 216 | 对每条 manifest row: | ||
| 217 | |||
| 218 | 1. `ensure_song()` | ||
| 219 | - 落 `media_entity` | ||
| 220 | 2. `ensure_asset()` | ||
| 221 | - 落 `audio_object(object_type='asset')` | ||
| 222 | 3. `ensure_window()` | ||
| 223 | - 落 `audio_object(object_type='window')` | ||
| 224 | 4. `ensure_feature()` | ||
| 225 | - 落 `feature_fact` | ||
| 226 | 5. `ensure_membership()` | ||
| 227 | - 落 `set_membership` | ||
| 228 | |||
| 229 | ### 6.2 它保证的核心绑定 | ||
| 230 | |||
| 231 | - window 绑定 asset:`parent_object_id` | ||
| 232 | - feature 绑定 window:`feature_fact.object_id` | ||
| 233 | - feature 归属 song:`feature_fact.song_id` | ||
| 234 | - membership 可绑定 `asset` 或 `song` | ||
| 235 | |||
| 236 | ### 6.3 输出物 | ||
| 237 | |||
| 238 | - import report: | ||
| 239 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_import_report.json` | ||
| 240 | |||
| 241 | ### 6.4 当前 fresh evidence | ||
| 242 | |||
| 243 | 来自 `songcentric_pipeline_import_report.json`: | ||
| 244 | - `media_entity = 9` | ||
| 245 | - `audio_object = 22` | ||
| 246 | - `feature_fact = 34` | ||
| 247 | - `set_membership = 9` | ||
| 248 | |||
| 249 | 当前可直接复核的一条 live 样例: | ||
| 250 | - `window_id = 22` | ||
| 251 | - `asset_id = 20` | ||
| 252 | - `song_id = 9` | ||
| 253 | - `title = song beta` | ||
| 254 | - `start_ms = 1000` | ||
| 255 | - `end_ms = 6000` | ||
| 256 | - 对应 feature: | ||
| 257 | - `feature_type = embedding` | ||
| 258 | - `model_name = mert-v1-95m` | ||
| 259 | - `model_version = hf-main` | ||
| 260 | - `feature_set_name = mert_5s_hop2.5_v1` | ||
| 261 | |||
| 262 | 这条样例证明: | ||
| 263 | |||
| 264 | ```text | ||
| 265 | feature -> window -> asset -> song | ||
| 266 | ``` | ||
| 267 | |||
| 268 | 这条回溯链现在已经真实落库,不只是文档设计。 | ||
| 269 | |||
| 270 | --- | ||
| 271 | |||
| 272 | ## 7. 查询阶段:怎么从命中的 feature 回到 song_id | ||
| 273 | |||
| 274 | 当前 repo 里,查询要分成两层理解: | ||
| 275 | |||
| 276 | ### 7.1 PostgreSQL 主链里的“回查” | ||
| 277 | |||
| 278 | 这层重点不是 online ANN 服务,而是: | ||
| 279 | - 命中某条 feature 后 | ||
| 280 | - 如何回查 window / asset / song | ||
| 281 | - 如何做 song-level 聚合 | ||
| 282 | |||
| 283 | 这部分 SQL 与样例已经在: | ||
| 284 | - [postgresql-data-model.md](./postgresql-data-model.md) | ||
| 285 | - [postgres_db_schema_samples.md](./postgres_db_schema_samples.md) | ||
| 286 | |||
| 287 | 重点章节: | ||
| 288 | - `在线检索时怎么从 feature 回到 song_id` | ||
| 289 | - `exact + semantic 双通道如何融合到 song 排序` | ||
| 290 | |||
| 291 | 最短可执行回查 SQL(命中某条 feature 后,直接回到 window / asset / song): | ||
| 292 | |||
| 293 | ```sql | ||
| 294 | select ff.feature_id, | ||
| 295 | ff.feature_type, | ||
| 296 | ff.model_name, | ||
| 297 | win.object_id as window_id, | ||
| 298 | win.start_ms, | ||
| 299 | win.end_ms, | ||
| 300 | ast.object_id as asset_id, | ||
| 301 | ast.storage_uri, | ||
| 302 | song.entity_id as song_id, | ||
| 303 | song.biz_key, | ||
| 304 | song.title | ||
| 305 | from acr_songcentric_test.feature_fact ff | ||
| 306 | join acr_songcentric_test.audio_object win | ||
| 307 | on win.object_id = ff.object_id and win.object_type = 'window' | ||
| 308 | join acr_songcentric_test.audio_object ast | ||
| 309 | on ast.object_id = win.parent_object_id and ast.object_type = 'asset' | ||
| 310 | join acr_songcentric_test.media_entity song | ||
| 311 | on song.entity_id = ff.song_id and song.entity_type = 'song' | ||
| 312 | where ff.feature_id = 34; | ||
| 313 | ``` | ||
| 314 | |||
| 315 | 如果要直接按 song 做聚合/融合,再回看: | ||
| 316 | - [postgresql-data-model.md](./postgresql-data-model.md) | ||
| 317 | - [postgres_db_schema_samples.md](./postgres_db_schema_samples.md) | ||
| 318 | |||
| 319 | ### 7.2 当前 selected20 文件级评测里的“查询” | ||
| 320 | |||
| 321 | 脚本: | ||
| 322 | - `acr-engine/scripts/evaluate_selected20_songid_retrieval.py` | ||
| 323 | |||
| 324 | 它做的是: | ||
| 325 | 1. 从 `type_11` 建 reference | ||
| 326 | 2. 用 `chromaprint_matcher` 做 exact 候选 | ||
| 327 | 3. 用 `MERT` embedding 做 semantic 候选 | ||
| 328 | 4. 按 `0.6 * exact + 0.4 * semantic` 做 fused 排序 | ||
| 329 | 5. 检查 true `song_id` 是否在 top1/top3 | ||
| 330 | |||
| 331 | 这条链主要用于: | ||
| 332 | - 快速看实战 song-level 正确率 | ||
| 333 | - 作为后续 MuQ challenger / 融合策略的回归基线 | ||
| 334 | |||
| 335 | --- | ||
| 336 | |||
| 337 | ## 8. selected20 实战评测:怎么操作、产物在哪、当前效果如何 | ||
| 338 | |||
| 339 | ### 8.1 复现命令 | ||
| 340 | |||
| 341 | ```bash | ||
| 342 | cd /workspace/acr-engine | ||
| 343 | /usr/local/miniconda3/bin/python scripts/evaluate_selected20_songid_retrieval.py \ | ||
| 344 | --downloads-dir /root/hikoon_song_files/output/selected_20_songs/downloads \ | ||
| 345 | --reference-type 11 \ | ||
| 346 | --query-types 1 7 12 16 \ | ||
| 347 | --duration 8.0 \ | ||
| 348 | --topk 3 \ | ||
| 349 | --exact-weight 0.6 \ | ||
| 350 | --semantic-weight 0.4 \ | ||
| 351 | --output-json /workspace/acr-engine/data/local_eval/selected20_songid_eval_report.json \ | ||
| 352 | --output-md /workspace/docs/selected20_songid_eval.md | ||
| 353 | ``` | ||
| 354 | |||
| 355 | ### 8.2 输出物 | ||
| 356 | |||
| 357 | - JSON:`acr-engine/data/local_eval/selected20_songid_eval_report.json` | ||
| 358 | - Markdown 摘要:`docs/selected20_songid_eval.md` | ||
| 359 | - 复现手册:`docs/selected20_songid_eval_repro.md` | ||
| 360 | |||
| 361 | ### 8.3 当前实测结果 | ||
| 362 | |||
| 363 | query 总数:`123` | ||
| 364 | |||
| 365 | | lane | top1 | top3 | | ||
| 366 | |---|---:|---:| | ||
| 367 | | exact | 0.6016 | 0.8130 | | ||
| 368 | | semantic | 0.4715 | 0.6016 | | ||
| 369 | | fused | 0.6341 | 0.8537 | | ||
| 370 | |||
| 371 | 分类型: | ||
| 372 | - `type_1`:最强,已打满 | ||
| 373 | - `type_12`:也很强,semantic 单 lane 最优 | ||
| 374 | - `type_7`:短板之一 | ||
| 375 | - `type_16`:短板之一 | ||
| 376 | |||
| 377 | 当前最重要的判断: | ||
| 378 | |||
| 379 | > **融合链路已经优于单独 exact 和单独 semantic,但 `type_7 / type_16` 仍是主要难点。** | ||
| 380 | |||
| 381 | --- | ||
| 382 | |||
| 383 | ## 9. 交付时你应该一起带走哪些文件 | ||
| 384 | |||
| 385 | ### 9.1 主链相关 | ||
| 386 | |||
| 387 | - `docs/postgresql-data-model.md` | ||
| 388 | - `docs/postgres_db_schema_samples.md` | ||
| 389 | - `docs/song-ingest-query-delivery.md` | ||
| 390 | - `docs/session-handoff.md` | ||
| 391 | - `docs/start-here.md` | ||
| 392 | - `docs/README.md` | ||
| 393 | |||
| 394 | ### 9.2 selected20 相关 | ||
| 395 | |||
| 396 | - `docs/selected20_songid_eval.md` | ||
| 397 | - `docs/selected20_songid_eval_repro.md` | ||
| 398 | - `acr-engine/data/local_eval/selected20_songid_eval_report.json` | ||
| 399 | |||
| 400 | ### 9.3 PostgreSQL live 产物相关 | ||
| 401 | |||
| 402 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_manifest.jsonl` | ||
| 403 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_build_report.json` | ||
| 404 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_manifest_with_features.jsonl` | ||
| 405 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_enrich_report.json` | ||
| 406 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_import_report.json` | ||
| 407 | - `acr-engine/data/pgvector_eval/music20/songcentric_pipeline_runner_report.json` | ||
| 408 | |||
| 409 | --- | ||
| 410 | |||
| 411 | ## 10. 最小交付检查清单 | ||
| 412 | |||
| 413 | ### 10.1 如果你要交付“入库链路” | ||
| 414 | |||
| 415 | 至少确认: | ||
| 416 | - schema 已建好 | ||
| 417 | - runner 可执行 | ||
| 418 | - manifest / enriched manifest / import report / runner report 都已生成 | ||
| 419 | - `feature -> window -> asset -> song` 的 live lineage sample 可回查 | ||
| 420 | |||
| 421 | ### 10.2 如果你要交付“效果评测” | ||
| 422 | |||
| 423 | 至少确认: | ||
| 424 | - selected20 命令可复现 | ||
| 425 | - JSON 报告存在 | ||
| 426 | - Markdown 摘要存在 | ||
| 427 | - overall / per-type 指标已写进交付说明 | ||
| 428 | |||
| 429 | ### 10.3 如果你要交付“下次可接手” | ||
| 430 | |||
| 431 | 至少确认: | ||
| 432 | - `README.md` 有入口 | ||
| 433 | - `start-here.md` 有最短命令 | ||
| 434 | - `session-handoff.md` 写清当前状态 | ||
| 435 | - `CHANGELOG.md` 记录这次交付 | ||
| 436 | |||
| 437 | --- | ||
| 438 | |||
| 439 | ## 11. 当前最务实的下一步 | ||
| 440 | |||
| 441 | 1. 不要回退 4 表 song-centric 主链 | ||
| 442 | 2. 把 `selected20` 继续作为小样本回归基线 | ||
| 443 | 3. 后续接入 MuQ challenger 后,第一时间复跑 selected20 | ||
| 444 | 4. 重点盯 `type_7 / type_16` 是否改善 | ||
| 445 | 5. PostgreSQL 主链保持 `feature -> window -> asset -> song` 的可回查性不被破坏 | ||
| 446 | |||
| 447 | --- | ||
| 448 | |||
| 449 | ## 12. 相关文档 | ||
| 450 | |||
| 451 | - [README.md](./README.md) | ||
| 452 | - [start-here.md](./start-here.md) | ||
| 453 | - [session-handoff.md](./session-handoff.md) | ||
| 454 | - [selected20_songid_eval.md](./selected20_songid_eval.md) | ||
| 455 | - [selected20_songid_eval_repro.md](./selected20_songid_eval_repro.md) | ||
| 456 | - [postgresql-data-model.md](./postgresql-data-model.md) | ||
| 457 | - [postgres_db_schema_samples.md](./postgres_db_schema_samples.md) | ||
| 458 | - [CHANGELOG.md](./CHANGELOG.md) |
| ... | @@ -35,12 +35,16 @@ acr-engine/scripts/start_songcentric_shortest_path.sh 'postgres://d2:d2pass@127. | ... | @@ -35,12 +35,16 @@ acr-engine/scripts/start_songcentric_shortest_path.sh 'postgres://d2:d2pass@127. |
| 35 | 35 | ||
| 36 | --- | 36 | --- |
| 37 | 37 | ||
| 38 | ## 2. 只读这 4 份文档 | 38 | ## 2. 只读这 8 份文档 |
| 39 | 39 | ||
| 40 | 1. [README.md](./README.md) | 40 | 1. [README.md](./README.md) |
| 41 | 2. [session-handoff.md](./session-handoff.md) | 41 | 2. [delivery-onepager.md](./delivery-onepager.md) |
| 42 | 3. [postgresql-data-model.md](./postgresql-data-model.md) | 42 | 3. [session-handoff.md](./session-handoff.md) |
| 43 | 4. [postgres_db_schema_samples.md](./postgres_db_schema_samples.md) | 43 | 4. [song-ingest-query-delivery.md](./song-ingest-query-delivery.md) |
| 44 | 5. [research-delivery-roadmap.md](./research-delivery-roadmap.md) | ||
| 45 | 6. [selected20_songid_eval.md](./selected20_songid_eval.md) | ||
| 46 | 7. [postgresql-data-model.md](./postgresql-data-model.md) | ||
| 47 | 8. [postgres_db_schema_samples.md](./postgres_db_schema_samples.md) | ||
| 44 | 48 | ||
| 45 | --- | 49 | --- |
| 46 | 50 | ||
| ... | @@ -117,6 +121,9 @@ flowchart TD | ... | @@ -117,6 +121,9 @@ flowchart TD |
| 117 | ### 第一优先级 | 121 | ### 第一优先级 |
| 118 | 把 semantic lane 从 `mert-v1-95m` baseline 扩展到 `MuQ` challenger,且不破坏现有宿主链。 | 122 | 把 semantic lane 从 `mert-v1-95m` baseline 扩展到 `MuQ` challenger,且不破坏现有宿主链。 |
| 119 | 123 | ||
| 124 | ### 并行专题优先级 | ||
| 125 | 把 `selected20`(20 首歌)作为小样本回归基线固定下来,后续任何 semantic challenger 或 fusion 调整都先复跑这份专题,重点看 `type_7 / type_16` 是否真的改善。 | ||
| 126 | |||
| 120 | ### 当前 host 事实 | 127 | ### 当前 host 事实 |
| 121 | - `torch` 已可导入 | 128 | - `torch` 已可导入 |
| 122 | - `torchaudio` 已可导入 | 129 | - `torchaudio` 已可导入 | ... | ... |
-
Please register or sign in to post a comment