rogermt commited on
Commit
3644a98
·
verified ·
1 Parent(s): 9a5b998

Fix profiler.py: new neurogolf_utils.score_network requires (model, trace_path) — use static fallback for local scoring, real scoring happens on Kaggle

Browse files
own-solver/neurogolf_solver/profiler.py CHANGED
@@ -1,84 +1,101 @@
1
  #!/usr/bin/env python3
2
  """Static profiling for ONNX models.
3
 
4
- Uses neurogolf_utils.score_network() (onnx_tool) when available this is
5
- the ONLY scoring that matches Kaggle. The static fallback is approximate
6
- and prints a WARNING. If onnx_tool returns (None, None, None), the model
7
- is REJECTED — do not submit it.
 
 
 
8
  """
9
 
 
10
  import onnx
11
  from onnx import numpy_helper
12
  from .constants import BANNED_OPS, GH, GW
13
 
14
- try:
15
- from neurogolf_utils import score_network as _score_network_official
16
- HAS_ONNX_TOOL = True
17
- except ImportError:
18
- HAS_ONNX_TOOL = False
19
-
20
- _WARNED_NO_ONNX_TOOL = False
21
-
22
 
23
  def score_network(path):
24
- """Score network. Returns (macs, memory, params) or (None, None, None).
25
 
26
- If onnx_tool is available: uses official scorer. (None,None,None) = REJECTED.
27
- If onnx_tool is NOT available: uses static fallback with WARNING.
 
28
  """
29
- global _WARNED_NO_ONNX_TOOL
30
- if HAS_ONNX_TOOL:
31
- # Official scorer — trust its result. Do NOT catch exceptions silently.
32
- try:
33
- result = _score_network_official(path)
34
- except Exception as e:
35
- print(f"WARNING: onnx_tool score_network failed on {path}: {e}")
36
- return None, None, None
37
- return result
38
- else:
39
- if not _WARNED_NO_ONNX_TOOL:
40
- print("WARNING: onnx_tool not installed. Scores are APPROXIMATE and may not match Kaggle.")
41
- print("WARNING: Models that fail onnx_tool profiling will be REJECTED on Kaggle.")
42
- print("WARNING: Run neurogolf_utils.verify_network() in a Kaggle notebook before submitting.")
43
- _WARNED_NO_ONNX_TOOL = True
44
- return _static_profile(path)
45
 
46
 
47
  def _static_profile(path):
48
- """Static profiling fallback. APPROXIMATE does not match Kaggle scoring.
49
- Only used when onnx_tool is not installed."""
 
 
 
 
 
50
  try:
51
  model = onnx.load(path)
52
- except:
53
  return None, None, None
54
- tensors = {}
55
  params = 0
56
- nbytes = 0
57
- macs = 0
 
58
  for init in model.graph.initializer:
59
- a = numpy_helper.to_array(init)
60
- tensors[init.name] = a
61
- params += a.size
62
- nbytes += a.nbytes
 
 
 
 
63
  for nd in model.graph.node:
64
  if nd.op_type == 'Constant':
65
  for attr in nd.attribute:
66
- if attr.t and attr.t.ByteSize() > 0:
67
  try:
68
  a = numpy_helper.to_array(attr.t)
69
- if nd.output:
70
- tensors[nd.output[0]] = a
71
  params += a.size
72
- nbytes += a.nbytes
73
- except:
74
  pass
75
- # Banned op check — UPPERCASE to match Kaggle
 
 
 
 
 
 
 
76
  if nd.op_type.upper() in {op.upper() for op in BANNED_OPS}:
77
  print(f"WARNING: Banned op '{nd.op_type}' found in {path}")
78
  return None, None, None
79
- if nd.op_type == 'Conv' and len(nd.input) >= 2 and nd.input[1] in tensors:
80
- w = tensors[nd.input[1]]
81
- if w.ndim == 4:
82
- co, ci, kh, kw = w.shape
83
- macs += co * ci * kh * kw * GH * GW
84
- return int(macs), int(nbytes), int(params)
 
 
 
 
 
 
 
 
 
