NeuralForecast VanillaTransformer MAE损失函数

2024-06-05 22:20

本文主要是介绍NeuralForecast VanillaTransformer MAE损失函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

NeuralForecast VanillaTransformer MAE损失函数

flyfish

nn.L1Loss() 和 自定义的class MAE(BasePointLoss): 在本质上都是计算 Mean Absolute Error (MAE),但是它们有一些不同之处,主要在于定制化和功能上的差异。
写一个自定义的MAE完整示例代码


import mathfrom typing import Optional, Union, Tupleimport math
import numpy as np
import torchimport torch.nn as nn
import torch.nn.functional as F
def _divide_no_nan(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:"""Auxiliary funtion to handle divide by 0"""div = a / bdiv[div != div] = 0.0div[div == float("inf")] = 0.0return div
def _weighted_mean(losses, weights):"""Compute weighted mean of losses per datapoint."""return _divide_no_nan(torch.sum(losses * weights), torch.sum(weights))
class BasePointLoss(torch.nn.Module):"""Base class for point loss functions.**Parameters:**<br>`horizon_weight`: Tensor of size h, weight for each timestamp of the forecasting window. <br>`outputsize_multiplier`: Multiplier for the output size. <br>`output_names`: Names of the outputs. <br>"""def __init__(self, horizon_weight, outputsize_multiplier, output_names):super(BasePointLoss, self).__init__()if horizon_weight is not None:horizon_weight = torch.Tensor(horizon_weight.flatten())self.horizon_weight = horizon_weightself.outputsize_multiplier = outputsize_multiplierself.output_names = output_namesself.is_distribution_output = Falsedef domain_map(self, y_hat: torch.Tensor):"""Univariate loss operates in dimension [B,T,H]/[B,H]This changes the network's output from [B,H,1]->[B,H]"""return y_hat.squeeze(-1)def _compute_weights(self, y, mask):"""Compute final weights for each datapoint (based on all weights and all masks)Set horizon_weight to a ones[H] tensor if not set.If set, check that it has the same length as the horizon in x."""if mask is None:mask = torch.ones_like(y, device=y.device)if self.horizon_weight is None:self.horizon_weight = torch.ones(mask.shape[-1])else:assert mask.shape[-1] == len(self.horizon_weight), "horizon_weight must have same length as Y"weights = self.horizon_weight.clone()weights = torch.ones_like(mask, device=mask.device) * weights.to(mask.device)return weights * maskclass MAE(BasePointLoss):"""Mean Absolute ErrorCalculates Mean Absolute Error between`y` and `y_hat`. MAE measures the relative predictionaccuracy of a forecasting method by calculating thedeviation of the prediction and the truevalue at a given time and averages these devationsover the length of the series.$$ \mathrm{MAE}(\\mathbf{y}_{\\tau}, \\mathbf{\hat{y}}_{\\tau}) = \\frac{1}{H} \\sum^{t+H}_{\\tau=t+1} |y_{\\tau} - \hat{y}_{\\tau}| $$**Parameters:**<br>`horizon_weight`: Tensor of size h, weight for each timestamp of the forecasting window. <br>"""def __init__(self, horizon_weight=None):super(MAE, self).__init__(horizon_weight=horizon_weight, outputsize_multiplier=1, output_names=[""])def __call__(self,y: torch.Tensor,y_hat: torch.Tensor,mask: Union[torch.Tensor, None] = None,):"""**Parameters:**<br>`y`: tensor, Actual values.<br>`y_hat`: tensor, Predicted values.<br>`mask`: tensor, Specifies datapoints to consider in loss.<br>**Returns:**<br>`mae`: tensor (single value)."""losses = torch.abs(y - y_hat)weights = self._compute_weights(y=y, mask=mask)return _weighted_mean(losses=losses, weights=weights)# 定义简单的线性模型示例
class SimpleModel(nn.Module):def __init__(self):super(SimpleModel, self).__init__()self.linear = nn.Linear(10, 1)  # 10个输入特征,1个输出def forward(self, x):return self.linear(x)# 初始化模型和损失函数
model = SimpleModel()
mae_loss = MAE(horizon_weight=None)# 生成示例数据
# 批次大小为5,时间步长为10,假设预测未来一个时间步的值
batch_size = 5
time_steps = 10
input_features = 10# 随机生成输入数据和真实标签
x = torch.randn(batch_size, input_features)
y = torch.randn(batch_size, 1)  # 真实值# 生成预测值
y_hat = model(x)# 调用 domain_map 函数
y_hat_mapped = mae_loss.domain_map(y_hat)# 调用损失函数
# 这里假设 mask 为 None,表示考虑所有数据点
mae_value = mae_loss(y, y_hat_mapped, mask=None)# 打印MAE值
print("Mean Absolute Error (MAE):", mae_value.item())

M A E = 1 H ∑ i = 1 H ∣ y i − y ^ i ∣ \mathrm{MAE} = \frac{1}{H} \sum_{i=1}^{H} | y_i - \hat{y}_i | MAE=H1i=1Hyiy^i

y i y_i yi表示实际值。
y ^ i \hat{y}_i y^i表示预测值。
H H H表示预测的时间步数或样本数量

这两个函数是用来计算加权平均损失的辅助函数。

_divide_no_nan(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:

这个函数用来处理两个张量相除时出现除以零的情况。它首先计算两个张量相除的结果 div,然后将结果中的 NaN 值(由除以零导致)替换为 0.0,并将结果中的正无穷值替换为 0.0,最后返回处理后的结果。

如果 b 中包含 0,那么 a / b 的计算会产生除以零的情况,这会导致结果中出现 NaN(“Not a Number”)或正无穷大(inf)值。_divide_no_nan 函数的目的是处理这些情况,确保输出结果中没有 NaN 或无穷大值。

让我们详细说明这一过程:

初始计算:
div = a / b 进行逐元素相除,如果 b 中有 0,结果 div 中相应位置会包含 NaN 或 inf。

替换 NaN 值:
div[div != div] = 0.0 这一行代码使用了一个技巧:由于 NaN 不等于任何值,包括它自己,div != div 会在 NaN 所在的位置返回 True。于是,div[div != div] 会选中所有 NaN 并将其设置为 0.0。

替换 inf 值:
div[div == float(“inf”)] = 0.0 这一行代码将所有正无穷大(inf)值替换为 0.0。

因此,如果 b 包含 0,函数 _divide_no_nan 会确保相应位置的结果是 0.0,而不是 NaN 或 inf。这保证了计算的稳定性和结果的可用性。

def _divide_no_nan(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:div = a / bdiv[div != div] = 0.0div[div == float("inf")] = 0.0return diva = torch.tensor([1.0, 2.0, 3.0])
b = torch.tensor([0.0, 2.0, 0.0])result = _divide_no_nan(a, b)
print(result)  # 输出 tensor([0., 1., 0.])

在这个例子中,a / b 会生成 [inf, 1.0, inf],然后 div[div != div] = 0.0 将 NaN 转换为 0.0(但在这个例子中没有 NaN),div[div == float(“inf”)] = 0.0 将 inf 转换为 0.0,最终结果是 [0.0, 1.0, 0.0]。

_weighted_mean(losses, weights):

这个函数用来计算加权平均损失。它接收两个参数,losses 表示每个数据点的损失值,weights 表示每个数据点的权重。函数首先计算每个损失值乘以相应的权重,然后将所有加权损失值相加,最后除以所有权重的总和。在这个过程中,_divide_no_nan 函数被用来处理除以零的情况,确保计算的稳定性。

BasePointLoss

BasePointLoss 是一个 PyTorch 模块类,用于定义时间序列预测中的基础点损失函数。它提供了一些通用的功能和参数设置,这些功能和设置可以在具体的点损失函数(如均方误差 MSE 或平均绝对误差 MAE)中继承和使用。

主要功能和参数
以下是 BasePointLoss 类的主要功能和参数说明:

horizon_weight:
这是一个大小为 h 的张量,表示预测窗口中每个时间戳的权重。如果没有提供,它将在计算时设置为全 1 的张量。

outputsize_multiplier:
这是一个用于调整输出大小的乘数。

output_names:
这是一个列表,包含输出的名称。

该方法根据 horizon_weight 和 mask 计算每个数据点的权重。如果 horizon_weight 未设置,它将默认为全 1 的张量;否则,它会检查 horizon_weight 的长度是否与 y 的最后一维相同。

import torch# 定义示例 horizon_weight 和 mask
horizon_weight = torch.Tensor([0.1, 0.3, 0.6])
mask = torch.Tensor([[1, 0, 1], [1, 1, 0]])# 定义 y(实际值),这里只是为了展示维度,具体值不影响计算权重
y = torch.Tensor([[2, 3, 4],[1, 2, 3]])# 模拟 BasePointLoss 类的 _compute_weights 方法
def compute_weights(horizon_weight, y, mask):if mask is None:mask = torch.ones_like(y, device=y.device)if horizon_weight is None:horizon_weight = torch.ones(mask.shape[-1])else:assert mask.shape[-1] == len(horizon_weight), "horizon_weight must have same length as Y"weights = horizon_weight.clone()weights = torch.ones_like(mask, device=mask.device) * weights.to(mask.device)return weights * mask# 计算权重
final_weights = compute_weights(horizon_weight, y, mask)# 打印最终权重
print("Final Weights:")
print(final_weights)

horizon_weight:
定义每个时间点的权重。例如,[0.1, 0.3, 0.6] 表示第一个时间点的权重为 0.1,第二个时间点为 0.3,第三个时间点为 0.6。

mask:
定义哪些数据点应被考虑。例如,[[1, 0, 1], [1, 1, 0]] 表示第一个样本的第二个时间点和第二个样本的第三个时间点不被考虑。

y:
实际值,仅用于展示维度。在这个例子中,假设每个样本在时间维度上有 3 个点。

compute_weights:
计算最终的权重。如果 mask 为 None,则默认为全 1。如果 horizon_weight 为 None,则默认为全 1。最终权重是 horizon_weight 和 mask 的逐元素乘积。

输出结果

Final Weights:
tensor([[0.1000, 0.0000, 0.6000],[0.1000, 0.3000, 0.0000]])

这个结果表明,权重和掩码的结合使得某些数据点被赋予了相应的权重,而被掩盖的点(即掩码为 0 的点)的权重为 0。

使用MAE 损失的原因

鲁棒性:
MAE 对于异常值的影响比均方误差 (MSE) 小,因为它计算的是绝对误差,而不是平方误差。这使得 MAE 在存在异常值或噪声的时间序列中表现更加稳健。

简单易解释:
MAE 直接衡量预测值与真实值之间的平均绝对差异,这使得其结果容易解释。它表示的是预测值与真实值之间的平均距离,这在实际应用中非常直观。

公平的误差惩罚:
MAE 对每个数据点的误差惩罚是线性的,这意味着每个预测误差都会被同等对待。相比之下,MSE 会对较大的误差赋予更高的惩罚,这在某些应用场景下可能会导致不必要的偏差。

nn.L1Loss和自定义的MAE比较下,体现一下自定义的功能

nn.L1Loss 的调用

PyTorch 内置的损失函数,用于计算预测值和真实值之间的平均绝对误差。其使用非常简单,默认情况下对每个数据点给予相同的权重,没有其他附加功能。

import torch
import torch.nn as nn# Example usage of nn.L1Loss
loss_fn = nn.L1Loss()
y = torch.tensor([1.0, 2.0, 3.0])
y_hat = torch.tensor([1.5, 2.5, 3.5])
loss = loss_fn(y_hat, y)
print(loss.item())  # Output: 0.5

class MAE(BasePointLoss) 的调用

这个自定义的 MAE 类继承自 BasePointLoss,是一个更复杂和定制化的实现。它具有以下特点:

可选的时间权重(horizon_weight):
MAE 类可以接受一个时间权重向量 horizon_weight,对预测窗口内的每个时间点赋予不同的权重。这在一些应用场景中非常有用,例如希望对特定时间点的预测误差给予更多的关注。

掩码(mask):
该类可以接受一个掩码 mask,指定哪些数据点应被纳入损失计算。这在处理缺失数据或不完整数据集时非常有用。

自定义功能:
由于继承自 BasePointLoss,这个 MAE 类可以进一步扩展和定制,以满足特定的需求。
示例实现中 _compute_weights 方法展示了如何计算权重,并在计算损失时使用这些权重。

具体示例比较

from typing import Optional, Union, Tuple
import math
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as Fdef _divide_no_nan(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:"""Auxiliary funtion to handle divide by 0"""div = a / bdiv[div != div] = 0.0div[div == float("inf")] = 0.0return div
def _weighted_mean(losses, weights):"""Compute weighted mean of losses per datapoint."""return _divide_no_nan(torch.sum(losses * weights), torch.sum(weights))
class BasePointLoss(torch.nn.Module):def __init__(self, horizon_weight, outputsize_multiplier, output_names):super(BasePointLoss, self).__init__()if horizon_weight is not None:horizon_weight = torch.Tensor(horizon_weight.flatten())self.horizon_weight = horizon_weightself.outputsize_multiplier = outputsize_multiplierself.output_names = output_namesself.is_distribution_output = Falsedef domain_map(self, y_hat: torch.Tensor):return y_hat.squeeze(-1)def _compute_weights(self, y, mask):if mask is None:mask = torch.ones_like(y, device=y.device)if self.horizon_weight is None:self.horizon_weight = torch.ones(mask.shape[-1])else:assert mask.shape[-1] == len(self.horizon_weight), "horizon_weight must have same length as Y"weights = self.horizon_weight.clone()weights = torch.ones_like(mask, device=mask.device) * weights.to(mask.device)return weights * maskclass MAE(BasePointLoss):"""Mean Absolute Error"""def __init__(self, horizon_weight=None):super(MAE, self).__init__(horizon_weight=horizon_weight, outputsize_multiplier=1, output_names=[""])def __call__(self,y: torch.Tensor,y_hat: torch.Tensor,mask: Union[torch.Tensor, None] = None,):losses = torch.abs(y - y_hat)weights = self._compute_weights(y=y, mask=mask)return _weighted_mean(losses=losses, weights=weights)# Example usage of custom MAE
mae_loss = MAE(horizon_weight=torch.tensor([1, 2, 3]))  # Custom weights for a 3-step horizon# 生成示例数据
batch_size = 2
horizon = 3# 随机生成输入数据和真实标签
y = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
y_hat = torch.tensor([[1.5, 2.5, 3.5], [4.5, 5.5, 6.5]])
mask = torch.tensor([[1, 0, 1], [1, 1, 1]])  # Consider some elements# 调用损失函数
loss = mae_loss(y, y_hat, mask=mask)# 打印 MAE 值
print("Mean Absolute Error (MAE):", loss.item())#Mean Absolute Error (MAE): 0.5

如果只是需要简单的 MAE,nn.L1Loss 就足够了;如果需要更多的控制和定制化,使用自定义的MAE(BasePointLoss)

这篇关于NeuralForecast VanillaTransformer MAE损失函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1034330

相关文章

Python的time模块一些常用功能(各种与时间相关的函数)

《Python的time模块一些常用功能(各种与时间相关的函数)》Python的time模块提供了各种与时间相关的函数,包括获取当前时间、处理时间间隔、执行时间测量等,:本文主要介绍Python的... 目录1. 获取当前时间2. 时间格式化3. 延时执行4. 时间戳运算5. 计算代码执行时间6. 转换为指

Python正则表达式语法及re模块中的常用函数详解

《Python正则表达式语法及re模块中的常用函数详解》这篇文章主要给大家介绍了关于Python正则表达式语法及re模块中常用函数的相关资料,正则表达式是一种强大的字符串处理工具,可以用于匹配、切分、... 目录概念、作用和步骤语法re模块中的常用函数总结 概念、作用和步骤概念: 本身也是一个字符串,其中

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

MySQL高级查询之JOIN、子查询、窗口函数实际案例

《MySQL高级查询之JOIN、子查询、窗口函数实际案例》:本文主要介绍MySQL高级查询之JOIN、子查询、窗口函数实际案例的相关资料,JOIN用于多表关联查询,子查询用于数据筛选和过滤,窗口函... 目录前言1. JOIN(连接查询)1.1 内连接(INNER JOIN)1.2 左连接(LEFT JOI

MySQL中FIND_IN_SET函数与INSTR函数用法解析

《MySQL中FIND_IN_SET函数与INSTR函数用法解析》:本文主要介绍MySQL中FIND_IN_SET函数与INSTR函数用法解析,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一... 目录一、功能定义与语法1、FIND_IN_SET函数2、INSTR函数二、本质区别对比三、实际场景案例分

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

C语言函数递归实际应用举例详解

《C语言函数递归实际应用举例详解》程序调用自身的编程技巧称为递归,递归做为一种算法在程序设计语言中广泛应用,:本文主要介绍C语言函数递归实际应用举例的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录前言一、递归的概念与思想二、递归的限制条件 三、递归的实际应用举例(一)求 n 的阶乘(二)顺序打印

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

Kotlin 作用域函数apply、let、run、with、also使用指南

《Kotlin作用域函数apply、let、run、with、also使用指南》在Kotlin开发中,作用域函数(ScopeFunctions)是一组能让代码更简洁、更函数式的高阶函数,本文将... 目录一、引言:为什么需要作用域函数?二、作用域函China编程数详解1. apply:对象配置的 “流式构建器”最

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda