{"record":{"id":"b0fd9d1f07ce7137","repo":"microsoft/qlib","slug":"model-is-not-fitted-yet-b0fd9d","errorCode":null,"errorMessage":"model is not fitted yet!","messagePattern":"model is not fitted yet!","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"qlib/contrib/model/pytorch_sfm.py","lineNumber":438,"sourceCode":"    def loss_fn(self, pred, label):\n        mask = ~torch.isnan(label)\n\n        if self.loss == \"mse\":\n            return self.mse(pred[mask], label[mask])\n\n        raise ValueError(\"unknown loss `%s`\" % self.loss)\n\n    def metric_fn(self, pred, label):\n        mask = torch.isfinite(label)\n\n        if self.metric in (\"\", \"loss\"):\n            return -self.loss_fn(pred[mask], label[mask])\n\n        raise ValueError(\"unknown metric `%s`\" % self.metric)\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.sfm_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.sfm_model(x_batch).detach().cpu().numpy()","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/contrib/model/pytorch_sfm.py#L420-L456","documentation":"Raised by SFM model predict() in qlib/contrib/model/pytorch_sfm.py:438 when self.fitted is False. The flag flips True only after fit() completes (including restoring best params); predict() guards inference with it. An unfitted or half-fit model raises immediately before touching the dataset.","triggerScenarios":"predict() called before fit(); predict() after a fit() that crashed (empty data, bad loss/metric config, OOM) — fitted remains False.","commonSituations":"Retry loops in backtest scripts that call predict without checking fit status; notebook cells run out of order after a failed training cell.","solutions":["Run fit() to completion before predict().","Check the fitted attribute (or 'best score' log line) before predicting in scripts.","Fix the underlying fit()-time failure if fit never finishes."],"exampleFix":"if not model.fitted:\n    model.fit(dataset)\npreds = model.predict(dataset, segment=\"test\")","handlingStrategy":"validation","validationCode":"if not model.fitted:\n    raise RuntimeError(\"SFM model must complete fit() before predict()\")\npreds = model.predict(dataset, segment=\"test\")","typeGuard":"def is_fitted(model) -> bool:\n    return bool(getattr(model, \"fitted\", False))","tryCatchPattern":"try:\n    preds = model.predict(dataset, segment)\nexcept ValueError as e:\n    if \"not fitted\" in str(e):\n        raise RuntimeError(\"fit() never completed; fix training errors first\") from e\n    raise","preventionTips":["Guard predict calls with model.fitted checks in pipelines.","Abort workflows on fit failure rather than attempting inference."],"tags":["qlib","pytorch","lifecycle","predict-before-fit","sfm"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}