Add training support: fine-tuning, from-scratch, v3/v4/v7 families - #140
Merged
Conversation
Adds a training path alongside the existing buffered inference path: - `train!(model, data)`: CIoU box loss + BCE objectness/class losses with best-anchor target assignment, Adam (or any Optimisers rule), LR warmup (darknet burn-in style), optional horizontal-flip augmentation, checkpointing, and a differentiable non-mutating forward pass. The layer closures became callable structs (RouteLayer, ShortcutAdd, CatLayer, ...) that keep the exact fast mutating inference path but record their source chain indices so the pure forward can resolve skips functionally. - The box loss combines CIoU on decoded boxes with a raw-space wh regression sharing the same optimum; CIoU alone has vanishing wh gradients once the exp decode over/undershoots, letting predictions run away. - Supports classic decode (v3 family, yolov4, yolov4-tiny: linear heads, logit BCE) and new_coords=1 scaled decode (scaled-YOLOv4 family, v7 family: logistic heads, probability BCE). yolov2 [region] models error cleanly. - `trainable_batchnorm=true` construction mode keeps live BatchNorm layers for from-scratch training; default remains load-time folding for fastest inference. Verified output-equivalent on pretrained weights. - Transfer learning: `weights_stop_layer` random-inits layers past a backbone split (15 = darknet's yolov3-tiny.conv.15) enabling custom class counts via cfgchanges; `allow_partial_weights` loads truncated .conv.XX files. - `save_weights` writes darknet-format .weights that round-trip with the same cfg: real BN params in trainable_batchnorm mode, identity-BN parameters for folded models. - Data handling: `TrainSample`, `load_darknet_dataset`/`load_darknet_labels` (darknet label format), letterbox-consistent box transforms. Tests: pure/buffered forward equivalence, gradient flow, toy-dataset overfit for random-init yolov3-tiny, from-scratch BN yolov3-tiny, yolov4-tiny and yolov7-tiny (new_coords via yolov7-tiny), yolov4 (mish) gradient smoke, pretrained COCO 2-class transfer fine-tune, save/reload roundtrips, checkpointing, label/letterbox handling. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Single-image Adam fine-tuning oscillates and BLAS differences make exact trajectories platform-dependent (failed on linux CI only, 3.33 vs 3.02). Use a gentler LR with warmup, more epochs, and smoothed endpoints: margins are now ~8x across random inits instead of ~10%. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Note
This PR was written by Claude (Claude Code), from training support requested and supervised by @IanButterworth.
Adds working training support to ObjectDetector, replacing the README's "Training using ObjectDetector is currently unproven/untested". Supports fine-tuning pretrained weights and from-scratch training, for the v3, v4, and v7 model families.
What's included
train!(model, data; ...)— full training loop: batching/shuffling, Adam (or any Optimisers rule), LR warmup (darknet burn-in style), optional horizontal-flip augmentation, per-epoch loss reporting, and checkpointing to darknet-format.weights.RouteLayer,ShortcutAdd,CatLayer, …) that keep the exact fast inference path but also record their source chain indices, letting a pure non-mutating forward resolve skips functionally. Verified numerically identical to the buffered forward.expdecode over/undershoots, which let predictions run away irrecoverably (found while producing the session below). Classic heads (v3 family, v4, v4-tiny: linear/logit) andnew_coords=1heads (scaled-YOLOv4 + v7 families: logistic/probability,(t·sxy)²decode) are both supported; yolov2[region]models error cleanly.trainable_batchnorm=truekeeps liveBatchNormlayers instead of folding them into the convs (default remains folding, which is faster for inference but freezes BN statistics). Verified output-equivalent to the folded path on pretrained weights.weights_stop_layer=Nrandom-inits everything past a backbone split (15 ≙ darknet'syolov3-tiny.conv.15), enabling custom class counts via the existingcfgchanges;allow_partial_weights=trueloads truncated.conv.XXfiles.save_weights— darknet-format output that round-trips with the same cfg: real BN parameters intrainable_batchnormmode, identity-BN parameters for folded models.TrainSample,load_darknet_dataset/load_darknet_labels(darknet label conventions), letterbox transforms applied to boxes consistently withprepare_image.Example session
Fine-tuning the pretrained COCO backbone to a single custom class ("white rectangle") at 160×160, on CPU. Output below is a genuine captured run.
From-scratch works the same way with
weights_stop_layer=0, trainable_batchnorm=true— the test suite overfits yolov3-tiny, yolov4-tiny, and yolov7-tiny from random init this way.Inference is unchanged
Training throughput vs darknet
Benchmarked
train!against darknet's own native training (train_network: forward + backward + update), driven throughDarknet_jll'slibdarknet— the same binary the test suite trusts for inference parity — via a new training API in Darknet.jl (training_data/train_network!ccall wrappers, to be PR'd there). Both sides trained the identical cfg (yolov3-tiny @ 416×416,batch=8,subdivisions=1) from the same pretrained weights on the identical batch content, no augmentation, CPU (macOS arm64; Julia BLAS 6 threads).train_networktrain!step~21× faster here, but read with care: the jll
libdarknetis a generic portable build, and darknet's real-world training performance comes from natively tuned builds (AVX on x86) or CUDA — this comparison says Julia/Flux training throughput is strongly competitive on CPU, not that darknet is inherently slow. Correctness of the darknet side was validated separately (its loss decreases and per-layer IoU climbs on toy data, and itssave_weightsoutput round-trips).A convergence-quality comparison (same dataset trained both ways, detections evaluated identically) is prepared but not yet run.
Notes
iou_loss,ignore_threshsemantics, multi-anchor assignment, mosaic etc. are not implemented).🤖 Generated with Claude Code