# Concepts

LuxonisTrain combines a model graph, dataset loaders, a trainer, and an experiment tracker in a `LuxonisModel`. Configure these
through YAML and run the same workflows from the CLI or Python.

## Predefined Models

Packaged configurations provide architectures and training settings for tasks such as classification, detection, segmentation,
keypoint detection, and OCR. Discover the available models and variants with:

```bash
luxonis_train list-models
luxonis_train info --model detection --variant light
```

Use `--model` to select a packaged configuration. `--variant` selects its model variant and requires `--model`:

```bash
luxonis_train train \
  --model detection --variant light \
  loader.params.dataset_name "my_dataset" \
  trainer.epochs 50
```

The equivalent Python call is:

```python
from luxonis_train import LuxonisModel

model = LuxonisModel(
    model="detection",
    variant="light",
    opts={
        "loader.params.dataset_name": "my_dataset",
        "trainer.epochs": 50,
    },
)
model.train()
```

`--model` and `--config` are mutually exclusive. In Python, use either `model=` or the `cfg` argument. A packaged configuration is
read from the installed package; it does not require a local `configs/` directory.

## Configuration

Use a YAML file when you want to maintain your own settings. The top-level sections are:

| Section | Purpose |
| --- | --- |
| `model` | Predefined architecture or a graph of nodes, with attached losses, metrics, and visualizers. |
| `loader` | Loader implementation, dataset parameters, and the splits used for each view. |
| `trainer` | Image preprocessing, batching, epochs, optimization, hardware, and callbacks. |
| `tracker` | Run names, output directory, and TensorBoard, MLflow, or Weights & Biases logging. |
| `exporter` | ONNX export, preprocessing metadata, platform conversion, and AIMET settings. |
| `archiver` | NN Archive naming and upload destinations. |
| `tuner` | Optuna search space, study storage, and optimization target. |

Each component's `name` selects a registered implementation, and `params` supplies its constructor arguments. The [configuration
schema](https://github.com/luxonis/luxonis-train/blob/main/luxonis_train/config/config.py) defines the fields and defaults; the
[packaged configurations](https://github.com/luxonis/luxonis-train/tree/main/luxonis_train/configs) provide task-specific
examples.

The [Training](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/training.md) page includes a
complete `config.yaml` example. Run it with:

```bash
luxonis_train train --config config.yaml trainer.epochs 50
```

Overrides are alternating dotted keys and values. In Python, pass them as `opts={"trainer.epochs": 50}`. The Python constructor
accepts a config path, a dictionary, or a `Config` instance; `opts` does not apply when you supply an already constructed `Config`
instance.

A LuxonisTrain checkpoint stores its configuration and dataset metadata. Commands with `--weights` can use that configuration when
neither `--config` nor `--model` is supplied. Dataset-based operations still need access to the dataset. See
[Evaluation](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/evaluation.md) and
[Exporting](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/exporting.md) for examples.

### Model Graphs

`model.predefined_model` expands a registered model family into nodes and attached modules. To define a graph yourself, use
`model.nodes`. A node's `inputs` names preceding nodes; `input_sources` names inputs from the loader. Use `alias` to distinguish
multiple instances of the same node class.

This classification model connects a backbone to a head and attaches a loss, a metric, and a visualizer:

```yaml
model:
  name: custom_classifier
  nodes:
    - name: ResNet
      alias: backbone
      variant: "18"
      input_sources: [image]
    - name: ClassificationHead
      inputs: [backbone]
      losses:
        - name: CrossEntropyLoss
      metrics:
        - name: Accuracy
          is_main_metric: true
      visualizers:
        - name: ClassificationVisualizer
```

Use this `model` section with loader and trainer settings for a classification dataset. On a dataset with multiple tasks, set each
head's `task_name` to the task it should learn. Set `is_main_metric: true` on the metric used to select the best metric
checkpoint. A node's `freezing` and `finetuning` settings control which parameters train and their optimizer or scheduler
settings.

## Customization

Custom nodes, losses, metrics, visualizers, and loaders subclass the corresponding base classes and register automatically.
Callbacks can be registered explicitly with `CALLBACKS`. The framework also exposes registries for optimizers, schedulers, and
training strategies.

### Custom Callback

Save this callback in `custom_components.py`:

```python
import lightning.pytorch as pl

from luxonis_train import LuxonisLightningModule
from luxonis_train.registry import CALLBACKS

@CALLBACKS.register()
class CustomCallback(pl.Callback):
    def __init__(self, message: str):
        super().__init__()
        self.message = message

    def on_train_epoch_end(
        self,
        trainer: pl.Trainer,
        pl_module: LuxonisLightningModule,
    ) -> None:
        print(self.message)
```

Add it to your configuration:

```yaml
trainer:
  callbacks:
    - name: CustomCallback
      params:
        message: "Training epoch completed"
```

### Custom Loss

A loss implements `forward` and declares its supported tasks. Input parameter names determine which predictions and labels it
receives: `predictions` selects the task's main output, and `target` selects its single required label. For tasks with multiple
labels, use names such as `target_boundingbox`.

Add this single-label classification loss to `custom_components.py`. It consumes classification logits and the loader's one-hot
class targets:

```python
import torch.nn.functional as F
from torch import Tensor

from luxonis_train import BaseLoss, Tasks

class SmoothedClassificationLoss(BaseLoss):
    supported_tasks = [Tasks.CLASSIFICATION]

    def __init__(self, smoothing: float = 0.1, **kwargs):
        super().__init__(**kwargs)
        self.smoothing = smoothing

    def forward(self, predictions: Tensor, target: Tensor) -> Tensor:
        return F.cross_entropy(
            predictions,
            target.argmax(dim=1),
            label_smoothing=self.smoothing,
        )
```

Use it with a classification model that has at least two classes:

```yaml
model:
  predefined_model:
    name: ClassificationModel
    variant: light
    params:
      loss: SmoothedClassificationLoss
      loss_params:
        smoothing: 0.1
```

### Load Custom Components

Import the component definitions before constructing the model. From the CLI, use the global `--source` option; repeat it for
multiple files:

```bash
luxonis_train --source custom_components.py train --config config.yaml
```

In Python:

```python
import custom_components
from luxonis_train import LuxonisModel

model = LuxonisModel("config.yaml")
model.train()
```
