This is the capstone: everything from the previous five lessons — backbones, the detection head, augmentation, the from_preset idiom — assembled into a working pipeline. The shape below is the one you'll reuse on every detection problem you ever touch, swapping only the dataset and the class list.
The two halves: inference and fine-tuning
Inference is the easy half (first Code block): load the COCO-pretrained detector, resize an image to the model's expected size, batch it, call predict, and read the boxes/classes/confidence back out. That alone is a usable object detector for the 80 COCO classes.
The half that earns the "practical" label is fine-tuning on your own classes (second Code block). The recipe is the same one every track lesson hinted at: load a detector with a pretrained backbone, freeze that backbone, attach a head sized to your number of classes, and train on your annotated data. Because the backbone already knows how to see, you need far less data and far less compute than training from scratch — a few hundred labeled images can take you to a usable prototype.
The honest punchline: the model code is the small part. The skill ceiling for production detection isn't the architecture — it's the data. Clean annotations, the right box format, a sane train/val split, and a confidence threshold tuned to your precision/recall needs matter more than which YOLO size you picked.
Code
Inference: load YOLOV8 and detect on one image·python
import keras_cv
# Load pretrained YOLOV8
model = keras_cv.models.YOLOV8Detector.from_preset(
"yolo_v8_m_coco",
)
# Prepare image
image = keras.utils.load_img("photo.jpg", target_size=(640, 640))
image_array = keras.utils.img_to_array(image)
image_batch = keras.ops.expand_dims(image_array, axis=0)
# Run detection
predictions = model.predict(image_batch)
# Process bounding boxes, classes, and confidence scores
Fine-tuning: a detection head sized to your classes·python
# Start from a pretrained backbone, new head for YOUR classes
detector = keras_cv.models.YOLOV8Detector.from_preset(
"yolo_v8_s_backbone_coco", # backbone weights only
num_classes=len(my_class_names),
bounding_box_format="xywh", # MUST match your dataset
)
detector.compile(
optimizer=keras.optimizers.Adam(1e-4),
box_loss="ciou",
classification_loss="binary_crossentropy",
)
detector.fit(train_ds, validation_data=val_ds, epochs=20)
# Persist and reload — same model, no extra glue
detector.save("my_detector.keras")
reloaded = keras.models.load_model("my_detector.keras")
Pick a small detection dataset (or annotate ~50 images yourself with a tool like Label Studio). Fine-tune YOLOv8s on it: set bounding_box_format correctly, freeze the backbone for the first few epochs, then unfreeze. Aim for mAP ≥0.4 (small datasets are noisy — that's a realistic bar, not a low one). Save the model with .save("...keras"), reload it in a fresh process, and confirm inference still works. Write down the single change that moved your mAP the most — odds are it was data, not a hyperparameter.
Hint
If mAP stays near zero, suspect the box format or the label-to-class-index mapping before the learning rate. Visualize a few training batches WITH their boxes drawn — if the boxes are wrong on the input, the model never had a chance.
Progress
Progress is local-only — sign in to sync across devices.