alexwengg commited on
Commit
4e3cd1b
ยท
verified ยท
1 Parent(s): 93954bc

Upload debug_protobuf_structure.py

Browse files
Files changed (1) hide show
  1. debug_protobuf_structure.py +320 -0
debug_protobuf_structure.py ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Debug the exact protobuf structure needed by CoreML
4
+ """
5
+
6
+ import struct
7
+ import binascii
8
+ from pathlib import Path
9
+
10
+ def debug_protobuf_structure():
11
+ """Debug CoreML protobuf structure by analyzing working models"""
12
+ print("๐Ÿ” DEBUGGING COREML PROTOBUF STRUCTURE")
13
+ print("=" * 60)
14
+
15
+ # Analyze working models to understand the exact structure
16
+ working_models = [
17
+ "/Users/kikow/Library/Application Support/FluidAudio/vad/silero_stft.mlmodel",
18
+ "/Users/kikow/Library/Application Support/FluidAudio/vad/silero_encoder.mlmodel",
19
+ "/Users/kikow/Library/Application Support/FluidAudio/vad/silero_rnn_decoder.mlmodel"
20
+ ]
21
+
22
+ for model_path in working_models:
23
+ if Path(model_path).exists():
24
+ print(f"\n๐Ÿ”ฌ Analyzing: {Path(model_path).name}")
25
+ analyze_model_structure(model_path)
26
+ else:
27
+ print(f"โŒ Model not found: {model_path}")
28
+
29
+ # Compare with our broken model
30
+ broken_model = "/Users/kikow/Library/Application Support/FluidAudio/vad/correct_classifier_conv1d.mlmodel"
31
+ if Path(broken_model).exists():
32
+ print(f"\n๐Ÿ”ฌ Analyzing BROKEN: {Path(broken_model).name}")
33
+ analyze_model_structure(broken_model)
34
+
35
+ def analyze_model_structure(model_path):
36
+ """Analyze the protobuf structure of a CoreML model"""
37
+ with open(model_path, 'rb') as f:
38
+ data = f.read()
39
+
40
+ print(f"๐Ÿ“Š File size: {len(data)} bytes")
41
+
42
+ # Analyze protobuf header
43
+ print("๐Ÿ” Protobuf Header Analysis:")
44
+ analyze_protobuf_header(data[:200])
45
+
46
+ # Look for ModelDescription section
47
+ print("\n๐Ÿ” ModelDescription Analysis:")
48
+ desc_section = find_model_description(data)
49
+ if desc_section:
50
+ analyze_model_description(desc_section)
51
+
52
+ # Look for input/output names
53
+ print("\n๐Ÿ” Input/Output Analysis:")
54
+ find_input_output_names(data)
55
+
56
+ # Analyze neural network section
57
+ print("\n๐Ÿ” Neural Network Section:")
58
+ nn_section = find_neural_network_section(data)
59
+ if nn_section:
60
+ analyze_neural_network_section(nn_section[:500]) # First 500 bytes
61
+
62
+ def analyze_protobuf_header(header_data):
63
+ """Analyze the protobuf header"""
64
+ print(f"First 50 bytes (hex): {header_data[:50].hex()}")
65
+
66
+ # Decode protobuf fields
67
+ pos = 0
68
+ field_count = 0
69
+
70
+ while pos < len(header_data) and field_count < 10:
71
+ try:
72
+ # Read field tag
73
+ field_tag = header_data[pos]
74
+ wire_type = field_tag & 0x07
75
+ field_number = field_tag >> 3
76
+
77
+ print(f" Field {field_number}, Wire Type {wire_type} at pos {pos}")
78
+
79
+ if wire_type == 0: # Varint
80
+ pos += 1
81
+ value, consumed = decode_varint(header_data[pos:])
82
+ print(f" Varint value: {value}")
83
+ pos += consumed
84
+ elif wire_type == 2: # Length-delimited
85
+ pos += 1
86
+ length, consumed = decode_varint(header_data[pos:])
87
+ pos += consumed
88
+ print(f" Length-delimited, length: {length}")
89
+ if pos + length <= len(header_data):
90
+ content = header_data[pos:pos+length]
91
+ print(f" Content preview: {content[:20]}")
92
+ pos += length
93
+ else:
94
+ pos += 1
95
+
96
+ field_count += 1
97
+
98
+ except:
99
+ break
100
+
101
+ def decode_varint(data):
102
+ """Decode protobuf varint"""
103
+ value = 0
104
+ shift = 0
105
+ pos = 0
106
+
107
+ while pos < len(data):
108
+ byte = data[pos]
109
+ value |= (byte & 0x7F) << shift
110
+ pos += 1
111
+ if (byte & 0x80) == 0:
112
+ break
113
+ shift += 7
114
+
115
+ return value, pos
116
+
117
+ def find_model_description(data):
118
+ """Find the ModelDescription section"""
119
+ # ModelDescription is typically field 2 in the top-level message
120
+ # Look for field tag 0x12 (field 2, wire type 2)
121
+
122
+ for i in range(len(data) - 10):
123
+ if data[i] == 0x12: # Field 2, length-delimited
124
+ try:
125
+ length, consumed = decode_varint(data[i+1:])
126
+ if length > 0 and length < 10000: # Reasonable size
127
+ start = i + 1 + consumed
128
+ end = start + length
129
+ if end <= len(data):
130
+ print(f"Found potential ModelDescription at {i}, length: {length}")
131
+ return data[start:end]
132
+ except:
133
+ continue
134
+
135
+ return None
136
+
137
+ def analyze_model_description(desc_data):
138
+ """Analyze ModelDescription protobuf data"""
139
+ print(f"ModelDescription size: {len(desc_data)} bytes")
140
+ print(f"First 100 bytes: {desc_data[:100].hex()}")
141
+
142
+ # Look for string patterns (input/output names)
143
+ strings = extract_strings(desc_data)
144
+ print(f"Strings found: {strings[:10]}") # First 10 strings
145
+
146
+ def find_input_output_names(data):
147
+ """Find input and output names in the model"""
148
+ # Look for common input/output names
149
+ common_names = [
150
+ b'audio_input', b'rnn_features', b'encoder_features', b'stft_features',
151
+ b'vad_probability', b'output', b'var_', b'h_out', b'c_out'
152
+ ]
153
+
154
+ for name in common_names:
155
+ positions = []
156
+ start = 0
157
+ while True:
158
+ pos = data.find(name, start)
159
+ if pos == -1:
160
+ break
161
+ positions.append(pos)
162
+ start = pos + 1
163
+
164
+ if positions:
165
+ print(f" '{name.decode()}' found at positions: {positions[:5]}")
166
+
167
+ def find_neural_network_section(data):
168
+ """Find the neural network section"""
169
+ # Neural network is typically field 200 (0xC8, 0x01) or similar
170
+ # Look for field tags that might indicate neural network
171
+
172
+ patterns = [
173
+ b'\x82\x0C', # Field 200, length-delimited
174
+ b'\xA2\x06', # Field 100, length-delimited
175
+ b'\x82\x06' # Field 100, length-delimited
176
+ ]
177
+
178
+ for pattern in patterns:
179
+ pos = data.find(pattern)
180
+ if pos != -1:
181
+ print(f"Found potential neural network section at {pos}")
182
+ try:
183
+ length, consumed = decode_varint(data[pos+2:])
184
+ start = pos + 2 + consumed
185
+ end = start + length
186
+ if end <= len(data):
187
+ return data[start:end]
188
+ except:
189
+ continue
190
+
191
+ return None
192
+
193
+ def analyze_neural_network_section(nn_data):
194
+ """Analyze neural network section"""
195
+ print(f"Neural network size: {len(nn_data)} bytes")
196
+ print(f"First 100 bytes: {nn_data[:100].hex()}")
197
+
198
+ def extract_strings(data):
199
+ """Extract readable strings from binary data"""
200
+ strings = []
201
+ current_string = b""
202
+
203
+ for byte in data:
204
+ if 32 <= byte <= 126: # Printable ASCII
205
+ current_string += bytes([byte])
206
+ else:
207
+ if len(current_string) >= 3: # Minimum length
208
+ try:
209
+ strings.append(current_string.decode('utf-8'))
210
+ except:
211
+ pass
212
+ current_string = b""
213
+
214
+ # Add final string if any
215
+ if len(current_string) >= 3:
216
+ try:
217
+ strings.append(current_string.decode('utf-8'))
218
+ except:
219
+ pass
220
+
221
+ return strings
222
+
223
+ def create_corrected_protobuf():
224
+ """Create a corrected protobuf based on analysis"""
225
+ print("\n๐Ÿ”ง Creating corrected protobuf based on analysis...")
226
+
227
+ # Use the STFT model as the most reliable template
228
+ stft_path = "/Users/kikow/Library/Application Support/FluidAudio/vad/silero_stft.mlmodel"
229
+ output_path = "/Users/kikow/Library/Application Support/FluidAudio/vad/correct_classifier_conv1d.mlmodel"
230
+
231
+ if not Path(stft_path).exists():
232
+ print("โŒ STFT template not available")
233
+ return False
234
+
235
+ with open(stft_path, 'rb') as f:
236
+ template_data = bytearray(f.read())
237
+
238
+ print(f"๐Ÿ“Š Template size: {len(template_data)} bytes")
239
+
240
+ # Step 1: Replace input name
241
+ if b'audio_input' in template_data:
242
+ # Replace with rnn_features (same length to avoid shifting offsets)
243
+ template_data = template_data.replace(b'audio_input', b'rnn_feature')
244
+ print("โœ… Replaced audio_input with rnn_feature")
245
+
246
+ # Step 2: Find and replace output names
247
+ output_patterns = [b'var_71', b'output']
248
+ for pattern in output_patterns:
249
+ if pattern in template_data:
250
+ # Replace with vad_probability (pad/truncate as needed)
251
+ if len(pattern) >= len(b'vad_prob'):
252
+ replacement = b'vad_prob' + b'\x00' * (len(pattern) - len(b'vad_prob'))
253
+ else:
254
+ replacement = b'vad_prob'[:len(pattern)]
255
+ template_data = template_data.replace(pattern, replacement)
256
+ print(f"โœ… Replaced {pattern} with {replacement}")
257
+
258
+ # Step 3: Try to modify tensor shapes in the protobuf
259
+ # Look for shape patterns and try to modify them
260
+ modify_tensor_shapes(template_data)
261
+
262
+ # Write the corrected model
263
+ with open(output_path, 'wb') as f:
264
+ f.write(template_data)
265
+
266
+ print(f"โœ… Created corrected protobuf: {output_path}")
267
+ return True
268
+
269
+ def modify_tensor_shapes(data):
270
+ """Try to modify tensor shapes in protobuf data"""
271
+ # Look for shape patterns - CoreML often encodes shapes as sequences of varints
272
+ # We need to change from audio input shape to RNN features shape
273
+
274
+ # Common shape patterns (as varints)
275
+ # [512] -> [1, 4, 128]
276
+ # This is very tricky without exact protobuf structure knowledge
277
+
278
+ print("โš ๏ธ Tensor shape modification is complex - keeping original shapes")
279
+
280
+ def test_corrected_model():
281
+ """Test the corrected model"""
282
+ print("\n๐Ÿงช Testing corrected model...")
283
+
284
+ import os
285
+ result = os.system("cd /Users/kikow/brandon/FluidAudioSwift && swift run fluidaudio vad-benchmark --num-files 1")
286
+
287
+ if result == 0:
288
+ print("โœ… Model test completed")
289
+ return True
290
+ else:
291
+ print("โŒ Model test failed")
292
+ return False
293
+
294
+ def main():
295
+ print("๐Ÿš€ COREML PROTOBUF DEBUGGING")
296
+ print("=" * 50)
297
+
298
+ try:
299
+ # Step 1: Debug existing models
300
+ debug_protobuf_structure()
301
+
302
+ # Step 2: Create corrected protobuf
303
+ if create_corrected_protobuf():
304
+ print("\nโœ… Corrected protobuf created")
305
+
306
+ # Step 3: Test it
307
+ if test_corrected_model():
308
+ print("\n๐ŸŽ‰ SUCCESS! Corrected model works")
309
+ else:
310
+ print("\nโš ๏ธ Model created but needs further refinement")
311
+ else:
312
+ print("\nโŒ Failed to create corrected protobuf")
313
+
314
+ except Exception as e:
315
+ print(f"โŒ Error: {e}")
316
+ import traceback
317
+ traceback.print_exc()
318
+
319
+ if __name__ == "__main__":
320
+ main()