Commit 5e373f5b 5e373f5b1bfcec928a9f91714da75c31da25685e by cnb.bofCdSsphPA

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
1 parent e7f2029f
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):变更历史
......
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)
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 后续路线图**。
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` 代表当前版权保护场景里更接近实战的合并结果。
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
......
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` 已可导入
......