【问题标题】:How to change PyTorch sigmoid function to be steeper如何将 PyTorch sigmoid 函数更改为更陡峭
【发布时间】:2021-07-16 02:38:14
【问题描述】:

我的模型在我使用torch.sigmoid 时有效。我试图通过创建一个新的 sigmoid 函数来使 sigmoid 更陡:

def sigmoid(x):
    return 1 / (1 + torch.exp(-1e5*x))

但由于某种原因,渐变没有流过它(我得到NaN)。我的函数有问题,还是有办法简单地将 PyTorch 实现更改为更陡峭(作为我的函数)?

代码示例:

def sigmoid(x):
  return 1 / (1 + torch.exp(-1e5*x))

a = torch.tensor(0.0, requires_grad=True)
b = torch.tensor(0.58, requires_grad=True)

c = sigmoid(a-b)
c.backward()
a.grad
>>> tensor(nan)

【问题讨论】:

  • 不确定这是否与您的问题相关,但是当我在 numpy 中实现此函数并使用 -0.58 (a-b) 调用它时,我得到了一个结果,还有一个 RuntimeWarning: overflow encountered in exp。可能pytorch对这么大的指数有问题,导致nan?

标签: python pytorch sigmoid


【解决方案1】:

问题似乎是当您的 sigmoid 实现的输入为负时,torch.exp 的参数变得非常大,导致溢出。按照here的建议使用torch.autograd.set_detect_anomaly(True),可以看到错误:

RuntimeError: Function 'ExpBackward' returned nan values in its 0th output.

如果你真的需要使用你定义的函数,一个可能的解决方法是对参数进行条件检查(但我不确定它是否稳定,所以我无法评论它的有用性):

def sigmoid(x):
    if x >= 0:
        return 1./(1+torch.exp(-1e5*x))
    else:
        return torch.exp(1e5*x)/(1+torch.exp(1e5*x)) 

这里,else 分支中的表达式等价于原始函数,将分子和分母乘以torch.exp(1e5*x)。这可确保 torch.exp 的参数始终为负数或接近于零。

正如trialNerror 所指出的,指数值非常高,以至于除了非常接近零的值之外,您的梯度将在其他任何地方计算为零,因为实际斜率将非常小并且无法通过数据类型解决。因此,如果您打算在网络中使用它,您可能会发现学习任何东西都非常困难,因为梯度几乎总是为零。根据您的用例,选择较小的指数可能会更好。

【讨论】:

  • 分支的想法是防止 NaN 发生的好方法,我没有想到这一点。然而,梯度将始终为 0,因为 0 和 exp(-1e5) 之间的差异远低于浮点精度。所以 NaN 消失了,但如果想通过它进行反向传播,实际问题实际上是 1e5 因素
  • @trialNerror 当您远离 0 时,梯度将很快为零,但在非常接近零时,它仍然是非零的。例如,在 0 处是 25000。我认为这是 OP 所需的预期行为,因为他们想要一个非常陡峭的函数,但我同意你的评论。
  • 有道理!我不得不将其更改为1e3,因为1e5 的梯度几乎总是为零,因此网络没有学习。但你的伎俩奏效了!谢谢:)
【解决方案2】:

您在指数中添加了 1e5 的膨胀。 1e5 的指数高得令人难以置信,以至于没有希望在这里得到有意义的结果。您可能会得到一个 NaN,因为您正试图通过计算图进行反向传播,该计算图在某些时候被评估为 inf(及以上!)

无论如何,要使函数的斜率更陡,请记住df(a.x)/dx = a.df(x)/dx,因此您需要将其参数乘以大于 1 的值(而不是负数,您将更改导数的符号),但不是那样巨大的 !尝试使用 10 可能,它还取决于您要放入函数中的输入的数量级

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-08
    • 2017-11-24
    • 1970-01-01
    相关资源
    最近更新 更多