yolo-training
This skill should be used when user asks to "improve my mAP", "why is my model overfitting", "my training is diverging", "read my results.csv", "interpret my training curves", "my AP50 is good but AP50-95 is bad", "my recall is low", "how do I pick learning rate", "which augmenta
Install
npx skills add https://github.com/fcakyon/claude-codex-settings/tree/main/plugins/ultralytics-dev/skills/yolo-training
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install fcakyon-claude-codex-settings@llmmart
git clone https://github.com/fcakyon/claude-codex-settings.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole fcakyon/claude-codex-settings collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
YOLO26 training
Read the run before changing anything. The results.csv and confusion matrix usually name the
problem already.
Order of operations
Ordered by cost to try, cheapest first, not by size of the potential win.
- Epochs and schedule. Undertrained looks like every other problem, and it costs nothing but time to rule out.
- Augmentation. The knob for the generalization gap, at no extra compute per epoch.
- Loss weights and LR. Cheap, and the curves usually say which one is wrong.
- Model size. Scale up when train loss is still falling at the end of the schedule and the train and val curves sit close together. That is underfitting, and it is the only case a bigger model reliably fixes.
- Resolution. Compute scales with the square of
imgsz, so 640 to 1280 is roughly 4x the training budget, and pretrained weights transfer worse the further you move from the size they were fit at. Justify it with the object sizes in your data, not as a default first move. - Data, label quality and class balance. The highest ceiling and the slowest to move. The package ships no dataset-analysis tooling, so any audit here is your own script plus looking at images. Worth it once the cheap knobs are spent.
Diagnostic loop
import pandas as pd
df = pd.read_csv("runs/detect/train/results.csv")
df.columns = df.columns.str.strip()
print(df.tail(10)[["epoch", "train/box_loss", "val/box_loss", "metrics/mAP50(B)", "metrics/mAP50-95(B)"]])
print("best epoch:", df["metrics/mAP50-95(B)"].idxmax(), "of", len(df))
Then read, in this order:
| Read | Question it answers |
|---|---|
| best epoch vs total epochs | undertrained, overtrained, or right |
| train loss vs val loss trend | which side of the generalization gap |
| mAP50 vs mAP50-95 | classification and recall vs localization |
| P vs R at the operating point | over-suppression vs over-firing |
| per-class AP spread | one broken class or a general weakness |
| confusion matrix background row and column | false positives vs missed detections |
references/diagnostics.md maps each pattern to a cause and a knob, and lists what to rule
out before turning that knob. Read it before recommending a change.
references/task-notes.md covers detect, segment, semantic, pose, obb, classify, and depth
specifics.
Defaults that will surprise you
These produce "I changed X and nothing happened". All six are current defaults.
optimizer=autoignoreslr0andmomentum. It is the default. It picksMuSGDat lr 0.01 whenceil(len(dataset) / max(batch, nbs)) * epochsexceeds 10000, otherwiseAdamWat0.002 * 5 / (4 + nc), and forceswarmup_bias_lr=0. Crossing that iteration count silently changes optimizer between two runs you meant to compare. Settinglr0while leavingoptimizer=autodoes nothing. Setoptimizer=AdamWoroptimizer=SGDexplicitly first.nbs=64normalizes the loss, so batch does not scale LR the way you assume. Below 64 the trainer accumulates gradients to an effective 64. Dropping batch 64 to 16 changes almost nothing about the effective step.close_mosaic=10turns off mosaic for the last 10 epochs. The late jump in mAP is that switch, not convergence. On a 20-epoch run it is half the schedule, and on a 10-epoch run mosaic never runs at all.- Fitness for detect is mAP50-95 alone, weights
[0, 0, 0, 1].best.ptandpatienceignore precision, recall, and mAP50 completely. Segment and pose sum both heads, classify uses(top1 + top5) / 2, semantic uses mIoU. A run whose precision is climbing while mAP50-95 is flat will still early-stop. max_det=300truncates validation on dense scenes. Above roughly 300 objects per image your recall ceiling is an artifact.- YOLO26
end2endmodels decode without NMS, soioudoes nothing on them.agnostic_nmsstill applies, the predictor passes it into the head, so only the IoU threshold is dead.
Starting recipe
Fine-tuning a pretrained checkpoint on a normal custom dataset:
yolo train model=yolo26s.pt data=my-data.yaml epochs=200 imgsz=640 batch=16 \
optimizer=AdamW lr0=0.001 lrf=0.01 cos_lr=True warmup_epochs=3 \
patience=50 close_mosaic=20
Deviate on evidence from the charts, one axis at a time. It differs from the shipped defaults
because epochs=100 is short for a small dataset, patience=100 never fires inside 100 epochs,
and close_mosaic=10 is too short a clean tail once epochs rise.
Change one thing per run and keep seed fixed. Run-to-run noise on a small dataset is often
0.5 to 1.0 mAP, so a 0.3 mAP "improvement" from a single run is not a result. Confirm anything
under about 1 point across three seeds.
Files (claude-codex-settings)
-
references
-
diagnostics.md 12.7 KB
# Reading a run Symptom, the cheap knob, and what to check when the knob does not move it. Compute and relabeling are the fallback, not the opener. ## Schedule patterns | Best epoch lands at | Meaning | Action | | ---------------------------- | -------------------------------- | ---------------------------------- | | final epoch | undertrained | more epochs, 1.5x to 2x | | 80 to 95 percent through | about right | leave it | | 40 to 60 percent, then decay | overtrained | more augmentation, or stop earlier | | under 25 percent | LR too high, or the data is tiny | lower `lr0`, add regularization | `patience=100` against `epochs=100` means early stopping never fires. Set `patience` to roughly a quarter of `epochs` when you want it to be real. ## Metric-shape patterns ### mAP50 high, mAP50-95 low Classification and coarse localization work. Tight localization does not. Start with the loss weights. Cheap, and aimed at the symptom: ```bash box=10.0 dfl=2.0 # from 7.5 / 1.5 ``` Raise `box` and `dfl` together, roughly 1.3x to 2x. YOLO26 sets `reg_max: 1`, which switches the distribution focal loss off, so `dfl` weights a plain L1 term on the box edges and the results.csv column is `train/l1_loss`, not `train/dfl_loss`. The knob still works, and it is still the more targeted of the two. Watch that `cls` does not get crowded out, a collapsing `metrics/precision(B)` means you went too far. If that moves nothing, the ceiling is probably not the loss: 1. **Loose ground truth.** Sloppy boxes cap mAP50-95 permanently and no loss weight recovers it. Overlay 30 to 50 GT boxes on their images and look. If human boxes sit 5 to 10 pixels outside the object, the ceiling is the labels and the only fix is relabeling. 2. **Ambiguous class boundaries.** "Where does the scratch end" has no tight answer, and annotators disagree with each other. Check inter-annotator spread before blaming the model. 3. **Objects too small for the resolution.** An object spanning 12 pixels cannot be localized to mAP50-95 precision at any loss weight. Read the Small objects section for what raising `imgsz` costs before reaching for it. ### Precision high, recall low The model fires rarely and is right when it does. It is missing objects. Rule out first: 1. **Missing annotations.** Unlabeled true objects train the model to suppress them, and then penalize it again at validation. Look at the highest-confidence false positives. If they are correct detections of unlabeled objects, the dataset is the bug. 2. **`conf` at inference.** Validation uses `conf=0.001`, prediction defaults to `0.25`. A recall complaint from `predict` output and one from `val` metrics are different problems. 3. **`max_det=300`** on dense scenes. 4. **Small objects** below the P3 stride, see the small-object section. Then: ```bash cls=1.0 # from 0.5, push classification confidence up cls_pw=0.5 # inverse-frequency class weighting if rare classes carry the loss ``` `cls_pw` is the underused one. `0.0` disables it, `1.0` is full inverse frequency, and values between dampen it. On a dataset with a 50:1 class ratio, `0.3` to `0.5` often moves rare-class recall several points where nothing else does. The trainer asserts `0 <= cls_pw <= 1`, so values above 1 raise rather than weight harder. ### Precision low, recall high The model fires often, including on nothing. Rule out first: 1. **Duplicate boxes.** Look at raw predictions. Many overlapping boxes on one object means NMS, not the model. Lower `iou` from `0.7` toward `0.5`. On YOLO26 `end2end` this knob does nothing, the model decodes without NMS. 2. **Background false positives.** Read the confusion matrix background column. If one class dominates it, that class needs hard negatives. 3. **Class confusion.** Off-diagonal mass between two classes means the taxonomy is the problem, not the threshold. Merging two classes that annotators cannot separate reliably often beats any amount of tuning. Then add background-only images (5 to 10 percent of the train set, no label file), and raise inference `conf`. ### Both precision and recall low across every class Undertrained, wrong LR, or broken data loading. Check that training loss is still falling. If the first-epoch mAP is near zero and stays there, verify the label format and `nc` before touching hyperparameters. ### One class far below the others Per-class AP spread is data, not hyperparameters, nine times out of ten. Check instance count, then look at 20 of its images. A class with 40 instances against classes with 4000 will underperform regardless of the recipe. Options, in order: collect more, merge it into a neighbor class, or apply `cls_pw`. ### mAP50-95 close to mAP50 Unusual, and normally means objects are large and easy. Little headroom on localization. Look at recall and per-class AP instead. ## Loss-curve patterns ### Train loss falls, val loss rises Overfitting. The gap opens, and mAP50-95 peaks then decays. Augmentation, strongest first: ```bash mixup=0.15 copy_paste=0.3 scale=0.9 degrees=10 mosaic=1.0 close_mosaic=20 ``` - `mixup` blends two images and their labels. The general-purpose regularizer, and the first thing to reach for. `0.1` to `0.2` on small datasets. - `copy_paste` pastes instances between images. It returns the labels untouched when they carry no segments, so on a box-only detect dataset it is a silent no-op, not a weak effect. Where masks exist it is the strongest single knob for rare-class recall. - `scale` is the most underrated. `0.5` means 0.5x to 1.5x. Raising to `0.9` widens the scale range the model must handle, and it costs nothing at inference. - `erasing` (classify) and `cutmix` are alternatives when mixup is already maxed. Then regularization: `weight_decay=0.001` from `0.0005`, and `dropout` for classify only. Then, if it persists: fewer epochs, a smaller model, more data. Do not raise `degrees`, `flipud`, or `perspective` without checking that the transform is valid for your domain. Rotating an X-ray 15 degrees is fine. Rotating a document or a road scene is not, and it costs accuracy. ### Train and val loss both plateau early, mAP flat Underfitting or the LR is too low. Confirm the LR actually applied (`optimizer=auto` overrides it), then raise `lr0` roughly 3x, extend epochs, or reduce augmentation. Heavy `mixup` plus heavy `mosaic` on a small model genuinely prevents convergence. ### Loss spikes to NaN or explodes Diverging. 1. Check AMP first. `amp=False` isolates fp16 overflow, which is the usual cause on custom architectures and unusual input statistics. 2. Then LR: drop `lr0` 10x. 3. Then warmup: raise `warmup_epochs` to 5, and confirm `warmup_bias_lr` is not the source. `optimizer=auto` sets it to 0, an explicit optimizer leaves it at `0.1`. 4. Check labels for out-of-range coordinates. Normalized values above 1.0 produce inf loss. Gradient clipping is not a knob. `trainer.py` calls `clip_grad_norm_` with a hardcoded `max_norm=10.0` and exposes no argument, so a run that still explodes needs the LR or the data fixed, not a tighter clip. ### mAP jumps sharply in the last N epochs That is `close_mosaic` disabling mosaic, not convergence. Expected. If the jump is large, the model was fighting mosaic the whole run, and a longer clean tail (`close_mosaic=20` or `30`) or `mosaic=0.5` will do better. ### Val metrics oscillate hard between epochs Small val split, or LR too high late in the schedule. Set `cos_lr=True` and `lrf=0.01` so the LR actually decays to near zero, and check the val split has at least a few hundred instances per class. Below that, epoch-to-epoch swings are sampling noise and reading them is a mistake. ## Learning rate `lr0` is the initial LR, `lrf` is a fraction, so the final LR is `lr0 * lrf`. Default `lrf=0.01` decays to 1 percent. | Situation | lr0 | | -------------------------------------------- | ------------ | | AdamW fine-tune from a pretrained checkpoint | 0.001 | | AdamW, small dataset under about 1000 images | 0.0005 | | SGD from scratch | 0.01 | | Diverging | current / 10 | | Flat loss, confirmed the value applied | current \* 3 | The default schedule is linear, `(1 - x/epochs) * (1 - lrf) + lrf`. `cos_lr=True` swaps in a one-cycle cosine and beats it on most fine-tunes. `warmup_epochs=3` is right for most runs, raise to 5 for large batches or an unstable start, and drop toward 1 on runs under 30 epochs where 3 epochs of warmup is 10 percent of the budget. It is clamped to `epochs - 1`, so a 10-epoch smoke test with the default spends 3 of its 10 epochs warming up. There is no per-group LR argument. To protect a pretrained backbone from a randomly initialized head, `freeze` it for the first run, or lower `lr0` for the whole model. A discriminative LR needs a callback that rewrites `optimizer.param_groups`. ## Batch size Set it to fill the GPU, not to tune accuracy. `nbs=64` normalization means small batches get gradient accumulation, so batch mostly buys throughput. - `batch=-1` or a float like `batch=0.8` uses AutoBatch to fill a memory fraction, `-1` targeting 60 percent. - Above 64, the effective LR does rise, so scale `lr0` roughly linearly. - Weight decay is rescaled the same way, `weight_decay * batch * accumulate / nbs`. At `batch=128` your `0.0005` is really `0.001`, which is a common unexplained regularization change when someone moves a recipe to a bigger GPU. - Very small batches (under 8) make BatchNorm statistics noisy, a real accuracy cost. ## Small objects Confirm the problem is real before spending anything on it. Nothing in the package reports box sizes, so measure them yourself: read the label files, multiply the normalized width and height by your training `imgsz`, and look at the distribution. Under roughly 32 pixels is the small regime, under 16 is the hard regime. If most boxes are above that, the ceiling is elsewhere. Free first: 1. `multi_scale=0.5` trains across a scale range and helps generalization across sizes. 2. `scale=0.9` widens the augmentation scale range. 3. A P2 head variant if objects sit below the P3 stride of 8 pixels. Then the expensive ones: 4. Tile large images into overlapping crops for training and inference. More images per epoch, but each stays at the resolution the pretrained weights expect, which is usually the better trade for 4K imagery with 20-pixel objects. 5. Raise `imgsz`. It works, and it is last because of the quadratic cost. Budget for a longer schedule rather than swapping `imgsz` into an otherwise unchanged recipe. ## Class imbalance 1. `cls_pw` in `0.3` to `0.5`, see Precision high, recall low for what the power does. 2. `copy_paste` to synthesize rare-class instances, and only if the labels carry segments. 3. Oversample rare-class images by duplicating their paths in the train list. Crude, effective. 4. Merge classes that annotators confuse anyway. Report per-class AP, never only the macro mAP. A macro number hides a class at 0.05. ## Transfer learning - `pretrained=True` uses the shipped COCO weights. Always start there, from-scratch training on a custom dataset is almost never right. - `cls_remap=True` (default) matches pretrained classification-head rows by class name, so shared names like `person` keep their learned weights across datasets. - `freeze=N` freezes model indices `0` to `N-1`. On YOLO26 the backbone ends at C2PSA, index 10, and the head starts at 11, so `freeze=11` is the whole backbone and the widely copied `freeze=10` leaves C2PSA trainable. Useful for a tiny dataset (under about 500 images) or a first sanity run. Unfreeze for the real run, a frozen backbone gives up several points once the dataset is large enough to move it. - Domain distance decides how much you fine-tune. Medical, thermal, and satellite imagery share little with COCO, so they need more epochs and a higher `lr0` than a natural-image dataset. ## Automated tuning ```python model.tune(data="my-data.yaml", epochs=30, iterations=300, optimizer="AdamW", plots=False, save=False) ``` Worth running only after the manual passes above are exhausted, and only on a dataset large enough that a 0.5 mAP difference is signal. It costs `iterations` full trainings. Use a reduced `epochs` per iteration, then retrain the winner at full length. ## Reporting a change - State the delta and the baseline: "mAP50-95 0.412 to 0.437, +2.5 points, one seed". - Name what else changed. A recipe with three simultaneous edits attributes nothing. - Compare at the same epoch count and the same `imgsz`. Different budgets are not comparable. -
task-notes.md 5.6 KB
# Per-task notes What differs by task. Everything in `diagnostics.md` still applies on top of this. ## Detection Loss gains: `box=7.5`, `cls=0.5`, `dfl=1.5`. Fitness is mAP50-95 alone. - YOLO26 `end2end` heads train two branches at once, `0.8 * one2many + 0.2 * one2one`, and decay the one2many weight to `0.1` across the schedule. Only the one2one losses reach `results.csv`, so the logged box loss is not the whole objective and comparing it against a non-end2end run compares different quantities. - `single_cls=True` collapses every class into one. The fastest way to answer "is this a localization problem or a classification problem", run it and compare mAP50-95. A large jump means the boxes were never the issue. - `rect=True` batches by aspect ratio instead of padding to square. Faster and slightly better on consistently non-square imagery, incompatible with shuffling so it interacts with mosaic. ## Instance segmentation Adds `overlap_mask=True` and `mask_ratio=4`. Fitness sums the mask and box components, so both heads gate early stopping. - `mask_ratio=4` downsamples masks 4x during training. Drop to `1` or `2` when objects are thin or small, since a 20-pixel object becomes 5 pixels of mask at the default and loses its shape. It costs memory. - `overlap_mask=True` merges instances into one mask per image. Set `False` when instances overlap heavily and you need them separated during training. - `copy_paste` only does anything here. The augmentation returns early when the labels carry no segments, so it is live on segment datasets and a no-op on box-only detect ones. `copy_paste=0.3` with `copy_paste_mode=flip` is a reasonable start. - Mask AP trailing box AP by a lot means polygon quality. Coarse polygons (4 to 6 points around a curved object) cap mask AP no matter what you train. - `retina_masks=True` at inference only, higher-resolution masks at some cost. ## Semantic segmentation Fitness is mIoU. Loss is cross-entropy with `ignore_index=255`, so 255 is the void label. - `cls_pw` applies ENet inverse-log weighting, `(1 / ln(1.02 + p)) ** cls_pw`, not the inverse-frequency form detection uses. It is the main knob for classes that occupy few pixels, which in semantic segmentation is most of the interesting ones. - mIoU is a mean over classes, so one collapsed class costs a full share regardless of its pixel count. Always print per-class IoU. - Binary (`nc=1`) uses BCE and ignores class weighting by design. - Resolution binds harder than in detection, since thin structures below a few pixels wide cannot survive the encoder stride. Measure their width before paying the quadratic cost. ## Pose Loss gains: `pose=12.0`, `kobj=1.0`, `rle=1.0`. Fitness sums pose and box. - **`flip_idx` in `data.yaml` is required for any flip augmentation.** Without it the trainer sets both `fliplr` and `flipud` to 0 and logs a warning, so pose runs quietly lose their cheapest augmentation. `flip_idx` is the permutation that swaps left and right keypoints, and a wrong one is worse than none because it trains mirrored labels. - `kpt_shape: [N, 3]` means x, y, visibility. `[N, 2]` means no visibility flag, and then occluded keypoints cannot be marked, which distorts the loss. - Keypoint accuracy far behind box accuracy often means resolution, since keypoints need more pixels on target than boxes do. Try `scale` and a longer schedule first, raising `imgsz` is the expensive answer. - Raise `pose` above 12.0 only after confirming boxes are already good, the two compete. ## Oriented bounding boxes Adds `angle=1.0`. - `degrees` is the augmentation that matters, and unlike axis-aligned detection it is safe. Aerial and document imagery have no canonical up, so `degrees=180` is reasonable. - `flipud=0.5` is equally valid for aerial imagery and off by default. - Angle-periodicity errors show as a class of predictions rotated 90 or 180 degrees from the target. Check the label convention before raising `angle`, a systematic offset is a data bug. - Objects with near-square aspect ratio have poorly defined angles and will always score worse. Exclude them from the analysis rather than tuning against them. - OBB validation runs at `conf=0.01`, the other tasks at `0.001`. OBB mAP is measured on a smaller candidate pool, so it is not directly comparable to an axis-aligned number. ## Classification Fitness is `(top1 + top5) / 2`. Different augmentation set from the detection tasks. - `auto_augment` accepts `randaugment` (default), `autoaugment`, `augmix`. `randaugment` is the right default. Set it empty for small fine-grained datasets where the transforms destroy the distinguishing detail. - `erasing=0.4` is random erasing, the classification analogue of cutout, and the main overfitting knob alongside `mixup` and `cutmix`. - `dropout` applies to the classification head only, and is 0 by default. `0.1` to `0.2` for a small dataset. - Mosaic, mixup ratios, and the box augmentations do not apply. - Top-1 far below top-5 means confusable classes, not general weakness. Read the confusion matrix and consider merging. - Class imbalance shows in top-1 immediately. Balance the sampler or the dataset, `cls_pw` is not wired into the classification loss. ## Depth Loss gains: `dlog=1.0` (SILog), `dgrad=0.5` (gradient), `dlam=1.0`. Fitness is `delta1`. - `dlam=1.0` is fully scale-invariant, `0.0` is plain log-RMSE. Lower it when absolute depth matters rather than relative structure. - `dgrad` sharpens depth discontinuities at object boundaries. Raise it when edges look smeared. - Reported metrics are `delta1`, `abs_rel`, `rmse`, `silog`. Only `delta1` is higher-is-better.
-
-
SKILL.md 5.4 KB
--- name: yolo-training description: This skill should be used when user asks to "improve my mAP", "why is my model overfitting", "my training is diverging", "read my results.csv", "interpret my training curves", "my AP50 is good but AP50-95 is bad", "my recall is low", "how do I pick learning rate", "which augmentations should I use", "should I use a bigger model", "tune hyperparameters", or asks how to train YOLO26 for detection, instance or semantic segmentation, pose, OBB, classification, or depth. --- # YOLO26 training Read the run before changing anything. The `results.csv` and confusion matrix usually name the problem already. ## Order of operations Ordered by cost to try, cheapest first, not by size of the potential win. 1. **Epochs and schedule.** Undertrained looks like every other problem, and it costs nothing but time to rule out. 2. **Augmentation.** The knob for the generalization gap, at no extra compute per epoch. 3. **Loss weights and LR.** Cheap, and the curves usually say which one is wrong. 4. **Model size.** Scale up when train loss is still falling at the end of the schedule and the train and val curves sit close together. That is underfitting, and it is the only case a bigger model reliably fixes. 5. **Resolution.** Compute scales with the square of `imgsz`, so 640 to 1280 is roughly 4x the training budget, and pretrained weights transfer worse the further you move from the size they were fit at. Justify it with the object sizes in your data, not as a default first move. 6. **Data**, label quality and class balance. The highest ceiling and the slowest to move. The package ships no dataset-analysis tooling, so any audit here is your own script plus looking at images. Worth it once the cheap knobs are spent. ## Diagnostic loop ```python import pandas as pd df = pd.read_csv("runs/detect/train/results.csv") df.columns = df.columns.str.strip() print(df.tail(10)[["epoch", "train/box_loss", "val/box_loss", "metrics/mAP50(B)", "metrics/mAP50-95(B)"]]) print("best epoch:", df["metrics/mAP50-95(B)"].idxmax(), "of", len(df)) ``` Then read, in this order: | Read | Question it answers | | ------------------------------------------ | ----------------------------------------- | | best epoch vs total epochs | undertrained, overtrained, or right | | train loss vs val loss trend | which side of the generalization gap | | mAP50 vs mAP50-95 | classification and recall vs localization | | P vs R at the operating point | over-suppression vs over-firing | | per-class AP spread | one broken class or a general weakness | | confusion matrix background row and column | false positives vs missed detections | `references/diagnostics.md` maps each pattern to a cause and a knob, and lists what to rule out before turning that knob. Read it before recommending a change. `references/task-notes.md` covers detect, segment, semantic, pose, obb, classify, and depth specifics. ## Defaults that will surprise you These produce "I changed X and nothing happened". All six are current defaults. 1. **`optimizer=auto` ignores `lr0` and `momentum`.** It is the default. It picks `MuSGD` at lr 0.01 when `ceil(len(dataset) / max(batch, nbs)) * epochs` exceeds 10000, otherwise `AdamW` at `0.002 * 5 / (4 + nc)`, and forces `warmup_bias_lr=0`. Crossing that iteration count silently changes optimizer between two runs you meant to compare. Setting `lr0` while leaving `optimizer=auto` does nothing. Set `optimizer=AdamW` or `optimizer=SGD` explicitly first. 2. **`nbs=64` normalizes the loss, so batch does not scale LR the way you assume.** Below 64 the trainer accumulates gradients to an effective 64. Dropping batch 64 to 16 changes almost nothing about the effective step. 3. **`close_mosaic=10` turns off mosaic for the last 10 epochs.** The late jump in mAP is that switch, not convergence. On a 20-epoch run it is half the schedule, and on a 10-epoch run mosaic never runs at all. 4. **Fitness for detect is mAP50-95 alone**, weights `[0, 0, 0, 1]`. `best.pt` and `patience` ignore precision, recall, and mAP50 completely. Segment and pose sum both heads, classify uses `(top1 + top5) / 2`, semantic uses mIoU. A run whose precision is climbing while mAP50-95 is flat will still early-stop. 5. **`max_det=300`** truncates validation on dense scenes. Above roughly 300 objects per image your recall ceiling is an artifact. 6. **YOLO26 `end2end` models decode without NMS**, so `iou` does nothing on them. `agnostic_nms` still applies, the predictor passes it into the head, so only the IoU threshold is dead. ## Starting recipe Fine-tuning a pretrained checkpoint on a normal custom dataset: ```bash yolo train model=yolo26s.pt data=my-data.yaml epochs=200 imgsz=640 batch=16 \ optimizer=AdamW lr0=0.001 lrf=0.01 cos_lr=True warmup_epochs=3 \ patience=50 close_mosaic=20 ``` Deviate on evidence from the charts, one axis at a time. It differs from the shipped defaults because `epochs=100` is short for a small dataset, `patience=100` never fires inside 100 epochs, and `close_mosaic=10` is too short a clean tail once epochs rise. Change one thing per run and keep `seed` fixed. Run-to-run noise on a small dataset is often 0.5 to 1.0 mAP, so a 0.3 mAP "improvement" from a single run is not a result. Confirm anything under about 1 point across three seeds.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.