{"record":{"id":"62adf267835c94e4","repo":"microsoft/qlib","slug":"model-is-not-fitted-yet-62adf2","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/pytorch_gru.py","lineNumber":294,"sourceCode":"                        self.logger.info(\"early stop\")\n                        break\n\n        self.logger.info(\"best score: %.6lf @ %d\" % (best_score, best_epoch))\n        self.gru_model.load_state_dict(best_param)\n        torch.save(best_param, save_path)\n\n        # Logging\n        rec = R.get_recorder()\n        for k, v_l in evals_result.items():\n            for i, v in enumerate(v_l):\n                rec.log_metrics(step=i, **{k: v})\n\n        if self.use_gpu:\n            torch.cuda.empty_cache()\n\n    def predict(self, dataset: DatasetH, segment: Union[Text, slice] = \"test\"):\n        if not self.fitted:\n            raise ValueError(\"model is not fitted yet!\")\n\n        x_test = dataset.prepare(segment, col_set=\"feature\", data_key=DataHandlerLP.DK_I)\n        index = x_test.index\n        self.gru_model.eval()\n        x_values = x_test.values\n        sample_num = x_values.shape[0]\n        preds = []\n\n        for begin in range(sample_num)[:: self.batch_size]:\n            if sample_num - begin < self.batch_size:\n                end = sample_num\n            else:\n                end = begin + self.batch_size\n\n            x_batch = torch.from_numpy(x_values[begin:end]).float().to(self.device)\n\n            with torch.no_grad():\n                pred = self.gru_model(x_batch).detach().cpu().numpy()","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/contrib/model/pytorch_gru.py#L276-L312","documentation":"Raised by GRUModel.predict when called before fit() completed. The `fitted` flag is set True only at the end of a successful fit; predict checks it first because the GRU weights are otherwise randomly initialized and predictions would be meaningless.","triggerScenarios":"model.predict(dataset) on a fresh GRUModel; predict after a fit() that raised earlier (so fitted stayed False); using a new model instance in an inference-only script.","commonSituations":"Two-stage workflows (train script, then predict script) that re-instantiate the model instead of restoring it; exception swallowing in a training loop letting the code proceed to backtest.","solutions":["Call fit() to completion before predict().","Persist the trained state (torch save_path) and reload the weights, then set model.fitted = True before predict.","Wrap fit in code that aborts the pipeline on failure so predict is never reached unfitted."],"exampleFix":"# before\nmodel = GRUModel(**params)\nmodel.predict(dataset)  # ValueError\n\n# after\nmodel = GRUModel(**params)\nmodel.fit(dataset)\nmodel.predict(dataset)","handlingStrategy":"validation","validationCode":"assert model.fitted, \"fit the model or load a checkpoint before predict\"","typeGuard":"def is_fitted(model) -> bool:\n    return bool(getattr(model, \"fitted\", False))","tryCatchPattern":"try:\n    model.predict(dataset)\nexcept ValueError as e:\n    if \"not fitted\" in str(e):\n        model.fit(dataset)\n        model.predict(dataset)\n    else:\n        raise","preventionTips":["Check model.fitted before every predict call in inference scripts.","Persist and reload checkpoints rather than re-instantiating models for prediction."],"tags":["qlib","pytorch","lifecycle","state-error"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}