问题
I am surprised by the output of the check_partial_derivatives()
method applied to the problem showed in my previous question : Paraboloid optimization requiring scaling. When I add the call to that method :
from __future__ import print_function import sys from openmdao.api import IndepVarComp, Component, Problem, Group, ScipyOptimizer class Paraboloid(Component): def __init__(self): super(Paraboloid, self).__init__() self.add_param('x', val=0.0) self.add_param('y', val=0.0) self.add_output('f_xy', val=0.0) def solve_nonlinear(self, params, unknowns, resids): x = params['x'] y = params['y'] #unknowns['f_xy'] = (x-3.0)**2 + x*y + (y+4.0)**2 - 3.0 unknowns['f_xy'] = (1000.*x-3.)**2 + (1000.*x)*(0.01*y) + (0.01*y+4.)**2 - 3. def linearize(self, params, unknowns, resids): """ Jacobian for our paraboloid.""" x = params['x'] y = params['y'] J = {} #J['f_xy', 'x'] = 2.0*x - 6.0 + y #J['f_xy', 'y'] = 2.0*y + 8.0 + x J['f_xy', 'x'] = 2000000.0*x - 6000.0 + 10.0*y J['f_xy', 'y'] = 0.0002*y + 0.08 + 10.0*x return J if __name__ == "__main__": top = Problem() root = top.root = Group() #root.fd_options['force_fd'] = True root.add('p1', IndepVarComp('x', 3.0)) root.add('p2', IndepVarComp('y', -4.0)) root.add('p', Paraboloid()) root.connect('p1.x', 'p.x') root.connect('p2.y', 'p.y') top.driver = ScipyOptimizer() top.driver.options['optimizer'] = 'SLSQP' top.driver.add_desvar('p1.x', lower=-1000, upper=1000, scaler=1000.) top.driver.add_desvar('p2.y', lower=-1000, upper=1000, scaler=0.001) top.driver.add_objective('p.f_xy') top.setup() top.check_partial_derivatives() # added line top.run() print('\n') print('Minimum of %f found at (%f, %f)' % (top['p.f_xy'], top['p.x'], top['p.y']))
I get the following output :
Partial Derivatives Check ---------------- Component: 'p' ---------------- p: 'f_xy' wrt 'x' Forward Magnitude : 6.000000e+03 Reverse Magnitude : 6.000000e+03 Fd Magnitude : 2.199400e+07 Absolute Error (Jfor - Jfd) : 2.200000e+07 Absolute Error (Jrev - Jfd) : 2.200000e+07 Absolute Error (Jfor - Jrev): 0.000000e+00 Relative Error (Jfor - Jfd) : 1.000273e+00 Relative Error (Jrev - Jfd) : 1.000273e+00 Relative Error (Jfor - Jrev): 0.000000e+00 Raw Forward Derivative (Jfor) [[-6000.]] Raw Reverse Derivative (Jrev) [[-6000.]] Raw FD Derivative (Jfor) [[ 21994001.]] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - p: 'f_xy' wrt 'y' Forward Magnitude : 8.000000e-02 Reverse Magnitude : 8.000000e-02 Fd Magnitude : 2.200000e+07 Absolute Error (Jfor - Jfd) : 2.200000e+07 Absolute Error (Jrev - Jfd) : 2.200000e+07 Absolute Error (Jfor - Jrev): 0.000000e+00 Relative Error (Jfor - Jfd) : 1.000000e+00 Relative Error (Jrev - Jfd) : 1.000000e+00 Relative Error (Jfor - Jrev): 0.000000e+00 Raw Forward Derivative (Jfor) [[ 0.08]] Raw Reverse Derivative (Jrev) [[ 0.08]] Raw FD Derivative (Jfor) [[ 22000000.08]] Optimization terminated successfully. (Exit mode 0) Current function value: [-27.33333333] Iterations: 4 Function evaluations: 6 Gradient evaluations: 4 Optimization Complete ----------------------------------- Minimum of -27.333333 found at (0.006667, -733.333333)
The optimization is correct (i.e. proving almost certainly that the derivatives are correct), but the check_partial_derivatives
output does not show consistent results between fd and forward/reverse methods.
回答1:
relf
So, you have encountered a limitation that has come up before, namely you can't calculate derivatives about a design point until you run your model at that point. The finite difference results are wrong because the model has never been run. To verify your partials, you need to move check_partial_derivatives
to after the run. Also, I always comment out the optimizer when I am checking derivatives so that I am checking derivatives about the initial point. When I did these two things, I got good results (see code below).
top = Problem()
root = top.root = Group()
#root.fd_options['force_fd'] = True
root.add('p1', IndepVarComp('x', 3.0))
root.add('p2', IndepVarComp('y', -4.0))
root.add('p', Paraboloid())
root.connect('p1.x', 'p.x')
root.connect('p2.y', 'p.y')
#top.driver = ScipyOptimizer()
#top.driver.options['optimizer'] = 'SLSQP'
#top.driver.add_desvar('p1.x', lower=-1000, upper=1000, scaler=1000.)
#top.driver.add_desvar('p2.y', lower=-1000, upper=1000, scaler=0.001)
#top.driver.add_objective('p.f_xy')
top.setup()
top.run()
top.check_partial_derivatives() # added line
print('\n')
print('Minimum of %f found at (%f, %f)' % (top['p.f_xy'], top['p.x'], top['p.y']))
There is a feature request on our github for the ability to run check_partial_derivatives without running the model first. I think it is feasible that we will can do this by just telling root to solve_nonlinear, ignoring the driver, so it will probably be added at some point.
来源:https://stackoverflow.com/questions/35627718/scaled-paraboloid-and-derivatives-checking