112 lines
3.5 KiB
Python
Executable File
112 lines
3.5 KiB
Python
Executable File
# This source code is provided for the purposes of scientific reproducibility
|
|
# under the following limited license from Element AI Inc. The code is an
|
|
# implementation of the N-BEATS model (Oreshkin et al., N-BEATS: Neural basis
|
|
# expansion analysis for interpretable time series forecasting,
|
|
# https://arxiv.org/abs/1905.10437). The copyright to the source code is
|
|
# licensed under the Creative Commons - Attribution-NonCommercial 4.0
|
|
# International license (CC BY-NC 4.0):
|
|
# https://creativecommons.org/licenses/by-nc/4.0/. Any commercial use (whether
|
|
# for the benefit of third parties or internally in production) requires an
|
|
# explicit license. The subject-matter of the N-BEATS model and associated
|
|
# materials are the property of Element AI Inc. and may be subject to patent
|
|
# protection. No license to patents is granted hereunder (whether express or
|
|
# implied). Copyright © 2020 Element AI Inc. All rights reserved.
|
|
|
|
"""
|
|
Loss functions for PyTorch.
|
|
"""
|
|
|
|
import torch as t
|
|
import torch.nn as nn
|
|
import numpy as np
|
|
import pdb
|
|
|
|
|
|
def divide_no_nan(a, b):
|
|
"""
|
|
a/b where the resulted NaN or Inf are replaced by 0.
|
|
"""
|
|
result = a / b
|
|
result[result != result] = 0.0
|
|
result[result == np.inf] = 0.0
|
|
return result
|
|
|
|
|
|
class mape_loss(nn.Module):
|
|
def __init__(self):
|
|
super(mape_loss, self).__init__()
|
|
|
|
def forward(
|
|
self,
|
|
insample: t.Tensor,
|
|
freq: int,
|
|
forecast: t.Tensor,
|
|
target: t.Tensor,
|
|
mask: t.Tensor,
|
|
) -> t.float:
|
|
"""
|
|
MAPE loss as defined in: https://en.wikipedia.org/wiki/Mean_absolute_percentage_error
|
|
|
|
:param forecast: Forecast values. Shape: batch, time
|
|
:param target: Target values. Shape: batch, time
|
|
:param mask: 0/1 mask. Shape: batch, time
|
|
:return: Loss value
|
|
"""
|
|
weights = divide_no_nan(mask, target)
|
|
return t.mean(t.abs((forecast - target) * weights))
|
|
|
|
|
|
class smape_loss(nn.Module):
|
|
def __init__(self):
|
|
super(smape_loss, self).__init__()
|
|
|
|
def forward(
|
|
self,
|
|
insample: t.Tensor,
|
|
freq: int,
|
|
forecast: t.Tensor,
|
|
target: t.Tensor,
|
|
mask: t.Tensor,
|
|
) -> t.float:
|
|
"""
|
|
sMAPE loss as defined in https://robjhyndman.com/hyndsight/smape/ (Makridakis 1993)
|
|
|
|
:param forecast: Forecast values. Shape: batch, time
|
|
:param target: Target values. Shape: batch, time
|
|
:param mask: 0/1 mask. Shape: batch, time
|
|
:return: Loss value
|
|
"""
|
|
return 200 * t.mean(
|
|
divide_no_nan(
|
|
t.abs(forecast - target), t.abs(forecast.data) + t.abs(target.data)
|
|
)
|
|
* mask
|
|
)
|
|
|
|
|
|
class mase_loss(nn.Module):
|
|
def __init__(self):
|
|
super(mase_loss, self).__init__()
|
|
|
|
def forward(
|
|
self,
|
|
insample: t.Tensor,
|
|
freq: int,
|
|
forecast: t.Tensor,
|
|
target: t.Tensor,
|
|
mask: t.Tensor,
|
|
) -> t.float:
|
|
"""
|
|
MASE loss as defined in "Scaled Errors" https://robjhyndman.com/papers/mase.pdf
|
|
|
|
:param insample: Insample values. Shape: batch, time_i
|
|
:param freq: Frequency value
|
|
:param forecast: Forecast values. Shape: batch, time_o
|
|
:param target: Target values. Shape: batch, time_o
|
|
:param mask: 0/1 mask. Shape: batch, time_o
|
|
:return: Loss value
|
|
"""
|
|
masep = t.mean(t.abs(insample[:, freq:] - insample[:, :-freq]), dim=1)
|
|
masked_masep_inv = divide_no_nan(mask, masep[:, None])
|
|
return t.mean(t.abs(target - forecast) * masked_masep_inv)
|