Repository navigation
Compile zero padding ahead of a convolution as a sampling phase, validated live - #49
Merged
Merged
Conversation
conv2d and grouped_conv2d take a keyword-only phase (default 0, today's graph): output pixel i on each axis is the centred convolution at input pixel stride * i + phase, which is PyTorch's convolution zero-padded kernel // 2 - phase pixels before the input. stride_sample takes the same phase. Both records name phase at its default until a live run varies it. The compiler recognises nn.ZeroPad2d, nn.ConstantPad2d with zero and F.pad with zero directly ahead of an nn.Conv2d or of F.conv2d with get_attr weights, and an Identity ahead of F.conv2d. BatchNorm folding, the receptive field and the weight collection reach through them.
…et trace checks A new raster selection, padded_conv: six integer-generated fixtures through compile_model on a stand-in padding module and F.conv2d, with the distinct graphs only (dense 3 x 3 stride 2 at phase 1 on an even and an odd map; depthwise 3 x 3 and 5 x 5 at phase 1; depthwise 5 x 5 at phase 0 through a symmetric pad; phases (1, 0)). The grouped mirror takes PyTorch's zero padding. SMP's efficientnet-b0 and efficientnet-b3 U-Nets are supported through adapt_smp_model at 256 x 256. A new setting on an existing block enters its record at its default until a live run varies it.
specialize_plumbing could turn a bare shape element in an F.pad into a literal at the compiled input size; zero padding must be written statically, so that pad is refused as padding computed from a tensor's shape.
tools/live_fixtures.py --offline padded_conv on a clean tree: six fixtures, twelve planned requests, zero cloud calls; the depthwise mirror within 5.96e-08 of float32 PyTorch, the dense fixtures compared with PyTorch only.
The padded_conv selection ran live once: 6 fixtures, 12 requests, all passed. Earth Engine equals the NumPy mirror in every value on the four depthwise fixtures; the two dense fixtures, which have no mirror, are within 5.96e-08 of float32 PyTorch. The run varied phase (0, 1 and (1, 0)), so it leaves both records, which return to their earlier wording. grouped_conv2d's status gains 5x5 at stride 2 and its radius becomes 3, the reach of a 5x5 kernel at phase 1. The docstrings and guides list the forms that ran; other kernels and phases are not validated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Zero padding directly ahead of a convolution now compiles through
compile_model, as the existing convolution blocks sampled at a phase. This covers "same" padding in the TensorFlow style, which pads more after the input than before it, and the functional form in which some encoders write their convolutions. Validated live in this pull request.phaseonconv2d,grouped_conv2dandlayouts.raster.stride_sample: an integer or a(phase_y, phase_x)pair,0 <= phase < stride, default 0. Output pixelion an axis is the centred convolution at input pixelstride * i + phase, which is PyTorch's convolution withkernel // 2 - phasezeros before the input, withceil((n - phase) / stride)outputs. The convolution itself is unchanged; only the sample grid moves. The default emits today's graph.nn.Conv2dmodule orF.conv2dwhose weight and bias are the model's own parameters, read only by that call. Zero padding directly ahead of it, asnn.ZeroPad2d,nn.ConstantPad2dwith 0, orF.padwith four fixed integers and 0, adds to the convolution's own padding. The rule is on the total: per axis, the padding before the input must bekernel // 2 - phaseand PyTorch's output count must match. BatchNorm folding,analyze_receptive_field, weight collection andMatchedWeightProviderwork through a recognised convolution, with state-dict paths as weight sources.efficientnet-b0andefficientnet-b3at 256 × 256 aresupportedbycheck_compatibilityafteradapt_smp_model. This is a trace status: neither model's graph has been evaluated.Every graph that compiles today is unchanged. No new block, policy or dependency.
Base revision:
98ad27c5(main).Changes by layer
tensor_ee/layouts/raster.py(stride_sample'sphase,sample_grids,phase_pair,canonical_phase).tensor_ee/blocks/raster/conv.py(phaseon both blocks; metadata).semantics.py(recognition, the fold),compiler.py,receptive_field.py,compatibility.py,weight_assets.py.tools/fixtures/raster.py(selectionpadded_conv, six fixtures on stand-in modules, integer-generated).tests/test_raster_padded_conv.py(new),tests/reference/raster_conv.py(apaddingkeyword on the existing mirror),tests/test_live_fixtures.py,tests/test_raster_blocks.py,tests/test_raster_grouped_conv.py,tests/test_raster_global_pool_multiply.py.CONTRIBUTING.md,docs/api.md,docs/smp-models.md,docs/building-blocks.md(regenerated),docs/evidence/raster-padded-conv-offline.json,docs/evidence/live-fixtures/20261112T185127Z-padded_conv.json.Checks on the head commit
Run locally on
5457c60with a clean tree (Python 3.14, all extras, trained checkpoints present):98ad27c5before the first edit.pytest -q: 4134 passed, 20 skipped.--offline padded_conv: 6 fixtures passed, zero cloud calls.--plan padded_conv: 12 requests, largest 400 values and 22,716 bytes, 0 assets, 0 tasks, within budget.--plangraphs and request counts ofcalibration,p02,hard_activations,batch_norm,bilinear,grouped_conv,standardizeandglobal_pool_multiplyare identical to the base's.Not reproduced locally: the hosted jobs on Python 3.11 and 3.12 without extras.
Largest absolute error per fixture (tolerance 1e-4). The three columns are separate claims:
raster/conv2d/pad0101-k3-s2-h8-w10raster/conv2d/pad0101-k3-s2-h7-w9raster/grouped_conv2d/depthwise-pad0101-k3-s2raster/grouped_conv2d/depthwise-pad1212-k5-s2raster/grouped_conv2d/depthwise-pad2222-k5-s2raster/grouped_conv2d/depthwise-pad1101-k3-s2The dense
conv2dblock has no NumPy mirror (its existing fixture has none either), so its two fixtures are compared with PyTorch only.Live run
The selection
padded_convran once on the clean revisiond162897c: 6 fixtures, 12 requests (the planned count), 0 assets, 0 tasks, no refused calls, 12.1 s. Every fixture passed. The record is committed unedited. The head commit's 12 planned graph hashes are identical to that revision's.Changes to the two blocks' catalogue entries, each following from the run:
phasewas named at its default in both records, so a nonzero phase was reported outside them. The run varied it (0, 1 and(1, 0)), so it leaves both records, which read as onmain:conv2d"any accepted call",grouped_conv2dclip_footprintsFalse.grouped_conv2dstatus: "5×5 stride 1" becomes "5×5 stride 1 or 2".grouped_conv2dradius: 2 becomes 3, the reach of a 5 × 5 kernel at phase 1.Limitations
(1, 0); depthwise 5 × 5 at phase 1 and, through a symmetric pad, at phase 0. Other kernels and phases, such as a dense 5 × 5 or 7 × 7 at a nonzero phase or a dense mixed phase, compile but are not validated.efficientnet-b0andefficientnet-b3were checked, as trace status. No whole model was evaluated offline or live, and there is no numerical or accuracy claim for these encoders. Without the adapter, the decoder's 10 in-place ReLUs still block the raw model.[0, stride)or whose output count differs, such as an unpadded stride-1 convolution.analyze_receptive_fielduses the left and top pads. On the two U-Nets it still stops at the decoder's shape read, as before, and their global means rule out tiling in any case.