Image classification

Image classification assigns one label distribution to a whole image and locates nothing inside it. The task key is classify.

Definition

Image classification produces one score per class for the whole image and no coordinates at all. It answers what is in the picture, never where, which is what separates it from object detection.

classify is the canonical task key, and the -cls suffix in a checkpoint filename selects it. That suffix is required rather than optional on classification families, so LibreResNet50.pt is not read as a classifier and only LibreResNet50-cls.pt is.

predict() fills result.probs and leaves boxes empty. .data is the full score vector, .top1 the index of the highest score and .top1conf its value, .top5 the five highest indices in descending order and .top5conf their scores. Indices point into result.names. Slicing a Results object never truncates probs, because the vector belongs to the image rather than to one row.

Models

Trainable image classifiers include: ResNet, ConvNeXt, MobileNetV4, EfficientNetV2 and DINOv2. The first four run on the base package and ship published weights. DINOv2 needs pip install "libreyolo[rfdetr]" and has no LibreYOLO-hosted checkpoint: it loads the upstream backbone with a randomly initialized linear head, so it is a fine-tuning starting point rather than a ready predictor.

Five more predict, validate and export, but their train() raises NotImplementedError: ViT, Swin, VGG, AlexNet and DeiT.

CLIP and SigLIP2 classify without a fixed label set. They score the image against text prompts, so set_classes() defines the classes at call time and there is no training step for a new label set at all. Both also serve the embed task.

ConvNeXt V2 adds supervised classification with CC-BY-NC-4.0 pretrained weights. PE supports zero-shot classification; V-JEPA 2 trains video classification probes.

Predict

Weights download from Hugging Face on first use and are cached locally.

Python
from libreyolo import LibreYOLO, SAMPLE_IMAGE # The -cls suffix in the filename selects the task, so no task# argument is needed.model = LibreYOLO("LibreResNet50-cls.pt")result = model(SAMPLE_IMAGE, save=True) print(result.names[result.probs.top1], float(result.probs.top1conf))print(result.probs.top5)
CLI
libreyolo predict model=LibreResNet50-cls.pt save=True \  source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg
The whole distribution
from libreyolo import LibreYOLO, SAMPLE_IMAGE result = LibreYOLO("LibreResNet50-cls.pt")(SAMPLE_IMAGE)probs = result.probs # .data is the full (C,) vector; top5/top5conf are ordered views.print(probs.data.shape)for index, score in zip(probs.top5, probs.top5conf):    print(result.names[index], float(score))
Zero-shot, no training
from libreyolo import LibreYOLO, SAMPLE_IMAGE # CLIP scores the image against text prompts, so the label set is# set at call time instead of baked into the checkpoint.model = LibreYOLO("LibreCLIPb32-cls.pt")model.set_classes(["a person jumping", "an empty street", "a parked car"])result = model(SAMPLE_IMAGE) print(model.names[result.probs.top1], float(result.probs.top1conf))

conf, iou and max_det have no effect here: there are no candidates to threshold or suppress, only one distribution. See prediction for sources, streaming and result handling.

Dataset format

Classification uses a directory tree, not label files and not a YAML. data is the dataset root.

dataset/  train/    tench/000001.jpg    parachute/000002.jpg  val/    tench/000101.jpg    parachute/000102.jpg

train/ is required for training and it defines the class-to-index mapping by sorted folder name, so the first folder alphabetically becomes class 0. val/ is required for validation. A test/ split may be present and the default train and validate commands do not use it. Any split other than train has to contain the same class folder names as the expected class set, which is what makes a mismatch fail loudly rather than score as a wrong prediction. The accepted image extensions are .jpg, .jpeg, .png, .bmp, .webp, .tif and .tiff.

data accepts three things: a path to a directory containing a train/ split, a .zip URL, or one of the known dataset names, imagenette160 and smoke10, which download and cache on first use.

The canonical loader is libreyolo.data.classify_dataset.

Train

Python
from libreyolo import LibreYOLO # imagenette160 is a known dataset name and downloads on first use.# Pass a directory with a train/ split for your own data.model = LibreYOLO("LibreResNet50-cls.pt")model.train(data="imagenette160", epochs=5)
CLI
libreyolo train model=LibreResNet50-cls.pt data=imagenette160 epochs=5
Multi-GPU
libreyolo train model=LibreResNet50-cls.pt data=imagenette160 \  epochs=50 device=0,1 batch=-1

There is no nc to declare: the class count comes from the folder names under train/, and the final linear layer is rebuilt to match it while the backbone transfers unchanged. See training for datasets, augmentation, multi-GPU and loggers.

ResNet, ConvNeXt, ConvNeXt V2, MobileNetV4, EfficientNetV2 and DINOv2 support cls_pw or class_weights loss weighting. Classification scale controls crop area, and crop_pct controls evaluation cropping. See augmentations.

The resize filter is part of the model. Set model.interpolation to "bilinear", "bicubic", "nearest" or "bilinear_no_antialias" before training: training, val(), predict(), INT8 calibration and export all use it, and save(), best.pt and last.pt record it, so a reload keeps it. Any other name raises ValueError. "bilinear_no_antialias" resizes with plain bilinear and no antialiasing everywhere, RandomResizedCrop included, for models that will run behind a runtime resize without antialiasing. MobileNetV4, ResNet, ConvNeXt, ConvNeXt V2, EfficientNetV2, DeiT, Swin and ViT default to bicubic; AlexNet and VGG to bilinear.

Validate

val() returns a dictionary of metrics/ keys, computed over the val/ split of the dataset root.

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreResNet50-cls.pt") # A dict of metrics/ keys, plus top1, top5 and confusion_matrix.metrics = model.val(data="imagenette160") print(metrics["metrics/accuracy_top1"])print(metrics["metrics/accuracy_top5"])print(metrics.top1, metrics.top5)
CLI
libreyolo val model=LibreResNet50-cls.pt data=imagenette160

metrics/accuracy_top1 is the share of images whose highest-scoring class is the true one, and it is the headline number, the one training uses to pick the best epoch. metrics/accuracy_top5 is the share whose true class appears anywhere in the five highest-scoring classes, which says less the fewer classes the dataset has. The dictionary also carries fitness, a copy of the top-1 value.

ImageFolder validation also returns macro metrics/precision, metrics/recall and metrics/f1, averaging over classes present in the validation targets. Unpredicted classes contribute zero precision. Default fitness remains top-1 accuracy. Validation and calibration use the model evaluation transform.

The result is a dict subclass with three attributes: top1 and top5 hold the two accuracy values, and confusion_matrix counts each image at its top-1 class against its label, indexed matrix[predicted, true]. plots=True writes it as counts to confusion_matrix.png and as shares of each label to confusion_matrix_normalized.png. Above 2,000 classes the dense matrix and its plots are unavailable, and nonzero(), tp_fp() and class_accuracy() read the counts instead; see Validation and metrics.

Export

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreResNet50-cls.pt")model.export(format="onnx")
CLI
libreyolo export model=LibreResNet50-cls.pt format=onnx
Use the exported file
from libreyolo import LibreYOLO, SAMPLE_IMAGE # The factory routes on the file suffix, so an exported artifact loads# like a checkpoint and returns the same Results object.model = LibreYOLO("LibreResNet50-cls.onnx")result = model(SAMPLE_IMAGE) print(result.probs.top1, result.probs.top1conf)

An exported artifact loads back through LibreYOLO() on its file suffix, so a .onnx or .engine file behaves like a checkpoint and returns the same Results. Format coverage differs by family; the matrix on each model page is generated from the validated set rather than typed by hand. See export and deploy for the formats, their extras and their constraints.

Verified against LibreYOLO v1.6.0.