Train Your First YOLO26 Detector
Fine-tune a YOLO26 detector on HomeObjects-3K and publish your results.
Introduction
30 Second Summary
You can glance at a room photo and spot the furniture in seconds. A computer needs examples before it can name each object or locate it.
In this project, you will train your first YOLO26n household-object detector in Google Colab using every one of the 2,285 HomeObjects-3K training images. Your public GitHub project will show how the best checkpoint performs across 404 separate validation images.
What You'll Build
Your finished Google Colab notebook shows household objects framed by labeled boxes on images the model never trained on.
By the end of this project, you'll have:
- Run a baseline prediction on an official sample image. Read its bounding boxes, class labels, and confidence scores.
- Completed full-split fine-tuning with every one of the 2,285 training images used in each epoch.
- Measured the best checkpoint across all 404 held-out images. Inspected three annotated predictions for visible strengths and mistakes.
- Secret Mission: Put the pretrained and fine-tuned models side by side on the same images. Explain each difference using evidence from the predictions.
Are there any prerequisites?
You need a Google account and a GitHub account. Google Colab provides the workspace, so you do not need to install Python or use a local GPU.
Before We Start
This first checkpoint locks in your goal before any hands-on work begins. Your goal is to train a 12-class household-object detector on all 2,285 training images per epoch while keeping 404 validation images separate for an unbiased evaluation.
Set Up Your YOLO Notebook
Your detector needs a repeatable workspace that keeps its setup beside every later experiment. A clear setup also makes your final results easier to explain.
In this step, Google Colab provides the hosted notebook. You will pin Ultralytics to 8.4.162 before recording the model, dataset, and compute profile.
In this step, get ready to:
- Create a named Colab notebook with a requested GPU runtime.
- Install the pinned Ultralytics package inside the hosted runtime.
- Configure the full-dataset profile and verify every setup value.
Create the notebook and request compute
Colab stores the notebook in Google Drive. Its runtime is temporary, so the notebook must contain everything needed to rebuild the environment.
A GPU can process model calculations faster than a CPU. Free Colab accelerator availability varies, so the notebook includes a full-data profile for either result.
- Go to Google Colab in your browser.
- Create a blank notebook from the Colab start page.
- Select the current notebook title in the upper-left corner.
- Enter first_yolo26_homeobjects.ipynb as the notebook name.
- Open Runtime from the top menu.
- Select Change Runtime Type.
- Request an available GPU accelerator.
- Apply the runtime setting.
Your compute result determines the profile used later in Cell 2. An attached accelerator uses gpu. A runtime without one uses cpu.
No GPU available?
Free Colab accelerators are not guaranteed. The CPU profile still processes all 2,285 training images during its single epoch.
Need help checking your runtime? Help me identify whether my Colab notebook has an attached GPU.
Install the pinned Ultralytics package
A pinned package version keeps every later notebook cell on the same API. This project uses Ultralytics 8.4.162 throughout.
- Select the first code cell in the notebook.
- Install the pinned package inside the Colab runtime by running this code:
# Cell 1: Install the pinned Ultralytics package
%pip install -q ultralytics==8.4.162
What does this cell do?
The %pip command installs packages into the active notebook runtime. The version pin keeps the project on the verified release.
The -q option reduces installation output. A quiet cell can still be working.
- Confirm Cell 1 changes from a running indicator to a completed checkmark.
Install cell did not complete?
Reconnect the Colab runtime if the session dropped. Run Cell 1 again after the connection returns.
Still stuck? Help me debug the Ultralytics installation in Colab.
Configure the full-dataset profile
Cell 2 records the model identifier, dataset identifier, output folder, image counts, and compute settings. These values become the shared configuration for every later cell.
- Add a second code cell beneath Cell 1 using the notebook toolbar.
- Build the first part of Cell 2 by pasting this code:
# Cell 2: Imports, verified identifiers, and the full-dataset profile
from pathlib import Path
import csv
import json
import shutil
from google.colab import files
from ultralytics import YOLO, __version__
assert __version__ == "8.4.162", f"Expected 8.4.162, found {__version__}"
MODEL_ID = "yolo26n.pt"
DATASET_ID = "HomeObjects-3K.yaml"
BASELINE_SOURCE = "https://ultralytics.com/images/bus.jpg"
BASE = Path.cwd() / "first_yolo26_homeobjects"
BASE.mkdir(parents=True, exist_ok=True)
What does this setup code do?
- The imports provide notebook file handling, structured data helpers, downloads, the YOLO framework, and the installed version value.
- The assertion stops the setup if the runtime does not contain Ultralytics 8.4.162.
- The yolo26n.pt file contains pretrained weights learned from COCO images.
- The HomeObjects-3K.yaml file describes the labeled household-object dataset.
- The BASE path gives later outputs one predictable working directory.
- Run Cell 2 using the play button on its left.
- Confirm Cell 2 finishes without an assertion error.
✔️ The pinned version is verified
Cell 2 completed because the installed version matched 8.4.162. Your notebook can continue with the verified API.
ⓧ I see an older version
The assertion reports the version currently loaded by the runtime. The setup needs the pinned release before continuing.
- Return to Cell 1.
- Run the installation cell again.
- Return to Cell 2.
- Run the setup cell again.
Still seeing the older version?
Ask for help with the visible assertion output before changing any notebook code. Help me load the pinned Ultralytics version in Colab.
ⓧ Ultralytics is unavailable
The import cannot complete until the package installation succeeds in the active runtime.
- Return to Cell 1.
- Reconnect the runtime if Colab shows a disconnected session.
- Run the installation cell again.
- Run Cell 2 after the installation completes.
Import still unavailable?
Share the visible installation output without including private account details. Help me diagnose why Ultralytics is unavailable in this Colab runtime.
- Return to Cell 2 from earlier.
- Place the cursor after the BASE.mkdir(parents=True, exist_ok=True) line.
- Extend Cell 2 with the dataset classes and compute profiles by pasting this code:
TRAIN_IMAGE_COUNT = 2285
VAL_IMAGE_COUNT = 404
FRACTION = 1.0
IMGSZ = 640
TARGET_CLASSES = [
"bed",
"sofa",
"chair",
"table",
"lamp",
"tv",
"laptop",
"wardrobe",
"window",
"door",
"potted plant",
"photo frame",
]
# Use "gpu" when a Colab GPU is attached. Otherwise use "cpu".
PROFILE = "gpu"
PROFILES = {
"gpu": {"epochs": 3, "batch": 16, "device": 0},
"cpu": {"epochs": 1, "batch": 4, "device": "cpu"},
}
if PROFILE not in PROFILES:
raise ValueError('PROFILE must be "gpu" or "cpu"')
How is the experiment configured?
- The image counts preserve the full split of 2,285 training images plus 404 validation images.
- The FRACTION value of 1.0 tells training to use the complete split.
- The TARGET_CLASSES list records the 12 household-object labels the detector will learn.
- The GPU profile uses three full epochs with batches of 16 images.
- The CPU profile uses one full epoch with batches of 4 images.
- Keep PROFILE = "gpu" when Colab has attached an accelerator.
- Replace "gpu" with "cpu" when the runtime has no attached accelerator.
- Run Cell 2 again.
- Confirm Cell 2 finishes without a profile error.
Seeing a profile error?
Check that PROFILE contains exactly "gpu" or "cpu". Lowercase letters and quotation marks matter.
Need another pair of eyes? Help me fix the PROFILE value in my Colab setup cell.
- Append the printable setup card to Cell 2 by pasting this code below the profile check:
cfg = PROFILES[PROFILE]
print("Ultralytics version:", __version__)
print("Model:", MODEL_ID)
print("Dataset:", DATASET_ID)
print("2,285 training images")
print("404 validation images")
print("fraction=1.0")
print("Profile:", PROFILE, cfg)
Why print a setup card?
The printed values make the notebook configuration visible before training begins. They also expose a wrong package version, image count, fraction, or profile early.
Before you run Cell 2, which version, image counts, fraction, and profile do you expect it to print?
- Run Cell 2 using the play button on its left.
You will see 8.4.162, 2,285 training images, 404 validation images, and fraction=1.0. The last line shows your selected profile plus its training configuration.
That verification gives you a repeatable full-dataset setup. Every later result now has a clear starting configuration.
How do the dataset splits work?
- An image is the visual input given to the detector.
- An annotation supplies the correct class plus its bounding box location.
- The training split provides examples that update the model weights.
- The validation split checks the model without updating those weights.
✔️ Awesome, I've got everything!
Your two setup cells are ready. The final output confirms the pinned package plus the full-dataset profile.
- Confirm the notebook title shows first_yolo26_homeobjects.ipynb.
ⓧ I'd like to double check the full code
- Compare your two notebook cells against this complete setup reference:
# Cell 1: Install the pinned Ultralytics package
%pip install -q ultralytics==8.4.162
# Cell 2: Imports, verified identifiers, and the full-dataset profile
from pathlib import Path
import csv
import json
import shutil
from google.colab import files
from ultralytics import YOLO, __version__
assert __version__ == "8.4.162", f"Expected 8.4.162, found {__version__}"
MODEL_ID = "yolo26n.pt"
DATASET_ID = "HomeObjects-3K.yaml"
BASELINE_SOURCE = "https://ultralytics.com/images/bus.jpg"
BASE = Path.cwd() / "first_yolo26_homeobjects"
BASE.mkdir(parents=True, exist_ok=True)
TRAIN_IMAGE_COUNT = 2285
VAL_IMAGE_COUNT = 404
FRACTION = 1.0
IMGSZ = 640
TARGET_CLASSES = [
"bed",
"sofa",
"chair",
"table",
"lamp",
"tv",
"laptop",
"wardrobe",
"window",
"door",
"potted plant",
"photo frame",
]
# Use "gpu" when a Colab GPU is attached. Otherwise use "cpu".
PROFILE = "gpu"
PROFILES = {
"gpu": {"epochs": 3, "batch": 16, "device": 0},
"cpu": {"epochs": 1, "batch": 4, "device": "cpu"},
}
if PROFILE not in PROFILES:
raise ValueError('PROFILE must be "gpu" or "cpu"')
cfg = PROFILES[PROFILE]
print("Ultralytics version:", __version__)
print("Model:", MODEL_ID)
print("Dataset:", DATASET_ID)
print("2,285 training images")
print("404 validation images")
print("fraction=1.0")
print("Profile:", PROFILE, cfg)
This reference combines Cell 1 with the complete cumulative contents of Cell 2. Your selected profile can show cpu when no accelerator is attached.
Your YOLO workspace is ready. Next up, you will use the pretrained model to make your first visible detection.
Make Your First Detection
Your Google Colab notebook now has the pinned environment and full-dataset profile from the previous step. It is ready to load a model and process an image.
A visible prediction makes YOLO concrete. The result also lets you test whether the model's existing vocabulary covers the HomeObjects-3K classes.
In this step, get ready to:
- Run the pretrained yolo26n.pt model on the bus image.
- Interpret the visual prediction.
- Compare the pretrained vocabulary with the HomeObjects-3K classes.
Run the baseline prediction
Pretrained weights contain patterns the model learned before this project. Inference uses those fixed weights to predict what appears in a new image.
- Add a new code cell directly below Cell 2 in the notebook from earlier.
- Paste the complete baseline cell below into the new cell:
# Cell 3: Make a baseline prediction before training
baseline_model = YOLO(MODEL_ID)
baseline_results = baseline_model.predict(
source=BASELINE_SOURCE,
save=True,
imgsz=IMGSZ,
device=cfg["device"],
project=str(BASE),
name="baseline",
exist_ok=True,
)
baseline_results[0].show()
pretrained_names = set(baseline_model.names.values())
missing_target_names = sorted(set(TARGET_CLASSES) - pretrained_names)
print("A box locates an object in the image.")
print("A class is the predicted object name.")
print("Confidence describes how strongly the model supports a prediction.")
print("Target names absent from the pretrained vocabulary:", missing_target_names)
print("Conclusion: inference used fixed weights and did not train on HomeObjects-3K.")
What does this code do?
- The YOLO(MODEL_ID) call loads the pretrained checkpoint defined during setup.
- The predict() call processes the bus image with your selected image size and device.
- The save=True setting writes an annotated copy to the baseline output directory.
- The baseline_results[0].show() call displays the first annotated result.
- The set comparison finds target class names that are absent from the pretrained vocabulary.
Before you run Cell 3, do you think a clear bus-image prediction proves that the model already knows every HomeObjects-3K class?
- Run Cell 3 using the play control on its left.
There it is. Your notebook has produced its first object-detection result.
You will see an annotated bus image with prediction boxes. The cell also prints a comparison between the pretrained vocabulary and your target classes.
Prediction did not appear?
- Run Cell 2 again if the notebook says that a required name is undefined.
- Check that PROFILE matches the accelerator currently attached to the notebook.
- Run Cell 3 again if the model or image download was interrupted.
Help me diagnose my baseline prediction.
Read the annotated image
A bounding box is the rectangle that locates an object. The nearby label names the predicted class.
A confidence score shows how strongly the model supports that prediction. Higher confidence means the current weights provide stronger support for the label.
- Inspect one rectangle drawn around an object in the displayed image.
- Read the class label attached to that rectangle.
- Read the confidence score shown beside that prediction.
- Use the notebook's file browser to expand first_yolo26_homeobjects.
- Confirm the baseline folder contains the saved annotated image.
The screen result proves that the model can turn image pixels into locations and object names. The saved copy gives you visual evidence to use later.
Compare the class vocabularies
A model vocabulary is the set of class names its current weights can predict. The pretrained model uses the COCO vocabulary while this project targets 12 HomeObjects-3K classes.
- Find the output line beginning with Target names absent from the pretrained vocabulary:.
- Inspect the class names inside missing_target_names.
- Find the final line stating that inference used fixed weights.
The non-empty list is the intended shortfall. A successful COCO prediction does not mean the model has learned the HomeObjects-3K task.
Why did no learning happen?
Prediction reads the existing weights without changing them. Training is the process that updates those weights from labeled images.
The bus image proves that the pretrained detector can perform inference. The missing target names prove that a visible prediction alone does not adapt its vocabulary.
Use the complete notebook reference below if your cell output differs from the expected result.
✔️ Awesome, I've got everything!
Your notebook contains setup Cells 1 and 2 followed by the completed baseline Cell 3.
ⓧ I'd like to double check the full code
# Cell 1: Install the pinned Ultralytics package
%pip install -q ultralytics==8.4.162
# Cell 2: Imports, verified identifiers, and the full-dataset profile
from pathlib import Path
import csv
import json
import shutil
from google.colab import files
from ultralytics import YOLO, __version__
assert __version__ == "8.4.162", f"Expected 8.4.162, found {__version__}"
MODEL_ID = "yolo26n.pt"
DATASET_ID = "HomeObjects-3K.yaml"
BASELINE_SOURCE = "https://ultralytics.com/images/bus.jpg"
BASE = Path.cwd() / "first_yolo26_homeobjects"
BASE.mkdir(parents=True, exist_ok=True)
TRAIN_IMAGE_COUNT = 2285
VAL_IMAGE_COUNT = 404
FRACTION = 1.0
IMGSZ = 640
TARGET_CLASSES = [
"bed",
"sofa",
"chair",
"table",
"lamp",
"tv",
"laptop",
"wardrobe",
"window",
"door",
"potted plant",
"photo frame",
]
# Use "gpu" when a Colab GPU is attached. Otherwise use "cpu".
PROFILE = "gpu"
PROFILES = {
"gpu": {"epochs": 3, "batch": 16, "device": 0},
"cpu": {"epochs": 1, "batch": 4, "device": "cpu"},
}
if PROFILE not in PROFILES:
raise ValueError('PROFILE must be "gpu" or "cpu"')
cfg = PROFILES[PROFILE]
print("Ultralytics version:", __version__)
print("Model:", MODEL_ID)
print("Dataset:", DATASET_ID)
print("2,285 training images")
print("404 validation images")
print("fraction=1.0")
print("Profile:", PROFILE, cfg)
# Cell 3: Make a baseline prediction before training
baseline_model = YOLO(MODEL_ID)
baseline_results = baseline_model.predict(
source=BASELINE_SOURCE,
save=True,
imgsz=IMGSZ,
device=cfg["device"],
project=str(BASE),
name="baseline",
exist_ok=True,
)
baseline_results[0].show()
pretrained_names = set(baseline_model.names.values())
missing_target_names = sorted(set(TARGET_CLASSES) - pretrained_names)
print("A box locates an object in the image.")
print("A class is the predicted object name.")
print("Confidence describes how strongly the model supports a prediction.")
print("Target names absent from the pretrained vocabulary:", missing_target_names)
print("Conclusion: inference used fixed weights and did not train on HomeObjects-3K.")
How to use this reference
Compare each notebook cell with this reference in order. Keep Cell 3 directly below the setup cells.
Before the final check, do you expect Cell 3 to show both a successful visual prediction and evidence of missing target classes?
- Rerun Cell 3 using the play control on its left.
Cell 3 displays the annotated bus image. Below it, the notebook prints a non-empty list of target names absent from the pretrained vocabulary.
The final line confirms that inference used fixed weights without training on HomeObjects-3K. Your baseline now demonstrates both what the pretrained detector can do and where it falls short.
Your baseline is saved and understood. Next, you will use every labeled training image to update the model's weights.
Train on the Full Dataset
Your pretrained YOLO model can already locate familiar objects. Its fixed weights still do not understand the 12 HomeObjects-3K classes as a new detection task.
Fine-tuning uses the labeled training images to update those weights. This step processes all 2,285 training images during every epoch.
In this step, get ready to:
- Learn how batches, epochs, and loss shape a training run.
- Fine-tune the pretrained weights on the complete HomeObjects-3K training split.
- Verify the saved checkpoints and exact dataset counts.
Learn the full-split training loop
Transfer learning starts with useful pretrained weights. Training then adjusts those weights for the household-object classes in HomeObjects-3K.
How does the training loop work?
A batch is a small group of images processed together. Your selected profile controls how many images fit into each batch.
An epoch is one complete pass over all 2,285 training images. The GPU profile completes three epochs. The CPU profile completes one epoch.
Loss measures the model's training error. The optimizer uses that signal to adjust the weights.
Train with the complete split
The training configuration uses fraction=1.0. This value keeps the complete training split in every epoch.
- Add a new code cell beneath the baseline cell in your Google Colab notebook.
- Build the full-split training run by pasting this code into the new cell:
# Cell 4: Fine-tune on the complete training split
print("Training fraction:", FRACTION, "which means the full split")
print("Epochs:", cfg["epochs"])
print("Each epoch is one complete pass over all 2,285 training images.")
train_model = YOLO(MODEL_ID)
train_metrics = train_model.train(
data=DATASET_ID,
epochs=cfg["epochs"],
imgsz=IMGSZ,
fraction=FRACTION,
batch=cfg["batch"],
device=cfg["device"],
pretrained=True,
seed=0,
verbose=True,
plots=True,
save=True,
val=True,
project=str(BASE),
name="train_full",
exist_ok=True,
)
What does this code do?
- The first three messages print the training fraction, epoch count, and meaning of one epoch before the run begins.
- The train_model variable loads the same pretrained weights used for the baseline.
- The fraction=FRACTION setting passes the configured value of 1.0 into training.
- The cfg values select the epoch count, batch size, and device for the active profile.
- The remaining settings enable validation, plots, saved weights, reproducible seeding, and detailed logs.
This is the long-running part of the project. Colab hardware varies, so the training time can vary.
Before you run the cell, what should fraction=1.0 mean for the number of training images used in each epoch?
- Run the training cell using its play button.
- Keep the notebook tab open while the epoch table updates.
You will first see the full training fraction and selected epoch count. The first run also downloads the HomeObjects-3K dataset before the epoch table starts.
Watch box_loss for box-placement error. Watch cls_loss for class-prediction error.
How should you read the epoch table?
Each completed row represents one full pass over the training split. The validation columns summarize performance on held-out images without using those images to update the weights.
A short run gives you a complete learning workflow. Its small epoch count does not establish production readiness.
Training stopped before an epoch completed?
Confirm that the profile matches the accelerator attached to your runtime. Use the CPU profile when no GPU is attached.
If the runtime disconnected during the dataset download, reconnect it before running the training cell again.
Need help diagnosing the training output? Help me understand why my HomeObjects-3K training cell stopped before completing an epoch.
That is the main compute finished. Your selected profile has now processed every training image for at least one complete epoch.
Check the saved run files
A checkpoint is a saved state of the learned weights. The training run creates a best checkpoint and a final checkpoint.
- Add another code cell beneath the training cell.
- Define the artifact paths by pasting this code into the new cell:
RUN = BASE / "train_full"
BEST = RUN / "weights" / "best.pt"
LAST = RUN / "weights" / "last.pt"
RESULTS_CSV = RUN / "results.csv"
assert BEST.exists(), f"Missing checkpoint: {BEST}"
assert LAST.exists(), f"Missing checkpoint: {LAST}"
assert RESULTS_CSV.exists(), f"Missing metrics file: {RESULTS_CSV}"
roots = [
path
for path in Path.cwd().rglob("homeobjects-3K")
if (path / "images" / "train").is_dir()
and (path / "images" / "val").is_dir()
]
if not roots:
raise FileNotFoundError("Could not locate the downloaded HomeObjects-3K dataset.")
DATASET_ROOT = roots[0]
What does this code check?
- The RUN path points to the saved train_full run directory.
- The three assertions confirm that best.pt, last.pt, and results.csv exist.
- The recursive search locates the downloaded dataset directory containing both image splits.
- The DATASET_ROOT variable stores the located dataset path for the final count.
- Run the artifact-check cell.
- Expand the first_yolo26_homeobjects/train_full directory in the Colab file browser.
- Confirm that the weights directory contains best.pt.
- Confirm that the weights directory contains last.pt.
- Confirm that results.csv appears inside train_full.
The cell should finish without an assertion error. That successful check proves the expected checkpoints and metrics file were saved.
Missing a training artifact?
Return to the epoch table and confirm that at least one epoch completed. Checkpoints are created after a completed epoch.
If the files exist under a different run directory, confirm that the training code still uses name="train_full".
Still missing a file? Help me find the missing YOLO training artifact in my Colab run directory.
Confirm every image was counted
File counts provide a direct check that this experiment used the intended dataset version. The training and validation directories must match the pinned HomeObjects-3K split.
- Add one more code cell beneath the artifact-check cell.
- Count the supported image files by pasting this code into the new cell:
IMAGE_SUFFIXES = {".jpg", ".jpeg", ".png", ".bmp", ".webp"}
train_images = [
path
for path in (DATASET_ROOT / "images" / "train").iterdir()
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
]
val_images = [
path
for path in (DATASET_ROOT / "images" / "val").iterdir()
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
]
assert len(train_images) == TRAIN_IMAGE_COUNT, len(train_images)
assert len(val_images) == VAL_IMAGE_COUNT, len(val_images)
print("Training images found:", len(train_images))
print("Validation images found:", len(val_images))
print("Best checkpoint:", BEST)
print("Training metrics:", RESULTS_CSV)
What does this code verify?
- The IMAGE_SUFFIXES set defines which files count as images.
- The train_images list gathers files from the training image directory.
- The val_images list gathers files from the validation image directory.
- The assertions stop the check if either split differs from the expected count.
- The final messages print both counts and the two key artifact paths.
Before you run the final check, what totals should appear if the complete pinned dataset is present?
- Run the image-count cell.
You should see 2,285 training images and 404 validation images. You should also see paths ending in weights/best.pt and results.csv.
Strong work. Your full-split run now has verified data counts, saved metrics, and two reusable checkpoints.
Seeing a different image count?
Confirm that DATASET_ROOT points to the directory containing the HomeObjects-3K training and validation image folders.
If an assertion reports a smaller number, rerun the training cell so the automatic dataset download can finish.
Need help checking the split? Help me diagnose why my HomeObjects-3K image counts do not match the expected training and validation totals.
✔️ Awesome, I've got everything!
Your notebook now contains the pinned setup, pretrained baseline, full-split training run, artifact checks, and exact image counts.
ⓧ I'd like to double check the full code
# Cell 1: Install the pinned Ultralytics package
%pip install -q ultralytics==8.4.162
# Cell 2: Imports, verified identifiers, and the full-dataset profile
from pathlib import Path
import csv
import json
import shutil
from google.colab import files
from ultralytics import YOLO, __version__
assert __version__ == "8.4.162", f"Expected 8.4.162, found {__version__}"
MODEL_ID = "yolo26n.pt"
DATASET_ID = "HomeObjects-3K.yaml"
BASELINE_SOURCE = "https://ultralytics.com/images/bus.jpg"
BASE = Path.cwd() / "first_yolo26_homeobjects"
BASE.mkdir(parents=True, exist_ok=True)
TRAIN_IMAGE_COUNT = 2285
VAL_IMAGE_COUNT = 404
FRACTION = 1.0
IMGSZ = 640
TARGET_CLASSES = [
"bed",
"sofa",
"chair",
"table",
"lamp",
"tv",
"laptop",
"wardrobe",
"window",
"door",
"potted plant",
"photo frame",
]
# Use "gpu" when a Colab GPU is attached. Otherwise use "cpu".
PROFILE = "gpu"
PROFILES = {
"gpu": {"epochs": 3, "batch": 16, "device": 0},
"cpu": {"epochs": 1, "batch": 4, "device": "cpu"},
}
if PROFILE not in PROFILES:
raise ValueError('PROFILE must be "gpu" or "cpu"')
cfg = PROFILES[PROFILE]
print("Ultralytics version:", __version__)
print("Model:", MODEL_ID)
print("Dataset:", DATASET_ID)
print("2,285 training images")
print("404 validation images")
print("fraction=1.0")
print("Profile:", PROFILE, cfg)
# Cell 3: Make a baseline prediction before training
baseline_model = YOLO(MODEL_ID)
baseline_results = baseline_model.predict(
source=BASELINE_SOURCE,
save=True,
imgsz=IMGSZ,
device=cfg["device"],
project=str(BASE),
name="baseline",
exist_ok=True,
)
baseline_results[0].show()
pretrained_names = set(baseline_model.names.values())
missing_target_names = sorted(set(TARGET_CLASSES) - pretrained_names)
print("A box locates an object in the image.")
print("A class is the predicted object name.")
print("Confidence describes how strongly the model supports a prediction.")
print("Target names absent from the pretrained vocabulary:", missing_target_names)
print("Conclusion: inference used fixed weights and did not train on HomeObjects-3K.")
# Cell 4: Fine-tune on the complete training split
print("Training fraction:", FRACTION, "which means the full split")
print("Epochs:", cfg["epochs"])
print("Each epoch is one complete pass over all 2,285 training images.")
train_model = YOLO(MODEL_ID)
train_metrics = train_model.train(
data=DATASET_ID,
epochs=cfg["epochs"],
imgsz=IMGSZ,
fraction=FRACTION,
batch=cfg["batch"],
device=cfg["device"],
pretrained=True,
seed=0,
verbose=True,
plots=True,
save=True,
val=True,
project=str(BASE),
name="train_full",
exist_ok=True,
)
RUN = BASE / "train_full"
BEST = RUN / "weights" / "best.pt"
LAST = RUN / "weights" / "last.pt"
RESULTS_CSV = RUN / "results.csv"
assert BEST.exists(), f"Missing checkpoint: {BEST}"
assert LAST.exists(), f"Missing checkpoint: {LAST}"
assert RESULTS_CSV.exists(), f"Missing metrics file: {RESULTS_CSV}"
roots = [
path
for path in Path.cwd().rglob("homeobjects-3K")
if (path / "images" / "train").is_dir()
and (path / "images" / "val").is_dir()
]
if not roots:
raise FileNotFoundError("Could not locate the downloaded HomeObjects-3K dataset.")
DATASET_ROOT = roots[0]
IMAGE_SUFFIXES = {".jpg", ".jpeg", ".png", ".bmp", ".webp"}
train_images = [
path
for path in (DATASET_ROOT / "images" / "train").iterdir()
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
]
val_images = [
path
for path in (DATASET_ROOT / "images" / "val").iterdir()
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
]
assert len(train_images) == TRAIN_IMAGE_COUNT, len(train_images)
assert len(val_images) == VAL_IMAGE_COUNT, len(val_images)
print("Training images found:", len(train_images))
print("Validation images found:", len(val_images))
print("Best checkpoint:", BEST)
print("Training metrics:", RESULTS_CSV)
Your fine-tuned weights are saved and your full-dataset claim is backed by exact counts. Next, you will evaluate the best checkpoint on all 404 held-out images.
Evaluate Your Best Checkpoint
Your full-split training run has adapted YOLO to the HomeObjects-3K classes. It saved the strongest checkpoint as best.pt.
Lower training loss only shows how the model responded to training. Held-out validation measures how those learned weights perform on images they never trained on.
In this step, get ready to:
- Validate best.pt across all 404 held-out validation images.
- Interpret mAP50 and mAP50-95 as detection metrics.
- Inspect three annotated predictions before downloading best.pt for safekeeping.
Validate the best checkpoint
A checkpoint stores the model weights from a particular point in training. Reloading best.pt lets you evaluate the selected weights without changing them.
How do the metrics differ?
Mean Average Precision measures detection quality across the dataset. mAP50 uses a more forgiving box-overlap threshold.
mAP50-95 averages performance across several stricter overlap thresholds. Neither metric represents the percentage of images that are simply correct.
- Evaluate the saved checkpoint by running Cell 5 below.
Before you run this cell, do you expect the two metric values to be identical?
# Cell 5: Validate the best checkpoint on all held-out images
shutil.rmtree(BASE / "validation", ignore_errors=True)
trained_model = YOLO(str(BEST))
metrics = trained_model.val(
data=DATASET_ID,
imgsz=IMGSZ,
batch=cfg["batch"],
device=cfg["device"],
plots=True,
project=str(BASE),
name="validation",
)
map50 = float(metrics.box.map50)
map50_95 = float(metrics.box.map)
print("Validation images:", len(val_images))
print(f"mAP50: {map50:.4f}")
print(f"mAP50-95: {map50_95:.4f}")
print("mAP50 is the more forgiving box-overlap summary.")
print("mAP50-95 averages performance across stricter overlap thresholds.")
What does this code do?
- The shutil.rmtree() call clears the previous validation output so the new plots describe this run.
- The trained_model variable reloads the learned weights from BEST.
- The trained_model.val() call evaluates those fixed weights with the existing image size and compute profile.
- The map50 variable stores metrics.box.map50.
- The map50_95 variable stores the stricter metrics.box.map summary.
Validation can take several minutes or longer because the model checks all 404 held-out images. A quiet cell during that work does not mean the runtime has stopped.
- Wait for Cell 5 to finish.
- Confirm the output reports 404 validation images.
- Confirm the output prints one value for mAP50.
- Confirm the output prints one value for mAP50-95.
- Confirm the notebook creates validation plots under first_yolo26_homeobjects/validation.
You have now measured the selected checkpoint across every held-out validation image. That gives you evidence beyond the training loss.
Validation cell not finishing?
- Confirm Cell 4 completed before you run Cell 5.
- Check that BEST still points to weights/best.pt.
- Rerun the earlier notebook cells if the temporary runtime restarted.
Need help finding the cause? Help me debug my checkpoint validation cell.
Inspect held-out predictions
Dataset-wide metrics summarize performance with numbers. Visual inspection shows where the detector places bounding boxes and which class labels it chooses.
The same images can also reveal missed objects and false positives. A false positive is a prediction for an object that should not have been detected there.
- Generate three held-out predictions by running Cell 6 below.
Before you run this cell, do you expect every visible household object to receive a correct box?
# Cell 6: Inspect three held-out predictions and save the checkpoint
sample_images = [str(path) for path in sorted(val_images)[:3]]
if len(sample_images) < 3:
raise RuntimeError("Expected at least three validation images.")
shutil.rmtree(BASE / "fine_tuned_predictions", ignore_errors=True)
fine_tuned_results = trained_model.predict(
source=sample_images,
save=True,
imgsz=IMGSZ,
device=cfg["device"],
project=str(BASE),
name="fine_tuned_predictions",
)
for result in fine_tuned_results:
result.show()
print("Inspect each image for correct boxes, labels, confidence, misses, and false positives.")
files.download(str(BEST))
What does this code do?
- The sample_images list holds the first three sorted paths from val_images.
- The trained_model.predict() call applies the fine-tuned checkpoint to those three held-out images.
- The prediction settings reuse IMGSZ and the selected device from cfg.
- The result.show() loop displays each annotated prediction for inspection.
- The files.download() call saves a local copy of best.pt before the temporary runtime disappears.
- Wait for all three annotated predictions to display.
- Inspect each image for box placement.
- Inspect each image for class labels.
- Inspect each image for confidence scores.
- Look for any missed objects.
- Look for any false positives.
- Confirm your browser downloads best.pt.
You should see three annotated validation images from fine_tuned_results. You should also have a local copy of best.pt.
That completes the safety check. Your trained weights now exist outside the temporary notebook runtime.
Predictions or download missing?
- Confirm val_images still contains the 404 paths found after training.
- Confirm Cell 5 created trained_model before you run Cell 6.
- Allow the browser download if your browser blocks the file automatically.
Still stuck? Help me debug my held-out predictions and checkpoint download.
✔️ Awesome, I've got everything!
Your notebook now contains Cells 1 through 6. Confirm the latest notebook changes are saved.
ⓧ I'd like to double check the full code
# Cell 1: Install the pinned Ultralytics package
%pip install -q ultralytics==8.4.162
# Cell 2: Imports, verified identifiers, and the full-dataset profile
from pathlib import Path
import csv
import json
import shutil
from google.colab import files
from ultralytics import YOLO, __version__
assert __version__ == "8.4.162", f"Expected 8.4.162, found {__version__}"
MODEL_ID = "yolo26n.pt"
DATASET_ID = "HomeObjects-3K.yaml"
BASELINE_SOURCE = "https://ultralytics.com/images/bus.jpg"
BASE = Path.cwd() / "first_yolo26_homeobjects"
BASE.mkdir(parents=True, exist_ok=True)
TRAIN_IMAGE_COUNT = 2285
VAL_IMAGE_COUNT = 404
FRACTION = 1.0
IMGSZ = 640
TARGET_CLASSES = [
"bed",
"sofa",
"chair",
"table",
"lamp",
"tv",
"laptop",
"wardrobe",
"window",
"door",
"potted plant",
"photo frame",
]
# Use "gpu" when a Colab GPU is attached. Otherwise use "cpu".
PROFILE = "gpu"
PROFILES = {
"gpu": {"epochs": 3, "batch": 16, "device": 0},
"cpu": {"epochs": 1, "batch": 4, "device": "cpu"},
}
if PROFILE not in PROFILES:
raise ValueError('PROFILE must be "gpu" or "cpu"')
cfg = PROFILES[PROFILE]
print("Ultralytics version:", __version__)
print("Model:", MODEL_ID)
print("Dataset:", DATASET_ID)
print("2,285 training images")
print("404 validation images")
print("fraction=1.0")
print("Profile:", PROFILE, cfg)
# Cell 3: Make a baseline prediction before training
baseline_model = YOLO(MODEL_ID)
baseline_results = baseline_model.predict(
source=BASELINE_SOURCE,
save=True,
imgsz=IMGSZ,
device=cfg["device"],
project=str(BASE),
name="baseline",
exist_ok=True,
)
baseline_results[0].show()
pretrained_names = set(baseline_model.names.values())
missing_target_names = sorted(set(TARGET_CLASSES) - pretrained_names)
print("A box locates an object in the image.")
print("A class is the predicted object name.")
print("Confidence describes how strongly the model supports a prediction.")
print("Target names absent from the pretrained vocabulary:", missing_target_names)
print("Conclusion: inference used fixed weights and did not train on HomeObjects-3K.")
# Cell 4: Fine-tune on the complete training split
print("Training fraction:", FRACTION, "which means the full split")
print("Epochs:", cfg["epochs"])
print("Each epoch is one complete pass over all 2,285 training images.")
train_model = YOLO(MODEL_ID)
train_metrics = train_model.train(
data=DATASET_ID,
epochs=cfg["epochs"],
imgsz=IMGSZ,
fraction=FRACTION,
batch=cfg["batch"],
device=cfg["device"],
pretrained=True,
seed=0,
verbose=True,
plots=True,
save=True,
val=True,
project=str(BASE),
name="train_full",
exist_ok=True,
)
RUN = BASE / "train_full"
BEST = RUN / "weights" / "best.pt"
LAST = RUN / "weights" / "last.pt"
RESULTS_CSV = RUN / "results.csv"
assert BEST.exists(), f"Missing checkpoint: {BEST}"
assert LAST.exists(), f"Missing checkpoint: {LAST}"
assert RESULTS_CSV.exists(), f"Missing metrics file: {RESULTS_CSV}"
roots = [
path
for path in Path.cwd().rglob("homeobjects-3K")
if (path / "images" / "train").is_dir()
and (path / "images" / "val").is_dir()
]
if not roots:
raise FileNotFoundError("Could not locate the downloaded HomeObjects-3K dataset.")
DATASET_ROOT = roots[0]
IMAGE_SUFFIXES = {".jpg", ".jpeg", ".png", ".bmp", ".webp"}
train_images = [
path
for path in (DATASET_ROOT / "images" / "train").iterdir()
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
]
val_images = [
path
for path in (DATASET_ROOT / "images" / "val").iterdir()
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
]
assert len(train_images) == TRAIN_IMAGE_COUNT, len(train_images)
assert len(val_images) == VAL_IMAGE_COUNT, len(val_images)
print("Training images found:", len(train_images))
print("Validation images found:", len(val_images))
print("Best checkpoint:", BEST)
print("Training metrics:", RESULTS_CSV)
# Cell 5: Validate the best checkpoint on all held-out images
shutil.rmtree(BASE / "validation", ignore_errors=True)
trained_model = YOLO(str(BEST))
metrics = trained_model.val(
data=DATASET_ID,
imgsz=IMGSZ,
batch=cfg["batch"],
device=cfg["device"],
plots=True,
project=str(BASE),
name="validation",
)
map50 = float(metrics.box.map50)
map50_95 = float(metrics.box.map)
print("Validation images:", len(val_images))
print(f"mAP50: {map50:.4f}")
print(f"mAP50-95: {map50_95:.4f}")
print("mAP50 is the more forgiving box-overlap summary.")
print("mAP50-95 averages performance across stricter overlap thresholds.")
# Cell 6: Inspect three held-out predictions and save the checkpoint
sample_images = [str(path) for path in sorted(val_images)[:3]]
if len(sample_images) < 3:
raise RuntimeError("Expected at least three validation images.")
shutil.rmtree(BASE / "fine_tuned_predictions", ignore_errors=True)
fine_tuned_results = trained_model.predict(
source=sample_images,
save=True,
imgsz=IMGSZ,
device=cfg["device"],
project=str(BASE),
name="fine_tuned_predictions",
)
for result in fine_tuned_results:
result.show()
print("Inspect each image for correct boxes, labels, confidence, misses, and false positives.")
files.download(str(BEST))
Your best checkpoint is measured and safely downloaded. Next, you will package the configuration, metrics, and visual evidence into a reproducible portfolio project.
Publish Your Portfolio Project
Your best checkpoint has now been evaluated against all 404 held-out images.
A model result is difficult to trust without its code, configuration, metrics, limitations, and visual evidence.
You will package that evidence in Google Colab. You will publish the completed experiment in a public GitHub repository.
In this step, get ready to:
- Package the experiment configuration, training metrics, written explanation, and selected result images.
- Download the portfolio bundle and completed notebook.
- Publish the reproducible experiment in a public repository.
Package the experiment evidence
The portfolio package gathers the details another person needs to understand your run. It also records the short training duration as a limitation.
Cell 7 ends by downloading the finished bundle. Your browser may ask for permission before that download begins.
- Return to the Google Colab notebook from earlier.
- Add a new code cell directly below Cell 6.
- Create the portfolio package by copying this complete Cell 7 into the new code cell:
# Cell 7: Package the reproducible portfolio evidence
with RESULTS_CSV.open(newline="", encoding="utf-8") as handle:
rows = list(csv.DictReader(handle))
PORTFOLIO = BASE / "portfolio"
shutil.rmtree(PORTFOLIO, ignore_errors=True)
PORTFOLIO.mkdir(parents=True)
experiment_config = {
"ultralytics_version": __version__,
"model": MODEL_ID,
"dataset": DATASET_ID,
"dataset_total_images": TRAIN_IMAGE_COUNT + VAL_IMAGE_COUNT,
"dataset_train_images": TRAIN_IMAGE_COUNT,
"dataset_validation_images": VAL_IMAGE_COUNT,
"dataset_classes": TARGET_CLASSES,
"training_fraction": FRACTION,
"profile": PROFILE,
"epochs": cfg["epochs"],
"batch": cfg["batch"],
"image_size": IMGSZ,
"device": cfg["device"],
"validation": {
"mAP50": round(map50, 6),
"mAP50-95": round(map50_95, 6),
},
}
(PORTFOLIO / "experiment_config.json").write_text(
json.dumps(experiment_config, indent=2),
encoding="utf-8",
)
shutil.copy2(RESULTS_CSV, PORTFOLIO / "training_results.csv")
for directory in [BASE / "baseline", RUN, BASE / "validation", BASE / "fine_tuned_predictions"]:
if not directory.exists():
continue
images = sorted(
path
for path in directory.iterdir()
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
)
for source in images[:3]:
shutil.copy2(source, PORTFOLIO / f"{directory.name}_{source.name}")
readme = f"""# My First YOLO26 HomeObjects Detector
## What I built
I fine-tuned `{MODEL_ID}` on the complete HomeObjects-3K training split and evaluated the saved checkpoint on the held-out validation split.
## Dataset
- {TRAIN_IMAGE_COUNT + VAL_IMAGE_COUNT} total images
- {TRAIN_IMAGE_COUNT} training images used in every epoch
- {VAL_IMAGE_COUNT} held-out validation images
- {len(TARGET_CLASSES)} household-object classes
## Beginner glossary
- Bounding box: the rectangle locating an object.
- Class: the predicted object category.
- Confidence: how strongly the model supports a prediction.
- Epoch: one full pass over the training split.
- Loss: the training error signal the optimizer tries to reduce.
- Checkpoint: a saved copy of learned model weights.
## Configuration
- Ultralytics: `{__version__}`
- Model: `{MODEL_ID}`
- Dataset: `{DATASET_ID}`
- Training fraction: `{FRACTION}`
- Profile: `{PROFILE}`
- Epochs: `{cfg['epochs']}`
- Batch size: `{cfg['batch']}`
- Image size: `{IMGSZ}`
## Validation results
- mAP50: `{map50:.4f}`
- mAP50-95: `{map50_95:.4f}`
## Limits
This is a short beginner fine-tuning run. It uses the full training split, but its small epoch count does not establish production accuracy, robustness, fairness, or deployment readiness. The checkpoint is kept as a separate local download rather than required in this repository.
## Reproduce the experiment
Open `first_yolo26_homeobjects.ipynb` in Google Colab and run its cells in order.
"""
(PORTFOLIO / "README.md").write_text(readme, encoding="utf-8")
bundle_path = shutil.make_archive(
str(BASE / "github_portfolio_bundle"),
"zip",
root_dir=PORTFOLIO,
)
print("Portfolio files:")
for path in sorted(PORTFOLIO.iterdir()):
print(path.name)
print("Bundle:", bundle_path)
files.download(bundle_path)
What does this code do?
- The rows list reads the training metrics from the existing CSV file.
- The experiment_config dictionary records the exact model, dataset, profile, image counts, training settings, and validation metrics.
- The portfolio folder receives an exact JSON configuration plus a copy of training_results.csv.
- The image loop copies selected evidence from the baseline, training, validation, and fine-tuned prediction folders.
- The README.md file explains the experiment in beginner-friendly language. Its limits section prevents a short learning run from being presented as a production model.
- The final lines create github_portfolio_bundle.zip as a ZIP archive for publishing.
- Run Cell 7.
- Allow the bundle download if your browser asks for permission.
- Use Colab's file browser to confirm that portfolio now contains README.md, experiment_config.json, training_results.csv, and selected result images.
- Confirm that github_portfolio_bundle.zip appears under first_yolo26_homeobjects.
The output lists each portfolio file plus the bundle path. That packaging cell has turned your training run into evidence another person can inspect.
Bundle missing or cell stopped?
Make sure Cells 1 through 6 have finished in the current runtime. Cell 7 depends on values such as RESULTS_CSV, map50, map50_95, and sample_images.
If an expected image folder is absent, confirm that the matching prediction or validation cell completed. The loop skips folders that do not exist.
If the browser blocks the download, allow downloads for the Colab page before running Cell 7 again.
Need help tracing the failed cell?
✔️ Cell 7 is complete
Great. Double-check that the notebook is saved with all seven cells before downloading it.
ⓧ I'd like to double check the full code
# Cell 1: Install the pinned Ultralytics package
%pip install -q ultralytics==8.4.162
# Cell 2: Imports, verified identifiers, and the full-dataset profile
from pathlib import Path
import csv
import json
import shutil
from google.colab import files
from ultralytics import YOLO, __version__
assert __version__ == "8.4.162", f"Expected 8.4.162, found {__version__}"
MODEL_ID = "yolo26n.pt"
DATASET_ID = "HomeObjects-3K.yaml"
BASELINE_SOURCE = "https://ultralytics.com/images/bus.jpg"
BASE = Path.cwd() / "first_yolo26_homeobjects"
BASE.mkdir(parents=True, exist_ok=True)
TRAIN_IMAGE_COUNT = 2285
VAL_IMAGE_COUNT = 404
FRACTION = 1.0
IMGSZ = 640
TARGET_CLASSES = [
"bed",
"sofa",
"chair",
"table",
"lamp",
"tv",
"laptop",
"wardrobe",
"window",
"door",
"potted plant",
"photo frame",
]
# Use "gpu" when a Colab GPU is attached. Otherwise use "cpu".
PROFILE = "gpu"
PROFILES = {
"gpu": {"epochs": 3, "batch": 16, "device": 0},
"cpu": {"epochs": 1, "batch": 4, "device": "cpu"},
}
if PROFILE not in PROFILES:
raise ValueError('PROFILE must be "gpu" or "cpu"')
cfg = PROFILES[PROFILE]
print("Ultralytics version:", __version__)
print("Model:", MODEL_ID)
print("Dataset:", DATASET_ID)
print("2,285 training images")
print("404 validation images")
print("fraction=1.0")
print("Profile:", PROFILE, cfg)
# Cell 3: Make a baseline prediction before training
baseline_model = YOLO(MODEL_ID)
baseline_results = baseline_model.predict(
source=BASELINE_SOURCE,
save=True,
imgsz=IMGSZ,
device=cfg["device"],
project=str(BASE),
name="baseline",
exist_ok=True,
)
baseline_results[0].show()
pretrained_names = set(baseline_model.names.values())
missing_target_names = sorted(set(TARGET_CLASSES) - pretrained_names)
print("A box locates an object in the image.")
print("A class is the predicted object name.")
print("Confidence describes how strongly the model supports a prediction.")
print("Target names absent from the pretrained vocabulary:", missing_target_names)
print("Conclusion: inference used fixed weights and did not train on HomeObjects-3K.")
# Cell 4: Fine-tune on the complete training split
print("Training fraction:", FRACTION, "which means the full split")
print("Epochs:", cfg["epochs"])
print("Each epoch is one complete pass over all 2,285 training images.")
train_model = YOLO(MODEL_ID)
train_metrics = train_model.train(
data=DATASET_ID,
epochs=cfg["epochs"],
imgsz=IMGSZ,
fraction=FRACTION,
batch=cfg["batch"],
device=cfg["device"],
pretrained=True,
seed=0,
verbose=True,
plots=True,
save=True,
val=True,
project=str(BASE),
name="train_full",
exist_ok=True,
)
RUN = BASE / "train_full"
BEST = RUN / "weights" / "best.pt"
LAST = RUN / "weights" / "last.pt"
RESULTS_CSV = RUN / "results.csv"
assert BEST.exists(), f"Missing checkpoint: {BEST}"
assert LAST.exists(), f"Missing checkpoint: {LAST}"
assert RESULTS_CSV.exists(), f"Missing metrics file: {RESULTS_CSV}"
roots = [
path
for path in Path.cwd().rglob("homeobjects-3K")
if (path / "images" / "train").is_dir()
and (path / "images" / "val").is_dir()
]
if not roots:
raise FileNotFoundError("Could not locate the downloaded HomeObjects-3K dataset.")
DATASET_ROOT = roots[0]
IMAGE_SUFFIXES = {".jpg", ".jpeg", ".png", ".bmp", ".webp"}
train_images = [
path
for path in (DATASET_ROOT / "images" / "train").iterdir()
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
]
val_images = [
path
for path in (DATASET_ROOT / "images" / "val").iterdir()
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
]
assert len(train_images) == TRAIN_IMAGE_COUNT, len(train_images)
assert len(val_images) == VAL_IMAGE_COUNT, len(val_images)
print("Training images found:", len(train_images))
print("Validation images found:", len(val_images))
print("Best checkpoint:", BEST)
print("Training metrics:", RESULTS_CSV)
# Cell 5: Validate the best checkpoint on all held-out images
shutil.rmtree(BASE / "validation", ignore_errors=True)
trained_model = YOLO(str(BEST))
metrics = trained_model.val(
data=DATASET_ID,
imgsz=IMGSZ,
batch=cfg["batch"],
device=cfg["device"],
plots=True,
project=str(BASE),
name="validation",
)
map50 = float(metrics.box.map50)
map50_95 = float(metrics.box.map)
print("Validation images:", len(val_images))
print(f"mAP50: {map50:.4f}")
print(f"mAP50-95: {map50_95:.4f}")
print("mAP50 is the more forgiving box-overlap summary.")
print("mAP50-95 averages performance across stricter overlap thresholds.")
# Cell 6: Inspect three held-out predictions and save the checkpoint
sample_images = [str(path) for path in sorted(val_images)[:3]]
if len(sample_images) < 3:
raise RuntimeError("Expected at least three validation images.")
shutil.rmtree(BASE / "fine_tuned_predictions", ignore_errors=True)
fine_tuned_results = trained_model.predict(
source=sample_images,
save=True,
imgsz=IMGSZ,
device=cfg["device"],
project=str(BASE),
name="fine_tuned_predictions",
)
for result in fine_tuned_results:
result.show()
print("Inspect each image for correct boxes, labels, confidence, misses, and false positives.")
files.download(str(BEST))
# Cell 7: Package the reproducible portfolio evidence
with RESULTS_CSV.open(newline="", encoding="utf-8") as handle:
rows = list(csv.DictReader(handle))
PORTFOLIO = BASE / "portfolio"
shutil.rmtree(PORTFOLIO, ignore_errors=True)
PORTFOLIO.mkdir(parents=True)
experiment_config = {
"ultralytics_version": __version__,
"model": MODEL_ID,
"dataset": DATASET_ID,
"dataset_total_images": TRAIN_IMAGE_COUNT + VAL_IMAGE_COUNT,
"dataset_train_images": TRAIN_IMAGE_COUNT,
"dataset_validation_images": VAL_IMAGE_COUNT,
"dataset_classes": TARGET_CLASSES,
"training_fraction": FRACTION,
"profile": PROFILE,
"epochs": cfg["epochs"],
"batch": cfg["batch"],
"image_size": IMGSZ,
"device": cfg["device"],
"validation": {
"mAP50": round(map50, 6),
"mAP50-95": round(map50_95, 6),
},
}
(PORTFOLIO / "experiment_config.json").write_text(
json.dumps(experiment_config, indent=2),
encoding="utf-8",
)
shutil.copy2(RESULTS_CSV, PORTFOLIO / "training_results.csv")
for directory in [BASE / "baseline", RUN, BASE / "validation", BASE / "fine_tuned_predictions"]:
if not directory.exists():
continue
images = sorted(
path
for path in directory.iterdir()
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
)
for source in images[:3]:
shutil.copy2(source, PORTFOLIO / f"{directory.name}_{source.name}")
readme = f"""# My First YOLO26 HomeObjects Detector
## What I built
I fine-tuned `{MODEL_ID}` on the complete HomeObjects-3K training split and evaluated the saved checkpoint on the held-out validation split.
## Dataset
- {TRAIN_IMAGE_COUNT + VAL_IMAGE_COUNT} total images
- {TRAIN_IMAGE_COUNT} training images used in every epoch
- {VAL_IMAGE_COUNT} held-out validation images
- {len(TARGET_CLASSES)} household-object classes
## Beginner glossary
- Bounding box: the rectangle locating an object.
- Class: the predicted object category.
- Confidence: how strongly the model supports a prediction.
- Epoch: one full pass over the training split.
- Loss: the training error signal the optimizer tries to reduce.
- Checkpoint: a saved copy of learned model weights.
## Configuration
- Ultralytics: `{__version__}`
- Model: `{MODEL_ID}`
- Dataset: `{DATASET_ID}`
- Training fraction: `{FRACTION}`
- Profile: `{PROFILE}`
- Epochs: `{cfg['epochs']}`
- Batch size: `{cfg['batch']}`
- Image size: `{IMGSZ}`
## Validation results
- mAP50: `{map50:.4f}`
- mAP50-95: `{map50_95:.4f}`
## Limits
This is a short beginner fine-tuning run. It uses the full training split, but its small epoch count does not establish production accuracy, robustness, fairness, or deployment readiness. The checkpoint is kept as a separate local download rather than required in this repository.
## Reproduce the experiment
Open `first_yolo26_homeobjects.ipynb` in Google Colab and run its cells in order.
"""
(PORTFOLIO / "README.md").write_text(readme, encoding="utf-8")
bundle_path = shutil.make_archive(
str(BASE / "github_portfolio_bundle"),
"zip",
root_dir=PORTFOLIO,
)
print("Portfolio files:")
for path in sorted(PORTFOLIO.iterdir()):
print(path.name)
print("Bundle:", bundle_path)
files.download(bundle_path)
Download the publishing files
The bundle contains the portfolio files for GitHub. The notebook preserves the complete sequence that produced those files.
- Confirm that your browser completed the github_portfolio_bundle.zip download.
- Use Colab's File menu to download the notebook in .ipynb format.
- Confirm that the downloaded notebook is named first_yolo26_homeobjects.ipynb.
- Extract github_portfolio_bundle.zip into a folder with the same name using the Windows archive tool.
- Leave the separately downloaded best.pt on your Windows device.
You now have the reproducible notebook plus the files needed for the repository. The learned checkpoint remains safely outside the required beginner upload flow.
Download or extraction incomplete?
If the bundle is missing, run Cell 7 again after allowing downloads for the Colab page. Confirm that the output prints a bundle path.
If the extracted folder is empty, extract the downloaded ZIP again. Do not upload the unopened archive as a replacement for its contents.
Need help locating the files?
Publish the repository
A public repository gives your experiment a stable page that other people can inspect. Its README explains what you trained plus the limits of the result.
- Return to the GitHub account from earlier.
- Select New repository.
- Enter first-yolo26-homeobjects as the repository name.
- Choose public visibility.
- Select the option to initialize the repository with a README.
- Select Create repository.
You should see the new repository page with its starter README. The repository is now ready to receive your experiment evidence.
Why keep the checkpoint separate?
The repository explains how to reproduce the experiment from the notebook. Your local best.pt remains the saved learned model.
Keeping that checkpoint separate makes the browser upload simpler. The README clearly records this choice.
- Return to the repository page.
- Select Add file.
- Select Upload files.
- Upload the contents of the extracted github_portfolio_bundle folder.
- Upload first_yolo26_homeobjects.ipynb.
- Enter Publish full HomeObjects-3K experiment as the commit message.
- Select Propose changes.
Before you inspect the repository, which five core files do you expect to see?
- Return to the repository's main file list.
- Confirm that the repository contains first_yolo26_homeobjects.ipynb, README.md, experiment_config.json, training_results.csv, and selected result images.
- Confirm that the repository uses public visibility.
You should see the complete notebook beside the configuration, metrics, README, and visual evidence. Strong work. Your full-dataset experiment now has a reproducible public record.
Files missing after upload?
Make sure you uploaded the extracted bundle contents. Uploading only github_portfolio_bundle.zip does not place its individual files in the repository list.
If the notebook is absent, repeat the upload flow for first_yolo26_homeobjects.ipynb.
Need help checking the repository contents?
Secret mission
Compare the Model Before and After Training
Run the pretrained and fine-tuned models on the same three validation images. Inspect their boxes, classes, confidence scores, misses, and false positives before publishing an evidence-based comparison.
Clean Up Your Resources
Clean Up Your Resources
This project created resources in Google Colab, Google Drive, Windows, and GitHub. All resources are expected to cost $0, so choose whether to keep them, pause the temporary runtime, or delete every copy.
Resources you used:
- The first_yolo26_homeobjects.ipynb notebook stored in Google Drive.
- The temporary Google Colab runtime containing the HomeObjects-3K dataset and the first_yolo26_homeobjects outputs.
- The locally downloaded first_yolo26_homeobjects.ipynb, best.pt, github_portfolio_bundle.zip, and secret_comparison_bundle.zip files on Windows.
- The public first-yolo26-homeobjects GitHub repository.
Keep everything running
No action needed. Choose this if you want to rerun the experiment or keep its reproducibility evidence available.
- Keep first_yolo26_homeobjects.ipynb in Google Drive for future experiments.
- Retain the local best.pt checkpoint as your independent copy of the trained weights.
- Retain github_portfolio_bundle.zip as a copy of the main reproducibility evidence.
- Retain secret_comparison_bundle.zip as a copy of the paired comparison evidence.
- Leave the first-yolo26-homeobjects repository public so its notebook, metrics, limitations, and comparison remain accessible.
- Continue using the current Colab runtime only while it remains attached.
Colab runtimes are temporary. Your Google Drive notebook, Windows downloads, and GitHub repository are the durable copies.
Pause - I'll come back to this later
Shut down the temporary runtime to release its compute resources. Your notebook, downloaded files, and public repository remain available.
- Confirm that first_yolo26_homeobjects.ipynb is available in Google Drive.
- Confirm that best.pt, github_portfolio_bundle.zip, and secret_comparison_bundle.zip are available on Windows.
- Return to the Google Colab notebook from earlier.
- Select Runtime in the top menu to reveal the runtime controls.
- Choose Disconnect and delete runtime.
The temporary runtime stops. The notebook in Google Drive and the files outside the runtime remain available for your return.
Delete - I don't want to use this again
Remove every project copy if you want a clean slate. Deletion is permanent, so each resource is separated below for a final check.
The Colab runtime contains the downloaded dataset and generated experiment outputs.
- Return to the Google Colab notebook from earlier to remove its temporary runtime.
- Select Runtime in the top menu to reveal the runtime controls.
- Choose Disconnect and delete runtime.
The runtime is no longer active. Its temporary first_yolo26_homeobjects directory and downloaded HomeObjects-3K dataset are removed with it.
The public repository holds the experiment files that other people can access.
- Return to the public first-yolo26-homeobjects repository.
- Open the repository settings page to locate its deletion controls.
- Use the repository deletion controls to request permanent deletion.
- Enter first-yolo26-homeobjects when GitHub asks you to confirm the repository name.
- Complete GitHub's deletion confirmation.
- Reload the former repository URL to verify that the project page is no longer accessible.
Your Google Drive notebook is a separate copy, so deleting the repository does not remove it.
- Return to Google Drive to find first_yolo26_homeobjects.ipynb.
- Move first_yolo26_homeobjects.ipynb to the Drive bin.
- Review the Drive bin to confirm it contains only files you want to remove.
- Empty the Drive bin.
- Search Google Drive for first_yolo26_homeobjects.ipynb to verify that the notebook has been removed.
The final copies are the files downloaded to your Windows device.
- Use Windows search to locate first_yolo26_homeobjects.ipynb, best.pt, github_portfolio_bundle.zip, and secret_comparison_bundle.zip.
- Delete every located project file.
- Review the Windows deleted-items area to confirm it contains only files you want to remove.
- Permanently clear the deleted project files from Windows.
- Search Windows for the four project filenames again to verify that no local copies remain.
Your notebook, runtime, downloaded files, and public repository are now removed.
Nice Work!
Nice Work!
Outstanding work! Your first YOLO household-object detector now has evidence covering its baseline prediction, full-split training, held-out evaluation, and published results.
What you learned:
- Interpreted the bounding boxes in a pretrained YOLO26n prediction. Connected each class label to the named object. Read each confidence score as the model's strength of belief. Explained why inference with fixed weights does not teach the model the HomeObjects-3K classes.
- Fine-tuned yolo26n.pt on all 2,285 training images in every epoch. Used box loss to follow changes in box placement. Used class loss to follow changes in class prediction. Saved the resulting checkpoints plus training evidence.
- Evaluated best.pt across all 404 held-out validation images. Interpreted mAP50 as the more forgiving box-overlap summary. Interpreted mAP50-95 as the stricter summary. Published a reproducible GitHub project containing the notebook, configuration, metrics, limitations, and visual evidence.
- Secret Mission: Created a same-image comparison between the pretrained model and the fine-tuned model. Documented one concrete difference for each validation image. Kept the conclusion honest about the limits of a short training run.
Ready to quiz yourself?