{"record":{"id":"0798f12b5f5e073c","repo":"microsoft/qlib","slug":"xgboost-doesn-t-support-multi-label-training","errorCode":null,"errorMessage":"XGBoost doesn't support multi-label training","messagePattern":"XGBoost doesn't support multi-label training","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"qlib/contrib/model/xgboost.py","lineNumber":45,"sourceCode":"        early_stopping_rounds=50,\n        verbose_eval=20,\n        evals_result=dict(),\n        reweighter=None,\n        **kwargs,\n    ):\n        df_train, df_valid = dataset.prepare(\n            [\"train\", \"valid\"],\n            col_set=[\"feature\", \"label\"],\n            data_key=DataHandlerLP.DK_L,\n        )\n        x_train, y_train = df_train[\"feature\"], df_train[\"label\"]\n        x_valid, y_valid = df_valid[\"feature\"], df_valid[\"label\"]\n\n        # Lightgbm need 1D array as its label\n        if y_train.values.ndim == 2 and y_train.values.shape[1] == 1:\n            y_train_1d, y_valid_1d = np.squeeze(y_train.values), np.squeeze(y_valid.values)\n        else:\n            raise ValueError(\"XGBoost doesn't support multi-label training\")\n\n        if reweighter is None:\n            w_train = None\n            w_valid = None\n        elif isinstance(reweighter, Reweighter):\n            w_train = reweighter.reweight(df_train)\n            w_valid = reweighter.reweight(df_valid)\n        else:\n            raise ValueError(\"Unsupported reweighter type.\")\n\n        dtrain = xgb.DMatrix(x_train.values, label=y_train_1d, weight=w_train)\n        dvalid = xgb.DMatrix(x_valid.values, label=y_valid_1d, weight=w_valid)\n        self.model = xgb.train(\n            self._params,\n            dtrain=dtrain,\n            num_boost_round=num_boost_round,\n            evals=[(dtrain, \"train\"), (dvalid, \"valid\")],\n            early_stopping_rounds=early_stopping_rounds,","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/contrib/model/xgboost.py#L27-L63","documentation":"Raised by XGBModel.fit when the training labels are not a single column. The wrapper squeezes labels to a 1D array for xgb.DMatrix, and only accepts y_train.values.ndim == 2 with exactly one label column; anything else (multi-column labels, 0-d, or 3-d arrays) is rejected because XGBoost's booster API requires a single-label target.","triggerScenarios":"Calling fit() with a dataset whose label col_set resolves to multiple columns, e.g. LABEL0 and LABEL1 both selected, or a custom label expression list producing >1 column. Also triggered if the label handler returns a shape that is not (n, 1).","commonSituations":"Alpha158/Alpha360 datasets configured to expose multiple labels (e.g. LABEL0 plus LABEL5 for horizon studies); custom DataHandlerLP with label expression list of length > 1; users assuming tree models support multi-output like some sklearn estimators do.","solutions":["Restrict the label to a single column, e.g. dataset.prepare(..., col_set=['feature','label']) after configuring the handler with only one label (drop extra LABELx columns via data_key/col_set filters or drop_raw_label).","If you have multiple horizons, train one XGBModel per label column by slicing the prepared dataframe per label.","If you truly need multi-label regression, switch to a model that supports it (e.g. a multi-output sklearn estimator wrapped in qlib, or a neural model) instead of XGBoost."],"exampleFix":"# before\n# handler label config exposes LABEL0 and LABEL1 -> 2 label columns\nmodel.fit(dataset)  # ValueError: XGBoost doesn't support multi-label training\n\n# after\n# keep only one label column in the data handler config\nhandler_config = {\n    \"class\": \"Alpha158\",\n    \"kwargs\": {\"label\": [\"Ref($close, -2) / Ref($close, -1) - 1\"]},  # single label\n}\nmodel.fit(dataset)","handlingStrategy":"validation","validationCode":"y = dataset.prepare(\"train\", col_set=\"label\", data_key=DataHandlerLP.DK_L)\nif y.values.ndim != 2 or y.values.shape[1] != 1:\n    raise RuntimeError(f\"XGBModel needs exactly 1 label column, got shape {y.values.shape}\")\nmodel.fit(dataset)","typeGuard":"def is_single_label(dataset) -> bool:\n    y = dataset.prepare(\"train\", col_set=\"label\", data_key=DataHandlerLP.DK_L)\n    return y.values.ndim == 2 and y.values.shape[1] == 1","tryCatchPattern":"try:\n    model.fit(dataset)\nexcept ValueError as e:\n    if \"multi-label\" in str(e):\n        # slice to one label column or reconfigure the handler's label expression\n        ...\n    raise","preventionTips":["Configure the data handler with exactly one label expression for tree models.","Train one model per horizon instead of multi-label XGBoost.","Log y_train.shape in training scripts to catch label shape drift early."],"tags":["qlib","xgboost","labels","configuration","multi-label"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}