Update NEXT_AGENT.md: new target bronze=6062.44, need 2 more tasks
Browse files- medal-solvers/NEXT_AGENT.md +98 -124
medal-solvers/NEXT_AGENT.md
CHANGED
|
@@ -2,165 +2,139 @@
|
|
| 2 |
|
| 3 |
## The Goal
|
| 4 |
|
| 5 |
-
|
| 6 |
|
| 7 |
-
|
| 8 |
|
| 9 |
-
|
| 10 |
-
cd medal-solvers
|
| 11 |
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
|
|
|
|
|
|
| 16 |
|
| 17 |
-
|
| 18 |
-
python build_task319_onnx.py \
|
| 19 |
-
--neurogolf-utils neurogolf_utils.py \
|
| 20 |
-
--task-data-dir ../task-data
|
| 21 |
|
| 22 |
-
#
|
| 23 |
-
# swap_and_submit.py automatically finds ALL .onnx files in optimized/ folder
|
| 24 |
-
python swap_and_submit.py \
|
| 25 |
-
--base /kaggle/working/neurogolf-solver/submission-6043.zip \
|
| 26 |
-
--task-data-dir task-data \
|
| 27 |
-
--output /kaggle/working/submission.zip
|
| 28 |
-
```
|
| 29 |
|
| 30 |
-
|
| 31 |
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
```
|
| 39 |
|
| 40 |
-
##
|
| 41 |
|
| 42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
```python
|
| 45 |
-
"""Build optimized ONNX model for Task
|
| 46 |
-
import
|
| 47 |
-
import onnx
|
| 48 |
-
from onnx import helper, TensorProto, numpy_helper
|
| 49 |
import numpy as np
|
| 50 |
-
import
|
| 51 |
-
|
| 52 |
-
def build_task319():
|
| 53 |
-
"""Build the ONNX graph. Returns onnx.ModelProto."""
|
| 54 |
-
nodes, inits, vis = [], [], []
|
| 55 |
-
# ... build graph ...
|
| 56 |
-
x = helper.make_tensor_value_info('input', TensorProto.FLOAT, [1, 10, 30, 30])
|
| 57 |
-
y = helper.make_tensor_value_info('output', TensorProto.FLOAT, [1, 10, 30, 30])
|
| 58 |
-
graph = helper.make_graph(nodes, 'task319', [x], [y], initializer=inits, value_info=vis)
|
| 59 |
-
model = helper.make_model(graph, ir_version=10, opset_imports=[helper.make_opsetid('', 18)])
|
| 60 |
-
return model
|
| 61 |
-
|
| 62 |
-
def validate_with_official_utils(model_path, neurogolf_utils_path, task_data_dir):
|
| 63 |
-
"""Run official neurogolf_utils.verify_network(). Must show 0 failures."""
|
| 64 |
-
# (see build_task285_scatter.py for the full validation function)
|
| 65 |
-
...
|
| 66 |
|
| 67 |
-
if __name__ == '__main__':
|
| 68 |
-
parser = argparse.ArgumentParser()
|
| 69 |
-
parser.add_argument('--neurogolf-utils', default='neurogolf_utils.py')
|
| 70 |
-
parser.add_argument('--task-data-dir', default='../task-data')
|
| 71 |
-
parser.add_argument('--output', default='optimized/task319.onnx')
|
| 72 |
-
args = parser.parse_args()
|
| 73 |
-
|
| 74 |
-
model = build_task319()
|
| 75 |
-
os.makedirs(os.path.dirname(args.output) or '.', exist_ok=True)
|
| 76 |
-
onnx.save(model, args.output)
|
| 77 |
-
validate_with_official_utils(args.output, args.neurogolf_utils, args.task_data_dir)
|
| 78 |
-
```
|
| 79 |
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
-
|
| 83 |
-
Objects can OVERLAP β a bbox may contain pixels of other colors that must be ignored.
|
| 84 |
|
| 85 |
-
### Algorithm:
|
| 86 |
|
|
|
|
|
|
|
| 87 |
```
|
| 88 |
-
Input: [1, 10, 30, 30] one-hot encoded grid
|
| 89 |
-
Output: [1, 10, 30, 30] one-hot encoded grid (one of the input objects)
|
| 90 |
|
| 91 |
-
|
| 92 |
-
1. Find largest uniform block size (bs_r, bs_c) in template mask
|
| 93 |
-
2. Downsample: one value per block β pattern
|
| 94 |
-
3. Check if pattern is a sub-region of any other non-bg channel
|
| 95 |
-
4. If match β output = that channel
|
| 96 |
|
| 97 |
-
|
| 98 |
-
|
|
|
|
| 99 |
|
| 100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
```
|
| 102 |
|
| 103 |
-
##
|
| 104 |
-
- **(2,2): 241/267** β handle this first, covers 90%!
|
| 105 |
-
- (4,2): 4, (2,4): 3, (2,1): 2
|
| 106 |
-
- Row/col grouping: 2 cases
|
| 107 |
-
- Second template needed: 17 cases
|
| 108 |
|
| 109 |
-
|
| 110 |
-
-
|
| 111 |
-
-
|
| 112 |
-
-
|
| 113 |
-
-
|
|
|
|
| 114 |
|
| 115 |
-
##
|
| 116 |
|
|
|
|
|
|
|
| 117 |
```
|
| 118 |
-
1. ch_sums = ReduceSum(input, axes=[2,3]) β [1,10] pixel counts per channel
|
| 119 |
-
2. bg_ch = ArgMax(ch_sums)
|
| 120 |
-
3. Mask out bg, template_ch = ArgMax(remaining)
|
| 121 |
-
4. Extract template mask via Gather on channel dim β [1,1,30,30]
|
| 122 |
-
5. Reshape to [15,2,15,2], check ReduceMin==ReduceMax per 2Γ2 block
|
| 123 |
-
6. If uniform: downsample = ReduceMax of blocks β [15,15] pattern
|
| 124 |
-
7. For each candidate channel c β bg, c β template:
|
| 125 |
-
- Conv2D(candidate_mask, pattern_kernel) β correlation map
|
| 126 |
-
- Where correlation == sum(pattern) β match!
|
| 127 |
-
8. Output = matched candidate in one-hot format
|
| 128 |
-
```
|
| 129 |
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
|
|
|
|
|
|
|
|
|
| 137 |
|
| 138 |
## Key Reference Files
|
| 139 |
|
| 140 |
| File | Purpose |
|
| 141 |
|------|---------|
|
| 142 |
-
| `
|
| 143 |
-
| `
|
| 144 |
-
| `
|
| 145 |
-
| `
|
| 146 |
-
| `neurogolf_utils.py` | Official scorer β final validation |
|
|
|
|
|
|
|
| 147 |
|
| 148 |
-
##
|
| 149 |
|
| 150 |
-
The script MUST end with official validation that prints:
|
| 151 |
```
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
```
|
| 157 |
-
|
| 158 |
-
If it shows ANY failures, the model is NOT ready and will score 1.0 on Kaggle.
|
| 159 |
-
|
| 160 |
-
## Score Budget
|
| 161 |
-
|
| 162 |
-
| Task | Current | After | Gain | Cumulative LB |
|
| 163 |
-
|------|---------|-------|------|---------------|
|
| 164 |
-
| 285 | 5.98 | 8.82 | +2.83 | 6045.68 |
|
| 165 |
-
| 255 | 6.64 | 11.57 | +4.93 | ~6050.61 |
|
| 166 |
-
| **319** | **7.92** | **~12.5** | **+4.5** | **~6055.1 (BRONZE!)** |
|
|
|
|
| 2 |
|
| 3 |
## The Goal
|
| 4 |
|
| 5 |
+
**Bronze medal = 6062.44.** Current LB = 6053.51. Gap = **8.93 pts.**
|
| 6 |
|
| 7 |
+
You need to optimize **2-3 more task models** to close the gap. Each optimization yields +3-6 pts.
|
| 8 |
|
| 9 |
+
## What's Already Done
|
|
|
|
| 10 |
|
| 11 |
+
```
|
| 12 |
+
optimized/
|
| 13 |
+
βββ task255.onnx β 11.570 pts (was 6.64, gain +4.93)
|
| 14 |
+
βββ task285.onnx β 8.818 pts (was 5.98, gain +2.83)
|
| 15 |
+
βββ task319.onnx β 11.580 pts (was 7.92, gain +3.66)
|
| 16 |
+
```
|
| 17 |
|
| 18 |
+
Total gain so far: +11.42 pts across 3 tasks.
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
+
## How to Add a New Task
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
+
### Step 1: Choose a Target Task
|
| 23 |
|
| 24 |
+
Pick from the largest models in `submission-6043.zip` (most room to optimize):
|
| 25 |
+
- **task209.onnx** (1.29MB) β likely scoring ~5-6 pts, room for +5-7
|
| 26 |
+
- **task366.onnx** (1.27MB) β likely scoring ~5-6 pts, room for +5-7
|
| 27 |
+
- **task084.onnx** (1.13MB) β likely scoring ~6-7 pts, room for +4-6
|
| 28 |
+
- **task076.onnx** (949KB) β likely scoring ~6-7 pts, room for +4-5
|
| 29 |
+
- **task233.onnx** (939KB) β likely scoring ~6-7 pts, room for +4-5
|
| 30 |
+
|
| 31 |
+
### Step 2: Reverse-Engineer the Rule
|
| 32 |
+
|
| 33 |
+
```bash
|
| 34 |
+
# Extract task data
|
| 35 |
+
unzip own-solver/neurogolf-2026.zip taskNNN.json -d task-data/
|
| 36 |
+
|
| 37 |
+
# Analyze examples
|
| 38 |
+
python -c "
|
| 39 |
+
import json, numpy as np
|
| 40 |
+
with open('task-data/taskNNN.json') as f:
|
| 41 |
+
data = json.load(f)
|
| 42 |
+
# Look at input/output patterns, find the transformation rule
|
| 43 |
+
"
|
| 44 |
```
|
| 45 |
|
| 46 |
+
### Step 3: Write Python Solver (100% pass required)
|
| 47 |
|
| 48 |
+
Create `taskNNN_solver.py` β must pass ALL train+test+arc-gen examples.
|
| 49 |
+
Use the approach from `task319_solver_267.py` as a template.
|
| 50 |
+
|
| 51 |
+
### Step 4: Build ONNX Model
|
| 52 |
+
|
| 53 |
+
Use the shared `onnx_builder.py`:
|
| 54 |
|
| 55 |
```python
|
| 56 |
+
"""Build optimized ONNX model for Task NNN."""
|
| 57 |
+
from onnx import TensorProto
|
|
|
|
|
|
|
| 58 |
import numpy as np
|
| 59 |
+
from onnx_builder import OnnxBuilder, build_and_validate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
+
def build_taskNNN():
|
| 63 |
+
b = OnnxBuilder()
|
| 64 |
+
const, nd = b.const, b.nd
|
| 65 |
+
|
| 66 |
+
# ... implement the rule in ONNX ops ...
|
| 67 |
|
| 68 |
+
return b.finish('taskNNN', last_tensor=final_output)
|
|
|
|
| 69 |
|
|
|
|
| 70 |
|
| 71 |
+
if __name__ == '__main__':
|
| 72 |
+
build_and_validate(build_taskNNN, task_num=NNN)
|
| 73 |
```
|
|
|
|
|
|
|
| 74 |
|
| 75 |
+
### Step 5: Validate and Submit
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
+
```bash
|
| 78 |
+
# Build and validate
|
| 79 |
+
python build_taskNNN_onnx.py --task-data-dir ../task-data
|
| 80 |
|
| 81 |
+
# Must show: "N pass, 0 fail" and "IS READY for submission!"
|
| 82 |
+
|
| 83 |
+
# Create submission with ALL optimized models
|
| 84 |
+
python swap_and_submit.py \
|
| 85 |
+
--base ../submission-6043.zip \
|
| 86 |
+
--task-data-dir ../task-data \
|
| 87 |
+
--output ../submission.zip
|
| 88 |
```
|
| 89 |
|
| 90 |
+
## Key Constraints
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
+
- **Opset β€ 18**
|
| 93 |
+
- **Banned ops**: LOOP, SCAN, NONZERO, UNIQUE, SCRIPT, FUNCTION, COMPRESS, Sequence*
|
| 94 |
+
- **File size < 1.44MB** per model
|
| 95 |
+
- **All tensor shapes must be statically known**
|
| 96 |
+
- **Input/output: [1, 10, 30, 30]** (one-hot encoded 30Γ30 grid with 10 colors)
|
| 97 |
+
- **MUST pass ALL examples or score = 1.0!** (not partial credit)
|
| 98 |
|
| 99 |
+
## Scoring Formula
|
| 100 |
|
| 101 |
+
```python
|
| 102 |
+
score = max(1.0, 25.0 - math.log(max(1.0, memory + params)))
|
| 103 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
+
Target: memory+params < 282,000 β score β₯ 12.45 pts
|
| 106 |
+
Good: memory+params < 700,000 β score β₯ 11.5 pts
|
| 107 |
+
|
| 108 |
+
## Memory Optimization Tips
|
| 109 |
+
|
| 110 |
+
1. **Use Conv `pads` attribute** instead of separate Pad op (saves intermediate tensor)
|
| 111 |
+
2. **Minimize intermediate tensor sizes** β each named tensor is counted by profiler
|
| 112 |
+
3. **Reuse constants** β same initializer referenced by multiple nodes costs nothing extra
|
| 113 |
+
4. **Avoid large MatMul intermediates** β [900Γ900] = 3.2MB!
|
| 114 |
+
5. **MaxPool propagation** β 30 iterations Γ [1,1,30,30] = 108KB (acceptable)
|
| 115 |
|
| 116 |
## Key Reference Files
|
| 117 |
|
| 118 |
| File | Purpose |
|
| 119 |
|------|---------|
|
| 120 |
+
| `onnx_builder.py` | **Shared OnnxBuilder DSL + validation** |
|
| 121 |
+
| `build_task319_onnx.py` | **Best example of new-style build script** |
|
| 122 |
+
| `build_task285_scatter.py` | Legacy style but good ONNX techniques |
|
| 123 |
+
| `build_task255_onnx.py` | Complex ONNX (CumSum, sparse table, MaxPool propagation) |
|
| 124 |
+
| `neurogolf_utils.py` | Official scorer β MUST USE for final validation |
|
| 125 |
+
| `swap_and_submit.py` | Creates submission zip (auto-discovers optimized/*.onnx) |
|
| 126 |
+
| `LEARNING.md` | All technical findings and what works/doesn't |
|
| 127 |
|
| 128 |
+
## Workflow Summary
|
| 129 |
|
|
|
|
| 130 |
```
|
| 131 |
+
1. Pick task (largest .onnx in submission)
|
| 132 |
+
2. Extract task data from neurogolf-2026.zip
|
| 133 |
+
3. Analyze input/output examples β find the rule
|
| 134 |
+
4. Write Python solver β verify 100% pass
|
| 135 |
+
5. Translate to ONNX using OnnxBuilder
|
| 136 |
+
6. Validate with neurogolf_utils.py
|
| 137 |
+
7. Add .onnx to optimized/ folder
|
| 138 |
+
8. Submit via swap_and_submit.py
|
| 139 |
+
9. Repeat until LB β₯ 6062.44
|
| 140 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|