FIG. 01
RECORD DATA
Category
Software
TARİH
Everybody does the same thing when they write their training command: they copy the augmentation lines out of a forum post or somebody's Kaggle notebook and paste them in. degrees=15, mixup=0.15, erasing=0.4... It looks like it works. Then validation mAP lands below what you expected and you can't figure out why.
It happened to us too. Our first dataset was mostly scraped from the web, with only a small share of it being real aerial footage. We piled aggressive settings on top of that on the theory that more augmentation is always better, and the model got worse on aerial imagery. The problem wasn't augmentation itself; it was that we had never asked which transforms were physically possible in our problem.
In this post we walk through the Ultralytics augmentation parameters one at a time: what they do, their official defaults, and — the part that actually matters — when they hurt. Every value here was checked against the Ultralytics default.yaml and the official documentation (as of 2026).
The full parameter table
In Ultralytics, augmentation isn't a separate library; it lives right inside the training configuration. Here are the defaults:
Parameter | Default | Range | What it does |
|---|---|---|---|
| 0.015 | 0.0–1.0 | Hue (color) shift |
| 0.7 | 0.0–1.0 | Saturation shift |
| 0.4 | 0.0–1.0 | Brightness shift |
| 0.0 | 0–180 | Rotation by ±N degrees |
| 0.1 | 0.0–1.0 | Shift by a fraction of the image size |
| 0.5 | 0.0–1.0 | Scaling between (1−scale) and (1+scale) |
| 0.0 | −180–+180 | Shear (skew transform) |
| 0.0 | 0.0–0.001 | Perspective warp |
| 0.0 | 0.0–1.0 | Probability of a vertical flip |
| 0.5 | 0.0–1.0 | Probability of a horizontal flip |
| 0.0 | 0.0–1.0 | Probability of an RGB↔BGR channel swap |
| 1.0 | 0.0–1.0 | Probability of stitching 4 images onto one canvas |
| 10 | integer | Disable mosaic for the last N epochs (0 = never disable) |
| 0.0 | 0.0–1.0 | Blend two images and their labels |
| 0.0 | 0.0–1.0 | Cut and paste rectangular regions between images |
| 0.0 | 0.0–1.0 | Object copying (segmentation only) |
|
| flip / mixup | Where the copied object comes from |
|
| — | Classification only |
| 0.4 | 0.0–0.9 | Classification only |
Note what stands out: by default only mosaic, fliplr, scale, translate and the HSV trio are active. Everything else is off. It's no accident that Ultralytics picked a non-aggressive baseline.
scale: the one that helps most and gets overlooked most
scale=0.5 randomly resizes the image somewhere between 0.5x and 1.5x. Scale diversity pays off directly in object detection, because the same target shows up at wildly different pixel sizes from 20 m and from 60 m.
For us this was critical: most of our dataset was ground-level imagery where the object filled the frame. In flight, targets are far smaller within the frame. scale is the cheapest tool available for closing part of that gap.
scale also accepts a (min, max) tuple instead of a float, which means you can hand it an asymmetric range. If small objects are your problem, it makes sense to weight the range toward shrinking:
A warning: mosaic already shrinks objects, since it packs four images onto a single canvas. Stack an aggressive scale on top of that and objects can drop to a handful of pixels, get caught by the label filters, and vanish completely. Tune the two together, not separately.
fliplr and flipud: from the air, both are fair game
fliplr (horizontal flip) defaults to 0.5, so half your images get mirrored. That's safe for most problems — but it destroys the signal for text, license plates, or any class that carries a left/right distinction.
flipud (vertical flip) defaults to 0.0, and the reason is obvious: in ground-level imagery, gravity is a real constraint. An upside-down person is a scene that has no business being in a training set.
Looking straight down, that constraint disappears. With a nadir camera, the heading the drone happens to be flying while holding altitude makes object orientation in the frame arbitrary. The same tent appears 180 degrees apart on a northbound pass and a southbound pass. That's why flipud=0.5 isn't merely safe on aerial datasets, it's actively useful — and unlike rotation, it doesn't distort the box geometry.
degrees: the quiet cost of loosening the box
Rotation looks innocent, but it fights with the label format. YOLO labels are axis-aligned boxes; their edges are always parallel to the image edges. There is no field in which to store a rotated box.
In the Ultralytics source, RandomPerspective.apply_bboxes does exactly this: it pushes the box's four corners through the transform matrix, then builds a new axis-aligned box from the x.min(), y.min(), x.max(), y.max() of the transformed corners. So the box drawn around a rotated object is wider than the original, and it carries pixels that don't belong to the object.
The loss peaks at 45 degrees, and it's far more pronounced on long, thin objects (a person lying down, for instance). On roughly square objects it's negligible. The practical upshot: keep degrees small — 5–10 covers most scenarios — or, if you genuinely need full 360-degree orientation diversity, reach for the flipud + fliplr combination instead of rotation. Neither of those loosens the box at all.
mosaic and close_mosaic: turn it on, then turn it off
mosaic=1.0 stitches four images into a 2x2 canvas for every training sample. That raises class diversity and context variation within a single frame, and it helps small-object performance too, because objects naturally get smaller.
But the frame it produces is not a real frame. It's a seamed composite of four different scenes, and the camera will never hand you an image like that in flight. That is exactly why close_mosaic=10 (the default) exists: mosaic shuts off for the last 10 epochs and the model fine-tunes on real, single-piece frames. Your validation metrics usually pick up noticeably right at that point.
Set close_mosaic to 0 and mosaic stays on until the end — on short runs that means the model never sees a "clean" frame at all. On a 100-epoch run, you should have a specific reason before you touch the default of 10.
mixup and cutmix: visitors from classification
mixup blends two images with transparency and mixes their labels along with them. cutmix cuts a rectangular region out of one image and pastes it into another. Both default to 0.0 — off.
These come out of the classification literature and are considerably more debatable on a detection task. mixup produces semi-transparent ghost objects. Only reach for either one when you're actually seeing overfitting, and then only at small values (0.05–0.15).
copy_paste: it does nothing without segmentation masks
This is the single most misunderstood parameter. copy_paste is the direct way to fix class imbalance — cut objects out of one image, paste them into another scene — which sounds exactly like the answer to our own 7:1 class imbalance.
Here's the catch: cutting an object out cleanly requires pixel-level segmentation masks. A box isn't enough. The Ultralytics source handles this silently:
So if you write copy_paste=0.5 on a box-only detection dataset, you get no error whatsoever; the augmentation simply never runs. You'll sit through epochs assuming it's doing something.
copy_paste_mode takes two values: flip (the default) copies objects from the same image, mixup pulls them from different images. In both cases the paste only goes through if the overlap with existing objects stays below 30%.
erasing and auto_augment: these belong to classification
The erasing=0.4 and auto_augment='randaugment' defaults sit under the "Classification-Specific Augmentations" heading in the documentation. Writing those lines into a detection run has no effect at all.
If you are training a classifier, there's still a caveat: when the object fills most of the frame, erasing buys you robustness to partial occlusion. When the object is already small, the erased rectangle can cover the whole thing, and you end up handing the model an X-free image together with a label that says "there is an X in this image" — straight noise. Note as well that erasing's scale, ratio and value sub-parameters can't be changed in the current implementation.
When each one hurts
Parameter | Hurts when | What to do |
|---|---|---|
| Objects are already small and mosaic is on too | Narrow the range; tune it together with mosaic |
| Left/right carries meaning (text, plates, arrows) | Set it to 0.0 |
| Ground-level or oblique view where gravity is visible | Leave it at 0.0 unless the view is nadir |
| Long, thin objects; camera orientation already fixed | Keep it between 0 and 10, or prefer the flips |
| Camera geometry is fixed and known | Leave them off |
| Color is what separates the classes (red vs. green target) | Dial down the hue shift, keep the brightness |
| The model never sees a clean frame | Don't set |
| Small dataset, detection task | Start with them off; turn them on only if you overfit |
| No mask labels | It silently does nothing — generate segmentation labels |
| Object is small within the frame | Lower it even in classification |
Traps people fall into
Using augmentation to cover for missing data. If a class has few examples, augmentation doesn't multiply them; it just shows the same handful of examples over and over. Our dataset had a 7:1 gap between classes, and trying to close it with augmentation got us nowhere. The fix was collecting more real aerial frames.
Leaking augmentation into the validation set. Ultralytics doesn't augment during validation, but if you write your own preprocessing step and bake offline augmentations into the dataset, make sure variants of the same image don't land on both the train and the val side. Split on the source image, not on the variant.
Generating physically impossible scenes. The test in Ultralytics' own training material is simple: could this image actually occur in deployment? If it couldn't, you're burning model capacity.
Changing everything at once. Move five parameters together and look at mAP, and you'll never learn what any one of them did. Start from the defaults and change one parameter at a time.
Confusing training resolution with flight resolution. We train at imgsz=1280 and run 640 full-frame inference in flight. Judge the effective object size that augmentation produces against the training resolution — but measure what actually works on a validation set that's close to flight conditions. The configuration that got us to 0.950 mAP50 on our aerial-only validation set looked like a completely different animal once we measured it on the mixed set.
Practical summary
Start from the defaults.
mosaic=1.0,fliplr=0.5,scale=0.5,translate=0.1, the HSV trio — everything else off. That baseline is sensible for most detection problems.If you're flying a nadir (down-looking) camera, turn
flipud=0.5on. It's the only transform that buys you orientation diversity without distorting the box geometry.Prefer the flips over
degrees. Rotation widens the axis-aligned box and packs background pixels into the label.Don't set
close_mosaicto 0. Fine-tuning on real frames in the final epochs cleans up mosaic's seams.Generate masks before you reach for
copy_paste. Without segmentation labels the parameter is silently ignored; don't sit there waiting for an error message.Measure every change on the same validation set and make sure that set represents flight conditions.
