You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyTorch自定义函数结合FFN训练时优化失效问题排查

问题说明
  • 已将实际遇到的问题转化为可复现的具象示例,核心目标是求解目标计算公式中的beta参数,该参数后续将用于其他业务场景
  • 涉及的计算公式示意图:多项式拟合公式示意图,公式中x为输入变量
  • 已知在该简单示例场景下,可先估计y值再通过OLS方法计算beta,但该方法无法适配实际业务场景,因此未采用
  • 实现方案:基于PyTorch追踪梯度,使用SGD优化器更新参数,采用简单FFN(前馈神经网络)作为函数f的近似结构
  • 故障表现:训练过程中自定义函数环节优化失效,模型拟合效果未达预期,需要定位故障原因并获取可行的优化方案
  • 完整复现代码如下:
import pandas as pd
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import copy
import torch.nn.functional as F
import matplotlib.pyplot as plt


# 构造数据集
df = pd.date_range('2020-12-01', '2022-05-31', freq='H')
df = pd.DataFrame(df[0:len(df)-1], columns={'DateTime'})
np.random.seed(1)
A = 10

X = pd.DataFrame(np.random.uniform(0, 1, len(df)), columns={'X1'})
X['X2'] = np.random.normal(0, 1, len(df))
X['X3'] = np.random.normal(0, 1, len(df))


Y = pd.DataFrame()


for i in range(0, len(df)):
    Y[str(i)] = X['X1'][i] ** 2 + X['X1'][i] * X['X2'][i] * range(0, A) + X['X1'][i] **2 * X['X2'][i] * np.array(range(0, A))**2



Y = Y.T.reset_index(drop=True)
df = pd.concat([df, Y], axis=1)


# 数据集划分配置
valid_start = '2021-07-01'
test_start = '2022-01-01'

Out = 2

X_array = X.copy()

Y_train = df[df['DateTime'].dt.date.astype(str) < valid_start].drop(columns='DateTime')
Y_valid = df[df['DateTime'].dt.date.astype(str) >= valid_start].drop(columns='DateTime')
Y_test = df[df['DateTime'].dt.date.astype(str) >= valid_start].drop(columns='DateTime')
Y_test_date = pd.DataFrame(df[df['DateTime'].dt.date.astype(str) >= valid_start]['DateTime'], columns={'DateTime'})


X_train = X_array[X_array.index.isin(Y_train.index)]
X_valid = X_array[X_array.index.isin(Y_valid.index)]
X_test = X_array[X_array.index.isin(Y_test.index)]

# 设备配置
torch.cuda.is_available()
torch.cuda.device_count()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)

# 转换为张量
Y_train_tensor = torch.tensor(np.float32(Y_train)).to(device)
X_train_tensor = torch.tensor(np.float32(X_train)).to(device)

Y_valid_tensor = torch.tensor(np.float32(Y_valid)).to(device)
X_valid_tensor = torch.tensor(np.float32(X_valid)).to(device)

Y_test_tensor = torch.tensor(np.float32(Y_test)).to(device)
X_test_tensor = torch.tensor(np.float32(X_test)).to(device)

## 模型定义
class Model(nn.Module):

    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(X_train_tensor.shape[1], 4)
        self.out = nn.Linear(4, int(Out) + 1)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = self.out(x)

        return x

net = Model().to(device)
criterion = nn.MSELoss()
optimizer = optim.SGD(net.parameters(), lr=0.001)
Range_tensor = torch.tensor(np.float32(range(0, A))).to(device)

# 早停配置
Network_weight = [copy.deepcopy(net.out.weight)]

Epoch = 100
patience = 10
Valloss_Array = []
trigger_times = 0
last_loss = 1000000000000000
Valloss_Array.append(last_loss)


