{"record":{"id":"f08fdcfffa996ed2","repo":"microsoft/qlib","slug":"lightgbm-doesn-t-support-multi-label-training-f08fdc","errorCode":null,"errorMessage":"LightGBM doesn't support multi-label training","messagePattern":"LightGBM doesn't support multi-label training","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"qlib/contrib/model/gbdt.py","lineNumber":46,"sourceCode":"    def _prepare_data(self, dataset: DatasetH, reweighter=None) -> List[Tuple[lgb.Dataset, str]]:\n        \"\"\"\n        The motivation of current version is to make validation optional\n        - train segment is necessary;\n        \"\"\"\n        ds_l = []\n        assert \"train\" in dataset.segments\n        for key in [\"train\", \"valid\"]:\n            if key in dataset.segments:\n                df = dataset.prepare(key, col_set=[\"feature\", \"label\"], data_key=DataHandlerLP.DK_L)\n                if df.empty:\n                    raise ValueError(\"Empty data from dataset, please check your dataset config.\")\n                x, y = df[\"feature\"], df[\"label\"]\n\n                # Lightgbm need 1D array as its label\n                if y.values.ndim == 2 and y.values.shape[1] == 1:\n                    y = np.squeeze(y.values)\n                else:\n                    raise ValueError(\"LightGBM doesn't support multi-label training\")\n\n                if reweighter is None:\n                    w = None\n                elif isinstance(reweighter, Reweighter):\n                    w = reweighter.reweight(df)\n                else:\n                    raise ValueError(\"Unsupported reweighter type.\")\n                ds_l.append((lgb.Dataset(x.values, label=y, weight=w, free_raw_data=False), key))\n        return ds_l\n\n    def fit(\n        self,\n        dataset: DatasetH,\n        num_boost_round=None,\n        early_stopping_rounds=None,\n        verbose_eval=20,\n        evals_result=None,\n        reweighter=None,","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/contrib/model/gbdt.py#L28-L64","documentation":"Thrown by LGBModel._prepare_data when the label block is not a single column. LightGBM's Dataset label must be 1D, so qlib squeezes only (N, 1) label arrays and rejects label DataFrames with multiple columns.","triggerScenarios":"Calling LGBModel.fit with a handler label of multiple expressions (shape[1] > 1); e.g. label: [expr1, expr2] in workflow config.","commonSituations":"Sharing one handler config across models where only some support multi-label; incremental label additions for custom evaluation.","solutions":["Keep the label list to one expression in the data handler config","Move extra targets into features or use a multi-output model (e.g. PyTorch-based)"],"exampleFix":"# before\nlabel: [\"Ref($close, -2)/Ref($close, -1) - 1\", \"Mean($close, 3)/$close - 1\"]\n\n# after\nlabel: [\"Ref($close, -2)/Ref($close, -1) - 1\"]","handlingStrategy":"validation","validationCode":"y = dataset.prepare(\"train\", col_set=\"label\", data_key=\"learn\")\nassert y.values.ndim == 2 and y.values.shape[1] == 1, \"LightGBM requires a single-column (1D) label\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["One label expression per handler for LGBModel","Centralize a label-shape assertion in shared experiment code"],"tags":["lightgbm","multi-label","label-config","qlib"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}