我喜欢使用quad_function
自动区分(Optim.jl
)来优化(最小化)以下给定函数(autodiff=true
)。
我的目标函数将 Real
值舍入为整数,因此就像步骤一样。
当我使用autodiff
选项时,我的Real
值会获得dual numbers(ForwardDiff.Dual
s)。但遗憾的是,round
类型没有实现ForwardDiff.Dual
功能。因此,我编写了一个roundtoint64
函数,它提取了实部。这种方法在优化过程中会出现问题。
using Plots
plotlyjs()
function roundtoint64(x)
if typeof(x) <: ForwardDiff.Dual
roundtoint64(x.value)
else
Int64(round(x))
end
end
function quad_function(xs::Vector)
roundtoint64(xs[1])^2 + roundtoint64(xs[2])^2
end
x, y = linspace(-5, 5, 100), linspace(-5, 5, 100)
z = Surface((x,y)->quad_function([x,y]), x, y)
surface(x,y,z, linealpha = 0.3)
问题是optimize
函数立即收敛但不会继续。
using Optim
res = Optim.optimize(
quad_function,
[5.0,5.0],
Newton(),
OptimizationOptions(
autodiff = true,
# show_trace = true
))
结果:
Results of Optimization Algorithm
* Algorithm: Newton's Method
* Starting Point: [5.0,5.0]
* Minimizer: [5.0,5.0]
* Minimum: 5.000000e+01
* Iterations: 0
* Convergence: true
* |x - x'| < 1.0e-32: false
* |f(x) - f(x')| / |f(x)| < 1.0e-32: false
* |g(x)| < 1.0e-08: true
* Reached Maximum Number of Iterations: false
* Objective Function Calls: 1
* Gradient Calls: 1
optimal_values = Optim.minimizer(res) # [5.0, 5.0]
optimum = Optim.minimum(res) # 50.0
我还尝试使用整数optimize
向量初始化[5,5]
函数以避免舍入事情,但这也导致在以下位置找到初始步长的问题:
ERROR: InexactError()
in alphainit(::Int64, ::Array{Int64,1}, ::Array{Int64,1}, ::Int64) at /home/sebastian/.julia/v0.5/Optim/src/linesearch/hz_linesearch.jl:63
in optimize(::Optim.TwiceDifferentiableFunction, ::Array{Int64,1}, ::Optim.Newton, ::Optim.OptimizationOptions{Void}) at /home/sebastian/.julia/v0.5/Optim/src/newton.jl:69
in optimize(::Function, ::Array{Int64,1}, ::Optim.Newton, ::Optim.OptimizationOptions{Void}) at /home/sebastian/.julia/v0.5/Optim/src/optimize.jl:169
问题:有没有办法让optimize
只探索整数空间?
更新
我认为Int64
转换方法的问题在于我不再有ForwardDiff.Dual
s因此无法计算任何导数/渐变。怎么可能有一个更好的round
函数,哪些轮也嵌套了双重并给出了双重函数?
答案 0 :(得分:2)
我会以更多以双数字为中心的答案回复您的更新,因为Erwin Kalvelagen在原始问题上击败了我。
事实上,a round
function implemented for ForwardDiff.Dual
具有您在原始帖子中提到的行为 - 它会截断偏导数组件,并仅将round
应用于实际组件。这是一个大致正确的定义,因为round
的导数几乎无处不在,并且在步骤发生的地方(即以0.5
的间隔)未定义。
可以制定这个定义&#34;更正确&#34;通过传播NaN
s或在导数未定义的点处出错,但从实际AD的角度来看,该策略并没有太大的好处。 round
方法会选择一个方面&#34;在不连续的情况下,所以对我们来说有意义的是选择一个方面&#34;选择一个方面&#34;当拿出衍生物时。这在round
的情况下很容易,因为不连续性两侧的导数为零。
您可以通过覆盖当前定义的方法来使用您喜欢的任何定义,但正如您所指出的,{{1}中间偏导数可能会产生不正确的整体导数。这主要是因为您不再区分相同的目标函数。
答案 1 :(得分:0)
正如 Erwin Kalvelagen 在我的问题评论中指出:给定的算法和解算器无法解决这类问题。
因此我改变了我的成本函数以至少有一些渐变,但仍然不顺利:
function quad_function_with_gradient(xs::Vector)
round(xs[1])*xs[1] + round(xs)[2]*xs[2]
end
看起来像这样:
但是我仍然需要解决双数字舍入问题。因此我编写了一个round
函数,它总是将实部和部分函数舍入:
using Optim
roundpartials{T<:ForwardDiff.Partials}(partials::T) = (round(v) for v in partials.values)
Base.round{T<:ForwardDiff.Dual}(dual::T) = ForwardDiff.Dual(
round(dual.value),
roundpartials(dual.partials)...)
这给了我一个解决方案,但问题略有不同:
res = Optim.optimize(
quad_function,
[5.0,5.0],
Newton(),
OptimizationOptions(
autodiff = true
))
Results of Optimization Algorithm
* Algorithm: Newton's Method
* Starting Point: [5.0,5.0]
* Minimizer: [0.0,0.0]
* Minimum: 0.000000e+00
* Iterations: 1
* Convergence: true
* |x - x'| < 1.0e-32: false
* |f(x) - f(x')| / |f(x)| < 1.0e-32: false
* |g(x)| < 1.0e-08: true
* Reached Maximum Number of Iterations: false
* Objective Function Calls: 5
* Gradient Calls: 5
由你们决定这是否是一个可行的解决方案。