{"record":{"id":"60110d42a621b35c","repo":"microsoft/qlib","slug":"model-is-not-fitted-yet-60110d","errorCode":null,"errorMessage":"model is not fitted yet!","messagePattern":"model is not fitted yet!","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"qlib/contrib/model/double_ensemble.py","lineNumber":249,"sourceCode":"            # Lightgbm need 1D array as its label\n            if y_train.values.ndim == 2 and y_train.values.shape[1] == 1:\n                y_train = np.squeeze(y_train.values)\n            else:\n                raise ValueError(\"LightGBM doesn't support multi-label training\")\n\n            N = x_train.shape[0]\n            loss_curve = pd.DataFrame(np.zeros((N, num_trees)))\n            pred_tree = np.zeros(N, dtype=float)\n            for i_tree in range(num_trees):\n                pred_tree += model.predict(x_train.values, start_iteration=i_tree, num_iteration=1)\n                loss_curve.iloc[:, i_tree] = self.get_loss(y_train, pred_tree)\n        else:\n            raise ValueError(\"not implemented yet\")\n        return loss_curve\n\n    def predict(self, dataset: DatasetH, segment: Union[Text, slice] = \"test\"):\n        if self.ensemble is None:\n            raise ValueError(\"model is not fitted yet!\")\n        x_test = dataset.prepare(segment, col_set=\"feature\", data_key=DataHandlerLP.DK_I)\n        pred = pd.Series(np.zeros(x_test.shape[0]), index=x_test.index)\n        for i_sub, submodel in enumerate(self.ensemble):\n            feat_sub = self.sub_features[i_sub]\n            pred += (\n                pd.Series(submodel.predict(x_test.loc[:, feat_sub].values), index=x_test.index)\n                * self.sub_weights[i_sub]\n            )\n        pred = pred / np.sum(self.sub_weights)\n        return pred\n\n    def predict_sub(self, submodel, df_data, features):\n        x_data = df_data[\"feature\"].loc[:, features]\n        pred_sub = pd.Series(submodel.predict(x_data.values), index=x_data.index)\n        return pred_sub\n\n    def get_feature_importance(self, *args, **kwargs) -> pd.Series:\n        \"\"\"get feature importance","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/contrib/model/double_ensemble.py#L231-L267","documentation":"Thrown by DEnsembleModel.predict when self.ensemble is None, intended to guard prediction before fit. Note a caveat: __init__ sets self.ensemble = [], so the attribute is normally an empty list rather than None — with a stock instance, predict before fit returns a zero vector divided by sum(sub_weights) instead of raising. The error effectively fires only if ensemble is explicitly set to None.","triggerScenarios":"Calling predict on a model whose fit failed before the ensemble loop or that was manually mutated (ensemble set to None); in practice the guard is largely dead code because fit/__init__ initialize an empty list.","commonSituations":"Exceptions during fit partially constructing the object; users assuming the guard protects them from untrained prediction when it actually does not reliably do so.","solutions":["Always call fit(dataset) and let it complete before predict(dataset)","Wrap fit in try/except and do not proceed to predict on failure","Defensively check isinstance(model.ensemble, list) and len(model.ensemble) == model.num_models before predicting, since the built-in None check is unreliable"],"exampleFix":"# before\nmodel = DEnsembleModel(num_models=5)\npred = model.predict(dataset)  # silent zeros, not the expected guard\n\n# after\nmodel.fit(dataset)\nassert len(model.ensemble) == model.num_models\npred = model.predict(dataset)","handlingStrategy":"validation","validationCode":"# built-in None-check is unreliable (ensemble is [] not None); validate explicitly\nassert isinstance(model.ensemble, list) and len(model.ensemble) == model.num_models, \"fit() must complete before predict()\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not rely on the built-in guard — check len(model.ensemble) == model.num_models yourself","Abort the pipeline on any fit exception before reaching predict"],"tags":["double-ensemble","lifecycle","dead-guard","qlib"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}