PyTorch 中使用自定义反向函数的损失 – 简单 MSE 示例中的损失爆炸

在处理更复杂的问题之前,我知道我需要实现自己的 backward 传递,所以我想先尝试一个简单且易于操作的例子。因此,我尝试使用 PyTorch 进行线性回归,并使用均方误差损失。然而,当我定义了自己的 backward 方法时,事情出了问题(见下面的第三种实现选项),我怀疑这是因为我没有清楚地思考应该向 PyTorch 提供什么样的梯度。所以,我认为我需要一些关于 PyTorch 期望我以何种形式提供什么样的解释/澄清/建议。

我使用的是 PyTorch 1.7.0,所以许多旧的例子不再适用(处理用户定义的自动梯度函数的方式与文档中描述的不同)。

第一种方法(标准的 PyTorch MSE 损失函数)

让我们先用标准的方式,不使用自定义损失函数来做这件事:

import torchimport torch.nn as nnimport torch.nn.functional as F# 让我们生成一些假数据torch.manual_seed(42)resid = torch.rand(100)    inputs = torch.tensor([ [ xx ] for xx in range(100)] , dtype=torch.float32)labels = torch.tensor([ (2 + 0.5*yy + resid[yy]) for yy in range(100)], dtype=torch.float32)# 现在我们定义一个线性回归模型class linearRegression(torch.nn.Module):    def __init__(self, inputSize, outputSize):        super(linearRegression, self).__init__()        self.bn = torch.nn.BatchNorm1d(num_features=1)        self.linear = torch.nn.Linear(inputSize, outputSize)    def forward(self, inx):        x = self.bn(inx) # 添加BN来标准化输入,有助于我们使用更高的学习率        x = self.linear(x)        return x    model = linearRegression(1, 1)     # 使用PyTorch的标准mse_lossepochs = 25    mseloss = F.mse_lossoptimizer = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=1e-3)scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.1)for epoch in range(epochs):    model.train()    optimizer.zero_grad()    outputs = model(inputs)    loss = mseloss(outputs.view(-1), labels)    loss.backward()    optimizer.step()    scheduler.step()            print(f'epoch {epoch}, loss {loss}')        

这个训练过程非常顺利,最终损失约为0.0824,拟合的图看起来也很好。

第二种方法(自定义损失函数,但依赖于PyTorch的自动梯度计算)

那么,现在我用自己的MSE损失实现替换了损失函数,但我仍然依赖PyTorch的自动梯度计算。这里我唯一改变的是定义了自定义损失函数,相应地定义了基于此的损失,以及我将预测和真实标签传递给损失函数的细微细节。

#######################################################3class MyMSELoss(nn.Module):        def __init__(self):        super(MyMSELoss, self).__init__()    def forward(self, inputs, targets):                tmp = (inputs-targets)**2        loss =  torch.mean(tmp)                return loss#######################################################3model = linearRegression(1, 1)     mseloss = MyMSELoss()optimizer = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=1e-3)scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.1)for epoch in range(epochs):    model.train()        outputs = model(inputs)    loss = mseloss(outputs.view(-1), labels)    loss.backward()        optimizer.step()    optimizer.zero_grad()    scheduler.step()        print(f'epoch {epoch}, loss {loss}')

这给出的结果与使用标准MSE损失函数完全相同。各轮次的损失看起来像这样:

epoch 0, loss 884.2006225585938epoch 1, loss 821.930908203125epoch 2, loss 718.7732543945312epoch 3, loss 538.1835327148438epoch 4, loss 274.50909423828125epoch 5, loss 55.115299224853516epoch 6, loss 2.405021905899048epoch 7, loss 0.47621214389801025epoch 8, loss 0.1584305614233017epoch 9, loss 0.09725229442119598epoch 10, loss 0.0853077694773674epoch 11, loss 0.08297089487314224epoch 12, loss 0.08251354098320007epoch 13, loss 0.08242412656545639epoch 14, loss 0.08240655809640884epoch 15, loss 0.08240310847759247epoch 16, loss 0.08240246027708054epoch 17, loss 0.08240233361721039epoch 18, loss 0.08240240067243576epoch 19, loss 0.08240223675966263epoch 20, loss 0.08240225911140442epoch 21, loss 0.08240220695734024epoch 22, loss 0.08240220695734024epoch 23, loss 0.08240220695734024epoch 24, loss 0.08240220695734024

第三种方法(自定义损失函数并使用我自己的backward方法)

现在,最后一个版本,我为MSE实现了自己的梯度。为此,我在损失函数类中定义了自己的 backward 方法,显然需要执行 mseloss = MyMSELoss.apply

from torch.autograd import Function#######################################################class MyMSELoss(Function):        @staticmethod    def forward(ctx, y_pred, y):            ctx.save_for_backward(y_pred, y)        return ( (y - y_pred)**2 ).mean()        @staticmethod    def backward(ctx, grad_output):        y_pred, y = ctx.saved_tensors        grad_input = torch.mean( -2.0 * (y - y_pred)).repeat(y_pred.shape[0])                # 这会失败,就像 grad_input = -2.0 * (y-y_pred) 一样        # 我还尝试过改变符号,但这不是唯一的问题。        return grad_input, None    #######################################################    model = linearRegression(1, 1) mseloss = MyMSELoss.applyoptimizer = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=1e-3)scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.1)for epoch in range(epochs):    model.train()    outputs = model(inputs)    loss = mseloss(outputs.view(-1), labels)    loss.backward()    optimizer.step()    optimizer.zero_grad()    scheduler.step()    print(f'epoch {epoch}, loss {loss}')    

这就是事情出错的地方,而不是训练损失减少,我得到的是增加的训练损失。现在看起来像这样:

epoch 0, loss 884.2006225585938epoch 1, loss 3471.384033203125epoch 2, loss 47768555520.0epoch 3, loss 1.7422577779621402e+33epoch 4, loss infepoch 5, loss nanepoch 6, loss nanepoch 7, loss nanepoch 8, loss nanepoch 9, loss nanepoch 10, loss nanepoch 11, loss nanepoch 12, loss nanepoch 13, loss nanepoch 14, loss nanepoch 15, loss nanepoch 16, loss nanepoch 17, loss nanepoch 18, loss nanepoch 19, loss nanepoch 20, loss nanepoch 21, loss nanepoch 22, loss nanepoch 23, loss nanepoch 24, loss nan

回答:

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注