ultralytics/ultralytics · error · ValueError

nc not specified. Must specify nc in model.yaml or function

Error message

nc not specified. Must specify nc in model.yaml or function arguments.

What it means

When building a ClassificationModel from YAML, the constructor requires a class count: either nc explicitly passed (e.g. YOLO('yolov8n-cls.yaml', nc=10)) or nc present in the YAML. If neither is set, there is no way to size the final classification head, so ValueError is raised before parse_model runs. Note the printed error message is generic even though this constructor is the classification path (stride=1, numeric default names, reshape_outputs helper below it).

Source

Thrown at ultralytics/nn/tasks.py:851

    def _from_yaml(self, cfg, ch, nc, verbose):
        """Set Ultralytics YOLO model configurations and define the model architecture.

        Args:
            cfg (str | dict): Model configuration file path or dictionary.
            ch (int): Number of input channels.
            nc (int, optional): Number of classes.
            verbose (bool): Whether to display model information.
        """
        self.yaml = cfg if isinstance(cfg, dict) else yaml_model_load(cfg)  # cfg dict

        # Define model
        ch = self.yaml["channels"] = self.yaml.get("channels", ch)  # input channels
        if nc and nc != self.yaml["nc"]:
            LOGGER.info(f"Overriding model.yaml nc={self.yaml['nc']} with nc={nc}")
            self.yaml["nc"] = nc  # override YAML value
        elif not nc and not self.yaml.get("nc", None):
            raise ValueError("nc not specified. Must specify nc in model.yaml or function arguments.")
        self.model, self.save = parse_model(deepcopy(self.yaml), ch=ch, verbose=verbose)  # model, savelist
        self.stride = torch.Tensor([1])  # no stride constraints
        self.names = {i: f"{i}" for i in range(self.yaml["nc"])}  # default names dict
        self.info()

    @staticmethod
    def reshape_outputs(model, nc):
        """Update a TorchVision classification model to class count 'nc' if required.

        Args:
            model (torch.nn.Module): Model to update.
            nc (int): New number of classes.
        """
        name, m = list((model.model if hasattr(model, "model") else model).named_children())[-1]  # last module
        if isinstance(m, Classify):  # YOLO Classify() head
            if m.linear.out_features != nc:
                m.linear = torch.nn.Linear(m.linear.in_features, nc)
        elif isinstance(m, torch.nn.Linear):  # ResNet, EfficientNet

View on GitHub (pinned to 0449ea011c)

Solutions

  1. Add nc: <N> to the model YAML (top level, next to channels/scales)
  2. Or pass nc explicitly: YOLO('my-cls.yaml', nc=10)
  3. Or start from an official template like yolov8n-cls.yaml which defines nc, and edit from there

Example fix

# before
model = YOLO('my-cls.yaml')  # YAML has no nc

# after
model = YOLO('my-cls.yaml', nc=10)
# or in my-cls.yaml add: nc: 10
Defensive patterns

Strategy: validation

Validate before calling

from ultralytics.utils import yaml_model_load
cfg = yaml_model_load('my-cls.yaml')
nc = cfg.get('nc') or passed_nc
if not nc:
    raise ValueError('set nc in yaml or pass nc=')

Prevention

When it happens

Trigger: Instantiating a classification model from a YAML that omits nc and not passing nc to the constructor or YOLO() call; creating ClassificationModel('my-cls.yaml') with only channels defined; custom YAML copied from another model with the nc line deleted.

Common situations: Authoring a new classification YAML and forgetting the nc: key; trimming a template YAML down for a minimal test config; porting a config from another framework where class count is inferred from data.

Related errors


AI-assisted analysis of ultralytics/ultralytics@0449ea011c (2026-08-15). Data as JSON: /api/errors/afbe940d35a9af08. Report an issue: GitHub.