Skip to content

Detection/OCR forward passes are not differentiable: scalar NumOps loops sever the gradient tape #2152

Description

@ooples

Summary

Ten of the fourteen detection/OCR models still contain forward-pass helpers written as scalar NumOps loops. Each one allocates a fresh tensor and copies elements through NumOps.ToDouble / NumOps.FromDouble, which severs the autodiff tape: any trainable layer upstream of such a call receives no gradient and never trains, silently.

This is the residual half of the work started in the detection/OCR test-family PR. That PR converted the elementwise helpers (ApplyReLU ×10, ApplySigmoid ×4, ApplySiLU/ApplySwish ×5) and NeckBase.Conv1x1/Add to engine ops, which restored the gradient path through every FPN / PANet / BiFPN — previously the backbone of every detector using a neck received nothing at all. The remaining helpers manipulate shape, not just arithmetic, so they are a real rewrite rather than a one-line swap and were deliberately deferred.

Current state, per model

Measured by scanning each model file for methods that return Tensor<T>, take a Tensor<T>, and contain a for loop using NumOps.ToDouble/FromDouble:

model remaining tape-severing transforms
YOLOv8 — none
YOLOv9 — none
YOLOv10 — none
FasterRCNN — none
YOLOv11 MaxPool
DETR ApplyFFN
DINO ApplyFFN
RTDETR ApplyFFN
CascadeRCNN RefineBoxes
CRAFT BilinearUpsample
DBNet ApplyDifferentiableBinarization, BilinearUpsample
EAST ApplyBatchNormReLU, BilinearUpsample
CRNN ApplyBatchNorm, ConvertToGrayscale, MaxPool2D
TrOCR AddPositionalEncoding, ApplyFFN, ComputeCausalAttention, ComputeCrossAttention, ComputeMultiHeadAttention

13 distinct helpers. Note CRNN.ConvertToGrayscale runs before any trainable layer, so it severs nothing that matters — it is listed for completeness, not as work.

ApplyFFN appears in four models (DETR, DINO, RTDETR, TrOCR) and is the highest-leverage single fix: it sits between every transformer block, so in those models the gradient currently reaches only the last projection.

Why it matters

Train on these bases is now a real tape-recorded step (it used to be an empty method body — see the linked PR), so the models do learn. But they learn only in the layers downstream of the last severing call. A user fine-tuning DETR today updates its output head and nothing else, and gets no error or warning saying so.

The family conformance fixtures assert Train_ShouldChangeParameters, which passes as soon as any parameter moves. That is deliberately the weakest honest claim: it catches a no-op Train, and it must not be mistaken for evidence that a model trains end to end.

Proposed work

Convert each helper to engine ops, pairing every conversion with a numerical-equivalence test that pins current output against the rewritten one before the swap. The arithmetic must not change; only tape visibility.

Rough order by leverage:

  1. ApplyFFN — 4 models, sits between every transformer block
  2. BilinearUpsample — 3 models, the entire text-detection decoder path
  3. MaxPool / MaxPool2D — YOLOv11, CRNN
  4. ApplyBatchNorm / ApplyBatchNormReLU — CRNN, EAST
  5. TrOCR attention helpers (ComputeMultiHeadAttention, ComputeCausalAttention, ComputeCrossAttention)
  6. ApplyDifferentiableBinarization (DBNet), RefineBoxes (CascadeRCNN), AddPositionalEncoding (TrOCR)

Acceptance

  • No model file in src/ComputerVision/Detection/ or src/ComputerVision/OCR/ contains a Tensor<T>-in/Tensor<T>-out helper with a scalar NumOps conversion loop.
  • Each conversion has an equivalence test comparing pre- and post-rewrite output.
  • The family bases can then be strengthened from Train_ShouldChangeParameters to asserting that loss actually decreases, and that mAP / H-mean / CER improve after training — using the metrics added alongside the family work (ObjectDetectionMetrics, TextDetectionMetrics, TextRecognitionMetrics).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions