> This is the documentation for LibreYOLO 1.5.0. Current version: https://www.libreyolo.com/docs/tasks/image-classification

# Image classification
Image classification assigns one label distribution to a whole image and locates nothing inside it. The task key is classify.
Verified against LibreYOLO v1.5.0.

## 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](/docs/v1.5.0/tasks/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

Five families both train and predict: [ResNet](/docs/v1.5.0/models/resnet),
[ConvNeXt](/docs/v1.5.0/models/convnext), [MobileNetV4](/docs/v1.5.0/models/mobilenetv4),
[EfficientNetV2](/docs/v1.5.0/models/efficientnetv2) and
[DINOv2](/docs/v1.5.0/models/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](/docs/v1.5.0/models/vit), [Swin](/docs/v1.5.0/models/swin),
[VGG](/docs/v1.5.0/models/vgg), [AlexNet](/docs/v1.5.0/models/alexnet) and
[DeiT](/docs/v1.5.0/models/deit).

[CLIP](/docs/v1.5.0/models/clip) and [SigLIP2](/docs/v1.5.0/models/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.

## Predict

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

**Python**

```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**

```bash
libreyolo predict model=LibreResNet50-cls.pt save=True \
  source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg
```

**The whole distribution**

```python
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**

```python
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](/docs/v1.5.0/predict) 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.

```text
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**

```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**

```bash
libreyolo train model=LibreResNet50-cls.pt data=imagenette160 epochs=5
```

**Multi-GPU**

```bash
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](/docs/v1.5.0/train) for datasets, augmentation,
multi-GPU and loggers.

## Validate

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

**Python**

```python
from libreyolo import LibreYOLO

model = LibreYOLO("LibreResNet50-cls.pt")

# val() returns a plain dict, not an object.
metrics = model.val(data="imagenette160")

print(metrics["metrics/accuracy_top1"])
print(metrics["metrics/accuracy_top5"])
```

**CLI**

```bash
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.

## Export

**Python**

```python
from libreyolo import LibreYOLO

model = LibreYOLO("LibreResNet50-cls.pt")
model.export(format="onnx")
```

**CLI**

```bash
libreyolo export model=LibreResNet50-cls.pt format=onnx
```

**Use the exported file**

```python
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](/docs/v1.5.0/export) for the formats, their extras and their
constraints.
