我们在之前的文章中介绍过如何使用DeepSeek的核心结构MoE以及MLA用于选股:
除了这两个结构之外,DeepSeek还采用了GRPO的强化学习方法用于更新策略。
本文介绍利用状态空间增强的多头潜在注意力(MLA)和组相对策略优化(GRPO)进行自适应预测
引言
DeepSeek的技术给我留下了深刻的印象——其高效的多头潜在注意力(MLA)和组相对策略优化(GRPO)技术启发了我,将它们应用于多产品时间序列预测。在我们的方法中,我们将MLA扩展为MLA-Mamba,允许潜在特征通过具有非线性激活的状态空间模型随时间动态演变。这赋予了我们的模型一种自适应记忆能力,能够像销售团队在市场激增期间调整策略一样适应趋势。与此同时,GRPO引入了一种智能决策过程,通过将预测与基线进行比较,持续优化预测结果,类似于管理者实时调整预测。这种动态调整有助于我们的模型有效应对销售模式的突然变化。
我们将这种方法与经典的ARMA模型和标准的基于GRU的网络进行了比较。虽然ARMA能够处理线性趋势,GRU能够捕捉时间依赖性,但我们的DeepSeek-TS框架旨在建模复杂的产品间关系并适应非线性动态,从而实现更准确、更稳健的预测。
在接下来的部分中,我们将详细剖析扩展的MLA(MLA-Mamba)和GRPO框架的技术细节,并展示它们的协同作用如何增强多产品时间序列预测。
多头潜在注意力(MLA)
DeepSeek中MLA的核心思想是将键(Key)和值(Value)压缩到低维潜在空间。这使得模型在推理过程中能够存储更小的KV缓存。该过程可以分解为几个关键的数学步骤:
键和值的潜在压缩
考虑一个输入标记,用向量 表示,其维度为 。在标准的Transformer中,该向量通过学习矩阵被投影到查询(Query)、键(Key)和值(Value)空间。然而,在DeepSeek的MLA中,我们首先将 压缩为一个专门用于键和值的低维潜在向量。这一过程可以通过以下公式表示:
其中:
-
是压缩后的潜在向量;
-
是降维投影矩阵;
-
是压缩维度,且 。
这种低秩近似类似于推荐系统中使用的矩阵分解技术,其中大矩阵通过两个较小矩阵的乘积来近似,以捕捉最重要的特征。在我们的案例中, 学习捕捉 中对计算注意力至关重要的方面。
从潜在代码重构键和值
一旦我们得到了 ,就需要通过上投影来重构用于注意力机制的键和值向量。这一过程可以通过以下公式实现:
其中:
-
和 分别是重构后的键和值向量;
-
和 是上投影矩阵,其维度为 (其中 是每个头的维度, 是头的数量)。
关键在于,在推理过程中,我们无需为每个标记缓存完整的键和值向量(这将需要存储每个标记的 个元素),而只需缓存压缩后的潜在向量 ,其中每个标记仅包含 个元素。当 远小于 时,这种减少是显著的。
查询压缩(可选,用于提高训练效率)
除了键和值之外,DeepSeek还对查询进行了类似的低秩压缩,以减少训练过程中的激活内存。该过程与上述类似:
其中:
-
是压缩后的查询向量;
-
和 是查询的降维和上投影矩阵;
-
是查询压缩维度。
虽然压缩查询并不会减少KV缓存(因为查询是即时计算的),但它有助于在训练过程中减少整体激活内存。
效率提升:一个示例
假设你有一个Transformer层,其中每个标记的原始键值维度为1024(即 )。使用MLA时,如果你选择压缩维度 ,那么每个标记缓存的数据量将从1024个元素减少到128个元素——减少了8倍。当处理长序列或大规模部署模型时,这一减少是显著的。
此外,在推理过程中,如果上投影矩阵 和 可以吸收进其他权重矩阵(例如 或 ),那么你可能根本无需显式计算或存储键和值。这将带来更大的效率提升。
组相对策略优化(GRPO)
虽然MLA专注于高效的注意力机制,但DeepSeek还引入了一种新颖的优化方法,即组相对策略优化(GRPO),用于更新模型的决策策略。这种方法基于强化学习原理,旨在平衡探索与利用,同时确保策略更新的稳定性。
策略优化基础
在强化学习中,策略 定义了在状态 下采取行动 的概率,参数为 。目标是最大化预期累积奖励:
其中:
-
是时间 的奖励;
-
是折扣因子,用于确定未来奖励的重要性。
使用组进行相对策略优化
GRPO引入了一种新思想,即通过将新策略的输出与旧策略(固定版本)的输出组进行比较来评估新策略。关键在于,通过将新策略的输出与旧策略的输出进行比较,可以更稳健地衡量某些行动的优势。
设:
-
是用于生成相同查询 的输出组的旧策略;
-
是当前可更新的策略。
比率:
衡量了新策略相对于旧策略的偏离程度。为了稳定训练,通常会将该比率限制在 和 之间,其中 是一个小的超参数。这种限制确保策略在单次更新中不会发生过大的变化。
优势估计
优势函数 衡量了行动 相对于平均表现的好坏程度:
其中 是一个基线价值函数,表示从状态 开始的预期奖励。在GRPO中,优势用于加权更新,确保那些导致高于平均水平奖励的行动得到强化。
策略梯度更新公式为:
该更新规则表明,我们应该调整 的方向,以增加具有正优势的行动的概率,同时减少具有负优势的行动的概率。
GRPO的新颖贡献
GRPO在先前的方法(如近端策略优化(PPO)和直接策略优化(DPO))的基础上进行了扩展,但引入了一种新机制,通过比较旧策略的输出组与新策略的输出来进行更新。这种“组相对”比较允许更稳定和可靠的更新。策略更新不仅考虑了新策略的输出,还考虑了它们相对于旧策略提供的稳定基线的表现。
关键公式(概念性):
其中:
clip函数限制了比率 ,目标是最大化 。该公式确保,如果新策略的偏离过大,更新将受到限制,从而防止可能导致训练不稳定的过度变化。
GRPO的实际应用示例
以我论文中的一个例子为例。假设我们的强化学习代理的任务是从给定的查询中选择最佳模型或模型组合。该查询来自我们的MLA过程生成的潜在表示 。假设旧策略 为某个特定查询分配了一个行动概率分布(例如选择XGBoost、LightGBM、DNN或混合模型),并为“选择DNN”这一行动赋予了0.25的概率。与此同时,新策略 可能为“选择DNN”这一行动赋予了0.35的概率。我们随后计算这些概率的比率。如果 设为0.2,我们将比率 限制在区间 [0.8, 1.2] 内,以确保学习过程中的稳定性。
如果 设为0.2,那么我们将 限制在区间 [0.8, 1.2] 内。假设计算出的“选择DNN”这一行动的优势 为 +0.5(表明在这种情况下选择DNN是有益的)。
策略梯度更新将使用限制后的比率:
这种受控的更新有助于确保策略仅逐渐向新的行动概率倾斜,基于该行动相对于组的表现有多好。
将MLA和GRPO整合到统一框架中
让我们总结一下DeepSeek的突破性成果——MLA和GRPO——并将它们整合到一个自适应模型中。目标是构建一个系统,该系统不仅使用高效的低秩注意力来处理长序列,还利用强化学习智能地选择或混合模型。
- 输入编码和潜在压缩:每个输入标记 首先由编码器处理。编码器使用以下公式将 压缩为潜在表示:
将原始维度 降低到较小的潜在空间维度 。
- 键和值的重构:通过以下公式重构用于注意力的键和值:
这种重构确保在保持KV缓存较小的同时保留了必要的上下文信息。
-
查询压缩(可选):
-
注意力计算:使用压缩后的查询、键和值计算多头注意力。每个头应用通常的注意力公式,但降低的维度使得该过程更加高效。
-
通过GRPO进行策略决策:模型随后使用强化学习模块来选择最佳行动——无论是选择一个模型还是混合多个模型。强化学习策略 接收状态(包括来自MLA模块的潜在特征和额外的统计摘要),并输出一个行动。GRPO通过比较新策略的输出与早期固定策略的输出组来更新策略。计算优势,并对更新进行限制以确保稳定性。
其中:
目标是最大化 。该公式确保,如果新策略的偏离过大,更新将受到限制,从而防止可能导致训练不稳定的过度变化。
将MLA和GRPO应用于多产品时间序列预测
在前一节中,我们讨论了MLA和GRPO在DeepSeek中的有效协同工作,它们构成了DeepSeek的核心技术。这促使我探索这些技术是否可以应用于LLM之外的其他机器学习领域。经过彻底的理论分析,我确认它们可以扩展到新的领域。因此,我提出了一个统一框架,将MLA和GRPO结合起来用于多产品时间序列预测。具体来说,使用一个包含“日期”、“产品1销售额”、“产品2销售额”……“产品5销售额”等列的DataFrame,我们的目标是预测每种产品在未来10天内的平均销售额。我们的方法将“Mamba风格”的状态空间建模与潜在注意力相结合,并利用基于强化学习的策略优化(GRPO)动态调整预测。
以下是数学基础、算法细节以及一个实际示例。
问题设置和数据描述
假设销售数据是一个多变量时间序列 ,其中每个 表示时间 时 种产品(此处 )的销售额。目标是预测每种产品在未来10天内的平均销售额。挑战在于时间序列可能存在内部相关性和滞后效应。例如,产品1在第 天的销售额可能不仅取决于其自身的过去销售额,还可能受到产品2或产品3过去几天销售额的影响。
将MLA扩展到状态空间(Mamba)框架
为了捕捉时间序列预测中固有的时间动态,我建议将潜在压缩步骤与通过非线性激活增强的状态空间更新相结合。在这个框架中,我假设压缩后的潜在向量随时间演变如下:
其中:
-
是一个转移矩阵,用于建模潜在状态动态;
-
是一个函数,将当前输入 (例如时间 的销售数据)映射到潜在空间中的一个修正项;
-
整个更新应用了ReLU激活,引入非线性并确保更新后的潜在状态是非负的。
类似地,可以对查询压缩应用类似的更新:
其中 的定义类似。
解释:这种状态空间更新“记住”了过去的潜在状态 ,并使用新信息 进行调整。通过对整个和 应用ReLU激活,模型捕捉了历史状态和新输入之间复杂的非线性相互作用。非线性有助于建模复杂的时序模式,同时确保潜在表示保持非负。这种方法类似于RNN或LSTM更新其隐藏状态的方式。
引入多头注意力
在获得动态潜在状态 和 后,我将它们投影到多头注意力的键、值和查询中。假设将潜在空间划分为 个头。对于头 :
其中 、、 是权重矩阵, 是每个头的维度。
每个头的注意力计算如下:
所有头的输出随后被拼接并使用输出矩阵 进行投影:
这种多头机制允许模型捕捉销售数据中不同方面的时序关系。例如,一个头可能学习趋势成分,另一个头可能专注于季节性等。
扩展GRPO以适应多时间序列预测
在这个框架中,我首先定义了一个给定输入窗口长度 和预测范围 天的预测问题。对于每个产品时间序列 ,目标 被计算为未来 天内原始销售额的平均值,即 。
其中 表示第 天的销售额。
GRPO模型使用两层GRU从归一化的输入序列 (其中 是产品数量)中提取时间特征。设最终时间步的隐藏状态为 。然后 通过两个独立的线性投影进行映射:
- 预测分支使用权重矩阵 计算预测值:
其中 是每个产品的预测平均销售额向量。
- 策略分支通过另一个线性映射 计算标量值 (策略值):
其中 。
GRPO启发的损失的核心思想是根据一个“优势”信号调整预测,该信号衡量预测误差相对于一个常数基线 (此处选择 )。
具体来说,优势定义为:
其中均值是在产品维度上取的。策略值与基线之间的比率 计算为:
为了确保训练过程中的稳定性并避免过大的策略更新,使用了裁剪机制。设:
其中 是一个小值(例如,0.1)。GRPO启发的策略损失随后被公式化为:
如果新策略(即预测)相对于基线没有足够改进,该损失将惩罚模型。总损失函数是预测损失和策略损失的组合:
其中:
是一个控制策略损失权重的超参数。
这种方法嵌入了一个时间外验证方案中:时间序列数据按时间顺序划分,确保仅使用过去的数据进行训练,未来数据用于验证——从而避免数据泄露。在验证过程中,归一化的预测 使用存储的均值和标准差还原回原始规模,并计算平均绝对百分比误差(MAPE):
其中 是一个小常数,用于避免除以零。
在这里,GRPO方法用于多时间序列预测,使用GRU编码器提取时间依赖特征,并生成预测和策略值。预测使用组合的均方误差(MSE)/平均绝对误差(MAE)进行评估,而策略分支使用裁剪的优势机制提供额外的梯度信号,最终导致对预测范围内平均销售额的更稳健预测。
代码实验:结果概述
在这个实验中,我们的目标是预测每种产品在未来5天内的平均销售额。使用AR(1)过程结合产品特定的噪声和偏移生成合成销售数据,生成一个现实的600天数据集。目标被定义为预测范围内的平均销售额。
数据被归一化,然后按时间顺序划分(前80%用于训练,剩余20%用于验证),以确保时间外评估且没有任何泄露。
我比较了四种预测方法。首先,我使用了一个受GRPO启发的模型,该模型结合了扩展的MLA模块、GRU编码器和额外的策略分支。其次,我实现了一个受GRPO启发的预测模型,该模型采用了扩展的MLA(Mamba风格)机制,通过ReLU激活的非线性状态空间方法更新潜在状态。第三,应用了一个简单的基于GRU的预测模型(没有GRPO修改)。最后,使用ARIMA(1,0,1)模型以滚动预测的方式在原始数据上进行经典ARMA方法的预测。
这个代码实验为后续的性能评估奠定了基础,我们将比较这四种方法的平均绝对百分比误差(MAPE)。
代码如下:
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from statsmodels.tsa.arima.model import ARIMA
# Load data
df = pd.read_csv("sales_data.csv")
# Optional: visualize the generated sales data
df.plot(x="date", y=["product1_sales", "product2_sales", "product3_sales", "product4_sales", "product5_sales"], figsize=(12, 6))
plt.title("Simulated Sales Data for 5 Products")
plt.show()
# -------------------------------
# Data Preparation for Time Series Forecasting with Normalization
# -------------------------------
class SalesDataset(Dataset):
def __init__(self, df, input_window=30, forecast_horizon=5):
"""
df: DataFrame with columns: date, product1_sales, ..., product5_sales
input_window: number of days used as input
forecast_horizon: number of days to forecast; target = avg sales over these days per product
"""
self.input_window = input_window
self.forecast_horizon = forecast_horizon
df['date'] = pd.to_datetime(df['date'])
df.sort_values('date', inplace=True)
self.df = df.reset_index(drop=True)
# Use only the sales columns (all except 'date')
data = df.drop(columns=['date']).values.astype(np.float32)
# Compute normalization parameters on the entire dataset
self.mean = data.mean(axis=0)
self.std = data.std(axis=0) + 1e-6 # avoid division by zero
# Normalize the data
self.data = (data - self.mean) / self.std
self.n_samples = len(self.data) - input_window - forecast_horizon + 1
def __len__(self):
return self.n_samples
def __getitem__(self, idx):
# Input: sales for input_window days (normalized)
x = self.data[idx: idx + self.input_window]
# Target: average sales over the next forecast_horizon days (normalized)
y = np.mean(self.data[idx + self.input_window: idx + self.input_window + self.forecast_horizon], axis=0)
return x, y
def prepare_dataloaders(df, input_window=30, forecast_horizon=5, batch_size=32, train_ratio=0.8):
dataset = SalesDataset(df, input_window, forecast_horizon)
n_total = len(dataset)
n_train = int(n_total * train_ratio)
# Chronological (out-of-time) split: first n_train samples for training, remaining for validation.
train_dataset = torch.utils.data.Subset(dataset, list(range(n_train)))
val_dataset = torch.utils.data.Subset(dataset, list(range(n_train, n_total)))
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=False)
val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
return train_loader, val_loader
train_loader, val_loader = prepare_dataloaders(df, input_window=30, forecast_horizon=5, batch_size=32, train_ratio=0.8)
# -------------------------------
# Model Components: Simple GRU-based Forecasting Model with GRPO-inspired Framework
# -------------------------------
class ForecastingGRPOModel(nn.Module):
def __init__(self, input_dim, hidden_dim, num_products, forecast_horizon, dropout=0.2, lambda_policy=0.1):
"""
This model forecasts the average sales of the next 'forecast_horizon' days for each product
using a GRU encoder. It includes a policy branch to compute a GRPO-inspired loss.
"""
super(ForecastingGRPOModel, self).__init__()
self.gru = nn.GRU(input_dim, hidden_dim, num_layers=2, batch_first=True, dropout=dropout)
self.fc_forecast = nn.Linear(hidden_dim, num_products)
self.policy_net = nn.Linear(hidden_dim, 1)
self.lambda_policy = lambda_policy
def forward(self, x):
# x: (batch, seq_len, input_dim)
gru_out, _ = self.gru(x)
last_hidden = gru_out[:, -1, :] # (batch, hidden_dim)
forecast = self.fc_forecast(last_hidden) # (batch, num_products)
policy_value = self.policy_net(last_hidden) # (batch, 1)
return forecast, policy_value
# -------------------------------
# Training and Validation Functions (Using MAPE as Error Metric)
# -------------------------------
def train_model_full(model, dataloader, optimizer, device, grad_clip=1.0):
model.train()
total_loss = 0.0
for x, y in dataloader:
x = x.to(device) # (batch, seq_len, input_dim)
y = y.to(device) # (batch, num_products)
optimizer.zero_grad()
forecast, policy_value = model(x)
# Forecast loss: use a combination of MSE and MAE
loss_mse = F.mse_loss(forecast, y)
loss_mae = F.l1_loss(forecast, y)
loss_forecast = 0.5 * loss_mse + 0.5 * loss_mae
# GRPO-inspired policy loss:
# Compute advantage as the mean error over products for each sample.
advantage = (y - forecast).mean(dim=1, keepdim=True)
baseline = 0.5 # chosen constant baseline
r_t = policy_value / baseline
epsilon = 0.1
r_t_clipped = torch.clamp(r_t, 1 - epsilon, 1 + epsilon)
policy_loss = -torch.min(r_t * advantage, r_t_clipped * advantage).mean()
loss = loss_forecast + model.lambda_policy * policy_loss
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), grad_clip)
optimizer.step()
total_loss += loss.item() * x.size(0)
return total_loss / len(dataloader.dataset)
def validate_model(model, dataloader, device, dataset_obj, debug=False):
model.eval()
all_preds = []
all_targets = []
with torch.no_grad():
for x, y in dataloader:
x = x.to(device)
y = y.to(device)
forecast, _ = model(x)
all_preds.append(forecast.cpu().numpy())
all_targets.append(y.cpu().numpy())
all_preds = np.concatenate(all_preds, axis=0)
all_targets = np.concatenate(all_targets, axis=0)
# Invert normalization: forecast_orig = forecast * std + mean
mean = dataset_obj.mean
std = dataset_obj.std
all_preds_orig = all_preds * std + mean
all_targets_orig = all_targets * std + mean
if debug:
print("Prediction range:", np.min(all_preds_orig), np.max(all_preds_orig))
print("Target range:", np.min(all_targets_orig), np.max(all_targets_orig))
mape = np.mean(np.abs((all_targets_orig - all_preds_orig) / (all_targets_orig + 1e-6))) * 100
return mape
# -------------------------------
# ARMA Forecasting for Comparison
# -------------------------------
def arma_forecast(series, forecast_horizon):
"""
Fits an ARIMA(1,0,1) model on the provided series and forecasts forecast_horizon steps ahead.
Returns the average forecast.
"""
try:
arma_model = ARIMA(series, order=(1, 0, 1))
arma_fit = arma_model.fit(disp=0)
forecast = arma_fit.forecast(steps=forecast_horizon)
return np.mean(forecast)
except Exception as e:
return series[-1]
def evaluate_arma(df, input_window=30, forecast_horizon=5, train_ratio=0.8):
"""
For each product (column), use a rolling ARMA forecast over the validation period on the raw data.
Returns a dictionary of MAPE values per product and the overall average MAPE.
"""
n = len(df)
train_end = int(n * train_ratio)
products = [col for col in df.columns if col != "date"]
mape_dict = {}
all_mapes = []
# Rolling forecast: start from i = (train_end - input_window) to (n - input_window - forecast_horizon + 1)
for prod in products:
preds = []
actuals = []
for i in range(train_end - input_window, n - input_window - forecast_horizon + 1):
series = df[prod].values[:i + input_window]
pred = arma_forecast(series, forecast_horizon)
preds.append(pred)
actual = np.mean(df[prod].values[i + input_window: i + input_window + forecast_horizon])
actuals.append(actual)
preds = np.array(preds)
actuals = np.array(actuals)
prod_mape = np.mean(np.abs((actuals - preds) / (actuals + 1e-6))) * 100
mape_dict[prod] = prod_mape
all_mapes.append(prod_mape)
overall_mape = np.mean(all_mapes)
return mape_dict, overall_mape
# -------------------------------
# Main Training
# -------------------------------
def main():
df = pd.read_csv("sales_data.csv")
# Prepare out-of-time (chronological) dataloaders (first 80% for training, remaining for validation)
train_loader, val_loader = prepare_dataloaders(df, input_window=30, forecast_horizon=5, batch_size=32, train_ratio=0.8)
# For validation inversion, we need access to the dataset normalization parameters
dataset_obj = SalesDataset(df, input_window=30, forecast_horizon=5)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Define model parameters
input_dim = train_loader.dataset[0][0].shape[-1] # e.g., 5 products
hidden_dim = 256 # Hidden dimension for GRU
num_products = input_dim # Predict average sales for each product
forecast_horizon = 10 # Note: This parameter is used in the models, even though targets are for next 5 days
lambda_policy = 0.06 # Weight for GRPO-inspired policy loss
# -------------------------------
# GRPO-inspired Forecasting Model (Existing)
# -------------------------------
model = ForecastingGRPOModel(input_dim, hidden_dim, num_products, forecast_horizon, dropout=0.2, lambda_policy=lambda_policy)
model.to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.0003)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.9)
n_epochs = 22
print("Training GRPO-inspired Forecasting Model...")
for epoch in range(n_epochs):
train_loss = train_model_full(model, train_loader, optimizer, device, grad_clip=1.0)
mape = validate_model(model, val_loader, device, dataset_obj, debug=True)
scheduler.step()
print(f"Epoch {epoch+1}/{n_epochs} - GRPO Model Train Loss: {train_loss:.4f}, MAPE: {mape:.2f}%")
# -------------------------------
# ARMA Evaluation for Comparison
# -------------------------------
print("\nEvaluating ARMA Forecasting on raw data...")
arma_mapes, overall_arma_mape = evaluate_arma(df, input_window=30, forecast_horizon=5, train_ratio=0.8)
print("ARMA MAPE per product:", arma_mapes)
print("Overall ARMA MAPE:", overall_arma_mape, "%")
# -------------------------------
# Simple GRU Forecasting Model for Comparison
# -------------------------------
class SimpleGRUForecastingModel(nn.Module):
def __init__(self, input_dim, hidden_dim, num_products, forecast_horizon, dropout=0.2):
super(SimpleGRUForecastingModel, self).__init__()
self.gru = nn.GRU(input_dim, hidden_dim, num_layers=2, batch_first=True, dropout=dropout)
self.fc_forecast = nn.Linear(hidden_dim, num_products)
def forward(self, x):
gru_out, _ = self.gru(x)
last_hidden = gru_out[:, -1, :]
forecast = self.fc_forecast(last_hidden)
return forecast
def train_simple_model(model, dataloader, optimizer, device, grad_clip=1.0):
model.train()
total_loss = 0.0
for x, y in dataloader:
x = x.to(device)
y = y.to(device)
optimizer.zero_grad()
forecast = model(x)
loss_mse = F.mse_loss(forecast, y)
loss_mae = F.l1_loss(forecast, y)
loss = 0.5 * loss_mse + 0.5 * loss_mae
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), grad_clip)
optimizer.step()
total_loss += loss.item() * x.size(0)
return total_loss / len(dataloader.dataset)
def validate_simple_model(model, dataloader, device, dataset_obj, debug=False):
model.eval()
all_preds = []
all_targets = []
with torch.no_grad():
for x, y in dataloader:
x = x.to(device)
y = y.to(device)
forecast = model(x)
all_preds.append(forecast.cpu().numpy())
all_targets.append(y.cpu().numpy())
all_preds = np.concatenate(all_preds, axis=0)
all_targets = np.concatenate(all_targets, axis=0)
mean = dataset_obj.mean
std = dataset_obj.std
all_preds_orig = all_preds * std + mean
all_targets_orig = all_targets * std + mean
if debug:
print("Simple GRU Prediction range:", np.min(all_preds_orig), np.max(all_preds_orig))
print("Simple GRU Target range:", np.min(all_targets_orig), np.max(all_targets_orig))
mape = np.mean(np.abs((all_targets_orig - all_preds_orig) / (all_targets_orig + 1e-6))) * 100
return mape
print("\nTraining Simple GRU Forecasting Model for Comparison...")
simple_model = SimpleGRUForecastingModel(input_dim, hidden_dim, num_products, forecast_horizon, dropout=0.2)
simple_model.to(device)
optimizer_simple = torch.optim.Adam(simple_model.parameters(), lr=0.0003)
scheduler_simple = torch.optim.lr_scheduler.StepLR(optimizer_simple, step_size=10, gamma=0.9)
n_epochs_simple = 22
for epoch in range(n_epochs_simple):
train_loss_simple = train_simple_model(simple_model, train_loader, optimizer_simple, device, grad_clip=1.0)
simple_mape = validate_simple_model(simple_model, val_loader, device, dataset_obj, debug=True)
scheduler_simple.step()
print(f"Epoch {epoch+1}/{n_epochs_simple} - Simple GRU Train Loss: {train_loss_simple:.4f}, MAPE: {simple_mape:.2f}%")
# -------------------------------
# New Method: GRPO-inspired Forecasting with Extended MLA (Mamba-style) Mechanism
# -------------------------------
class ForecastingGRPOMLAModel(nn.Module):
def __init__(self, input_dim, hidden_dim, num_products, forecast_horizon, dropout=0.3, lambda_policy=0.06):
"""
This model extends the GRPO-inspired forecasting approach by incorporating an
extended MLA (Mamba-style) mechanism. The latent state is updated in a state-space
manner with a nonlinear activation applied to the entire update.
"""
super(ForecastingGRPOMLAModel, self).__init__()
self.hidden_dim = hidden_dim
self.lambda_policy = lambda_policy
self.dropout = nn.Dropout(dropout)
# Map the input to the latent space.
self.input_transform = nn.Linear(input_dim, hidden_dim)
# State-space transition matrix (M)
self.M = nn.Linear(hidden_dim, hidden_dim, bias=False)
# Nonlinear activation function for the complete state update.
self.activation = nn.ReLU()
self.fc_forecast = nn.Linear(hidden_dim, num_products)
self.policy_net = nn.Linear(hidden_dim, 1)
def forward(self, x):
# x: (batch, seq_len, input_dim)
batch_size, seq_len, _ = x.size()
# Initialize latent state as zeros.
h = torch.zeros(batch_size, self.hidden_dim, device=x.device)
# Iteratively update latent state with a state-space update.
for t in range(seq_len):
x_t = x[:, t, :] # (batch, input_dim)
# Compute the correction without activation first.
correction = self.input_transform(x_t)
# Update latent state using the ReLU activation applied to the entire sum.
h = self.activation(self.M(h) + correction)
h = self.dropout(h)
forecast = self.fc_forecast(h)
policy_value = self.policy_net(h)
return forecast, policy_value
print("\nTraining GRPO-inspired Forecasting Model with Extended MLA (Mamba-style) Mechanism...")
model_extended = ForecastingGRPOMLAModel(input_dim, hidden_dim, num_products, forecast_horizon, dropout=0.2, lambda_policy=lambda_policy)
model_extended.to(device)
optimizer_extended = torch.optim.Adam(model_extended.parameters(), lr=0.0003)
scheduler_extended = torch.optim.lr_scheduler.StepLR(optimizer_extended, step_size=10, gamma=0.9)
n_epochs_extended = 22
for epoch in range(n_epochs_extended):
train_loss_extended = train_model_full(model_extended, train_loader, optimizer_extended, device, grad_clip=1.0)
mape_extended = validate_model(model_extended, val_loader, device, dataset_obj, debug=True)
scheduler_extended.step()
print(f"Epoch {epoch+1}/{n_epochs_extended} - Extended MLA Model Train Loss: {train_loss_extended:.4f}, MAPE: {mape_extended:.2f}%")
if __name__ == "__main__":
main()
评估GRPO启发的预测模型
GRPO启发的模型整合了扩展的MLA模块与GRU,并新增了一个策略分支,在经过22个训练周期后,其平均绝对百分比误差(MAPE)稳步下降并最终稳定在大约21.6%。该模型的预测范围始终与目标范围高度一致,这表明其自适应机制能够有效捕捉底层的销售模式。
与简单GRU模型的对比
相比之下,缺乏GRPO特定改进的简单GRU模型,其MAPE平均值略高,约为22.3%。尽管简单GRU的预测范围也与目标范围相似,但在GRPO模型中观察到的微小改进表明,额外的策略损失和扩展的潜在更新为降低预测误差做出了适度但有意义的贡献。
扩展的MLA(Mamba风格)模型的见解
进一步采用扩展的MLA(Mamba风格)机制的GRPO启发模型,通过在完整的状态更新中应用ReLU激活的非线性状态空间更新,实现了低至20.8%至21.3%的MAPE。这一改进凸显了利用更丰富的潜在表示来捕捉时间序列动态的优势。
经典ARMA方法
最后,ARMA方法显示出显著更高的误差。每个产品的MAPE大致在12.6%到43%之间,整体MAPE约为26.3%。与深度学习方法相比,ARMA在处理复杂的多维销售数据方面效果较差。
总结
在本研究中,我们探索了一种创新的多时间序列预测方法,通过结合GRPO和采用Mamba风格状态空间更新的扩展MLA模块来实现。实验结果表明,这种GRPO启发的模型能够实现比简单GRU模型和经典ARMA方法更低的MAPE。由策略分支和状态更新中的非线性激活驱动的增强潜在表示,似乎能够更有效地捕捉销售数据的复杂动态。
展望未来,将GRPO和扩展MLA框架应用于其他领域具有巨大的潜力。例如,这种方法可以适应于金融时间序列预测,其中捕捉市场趋势的微妙变化至关重要。它也可能有助于医疗保健诊断,通过从多个时间依赖信号预测患者结果,从而实现早期干预。
未来的研究可以专注于通过尝试不同的基线值或裁剪阈值来进一步完善GRPO机制,以及探索扩展的MLA模块如何与其他深度学习架构相结合。此外,引入元学习技术可能使模型能够更好地泛化到不同的领域。总体而言,本研究表明,将强化学习与先进的注意力机制相结合是构建更智能、更具适应性的预测系统的一个有希望的方向。
如何学习AI大模型 ?
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。【保证100%免费】🆓
CSDN粉丝独家福利
这份完整版的 AI 大模型学习资料已经上传CSDN,朋友们如果需要可以扫描下方二维码&点击下方CSDN官方认证链接免费领取 【保证100%免费】
读者福利: 👉👉CSDN大礼包:《最新AI大模型学习资源包》免费分享 👈👈
对于0基础小白入门:
如果你是零基础小白,想快速入门大模型是可以考虑的。
一方面是学习时间相对较短,学习内容更全面更集中。
二方面是可以根据这些资料规划好学习计划和方向。
👉1.大模型入门学习思维导图👈
要学习一门新的技术,作为新手一定要先学习成长路线图,方向不对,努力白费。
对于从来没有接触过AI大模型的同学,我们帮你准备了详细的学习成长路线图&学习规划。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。(全套教程文末领取哈)
👉2.AGI大模型配套视频👈
很多朋友都不喜欢晦涩的文字,我也为大家准备了视频教程,每个章节都是当前板块的精华浓缩。
👉3.大模型实际应用报告合集👈
这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。(全套教程文末领取哈)
👉4.大模型落地应用案例PPT👈
光学理论是没用的,要学会跟着一起做,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。(全套教程文末领取哈)
👉5.大模型经典学习电子书👈
随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。(全套教程文末领取哈)
👉6.大模型面试题&答案👈
截至目前大模型已经超过200个,在大模型纵横的时代,不仅大模型技术越来越卷,就连大模型相关的岗位和面试也开始越来越卷了。为了让大家更容易上车大模型算法赛道,我总结了大模型常考的面试题。(全套教程文末领取哈)
👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;
• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;
• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;
• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习
CSDN粉丝独家福利
这份完整版的 AI 大模型学习资料已经上传CSDN,朋友们如果需要可以扫描下方二维码&点击下方CSDN官方认证链接免费领取 【保证100%免费】
读者福利: 👉👉CSDN大礼包:《最新AI大模型学习资源包》免费分享 👈👈