File size: 16,554 Bytes
09e2da6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | {
"capabilities": {
"align": false,
"aux": true,
"axis_knobs": true,
"beat_head": true,
"complexity": false,
"ctx": false,
"dual": false,
"func_time": false,
"global_ctx": false,
"hierarchical_ctx": true,
"mask_infill": false,
"plan": false,
"sibling": false,
"slot": false,
"style": false,
"sync_token": true
},
"code": {
"git": {
"branch": null,
"commit": null,
"dirty": false
},
"module_md5": {
"src/softchart/__init__.py": "d41d8cd98f00b204e9800998ecf8427e",
"src/softchart/barscript.py": "9afc715a56ac28e9ec5675d64cd0536c",
"src/softchart/baseline.py": "875b63450c59bb62f5d50965ec7b218c",
"src/softchart/checkpoint.py": "10e215e86ac77ada9046a164bffa0644",
"src/softchart/data.py": "d0cf18237bc644f49bf2b62b9eef0ee3",
"src/softchart/evaluate.py": "72d588b7f0711dfa6d1f49b161172061",
"src/softchart/generate.py": "73b6e49b00fb60f3229c8539bc7a1029",
"src/softchart/grid.py": "eba9fddd432bee0b62ddfc13bdefc06d",
"src/softchart/hf.py": "727db85293862948587c99636ea1d1ef",
"src/softchart/meter.py": "18ccd6a003086f81276f2a916ada4c64",
"src/softchart/model.py": "eace5d89abeebf0edcf28d10b5ad3e68",
"src/softchart/preprocess.py": "b87fa0cc8af9628ae47a2fbe86de266a",
"src/softchart/proxies.py": "918c72c22d48e4ccb762e0fe814d0bfe",
"src/softchart/rhythm.py": "11cca7c58809b339f7384dcc0bf1fcaa",
"src/softchart/tja.py": "360b95e19dabe925bf8eab03fa3fd110",
"src/softchart/trace.py": "e707f7204f0d86deacca6b6325f031f1",
"src/softchart/train.py": "7f6323973c26992873717307f6652f91",
"src/softchart/vocab.py": "0f948a0efc52f5ce60e8219fcaeb136c"
},
"torch": "2.9.1+cu128"
},
"created_utc": "2026-07-26T00:17:52+00:00",
"model": {
"adapter_rank": 0,
"adapter_rank_ffn": 0,
"aux": true,
"beat": "hires",
"clean_phase": true,
"d_model": 256,
"dec_layers": 4,
"dec_share": null,
"depth_emb": false,
"dropout": 0.1,
"emb_factor": 64,
"enc_layers": 4,
"enc_share": null,
"ffn": 960,
"func_time": false,
"global_ctx": false,
"hierarchical_ctx": true,
"in_ch": 128,
"max_tgt": 960,
"nhead": 8,
"ptr": false,
"rich_section_stats": true,
"section_ffn": 512,
"section_layers": 2,
"span_duration_head": true,
"stage_emb": true,
"tempo_head": true,
"unique_layernorm": false,
"unshare_last_dec": false,
"vocab_size": 2071
},
"release_name": "sc2_c1_v13_s1234_best",
"schema_version": "softchart2_release_config_v1",
"serving": {
"bar_denom_mask": "lattice",
"bar_denoms": "supplied",
"degenerate_bar_sec": 0.25,
"family_bias": 2.0,
"family_hand_bias": 1.5,
"family_mode": "bias",
"greedy": false,
"min_onset_gap_sec": 0.02,
"motif_constraint": "auto",
"plan_temperature": null,
"span_bucket0_profile": null,
"span_min_beats": 0.3333333333333333,
"span_min_sec": 0.0833,
"temperature": 1.0,
"top_p": 0.95
},
"serving_basis": {
"bar_denom_mask": "Only consulted when bar_denoms='model'. 'lattice' = divisors of the bar's own TJA export lattice; a superset of 'div96' outside 4/4.",
"bar_denoms": "'supplied' teacher-forces the grid's per-bar denominators into every [BAR] header. It is the regime this checkpoint was TRAINED and EVALUATED under -- every published number belongs to it -- so it is the DEFAULT, and it is correct when the grid comes from a real authored/traced chart. It is the WRONG value on a synthetic uniform BPM grid: BAR_DENOM is a trained output token and the model reads a supplied denominator as a DENSITY ANNOUNCEMENT, so a forced /16 grid emits 1.69x / 1.46x / 1.30x / 1.21x the authored onsets on easy/normal/hard/oni and erases every triplet (off-/16 onsets 0 % against an authored 7.4-14.5 %). Use the 'deploy_bpm_grid' profile there. DEPLOY_GRID_FIX.md §5.2, §7; 13 songs x 4 courses, one seed.",
"degenerate_bar_sec": "Across 3 010 bars the authored side places ZERO hits in any bar shorter than 0.375 s (28 such bars, 2 songs); the shortest authored hit-bearing bar is 0.375 s. 0.25 s is the largest round threshold with zero authored counterexamples and a 1.5x margin, and it is the threshold the degenerate_bar_empty gate uses, so fix and gate cannot disagree. Blocks HITS only: authors DO route span geometry through sub-beat scaffolding measures. Source: src/softchart/generate.py DEGENERATE_BAR_SEC comment.",
"family_bias": "generate.FAMILY_BIAS. Additive logit bias toward the harder sibling's onset POSITIONS when courses are decoded as one nested difficulty family (generate_chart_family). 0 is the independent decode; 'subset' mode is the bias=infinity ablation arm.",
"family_hand_bias": "generate.FAMILY_HAND_BIAS. Additive logit bias toward the harder sibling's don/ka HAND at an inherited position. Steers type only, never position.",
"family_mode": "'bias' is the soft steering and the shipped value; 'subset' hard-restricts the easier course to the harder chart's positions and is kept for ablation only.",
"greedy": "Sampling, not argmax -- the evaluation population was produced at temperature 1.0 and every published number belongs to that decoder.",
"min_onset_gap_sec": "DEGENERACY GUARD, not a corpus percentile. Two earlier revisions (0.0395 s from a 43 387-pair subset; 0.0300 s, the 1 200-chart / 427 016-pair corpus minimum) were both wrong because a 1 000-song corpus is not the domain. The series record for two adjacent notes is 29.4118 ms (TAIKO-TONGUE-TWISTER oni, BPM 170, 48th notes, 34 hits/s); a 30 ms floor blocks the record itself. 0.020 s clears every documented chart with headroom and still blocks the observed degeneracy (six ka notes 16.7 ms apart rendering as one blob). The model's real defect on this axis is a RATE, not a value -- sub-40 ms gaps are 0.045 % of authored pairs and ~0.35 % of generated ones -- and a mask cannot fix a distribution. Source: src/softchart/generate.py MIN_ONSET_GAP_SEC comment.",
"motif_constraint": "'auto' = ON iff the checkpoint's own training cache (<cache>/<args.bs_subdir>/manifest.json) records a barscript_md5 equal to the md5 of the serving barscript.py. Only then did the gold sequences the checkpoint trained on satisfy the MOTIF hard constraint the serving FSM would apply, so only then is serving under it train/serve matched. This package records the RESOLVED value and its inputs under train_provenance.motif_constraint. Source: scripts/sc2_generate_eval.py MOTIF_AUTO_RULE (harness contract v2.1).",
"plan_temperature": "null = follow `temperature`. The four plan steps have an independent diversity knob; it was not exercised in the evaluation, so the package ships it unset rather than tuned.",
"span_bucket0_profile": "DISABLED (null) -- and this is a KNOWN GAP, not a choice on the merits. Duration bucket 0 is open at the bottom, so nothing bounds how short a span may be beyond the corpus floor and the realized length piles up on the lattice minimum; this checkpoint puts 21.2 % of its spans under 0.25 s against an authored 4.2 %. The mitigation exists in code (generate.SpanBucket0Profile / generate_song_barscript(span_bucket0_profile=...)) but NO PROFILE ARTIFACT HAS BEEN BUILT -- scripts/build_span_bucket0_profile.py has never been run on the train split, and no such file exists on the training host. Absent it the serving path is bit-identical to the shipped one. Building it is the shortest available micro-span fix.",
"span_min_beats": "Beat-relative companion to span_min_sec (1/3 quarter-note beat), so the floor survives a tempo change. Source: src/softchart/generate.py SPAN_MIN_BEATS.",
"span_min_sec": "Authored population minimum span duration; steers span closures and masks duration buckets away from lengths no author wrote. Fail-open at the mask level, counted as span_close_floor / span_floor_carry / dur_bucket_floor. Source: src/softchart/generate.py SPAN_MIN_SEC.",
"temperature": "1.0, as evaluated.",
"top_p": "0.95, as evaluated."
},
"serving_prefix_fix": "sync_token is forced ON at serving. train.py records sync_token=False, but the training prefix carries the SYNC slot, so a loader MUST set model._has_sync = True before decoding or every sync bucket reaches the decoder as the same prefix. sc2_package.load_release does this; see the SYNC note in scripts/sc2_generate_eval.py.",
"serving_profiles": {
"default": {
"overrides": {},
"when": "the supplied grid comes from a real chart trace (barscript_grid_from_trace) -- i.e. the bar denominators are the authored ones. This is the regime every published metric was measured in."
},
"deploy_bpm_grid": {
"basis": "DEPLOY_GRID_FIX.md §7, verbatim: 'Recommended now: model-emitted BAR_DENOM, masked to divisors of 96.' What it buys: note counts within 8 % of authored at every difficulty instead of 21-69 % over; the DENS plan back on the authored-grid distribution; triplets back from zero; span counts back to authored levels. What it COSTS, and this is not small: nesting coverage 0.635 / 0.706 / 0.778 against 0.835 / 0.845 / 0.866, worse on 12-13 of 13 songs, p <= .003 on all three course pairs -- the family decode's headline benefit is gone. What it does NOT fix: p90 onset offset 7-34 ms, frac_within_jnd 0.34-0.40, micro-spans 26-42 % on hard/oni. The report states the tradeoff as the finding: 'No arm here is acceptable on both axes, and that is the finding. The choice is between a chart whose difficulty is right and a chart whose difficulty ladder is nested, because one token controls both.' Evidence: 13 songs x 4 courses, one seed, this checkpoint. NOTE: DEPLOY_GRID_FIX measured this arm by runtime injection because bar_denoms='model' was then only a proposed interface; it is now implemented in generate_song_barscript, and this profile selects it directly.",
"overrides": {
"bar_denom_mask": "div96",
"bar_denoms": "model"
},
"when": "the grid is synthesized from user BPM/offset (barscript_grid_from_bpm), where every bar carries the same denominator and the model would read it as a density announcement."
}
},
"source": {
"best_val": 0.5769365892960475,
"checkpoint_file": "best.pt",
"checkpoint_select": "best",
"checkpoint_sha256": "44980bf64b1611ea73c1433c41adceff7596cfeece30dfaad5d228eae4a14e6e",
"mode": "c1",
"run_dir": "/home/jacob/softchart2/runs/sc2_c1_v13_s1234",
"seed": 1234,
"step": 96000
},
"train_provenance": {
"barscript_md5": "634e3dc3ddca8bcdceffd760e663e1b0",
"bs_subdir": "barscript",
"cache_manifest": "cache/barscript/manifest.json",
"dataset": "/home/jacob/softchart2/data/taiko-1000-parsed-clean",
"labels_version": "barscript_labels_v1",
"motif_constraint": {
"requested": "auto",
"resolved_motif_hard": false,
"serving_barscript_md5": "9afc715a56ac28e9ec5675d64cd0536c",
"source": "auto",
"train_cache_barscript_md5": "634e3dc3ddca8bcdceffd760e663e1b0",
"train_cache_built_by_serving_barscript": false,
"train_serve_matched": true
},
"revision": "b72da4616d643018e81f372cea06ce51349285e0",
"serving_barscript_md5": "9afc715a56ac28e9ec5675d64cd0536c",
"serving_barscript_path": "/home/jacob/softchart2/pkgstage/src/softchart/barscript.py",
"spec_file": "spec.json",
"spec_frozen_path": "/home/jacob/softchart2/cache/barscript_spec_frozen.json",
"spec_frozen_sha1": "cd8767684457915ece0d3d58ab80ade137ad79f5",
"spec_parameters_match_cache_manifest": true,
"spec_sha1": "cd8767684457915ece0d3d58ab80ade137ad79f5",
"spec_sha1_matches_cache_manifest_record": false,
"spec_sha1_matches_frozen_file": true,
"spec_sha1_note": "Three sha1s, and they are allowed to differ. spec_sha1 is the file shipped here; spec_frozen_sha1 is the spec artifact currently on the training host; spec_sha1_recorded_in_cache_manifest is what the cache builder recorded when the cache was built. The binding claim is spec_parameters_match_cache_manifest -- the bucket edges, motif tau and deltas shipped here are field-for-field the ones the checkpoint trained under. A sha1 mismatch means a re-serialization, not a different label contract.",
"spec_sha1_recorded_in_cache_manifest": "0893edb05b6473b983faff2aab166bcc18b4b3d6",
"train_args": {
"adapter_rank": 0,
"adapter_rank_ffn": 0,
"align": false,
"aux": true,
"axis_knobs": true,
"balance_cap": 2.5,
"balance_exclude_big": true,
"balanced": true,
"batch": 64,
"beat_all_window": false,
"beat_head": true,
"beat_hires": true,
"beat_weight": 1.0,
"bs_subdir": "barscript",
"cache": "/home/jacob/softchart2/cache",
"cal_axes": [
"big",
"span",
"density"
],
"cal_dens_margin_frac": 0.05,
"cal_margin": 0.5,
"cal_rows": 16,
"cal_weight": 0.05,
"clean_phase": true,
"complexity": false,
"cond_corrupt": 0.1,
"cond_drop": 0.15,
"ctx": false,
"d_model": 256,
"data_frac": 1.0,
"dec_layers": 4,
"dec_share": null,
"denom_augment": 0.5,
"denom_augment_mode": "lattice",
"depth_emb": false,
"distill": null,
"distill_tau": 2.0,
"distill_teacher": null,
"distill_weight": 0.5,
"dpo": null,
"dpo_beta": 0.1,
"dpo_hard": false,
"dropout": 0.1,
"dual": false,
"dual_slot_p": 0.65,
"emb_factor": 64,
"enc_layers": 4,
"enc_share": null,
"entropy_floor": 0.0,
"entropy_floor_weight": 0.1,
"ffn": 960,
"func_time": false,
"global_ctx": false,
"grammar_mask": true,
"hierarchical_ctx": true,
"importance_sampling": true,
"init_from": null,
"kd_alpha": 0.5,
"kd_tau": 2.0,
"lambda_plan": 1.0,
"lambda_type": 1.5,
"lr": 0.0005,
"lr_shared_core": 1.0,
"mask_infill": 0.0,
"max_tgt": 960,
"mode": "c1",
"nhead": 8,
"no_augment": false,
"out": "/home/jacob/softchart2/runs/sc2_c1_v13_s1234",
"plan": false,
"plan_dropout": 0.3,
"plan_jitter": 0.15,
"resume": null,
"rich_section_stats": true,
"section_ffn": 512,
"section_layers": 2,
"seed": 1234,
"sibling": false,
"slot": false,
"span_dur_weight": 1.0,
"span_duration_head": true,
"stage_emb": true,
"steps": 100000,
"style": false,
"sync_token": false,
"tempo_aug": false,
"tempo_head": true,
"tempo_weight": 0.2,
"unique_layernorm": false,
"unlikelihood_alpha": 1.0,
"unlikelihood_scope": "hits",
"unshare_last_dec": false,
"use_aug": false,
"val_every": 2000,
"warmup": 1000,
"workers": 12
}
},
"v2_flags": {
"axis_knobs": true,
"rich_section_stats": true,
"span_duration_head": true,
"stage_emb": true,
"tempo_head": true
},
"vocab": {
"checkpoint_rows": 2071,
"growth_policy": "append-only. checkpoint.expand_vocab_rows grows an older checkpoint's tok_emb (and a tied out.weight) to the current table with N(0, 0.02) rows under seed 20260725; a checkpoint with MORE rows than the runtime is refused.",
"mapping_sha256": "f139fbea1a13397015088b1ab2c33a952e97b05bb447bc86ce02f350d50dd679",
"max_tgt": 960,
"max_tgt_source": "checkpoint args",
"size": 2071,
"slots_per_4_4": 96,
"window_frames": 1728
},
"weights": {
"cast_cost": {
"float_elements": 9107291,
"max_abs_delta": 0.007111072540283203,
"max_abs_delta_relative_to_tensor_max": 0.0034862233048405743,
"max_abs_delta_tensor": "frontend.2.weight",
"note": "bfloat16 keeps 8 mantissa bits, so a cast costs up to 2^-9 relative. Training ran bf16 autocast (train.py) and inference runs bf16 autocast on CUDA (generate._autocast), so fp32 storage carried no information the forward pass could use -- the same argument the v1.5 release made and verified. It is an argument about the COMPUTE dtype, not a proof that the two checkpoints decode identically: sampling is chaotic in the logits, so individual charts can and do differ. fp32 is preserved in the source run directory."
},
"cast_from": "float32",
"dtype": "bfloat16",
"file": "model.safetensors",
"parameters": 9107291
}
}
|