microsoft/qlib · error · ValueError

model is not fitted yet!

Error message

model is not fitted yet!

What it means

HISTModel.predict raises this guard when self.fitted is still False. The fitted flag is only set to True after fit() completes its training loop, so predicting before a successful fit (or after a fit that crashed mid-way) is rejected.

Source

Thrown at qlib/contrib/model/pytorch_hist.py:332

            if val_score > best_score:
                best_score = val_score
                stop_steps = 0
                best_epoch = step
                best_param = copy.deepcopy(self.HIST_model.state_dict())
            else:
                stop_steps += 1
                if stop_steps >= self.early_stop:
                    self.logger.info("early stop")
                    break

        self.logger.info("best score: %.6lf @ %d" % (best_score, best_epoch))
        self.HIST_model.load_state_dict(best_param)
        torch.save(best_param, save_path)

    def predict(self, dataset: DatasetH, segment: Union[Text, slice] = "test"):
        if not self.fitted:
            raise ValueError("model is not fitted yet!")

        stock2concept_matrix = np.load(self.stock2concept)
        stock_index = np.load(self.stock_index, allow_pickle=True).item()
        df_test = dataset.prepare(segment, col_set="feature", data_key=DataHandlerLP.DK_I)
        df_test["stock_index"] = 733
        df_test["stock_index"] = df_test.index.get_level_values("instrument").map(stock_index)
        stock_index_test = df_test["stock_index"].values
        stock_index_test[np.isnan(stock_index_test)] = 733
        stock_index_test = stock_index_test.astype("int")
        df_test = df_test.drop(["stock_index"], axis=1)
        index = df_test.index

        self.HIST_model.eval()
        x_values = df_test.values
        preds = []

        # organize the data into daily batches
        daily_index, daily_count = self.get_daily_inter(df_test, shuffle=False)

View on GitHub (pinned to 79633dd950)

Solutions

  1. Call model.fit(dataset) successfully before model.predict(dataset)
  2. If the exception from fit is being swallowed, fix the control flow so predict is unreachable on failure
  3. To reuse trained weights without refitting, restore state and set model.fitted = True after loading the saved checkpoint with load_state_dict

Example fix

# before
model = HISTModel()
preds = model.predict(dataset)  # fitted is False

# after
model = HISTModel()
model.fit(dataset)
preds = model.predict(dataset)
Defensive patterns

Strategy: validation

Validate before calling

if not getattr(model, "fitted", False):
    raise RuntimeError("call model.fit(dataset) before predict")

Type guard

def is_fitted(model) -> bool:
    return bool(getattr(model, "fitted", False))

Try / catch

try:
    preds = model.predict(dataset)
except ValueError as e:
    if "not fitted" in str(e):
        model.fit(dataset)
        preds = model.predict(dataset)
    else:
        raise

Prevention

When it happens

Trigger: Calling model.predict(dataset) on a fresh HISTModel that never ran fit(), or after fit() raised an exception before setting self.fitted = True, or using a model instance reloaded from a pickled workflow where fit was never executed.

Common situations: Two-stage scripts where prediction runs in a separate process/model object than training; a fit() that failed early (e.g. empty data) but the caller ignores the exception and proceeds; refactoring code that splits train and infer.

Related errors


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/ad2e9d4e6a1d26eb. Report an issue: GitHub.