File size: 3,540 Bytes
0939cc2 3644a98 8c12a26 0939cc2 3644a98 0939cc2 8c12a26 0939cc2 8c12a26 3644a98 0939cc2 8c12a26 3644a98 8c12a26 3644a98 8c12a26 3644a98 0939cc2 3644a98 8c12a26 3644a98 0939cc2 3644a98 8c12a26 3644a98 0939cc2 3644a98 0939cc2 3644a98 0939cc2 3644a98 0939cc2 3644a98 0939cc2 3644a98 0939cc2 8c12a26 3644a98 | 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 | #!/usr/bin/env python3
"""Static profiling for ONNX models.
The new neurogolf_utils.py (May 14 2026) changed score_network() to require
(sanitized_model, trace_path) instead of just a filename. This requires running
ORT with profiling enabled, which is too heavy for local model generation.
Strategy: Use static fallback for local scoring during model generation.
Real scoring happens on Kaggle at submission time via the official utils.
Models are NOT rejected locally — they're validated via inference correctness.
Returns (macs=0, memory, params) for backward compatibility with solver_registry.py.
"""
import math
import onnx
from onnx import numpy_helper
from .constants import BANNED_OPS, GH, GW
def score_network(path):
"""Score network locally. Returns (macs, memory, params) or (None, None, None).
macs is always 0 (no longer used in Kaggle scoring since May 4 2026).
memory and params are static estimates sufficient for local development.
Real scoring uses ORT profiler on Kaggle.
"""
result = _static_profile(path)
if result is None:
return None, None, None
memory, params = result
return 0, memory, params
def estimate_score(path):
"""Estimate score under new formula: 25 - ln(memory + params)."""
result = _static_profile(path)
if result is None:
return None
memory, params = result
cost = memory + params
if cost <= 0:
return 25.0
return max(1.0, 25.0 - math.log(cost))
def _static_profile(path):
"""Static profiling: estimate memory + params.
memory = sum of all initializer bytes + estimated intermediate tensor bytes
params = sum of all initializer element counts + Constant node values
Returns (memory, params) or None if model is invalid.
"""
try:
model = onnx.load(path)
except Exception:
return None
params = 0
memory = 0 # bytes
# Count initializers (weights)
for init in model.graph.initializer:
try:
a = numpy_helper.to_array(init)
params += a.size
memory += a.nbytes
except Exception:
pass
# Count Constant nodes
for nd in model.graph.node:
if nd.op_type == 'Constant':
for attr in nd.attribute:
if attr.name == 'value' and attr.t and attr.t.ByteSize() > 0:
try:
a = numpy_helper.to_array(attr.t)
params += a.size
memory += a.nbytes
except Exception:
pass
elif attr.name == 'value_floats':
params += len(attr.floats)
memory += len(attr.floats) * 4
elif attr.name == 'value_ints':
params += len(attr.ints)
memory += len(attr.ints) * 8
# Banned op check
if nd.op_type.upper() in {op.upper() for op in BANNED_OPS}:
print(f"WARNING: Banned op '{nd.op_type}' found in {path}")
return None
# Estimate intermediate tensor memory (node outputs that aren't 'output')
n_intermediates = 0
for nd in model.graph.node:
for out_name in nd.output:
if out_name and out_name != 'output':
n_intermediates += 1
# Conservative estimate: average intermediate ~20KB (mix of small and large tensors)
memory += n_intermediates * 20000
return int(memory), int(params)
|