Julia:尝试使用optimize来最小化函数时出错

时间:2017-09-11 13:59:19

标签: optimization julia

我有以下函数,我想用Optim.jl最小化多个参数:

function post(parm,y,x,n)
  # Evaluate the log of the marginal posterior for parm at a point

  fgamma=zeros(n,1);
  for ii = 1:2
    fgamma = fgamma + parm[ii+1]*(x[:,ii+1].^parm[4]);
  end

  fgamma = fgamma.^(1/parm[4]);
  fgamma = fgamma + parm[1]*ones(n,1);

  lpost = .5*n*log.((y - fgamma)'*(y-fgamma));
end

但是,当我尝试使用optimize时,Julia会返回错误。

旧错误(使用parm):

MethodError: no method matching finite_difference!(::##1#2, ::Array{Float64,2}, ::Array{Float64,2}, ::Symbol)

新错误(使用parm2):

MethodError: Cannot `convert` an object of type Array{Float64,2} to an object of type Float64

我正在使用的包含数据和优化调用的完整脚本是:

using Distributions
using Optim

n = 200;
k = 3;
x = ones(n,k);
fgamma=zeros(n,1);
gam = [1.01; 0.6; 0.8; 1.5];
x[:,2] = rand(Chisq(10),n);
x[:,3] = rand(Chisq(5),n);
epsl = rand(Normal(0,1),n);
y = zeros(n,1);

for i = 1:n
    y[i,1] = gam[1] + (gam[2]*x[i,2]^gam[4] + gam[3]*x[i,3]^gam[4])^(1/gam[4]) + epsl[i];
end

# Sim
bols = inv(x'x)x'y;
s2 = (y-x*bols)'*(y-x*bols)/(n-k);
sse=(n-k)*s2;
bolscov = s2.*inv(x'*x);
bolssd=zeros(k,1);
for i = 1:k
  bolssd[i,1]=sqrt(bolscov[i,i]);
end

# Calculate posterior mode and Hessian at mode
nparam=k+1;
parm = ones(nparam,1);
parm[1:k,1]=bols;
parm2 = vec(parm);

opt = Optim.Options(f_tol = 1e-8, iterations = 1000);
Optim.after_while!{T}(d, state::Optim.BFGSState{T}, method::BFGS, options) = global invH = state.invH

res = optimize(p -> post(p,y,x,n), parm2, BFGS(), opt)

有谁知道我做错了什么?我认为函数lpostpost的类型存在问题,因为它返回1x1数组{Float64,2}。不幸的是,我无法很好地处理它。

1 个答案:

答案 0 :(得分:0)

错误消息

MethodError: Cannot `convert` an object of type Array{Float64,2} to an object of type Float64

是由尝试将矩阵转换为标量引起的。一般情况下这是不可能的,但是当矩阵是1x1矩阵时(正如问题所指出的那样),存在一个自然变换:scalar = matrix[1,1]

optimize需要返回标量值,因为它是标量非线性优化例程。优化矢量值甚至难以明确定义(Pareto optima等概念试图这样做)。

因此,在这个前奏之后,修复很简单,同时解决了复杂优化@fst(海报)的问题。同样,需要单维标量,因此real(...)用于从复数值(更准确地说是有序标量)中生成标量,因为复数也是标量。生成的post函数为:

function post(parm,y,x,n)
  # Evaluate the log of the marginal posterior for parm at a point

  fgamma=zeros(n,1);
  for ii = 1:2
    fgamma = fgamma + parm[ii+1]*(x[:,ii+1].^parm[4]);
  end

  fgamma = fgamma.^Complex(1/parm[4]);
  fgamma = fgamma + parm[1]*ones(n,1);

  lpost = .5*n*log.((y - fgamma)'*(y-fgamma));
  return real(lpost[1,1])
end