finnvoorhees commited on
Commit
5ac5bec
·
verified ·
1 Parent(s): 47e2d59

Move the frontend into the SDK; one artifact per runtime

Browse files

The log-mel frontend is now host code rather than part of the graph. It cannot run in float16 -- the power spectrum is squared magnitudes floored at 1e-10 and 80% of its bins fall below float16's smallest normal number -- so shipping it as a Core ML program meant either destroying the features (routing 97.5% to 84.2%) or a float32 program that cannot use the Neural Engine at all.

Both runtimes now take log-mel and return logits, so every platform feeds the same tensor to the same graph. mel_filters.f32 carries the filterbank so host code does not reimplement slaney-normalized librosa filters and drift from it silently.

ear.mlmodelc: 256 ops, 100% Neural Engine, palette6-group32.
ear.tflite: int8 with Hadamard rotation, signature input 'mel'.

{detector.mlmodelc → ear.mlmodelc}/analytics/coremldata.bin RENAMED
File without changes
{detector.mlmodelc → ear.mlmodelc}/coremldata.bin RENAMED
File without changes
{detector.mlmodelc → ear.mlmodelc}/model.mil RENAMED
File without changes
{detector.mlmodelc → ear.mlmodelc}/weights/weight.bin RENAMED
File without changes
ear.tflite CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:eafb390863c39b28b607094fbbb7ae9202ece7ce0d5f5a28839fdc69b5770853
3
- size 23947568
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:71cc15c3f24abbea2dcdf2892370d85806891ad7822cd3107abc0629f10bc955
3
+ size 23062016
ear_meta.json CHANGED
@@ -24,39 +24,35 @@
24
  },
25
  "input_shape": [
26
  1,
27
- 160,
28
- 1,
29
- 3002
30
  ],
31
- "input_layout": "waveform reflect-padded by n_fft/2, reshaped to [1, hop, 1, rows]; framing is host-side"
 
 
32
  },
33
  "artifacts": {
34
  "coreml": {
35
- "mel": {
36
- "file": "mel.mlmodelc",
37
- "precision": "fp32",
38
- "input": "audio_rows",
39
- "output": "mel",
40
- "note": "fp32 is required, not a default. The power spectrum is squared magnitudes clamped at 1e-10, and 80% of its bins fall below fp16's smallest normal number. In fp16 this stage measures 27 dB on real audio against 200 dB in fp32, and end-to-end routing drops from 97.5% to 84.2%."
41
- },
42
- "detector": {
43
- "file": "detector.mlmodelc",
44
- "precision": "fp16",
45
- "compression": "palette6-group32",
46
- "input": "mel",
47
- "output": "logits",
48
- "ops": 256,
49
- "ane_residency": 1.0
50
- }
51
  },
52
  "litert": {
53
- "model": {
54
- "file": "ear.tflite",
55
- "quantization": "dynamic_wi8c_hr_afp32",
56
- "input": "audio_rows",
57
- "output": "logits",
58
- "note": "One file: LiteRT runs the frontend in fp32, so it is fused. Hadamard rotation is what makes 8-bit work here; plain min-max int8 loses 11 points of routing."
59
- }
 
 
 
60
  }
61
  },
62
  "output": {
 
24
  },
25
  "input_shape": [
26
  1,
27
+ 80,
28
+ 3000
 
29
  ],
30
+ "input_layout": "log-mel, mel-major. The frontend is host-side because it cannot run in float16: the power spectrum is squared magnitudes floored at 1e-10 and 80% of its bins fall below float16's smallest normal number. Measured on real audio it holds 200 dB in float32 and 27 dB in float16, and folding it into the float16 graph takes routing accuracy from 97.5% to 84.2%.",
31
+ "computed_by": "the SDK, in host code",
32
+ "filterbank": "mel_filters.f32"
33
  },
34
  "artifacts": {
35
  "coreml": {
36
+ "file": "ear.mlmodelc",
37
+ "precision": "fp16",
38
+ "compression": "palette6-group32",
39
+ "input": "mel",
40
+ "output": "logits",
41
+ "ops": 256,
42
+ "ane_residency": 1.0,
43
+ "note": "A compiled program, not an .mlpackage. Core ML keys its specialized Neural Engine cache on the compiled model's path, so an .mlpackage recompiles on every launch and never hits it."
 
 
 
 
 
 
 
 
44
  },
45
  "litert": {
46
+ "file": "ear.tflite",
47
+ "quantization": "dynamic_wi8c_hr_afp32",
48
+ "input": "mel",
49
+ "output": "output_0",
50
+ "note": "Hadamard rotation is what makes 8 bits work here. Plain min-max int8 is smaller and faster and loses 11 points of routing accuracy."
51
+ },
52
+ "filterbank": {
53
+ "file": "mel_filters.f32",
54
+ "layout": "uint32 mels, uint32 bins, then mels*bins float32, little-endian, mel-major",
55
+ "note": "Shipped as a table rather than rebuilt in host code: the reference filters are slaney-normalized librosa output and reimplementing that is silent drift."
56
  }
57
  },
