| |
| """ |
| fix_scoring_bugs.py — Fix ONNX models with scoring bugs. |
| |
| Bug 1: dims=[0] initializers (scalar_shape) |
| - The scorer rejects any initializer with dims containing 0 |
| - Fix: Replace Reshape(x, scalar_shape) with Squeeze(x) |
| |
| Bug 2: Missing value_info (only fixable if shapes are static) |
| - Run shape inference to populate value_info |
| - Only works if model has no dynamic-shape ops (NonZero, Where with data-dependent shapes) |
| |
| Usage: |
| python fix_scoring_bugs.py --input task291.onnx --output fixed/task291.onnx --task-num 291 |
| """ |
| import argparse |
| import os |
| import sys |
| import onnx |
| import onnxruntime as ort |
| import numpy as np |
| import json |
| from onnx import helper, TensorProto, numpy_helper, shape_inference |
|
|
|
|
| def fix_scalar_shape(model): |
| """Replace Reshape(x, scalar_shape) with Squeeze(x) where scalar_shape has dims=[0].""" |
| |
| bad_inits = {init.name for init in model.graph.initializer if any(d <= 0 for d in init.dims)} |
| |
| if not bad_inits: |
| return model, False |
| |
| |
| new_inits = [init for init in model.graph.initializer if init.name not in bad_inits] |
| |
| |
| new_nodes = [] |
| fixed = False |
| for node in model.graph.node: |
| if node.op_type == 'Reshape' and any(inp in bad_inits for inp in node.input): |
| squeeze_node = helper.make_node( |
| 'Squeeze', |
| inputs=[node.input[0]], |
| outputs=list(node.output), |
| ) |
| new_nodes.append(squeeze_node) |
| fixed = True |
| else: |
| new_nodes.append(node) |
| |
| del model.graph.initializer[:] |
| model.graph.initializer.extend(new_inits) |
| del model.graph.node[:] |
| model.graph.node.extend(new_nodes) |
| |
| return model, fixed |
|
|
|
|
| def fix_missing_value_info(model): |
| """Run shape inference to add value_info for models that lack it.""" |
| if len(model.graph.value_info) > 0: |
| return model, False |
| |
| try: |
| inferred = shape_inference.infer_shapes(model, strict_mode=True) |
| if len(inferred.graph.value_info) > 0: |
| return inferred, True |
| except: |
| pass |
| |
| try: |
| inferred = shape_inference.infer_shapes(model, strict_mode=False) |
| if len(inferred.graph.value_info) > 0: |
| return inferred, True |
| except: |
| pass |
| |
| return model, False |
|
|
|
|
| def validate_model(model_path, task_num, task_data_dir): |
| """Validate model produces correct outputs for all examples.""" |
| task_json = os.path.join(task_data_dir, f'task{task_num:03d}.json') |
| if not os.path.exists(task_json): |
| print(f" Warning: task data not found at {task_json}") |
| return True |
| |
| sess = ort.InferenceSession(model_path) |
| with open(task_json) as f: |
| data = json.load(f) |
| |
| right, wrong = 0, 0 |
| for ex in data['train'] + data['test'] + data.get('arc-gen', []): |
| inp = np.zeros((1, 10, 30, 30), dtype=np.float32) |
| for r, row in enumerate(ex['input']): |
| for c, v in enumerate(row): |
| if r < 30 and c < 30: |
| inp[0][v][r][c] = 1.0 |
| result = sess.run(['output'], {'input': inp}) |
| out = (result[0] > 0.0).astype(float) |
| exp = np.zeros((1, 10, 30, 30), dtype=np.float32) |
| for r, row in enumerate(ex['output']): |
| for c, v in enumerate(row): |
| if r < 30 and c < 30: |
| exp[0][v][r][c] = 1.0 |
| if np.array_equal(out, exp): |
| right += 1 |
| else: |
| wrong += 1 |
| |
| print(f" Validation: {right} pass, {wrong} fail") |
| return wrong == 0 |
|
|
|
|
| def main(): |
| parser = argparse.ArgumentParser(description="Fix ONNX scoring bugs") |
| parser.add_argument('--input', required=True, help='Input ONNX model') |
| parser.add_argument('--output', required=True, help='Output ONNX model') |
| parser.add_argument('--task-num', type=int, required=True, help='Task number') |
| parser.add_argument('--task-data-dir', default='/app/task-data', help='Task data directory') |
| args = parser.parse_args() |
| |
| print(f"Loading {args.input}...") |
| model = onnx.load(args.input) |
| print(f" Nodes: {len(model.graph.node)}") |
| print(f" Initializers: {len(model.graph.initializer)}") |
| print(f" Value_info: {len(model.graph.value_info)}") |
| |
| |
| model, fixed_scalar = fix_scalar_shape(model) |
| if fixed_scalar: |
| print(" ✓ Fixed scalar_shape (dims=[0]) bug") |
| |
| model, fixed_vi = fix_missing_value_info(model) |
| if fixed_vi: |
| print(f" ✓ Added value_info via shape inference ({len(model.graph.value_info)} entries)") |
| |
| if not fixed_scalar and not fixed_vi: |
| print(" No fixes needed") |
| return |
| |
| |
| os.makedirs(os.path.dirname(args.output) or '.', exist_ok=True) |
| onnx.save(model, args.output) |
| print(f" Saved to {args.output}") |
| |
| |
| if validate_model(args.output, args.task_num, args.task_data_dir): |
| print(" ✓ Model passes all examples!") |
| else: |
| print(" ✗ Model FAILS validation — do NOT use!") |
| sys.exit(1) |
|
|
|
|
| if __name__ == '__main__': |
| main() |
|
|