1
  #!/usr/bin/env python3
2
  """Static profiling for ONNX models.
3
 
4
+ The new neurogolf_utils.py (May 14 2026) changed score_network() to require
5
+ (sanitized_model, trace_path) instead of just a filename. This requires running
6
+ ORT with profiling enabled, which is too heavy for local model generation.
7
+
8
+ Strategy: Use static fallback for local scoring during model generation.
9
+ Real scoring happens on Kaggle at submission time via the official utils.
10
+ Models are NOT rejected locally — they're validated via inference correctness.
11
  """
12
 
13
+ import math
14
  import onnx
15
  from onnx import numpy_helper
16
  from .constants import BANNED_OPS, GH, GW
17
 
 
 
 
 
 
 
 
 
18
 
19
  def score_network(path):
20
+ """Score network locally. Returns (memory, params) or (None, None, None).
21
 
22
+ Uses static estimation (sum of tensor sizes + param count).
23
+ This is APPROXIMATE but sufficient for local development.
24
+ Real scoring uses ORT profiler on Kaggle.
25
  """
26
+ return _static_profile(path)
27
+
28
+
29
+ def estimate_score(path):
30
+ """Estimate score under new formula: 25 - ln(memory + params)."""
31
+ result = _static_profile(path)
32
+ if result is None or result[0] is None:
33
+ return None
34
+ memory, params = result[0], result[1] # memory in bytes, params in elements
35
+ cost = memory + params
36
+ if cost <= 0:
37
+ return 25.0
38
+ return max(1.0, 25.0 - math.log(cost))
 
 
 
39
 
40
 
41
  def _static_profile(path):
42
+ """Static profiling: estimate memory + params.
43
+
44
+ memory = sum of all initializer bytes + estimated intermediate tensor bytes
45
+ params = sum of all initializer element counts + Constant node values
46
+
47
+ Returns (memory, params) or (None, None, None) if model is invalid.
48
+ """
49
  try:
50
  model = onnx.load(path)
51
+ except Exception:
52
  return None, None, None
53
+
54
  params = 0
55
+ memory = 0 # bytes
56
+
57
+ # Count initializers (weights)
58
  for init in model.graph.initializer:
59
+ try:
60
+ a = numpy_helper.to_array(init)
61
+ params += a.size
62
+ memory += a.nbytes
63
+ except Exception:
64
+ pass
65
+
66
+ # Count Constant nodes
67
  for nd in model.graph.node:
68
  if nd.op_type == 'Constant':
69
  for attr in nd.attribute:
70
+ if attr.name == 'value' and attr.t and attr.t.ByteSize() > 0:
71
  try:
72
  a = numpy_helper.to_array(attr.t)
 
 
73
  params += a.size
74
+ memory += a.nbytes
75
+ except Exception:
76
  pass
77
+ elif attr.name == 'value_floats':
78
+ params += len(attr.floats)
79
+ memory += len(attr.floats) * 4
80
+ elif attr.name == 'value_ints':
81
+ params += len(attr.ints)
82
+ memory += len(attr.ints) * 8
83
+
84
+ # Banned op check
85
  if nd.op_type.upper() in {op.upper() for op in BANNED_OPS}:
86
  print(f"WARNING: Banned op '{nd.op_type}' found in {path}")
87
  return None, None, None
88
+
89
+ # Estimate intermediate tensor memory (node outputs that aren't 'output')
90
+ # Each intermediate tensor is approximately (1,10,30,30) float32 = 36,000 bytes
91
+ # This is rough but gives directional guidance
92
+ n_intermediates = 0
93
+ for nd in model.graph.node:
94
+ for out_name in nd.output:
95
+ if out_name and out_name != 'output':
96
+ n_intermediates += 1
97
+
98
+ # Conservative estimate: average intermediate ~20KB (mix of small and large tensors)
99
+ memory += n_intermediates * 20000
100
+
101
+ return int(memory), int(params)