{"record":{"id":"368a614bb5c1d43a","repo":"microsoft/qlib","slug":"model-is-not-fitted-yet-368a61","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_igmtf.py","lineNumber":329,"sourceCode":"                stop_steps = 0\n                best_epoch = step\n                best_param = copy.deepcopy(self.igmtf_model.state_dict())\n            else:\n                stop_steps += 1\n                if stop_steps >= self.early_stop:\n                    self.logger.info(\"early stop\")\n                    break\n\n        self.logger.info(\"best score: %.6lf @ %d\" % (best_score, best_epoch))\n        self.igmtf_model.load_state_dict(best_param)\n        torch.save(best_param, save_path)\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        x_train = dataset.prepare(\"train\", col_set=\"feature\", data_key=DataHandlerLP.DK_L)\n        train_hidden, train_hidden_day = self.get_train_hidden(x_train)\n        x_test = dataset.prepare(segment, col_set=\"feature\", data_key=DataHandlerLP.DK_I)\n        index = x_test.index\n        self.igmtf_model.eval()\n        x_values = x_test.values\n        preds = []\n\n        daily_index, daily_count = self.get_daily_inter(x_test, shuffle=False)\n\n        for idx, count in zip(daily_index, daily_count):\n            batch = slice(idx, idx + count)\n            x_batch = torch.from_numpy(x_values[batch]).float().to(self.device)\n\n            with torch.no_grad():\n                pred = (\n                    self.igmtf_model(x_batch, train_hidden=train_hidden, train_hidden_day=train_hidden_day)\n                    .detach()","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/contrib/model/pytorch_igmtf.py#L311-L347","documentation":"IGMTFModel.predict requires self.fitted == True. The flag is set only after fit() finishes (including its early-stop loop and state restore), so predicting from an unfitted instance — or one whose fit crashed — raises this ValueError.","triggerScenarios":"Calling predict() on a model that never completed fit(); or after a fit() exception (empty data, bad metric, NaN loss) left fitted False while the caller continued.","commonSituations":"Train/infer split across processes or notebooks where the infer side builds a fresh IGMTFModel; swallowing fit exceptions; pickling a model before fit finished.","solutions":["Fit the model to completion before calling predict","Load saved weights and set model.fitted = True if you intentionally skip refitting","Ensure fit() exceptions abort the pipeline instead of falling through to predict"],"exampleFix":"# before\nmodel = IGMTFModel()\nmodel.predict(dataset)  # not fitted\n\n# after\nmodel.fit(dataset)\nmodel.predict(dataset)","handlingStrategy":"validation","validationCode":"if not model.fitted:\n    raise RuntimeError(\"IGMTFModel must complete fit() before predict()\")","typeGuard":"def is_fitted(model) -> bool:\n    return bool(getattr(model, \"fitted\", False))","tryCatchPattern":"try:\n    preds = model.predict(dataset)\nexcept ValueError as e:\n    if \"not fitted\" in str(e):\n        model.fit(dataset)\n        preds = model.predict(dataset)\n    else:\n        raise","preventionTips":["Gate predict calls on model.fitted","In inference-only scripts, load the checkpoint and set fitted=True explicitly"],"tags":["qlib","igmtf","lifecycle","predict-before-fit"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}