我正在尝试在c++中计算光滑函数的数值梯度。参数值可以从0到非常大的数字(可能是1e10到1e20?)
我使用函数f(x,y) = 10*x^3 + y^3作为测试平台,但我发现如果x或y太大,我无法获得正确的梯度。
下面是我计算梯度的代码:
#include <iostream>
#include <cmath>
#include <cassert>
using namespace std;
double f(double x, double y)
{
// black box expensive function
return 10 * pow(x, 3) + pow(y, 3);
}
int main()
{
// double x = -5897182590.8347721;
// double y = 269857217.0017581;
double x = 1.13041e+19;
double y = -5.49756e+14;
const double epsi = 1e-4;
double f1 = f(x, y);
double f2 = f(x, y+epsi);
double f3 = f(x, y-epsi);
cout << f1 << endl;
cout << f2 << endl;
cout << f3 << endl;
cout << f1 - f2 << endl; // 0
cout << f2 - f3 << endl; // 0
return 0;
}
如果我使用上面的代码来计算梯度,那么梯度将为零!
测试平台函数,10*x^3 + y^3,只是一个演示,我需要解决的真正问题实际上是一个黑盒函数。
那么,有没有什么“标准”的方法来计算数值梯度呢?
发布于 2016-08-10 04:18:16
首先,你应该使用中心差分格式,它更精确(通过取消泰勒发展的多一项)。
(f(x + h) - f(x - h)) / 2h
而不是
(f(x + h) - f(x)) / h
那么h
的选择是至关重要的,使用固定的常量是最糟糕的选择。因为对于较小的x
,h
会过大,使得近似公式不再适用;而对于较大的x
,h
会太小,导致严重的截断误差。
一个更好的选择是采用相对值h = x√ε
,其中ε
是机器epsilon (1 ulp),这提供了一个很好的折衷。
(f(x(1 + √ε)) - f(x(1 - √ε))) / 2x√ε
请注意,当为x = 0
时,相对值不起作用,您需要回退到常量。但是,没有任何东西告诉您应该使用哪一个!
发布于 2016-08-10 00:31:22
您需要考虑所需的精度。
乍一看,由于|y| = 5.49756e14
和epsi = 1e-4
,您至少需要⌈log2(5.49756e14)-log2(1e-4)⌉ = 63
位的有效位精度(即用于编码数字的位数,也称为尾数),才能将y
和y+epsi
视为不同。
双精度浮点格式只有53位有效数精度(假设它是8字节)。因此,目前f1
、f2
和f3
是完全相同的,因为y
、y+epsi
和y-epsi
是相等的。
现在,让我们考虑一下限制:y = 1e20
,以及函数10x^3 + y^3
的结果。让我们暂时忽略x
,所以让我们以f = y^3
为例。现在我们可以计算出f(y)
和f(y+epsi)
需要不同的精度:f(y) = 1e60
和f(epsi) = 1e-12
。这提供了⌈log2(1e60)-log2(1e-12)⌉ = 240
位的最小有效位精度。
即使您使用long double
类型,假设它是16字节,您的结果也不会有所不同:f1
、f2
和f3
仍然是相等的,即使y
和y+epsi
不是。
如果我们将x
考虑在内,f
的最大值将是11e60
(带有x = y = 1e20
)。因此精度的上限是⌈log2(11e60)-log2(1e-12)⌉ = 243
位,或至少31个字节。
解决问题的一种方法是使用另一种类型,可能是用作定点的bignum。
另一种方法是重新思考你的问题,并以不同的方式处理它。归根结底,您需要的是f1 - f2
。您可以尝试分解f(y+epsi)
。同样,如果忽略x
,f(y+epsi) = (y+epsi)^3 = y^3 + 3*y^2*epsi + 3*y*epsi^2 + epsi^3
。所以f(y+epsi) - f(y) = 3*y^2*epsi + 3*y*epsi^2 + epsi^3
。
发布于 2016-08-09 23:14:38
计算梯度的唯一方法是微积分。
渐变是一个向量:
g(x, y) = Df/Dx i + Df/Dy j
其中(i,j)分别是x和y方向上的单位向量。
近似导数的一种方法是一阶差分:
Df/Dx ~ (f(x2, y)-f(x1, y))/(x2-x1)
和
Df/Dy ~ (f(x, y2)-f(x, y1))/(y2-y1)
这看起来不像你在做的事。
您有一个封闭形式的表达式:
g(x, y) = 30*x^2 i + 3*y^2 j
您可以插入(x,y)的值,并在任何点精确计算梯度。将其与您的差异进行比较,看看您的近似值做得有多好。
如何在数字上实现它是你的责任。(10^19)^3 = 10^57,对吗?
你的机器上的双倍大小是多少?它是64位IEEE双精度浮点数吗?
https://stackoverflow.com/questions/38854363
复制相似问题