python - 为什么 scipy.optimize.minimize(默认)报告成功而不用 Skyfield 移动?

标签 python scipy minimize skyfield

scipy.optimize.minimize 使用默认方法返回初始值作为结果,没有任何错误或警告消息。在使用 this answer 建议的 Nelder-Mead 方法时解决了问题,我想明白:

为什么默认方法返回没有警告的错误答案作为答案的起点 - 有什么方法可以防止“没有警告的错误答案” 在这种情况下避免这种行为?

请注意,separation 函数使用 python 包 Skyfield生成不保证平滑的要最小化的值,这可能是 Simplex 在这里更好的原因。

结果:

测试结果:[ 2.14159739] 'correct': 2.14159265359 initial: 0.0

默认结果:[ 10000.] '正确':13054 初始值:10000

Nelder-Mead 结果:[ 13053.81011963] '正确':13054 初始值:10000

FULL OUTPUT using DEFAULT METHOD:
   status: 0
  success: True
     njev: 1
     nfev: 3
 hess_inv: array([[1]])
      fun: 1694.98753895812
        x: array([ 10000.])
  message: 'Optimization terminated successfully.'
      jac: array([ 0.])
      nit: 0

FULL OUTPUT using Nelder-Mead METHOD:
  status: 0
    nfev: 63
 success: True
     fun: 3.2179306044608054
       x: array([ 13053.81011963])
 message: 'Optimization terminated successfully.'
     nit: 28

这是完整的脚本:

def g(x, a, b):
    return np.cos(a*x + b)

def separation(seconds, lat, lon):
    lat, lon, seconds = float(lat), float(lon), float(seconds) # necessary it seems
    place = earth.topos(lat, lon)
    jd = JulianDate(utc=(2016, 3, 9, 0, 0, seconds))
    mpos = place.at(jd).observe(moon).apparent().position.km
    spos = place.at(jd).observe(sun).apparent().position.km
    mlen = np.sqrt((mpos**2).sum())
    slen = np.sqrt((spos**2).sum())
    sepa = ((3600.*180./np.pi) *
            np.arccos(np.dot(mpos, spos)/(mlen*slen)))
    return sepa

from skyfield.api import load, now, JulianDate
import numpy as np
from scipy.optimize import minimize

data = load('de421.bsp')

sun   = data['sun']
earth = data['earth']
moon  = data['moon']

x_init = 0.0
out_g = minimize(g, x_init, args=(1, 1))
print "test result: ", out_g.x, "'correct': ", np.pi-1, "initial: ", x_init    # gives right answer

sec_init = 10000
out_s_def = minimize(separation, sec_init, args=(32.5, 215.1))
print "default result: ", out_s_def.x, "'correct': ", 13054, "initial: ", sec_init

sec_init = 10000
out_s_NM = minimize(separation, sec_init, args=(32.5, 215.1),
                 method = "Nelder-Mead")
print "Nelder-Mead result: ", out_s_NM.x, "'correct': ", 13054, "initial: ", sec_init

print ""
print "FULL OUTPUT using DEFAULT METHOD:"
print out_s_def
print ""
print "FULL OUTPUT using Nelder-Mead METHOD:"
print out_s_NM

最佳答案

1)

您的函数是分段常数(具有小规模的“阶梯”模式)。 它并非处处可微。

初始猜测时函数的梯度为零。

默认的 BFGS 优化器看到零梯度并根据其标准(基于在这种情况下不正确的输入函数假设,例如可微性)确定它是局部最小值。

基本上,完全平坦的区域会轰炸优化器。优化器在初始点周围的小而平坦的区域中探测函数,其中一切看起来都像函数只是一个常数,所以它认为你给了它一个常数函数。因为你的函数不是处处可微的,所以几乎所有的初始点都可能在这样的平坦区域内,所以在选择初始点时没有运气不好就可以发生这种情况。

另请注意,Nelder-Mead 对此并非免疫——恰好它的初始单纯形大于楼梯的大小,因此它会在更大的点周围探测函数。如果初始单纯形小于阶梯大小,则优化器的行为类似于 BFGS。

2)

一般答案:局部优化器返回局部最优值。这些是否与真正的最优值一致取决于函数的属性。

一般来说,要查看您是否陷入局部最优,请尝试不同的初始猜测。

此外,在不可微函数上使用基于导数的优化器也不是一个好主意。如果函数在“大”尺度上可微,则可以调整数值微分的步长。

因为没有廉价/通用的方法来检查函数是否处处可微,所以没有进行此类检查 --- 相反,它是优化方法中的一个假设,必须由输入目标函数并选择的人确保优化方法。

关于python - 为什么 scipy.optimize.minimize(默认)报告成功而不用 Skyfield 移动?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36110998/

相关文章:

javascript - 如何最小化 jQuery 中的代码

python - 使用 Scrapy 和 Python 的 XPath 无法使 XPath 正常工作

python - 如何比较列表内部的列表并选择第一个值最大的列表?

python - 在 Python 中使用 'nearest' 方法进行外推

python - Python 中的高效二维边缘检测

javascript - 你用什么来最小化和压缩 JavaScript 库?

python 2.7 : test if characters in a string are all Chinese characters

python - 图形中的重复节点名称 : 'conv2d_0/kernel/Adam'

python - scikit-learn 加载失败

php - 最小化 HTML、PHP 或 CSS 文件对网页来说真的是一个很大的改进,还是没有什么大的区别?