Maintained
YOLO Clover Detection Experiments: P2, Resolution, and Confidence Thresholds
A qualified first-party report on a YOLOv8 clover detector: the baseline, P2 training recipe, 640-versus-768 result, and confidence-threshold trade-offs.
- yolo
- machine-learning
- deep-learning
This project explored a practical question: how should a small-object detector for four-leaf clovers be trained and filtered for an iOS application? Three experiment notes were combined here so the measured results, training changes, and product threshold decision can be read together.
The numbers below are first-party observations, not a reproducible benchmark. The original run record did not preserve the Ultralytics version, dataset split, seeds, training logs, or repeated runs. Treat the results as hypotheses for the next controlled experiment rather than universal YOLO guidance.
Start with the baseline and the actual failure
The initial YOLOv8n run trained for 50 epochs and stopped early:
| Metric | Observed value |
|---|---|
| [email protected] | 58.4% |
| Precision | 80.2% |
| Recall | 52.6% |
For a clover-finding interface, recall was the practical problem: the model missed nearly half of the labeled objects. Precision alone did not describe the user experience.
The next recipe changed several variables together. It moved to a YOLOv8s-P2 configuration, increased early-stopping patience, changed the learning-rate schedule, and added stronger augmentation. The P2 architecture adds a stride-4 detection head, making it a plausible candidate for small objects, but this experiment cannot assign the improvement to that head alone.
from ultralytics import YOLO
model = YOLO("yolov8s-p2.yaml").load("yolov8s.pt")
results = model.train(
data="dataset.yaml",
epochs=200,
patience=50,
imgsz=640,
batch=8,
device=0,
lr0=0.01,
lrf=0.1,
cos_lr=True,
warmup_epochs=5.0,
cls=0.75,
flipud=0.5,
degrees=25.0,
scale=0.7,
translate=0.2,
hsv_h=0.02,
hsv_v=0.5,
mosaic=1.0,
close_mosaic=15,
mixup=0.15,
copy_paste=0.1,
multi_scale=True,
optimizer="AdamW",
)
This configuration is an experiment record, not a recommended default. In particular, vertical flipping is appropriate only if orientation does not change the label, and aggressive synthetic augmentation should be inspected for unrealistic clover and background combinations.
The 640 and 768 runs produced different outcomes
Two later YOLOv8s-P2 runs reported:
| Run | Resolution | Batch recorded in comparison | [email protected] | Recall |
|---|---|---|---|---|
| P2 | 640 | 16 | 88.8% | 83.7% |
| P2_768 | 768 | 12 | 87.1% | 76.4% |
The 640 run was 1.7 percentage points higher in [email protected] and 7.3 points higher in recall. That result supports one narrow conclusion: in these recorded runs, increasing input resolution did not improve the measured validation outcome.
It does not prove why. Resolution and batch size changed together, while the earlier full configuration recorded a batch size of 8. The surviving notes do not establish which configuration produced every metric. Claims about pretrained-resolution mismatch, inevitable overfitting, gradient stability, augmentation coverage, or a P2 “sweet spot” remain plausible explanations, not isolated causes.
The next experiment should hold the split, seed, model weights, optimizer, schedule, augmentation, effective batch size, and stopping rule constant while changing only resolution. Preserve the environment and output artifacts:
- dataset version, train/validation/test split, and label audit;
- Ultralytics, PyTorch, CUDA, and Core ML conversion versions;
- random seeds and complete training configuration;
- precision-recall and F1-confidence curves;
- per-size and per-scene errors, not only aggregate mAP;
- at least several repeated runs with variance;
- latency, memory, and battery measurements on the target iPhone.
Choose the confidence threshold from product costs
The detector’s confidence threshold trades recall against false positives. A higher threshold shows fewer uncertain boxes; a lower threshold finds more candidates and usually admits more false alarms.
For this app, a missed clover and a false alert have asymmetric costs. A user can dismiss a false positive, but cannot recover an object the app never showed. That supports offering a recall-oriented setting, but it does not make one threshold statistically optimal.
The project used 0.25 as a heuristic default and exposed 0.15 and 0.40 as more sensitive and more conservative choices:
enum Sensitivity {
case low
case medium
case high
var threshold: Float {
switch self {
case .low: return 0.40
case .medium: return 0.25
case .high: return 0.15
}
}
}
func filter(
_ observations: [VNRecognizedObjectObservation],
threshold: Float
) -> [VNRecognizedObjectObservation] {
observations.filter { $0.confidence >= threshold }
}
These values were not selected by a reported F1 sweep. Before treating them as production defaults, evaluate the converted on-device model on a held-out set, plot precision and recall across thresholds, and test the result with representative users. Log aggregate false-positive dismissal and missed-detection feedback only with appropriate consent and privacy controls.
What the experiment supports
The consolidated evidence supports three practical lessons:
- Start from the product failure mode, not one headline metric. Recall mattered more than precision for the original clover-finding flow.
- A higher input resolution is not automatically better. Measure it under a controlled recipe and include device costs.
- Threshold selection is a product decision constrained by a validation curve. A sensitivity control can expose the trade-off, but its presets still require evidence.
The reported jump from the 58.4% baseline to an 88.8% P2 run is encouraging, but too many variables changed to credit a single technique. The strongest next step is a reproducible ablation matrix, not another untracked configuration change.