microsoft/qlib · error · ValueError

CatBoost doesn't support multi-label training

Error message

CatBoost doesn't support multi-label training

What it means

Thrown by CatBoostModel.fit when the label block is not a single-column 2D array. CatBoost's Pool accepts only a 1D label (scalar per sample), so qlib squeezes the label only when it is exactly (N, 1); anything else (multi-column labels, or a raw 1D series that already lost its column axis) is rejected.

Source

Thrown at qlib/contrib/model/catboost_model.py:52

        evals_result=dict(),
        reweighter=None,
        **kwargs,
    ):
        df_train, df_valid = dataset.prepare(
            ["train", "valid"],
            col_set=["feature", "label"],
            data_key=DataHandlerLP.DK_L,
        )
        if df_train.empty or df_valid.empty:
            raise ValueError("Empty data from dataset, please check your dataset config.")
        x_train, y_train = df_train["feature"], df_train["label"]
        x_valid, y_valid = df_valid["feature"], df_valid["label"]

        # CatBoost needs 1D array as its label
        if y_train.values.ndim == 2 and y_train.values.shape[1] == 1:
            y_train_1d, y_valid_1d = np.squeeze(y_train.values), np.squeeze(y_valid.values)
        else:
            raise ValueError("CatBoost doesn't support multi-label training")

        if reweighter is None:
            w_train = None
            w_valid = None
        elif isinstance(reweighter, Reweighter):
            w_train = reweighter.reweight(df_train).values
            w_valid = reweighter.reweight(df_valid).values
        else:
            raise ValueError("Unsupported reweighter type.")

        train_pool = Pool(data=x_train, label=y_train_1d, weight=w_train)
        valid_pool = Pool(data=x_valid, label=y_valid_1d, weight=w_valid)

        # Initialize the catboost model
        self._params["iterations"] = num_boost_round
        self._params["early_stopping_rounds"] = early_stopping_rounds
        self._params["verbose_eval"] = verbose_eval
        self._params["task_type"] = "GPU" if get_gpu_device_count() > 0 else "CPU"

View on GitHub (pinned to 79633dd950)

Solutions

  1. Reduce the label to exactly one expression in the handler config, e.g. label: ["Ref($close, -2)/Ref($close, -1) - 1"]
  2. If multi-output prediction is required, use a model that supports it (e.g. a PyTorch model with a matching output head)

Example fix

# before (qlib config yaml)
label: ["Ref($close, -2)/Ref($close, -1) - 1", "$volume"]

# after
label: ["Ref($close, -2)/Ref($close, -1) - 1"]
Defensive patterns

Strategy: validation

Validate before calling

y = dataset.prepare("train", col_set="label", data_key="learn")
assert y.values.ndim == 2 and y.values.shape[1] == 1, f"CatBoost needs a single label column, got {y.shape[1]}"

Prevention

When it happens

Trigger: Configuring the DatasetH label as multiple expressions (e.g. "Ref($close, -2)/Ref($close, -1) - 1; $volume/$amount"), producing y_train.values.shape[1] > 1; or a label pipeline that returns a DataFrame with != 1 columns.

Common situations: Copying a workflow config with a multi-task label; adding extra label columns for custom metrics and forgetting the model constraint; switching from a model that tolerates multi-label (e.g. some neural models) to CatBoostModel.

Related errors


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/7d9e039bd4118c5f. Report an issue: GitHub.