for epoch in range(Epoch):  # 训练循环
    x = X_train_tensor
    y = Y_train_tensor

    # 梯度清零
    optimizer.zero_grad()

    # 前向传播 + 反向传播 + 参数更新
    outputs = net(x)

    y_hat = []
    for e in range(0, len(y)):
        y_hat_temp = outputs[e, 0] + outputs[e, 1] * Range_tensor + outputs[e, 2] * torch.pow(Range_tensor, 2)

        y_hat.append(y_hat_temp)


    y_hat = torch.stack(y_hat)

    loss = criterion(y_hat, y)

    loss.backward()
    optimizer.step()
    print('Running training loss is; ' + str(loss.item()))
    Network_weight.append([copy.deepcopy(net.fc1.weight), copy.deepcopy(net.out.weight)])

    ################## 验证环节 ##################
    x = X_valid_tensor
    y = Y_valid_tensor

    outputs = net(x)

    y_hat = []
    for e in range(0, len(y)):

        y_hat_temp = outputs[e, 0] + outputs[e, 1] * Range_tensor + outputs[e, 2] * torch.pow(Range_tensor, 2)

        y_hat.append(y_hat_temp)

    y_hat = torch.stack(y_hat)


    Valloss = criterion(y_hat, y)


    if Valloss.item() >= min(Valloss_Array):
        trigger_times += 1

        if trigger_times >= patience:
            print('Early stopping!')
            break

    else:
        trigger_times = 0
        Best = [copy.deepcopy(net.fc1.weight), copy.deepcopy(net.out.weight)]

    Valloss_Array.append(Valloss.item())


# 加载最优权重
net.fc1.weight = Best[0]
net.out.weight = Best[1]


outputs = net(X_test_tensor)

y_hat = []
for e in range(0, len(Y_test_tensor)):

    y_hat_temp = outputs[e, 0] + outputs[e, 1] * Range_tensor + outputs[e, 2] * torch.pow(Range_tensor, 2)

    y_hat.append(y_hat_temp)

y_hat = torch.stack(y_hat)

BestModelLoss = criterion(y_hat, Y_test_tensor)


Y_Prediction = pd.DataFrame(y_hat.cpu().detach().numpy(), index=Y_test.index, columns={0, 1, 2, 3, 4, 5, 6, 7, 8, 9})

Y_test_date['SE'] = ((Y_Prediction - Y_test) ** 2).sum(axis=1)

Y_Prediction['DateTime'] = Y_test_date['DateTime']
Y_test['DateTime'] = Y_test_date['DateTime']

### 结果可视化
MinDate = Y_test_date.loc[Y_test_date['SE'] == Y_test_date['SE'].min(), 'DateTime']
YMinForecast = np.array(Y_Prediction[(Y_Prediction['DateTime'] == MinDate.values[0])].drop(columns='DateTime'))
YMin = np.array(Y_test[(Y_test['DateTime'] == MinDate.values[0])].drop(columns='DateTime'))

MaxDate = Y_test_date.loc[Y_test_date['SE'] == Y_test_date['SE'].max(), 'DateTime']
YMaxForecast = np.array(Y_Prediction[(Y_Prediction['DateTime'] == MaxDate.values[0])].drop(columns='DateTime'))
YMax = np.array(Y_test[(Y_test['DateTime'] == MaxDate.values[0])].drop(columns='DateTime'))

MedianValue = Y_test_date['SE'].iloc[(Y_test_date['SE']-Y_test_date['SE'].median()).abs().argsort()[:2]].values[0]

MedDate = Y_test_date.loc[Y_test_date['SE'] == MedianValue, 'DateTime']
YMedForecast = np.array(Y_Prediction[(Y_Prediction['DateTime'] == MedDate.values[0])].drop(columns='DateTime'))
YMed = np.array(Y_test[(Y_test['DateTime'] == MedDate.values[0])].drop(columns='DateTime'))


# 绘图
plt.plot(range(0, y_hat.shape[1]), YMinForecast.reshape(-1), label='Forecast')
plt.plot(range(0, y_hat.shape[1]), YMin.reshape(-1), label='Actual')
plt.grid()
plt.title("Best performance")
plt.legend()
plt.show()

plt.plot(range(0, y_hat.shape[1]), YMaxForecast.reshape(-1), label='Forecast')
plt.plot(range(0, y_hat.shape[1]), YMax.reshape(-1), label='Actual')
plt.grid()
plt.title("Poor performance")
plt.legend()
plt.show()

plt.plot(range(0, y_hat.shape[1]), YMedForecast.reshape(-1), label='Forecast')
plt.plot(range(0, y_hat.shape[1]), YMed.reshape(-1), label='Actual')
plt.grid()
plt.title("Median performance")
plt.legend()
plt.show()

内容的提问来源于stack exchange,提问作者Chri571p

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 01:24:20