[tmva][sofie] Implement padding generation for ConvTranspose with explicitly set output_shape - #23088
Conversation
Test Results 23 files 23 suites 3d 17h 35m 26s ⏱️ For more details on these failures, see this check. Results for commit 391dd8a. ♻️ This comment has been updated with latest results. |
…icitly set output_shape
97adbb9 to
391dd8a
Compare
guitargeek
left a comment
There was a problem hiding this comment.
Thanks for tackling this! The total-padding formula itself is right and matches the ONNX spec, including the output_padding term and the dilated kernel. But there are two correctness issues that need fixing before this can go in, plus a missing test.
1. The padding split is backwards. The spec says:
If (auto_pads == SAME_UPPER): pads[start_i] = total/2; pads[end_i] = total - total/2
Else: pads[start_i] = total - total/2; pads[end_i] = total/2
The reachable path here is always auto_pad == NOTSET (the block above throws for SAME_*), i.e. the Else case — so the larger half belongs at the start, not the end. The commented-out legacy code you removed actually had this part right. (The SAME_UPPER variant is what the PR currently implements unconditionally.)
2. Asymmetric pads are silently discarded downstream, so the result is wrong.
Generate() can't express asymmetric padding: col2im takes a single pad_h/pad_w, so when pads[begin] != pads[end] it prints to std::cout and averages them. The averaged padding no longer matches the fShapeY that ShapeInference() derived, and col2im then walks data_col with the wrong column count, which garbles the whole output rather than just shifting it.
Concretely, for the classic 2× upsample (kernel 3, stride 2, dilation 1, output_shape = 2 * input): total_padding == 1 → pads (0, 1) -> averaged to 0 -> col2im consumes 3 of the 4 input columns. So the feature is correct when total_padding is even and silently wrong when it's odd, and odd is exactly the most common real-world ConvTranspose block. The only signal to the user is a std::cout line at code-generation time.
Either fix is fine by me:
- Minimal: fix the split per the spec, then
throwwhenfAttrPads[i] != fAttrPads[i + fDim]with a message saying asymmetric padding isn't supported yet. Still strictly better than today's blanket throw. - Proper, and not much more work: generalise
col2imintmva/sofie/src/SOFIE_common_helpers.cxxto takepad_begin/pad_endper axis. Its structure makes this easy —output_hbecomes(height + pad_h_begin + pad_h_end - eff_kernel) / stride + 1andinput_rowstarts at-pad_h_begin; nothing else changes. That would also let the averaging hack inGenerate()be deleted.
3. Please add a test.
tmva/sofie/test/generate_input_models.py computes expected outputs with onnx's ReferenceEvaluator, so a make_ConvTranspose2dOutputShape() model (kernel 3, stride 2, output_shape = [2h, 2w]) plus a TEST_INPUTS entry and a TEST(ONNX, ...) in0 TestCustomModelsFromONNX.cxx gets you spec conformance checked automatically, and would have caught both issues above. Since the whole point of the PR is spec conformance, I'd like to see this before merging.
Unrelated: the one red CI check (test-stressgraphics-chrome on fedora44) is a known graphics flake, nothing to do with your change.
This Pull request:
Changes or fixes:
TODOinROperator_ConvTranspose.hxxby dynamically calculating head and tail padding based on the ONNX specification whenoutput_shapeis explicitly provided.runtime_errorexception and cleans up legacy commented-out padding attempts to streamline the control flow.size_t) if an invalid or mathematically impossibleoutput_shapeis passed to the engine.Checklist:
TMVAlocally to verify C++ shape inference logic and syntax)