58
  "output": {
mel.mlmodelc/analytics/coremldata.bin DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:e926bc1b285e259baaa9b564155ff49c46b5cd027837dda998945b580ba20389
3
- size 243
 
 
 
 
mel.mlmodelc/coremldata.bin DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:7ce5ca49f387402b03f700a2f15138b6daadecc8bdd36e90d50bebf0f2d889c2
3
- size 377
 
 
 
 
mel.mlmodelc/model.mil DELETED
@@ -1,55 +0,0 @@
1
- program(1.3)
2
- [buildInfo = dict<string, string>({{"coremlc-component-MIL", "3600.16.1"}, {"coremlc-version", "3600.25.2"}, {"coremltools-component-torch", "2.13.0"}, {"coremltools-source-dialect", "TorchScript"}, {"coremltools-version", "9.0"}})]
3
- {
4
- func main<ios18>(tensor<fp32, [1, 160, 1, 3002]> audio_rows) {
5
- fp32 var_3 = const()[name = string("op_3"), val = fp32(0x1.b7cdfep-34)];
6
- bool var_14 = const()[name = string("op_14"), val = bool(true)];
7
- tensor<fp32, [80, 201, 1, 1]> self_mel_weight = const()[name = string("self_mel_weight"), val = tensor<fp32, [80, 201, 1, 1]>(BLOBFILE(path = string("@model_path/weights/weight.bin"), offset = uint64(64)))];
8
- tensor<fp32, [402, 160, 1, 3]> self_stft_weight = const()[name = string("self_stft_weight"), val = tensor<fp32, [402, 160, 1, 3]>(BLOBFILE(path = string("@model_path/weights/weight.bin"), offset = uint64(64448)))];
9
- string spectrum_pad_type_0 = const()[name = string("spectrum_pad_type_0"), val = string("valid")];
10
- tensor<int32, [2]> spectrum_strides_0 = const()[name = string("spectrum_strides_0"), val = tensor<int32, [2]>([1, 1])];
11
- tensor<int32, [4]> spectrum_pad_0 = const()[name = string("spectrum_pad_0"), val = tensor<int32, [4]>([0, 0, 0, 0])];
12
- tensor<int32, [2]> spectrum_dilations_0 = const()[name = string("spectrum_dilations_0"), val = tensor<int32, [2]>([1, 1])];
13
- int32 spectrum_groups_0 = const()[name = string("spectrum_groups_0"), val = int32(1)];
14
- tensor<fp32, [1, 402, 1, 3000]> spectrum = conv(dilations = spectrum_dilations_0, groups = spectrum_groups_0, pad = spectrum_pad_0, pad_type = spectrum_pad_type_0, strides = spectrum_strides_0, weight = self_stft_weight, x = audio_rows)[name = string("spectrum")];
15
- tensor<int32, [4]> real_begin_0 = const()[name = string("real_begin_0"), val = tensor<int32, [4]>([0, 0, 0, 0])];
16
- tensor<int32, [4]> real_end_0 = const()[name = string("real_end_0"), val = tensor<int32, [4]>([1, 201, 1, 3000])];
17
- tensor<bool, [4]> real_end_mask_0 = const()[name = string("real_end_mask_0"), val = tensor<bool, [4]>([true, false, true, true])];
18
- tensor<fp32, [1, 201, 1, 3000]> real = slice_by_index(begin = real_begin_0, end = real_end_0, end_mask = real_end_mask_0, x = spectrum)[name = string("real")];
19
- tensor<int32, [4]> imag_begin_0 = const()[name = string("imag_begin_0"), val = tensor<int32, [4]>([0, 201, 0, 0])];
20
- tensor<int32, [4]> imag_end_0 = const()[name = string("imag_end_0"), val = tensor<int32, [4]>([1, 402, 1, 3000])];
21
- tensor<bool, [4]> imag_end_mask_0 = const()[name = string("imag_end_mask_0"), val = tensor<bool, [4]>([true, true, true, true])];
22
- tensor<fp32, [1, 201, 1, 3000]> imag = slice_by_index(begin = imag_begin_0, end = imag_end_0, end_mask = imag_end_mask_0, x = spectrum)[name = string("imag")];
23
- tensor<fp32, [1, 201, 1, 3000]> var_21 = mul(x = real, y = real)[name = string("op_21")];
24
- tensor<fp32, [1, 201, 1, 3000]> var_22 = mul(x = imag, y = imag)[name = string("op_22")];
25
- tensor<fp32, [1, 201, 1, 3000]> input = add(x = var_21, y = var_22)[name = string("input")];
26
- string var_24_pad_type_0 = const()[name = string("op_24_pad_type_0"), val = string("valid")];
27
- tensor<int32, [2]> var_24_strides_0 = const()[name = string("op_24_strides_0"), val = tensor<int32, [2]>([1, 1])];
28
- tensor<int32, [4]> var_24_pad_0 = const()[name = string("op_24_pad_0"), val = tensor<int32, [4]>([0, 0, 0, 0])];
29
- tensor<int32, [2]> var_24_dilations_0 = const()[name = string("op_24_dilations_0"), val = tensor<int32, [2]>([1, 1])];
30
- int32 var_24_groups_0 = const()[name = string("op_24_groups_0"), val = int32(1)];
31
- tensor<fp32, [1, 80, 1, 3000]> var_24 = conv(dilations = var_24_dilations_0, groups = var_24_groups_0, pad = var_24_pad_0, pad_type = var_24_pad_type_0, strides = var_24_strides_0, weight = self_mel_weight, x = input)[name = string("op_24")];
32
- tensor<int32, [4]> var_27_begin_0 = const()[name = string("op_27_begin_0"), val = tensor<int32, [4]>([0, 0, 0, 0])];
33
- tensor<int32, [4]> var_27_end_0 = const()[name = string("op_27_end_0"), val = tensor<int32, [4]>([1, 80, 1, 3000])];
34
- tensor<bool, [4]> var_27_end_mask_0 = const()[name = string("op_27_end_mask_0"), val = tensor<bool, [4]>([true, true, false, true])];
35
- tensor<bool, [4]> var_27_squeeze_mask_0 = const()[name = string("op_27_squeeze_mask_0"), val = tensor<bool, [4]>([false, false, true, false])];
36
- tensor<fp32, [1, 80, 3000]> var_27 = slice_by_index(begin = var_27_begin_0, end = var_27_end_0, end_mask = var_27_end_mask_0, squeeze_mask = var_27_squeeze_mask_0, x = var_24)[name = string("op_27")];
37
- fp32 const_0 = const()[name = string("const_0"), val = fp32(0x1.fffffep+127)];
38
- tensor<fp32, [1, 80, 3000]> clip_0 = clip(alpha = var_3, beta = const_0, x = var_27)[name = string("clip_0")];
39
- fp32 log_0_epsilon_0 = const()[name = string("log_0_epsilon_0"), val = fp32(0x1p-149)];
40
- tensor<fp32, [1, 80, 3000]> log_0 = log(epsilon = log_0_epsilon_0, x = clip_0)[name = string("log_0")];
41
- fp32 mul_0_y_0 = const()[name = string("mul_0_y_0"), val = fp32(0x1.bcb7b2p-2)];
42
- tensor<fp32, [1, 80, 3000]> mul_0 = mul(x = log_0, y = mul_0_y_0)[name = string("mul_0")];
43
- tensor<int32, [2]> var_31_axes_0 = const()[name = string("op_31_axes_0"), val = tensor<int32, [2]>([1, 2])];
44
- tensor<fp32, [1, 1, 1]> var_31 = reduce_max(axes = var_31_axes_0, keep_dims = var_14, x = mul_0)[name = string("op_31")];
45
- fp32 var_32 = const()[name = string("op_32"), val = fp32(0x1p+3)];
46
- tensor<fp32, [1, 1, 1]> var_33 = sub(x = var_31, y = var_32)[name = string("op_33")];
47
- tensor<fp32, [1, 80, 3000]> log = maximum(x = mul_0, y = var_33)[name = string("log")];
48
- fp32 var_35 = const()[name = string("op_35"), val = fp32(0x1p+2)];
49
- tensor<fp32, [1, 80, 3000]> var_36 = add(x = log, y = var_35)[name = string("op_36")];
50
- fp32 _inversed_38_y_0 = const()[name = string("_inversed_38_y_0"), val = fp32(0x1p-2)];
51
- tensor<fp32, [1, 80, 3000]> _inversed_38 = mul(x = var_36, y = _inversed_38_y_0)[name = string("_inversed_38")];
52
- string cast_2_dtype_0 = const()[name = string("cast_2_dtype_0"), val = string("fp16")];
53
- tensor<fp16, [1, 80, 3000]> mel = cast(dtype = cast_2_dtype_0, x = _inversed_38)[name = string("cast_3")];
54
- } -> (mel);
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
mel.mlmodelc/weights/weight.bin DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:ea98097dba120b4236368cca2bd6ae5202d246faddc9d46b91964646b48a9f10
3
- size 836352
 
 
 
 
mel_filters.f32 ADDED
Binary file (64.3 kB). View file