microsoft/qlib · error · ValueError

unknown loss `%s`

Error message

unknown loss `%s`

What it means

GATsModel.loss_fn supports exactly one loss: self.loss == 'mse'. Any other value skips the branch and raises ValueError from loss_fn, which is called by both the training loop and metric_fn, so the error surfaces on the first training batch of fit().

Source

Thrown at qlib/contrib/model/pytorch_gats.py:154

        self.fitted = False
        self.GAT_model.to(self.device)

    @property
    def use_gpu(self):
        return self.device != torch.device("cpu")

    def mse(self, pred, label):
        loss = (pred - label) ** 2
        return torch.mean(loss)

    def loss_fn(self, pred, label):
        mask = ~torch.isnan(label)

        if self.loss == "mse":
            return self.mse(pred[mask], label[mask])

        raise ValueError("unknown loss `%s`" % self.loss)

    def metric_fn(self, pred, label):
        mask = torch.isfinite(label)

        if self.metric in ("", "loss"):
            return -self.loss_fn(pred[mask], label[mask])

        raise ValueError("unknown metric `%s`" % self.metric)

    def get_daily_inter(self, df, shuffle=False):
        # organize the train data into daily batches
        daily_count = df.groupby(level=0, group_keys=False).size().values
        daily_index = np.roll(np.cumsum(daily_count), 1)
        daily_index[0] = 0
        if shuffle:
            # shuffle data
            daily_shuffle = list(zip(daily_index, daily_count))
            np.random.shuffle(daily_shuffle)

View on GitHub (pinned to 79633dd950)

Solutions

  1. Set loss='mse' (the default) in the GATsModel constructor or workflow config.
  2. Audit the loss hyperparameter for typos or values ported from other model configs.
  3. Subclass GATsModel and extend loss_fn with a new elif branch for a custom loss.

Example fix

# before
model = GATsModel(loss='huber')

# after
model = GATsModel(loss='mse')
Defensive patterns

Strategy: validation

Validate before calling

assert model.loss == 'mse', f"GATsModel only supports loss='mse', got {model.loss!r}"

Type guard

def is_supported_gats_loss(loss: str) -> bool:
    return loss == 'mse'

Try / catch

try:
    model.fit(dataset)
except ValueError as e:
    if 'unknown loss' in str(e):
        model.loss = 'mse'
        model.fit(dataset)
    else:
        raise

Prevention

When it happens

Trigger: Constructing GATsModel(loss='mae') or loss='' and calling fit(); feeding a loss name valid in another qlib model (ALSTM, TRA) into GATs.

Common situations: Copying a hyperparameter YAML between qlib contrib models where 'mse' is the only supported value here; leaving loss unset in a config template whose default differs from the class default.

Related errors


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