Julia中的2D曲线拟合

时间:2018-12-10 05:10:30

标签: julia curve-fitting data-fitting

我在Julia中有一个数组Z,它表示2D高斯函数的图像。即Z[i,j]是像素i,j处高斯的高度。我想通过某种曲线拟合来确定高斯的参数(均值和协方差)。

我研究了各种适合Z的方法:我首先尝试了Distributions软件包,但是它是为某种不同的情况(随机选择的点)而设计的。然后,我尝试了LsqFit程序包,但是它似乎是为1D拟合量身定制的,因为当我尝试拟合2D数据时它会抛出错误,并且找不到任何文档可以帮助我找到解决方案。

  

如何在Julia中将高斯拟合到2D数组?

2 个答案:

答案 0 :(得分:1)

最简单的方法是使用Optim.jl。这是一个示例代码(它并未针对速度进行优化,但是应该向您展示如何解决该问题):

using Distributions, Optim

# generate some sample data    
true_d = MvNormal([1.0, 0.0], [2.0  1.0; 1.0 3.0])
const xr = -3:0.1:3
const yr = -3:0.1:3
const s = 5.0
const m = [s * pdf(true_d, [x, y]) for x in xr, y in yr]

decode(x) = (mu=x[1:2], sig=[x[3] x[4]; x[4] x[5]], s=x[6])

function objective(x)
    mu, sig, s = decode(x)
    try # sig might be infeasible so we have to handle this case
        est_d = MvNormal(mu, sig)
        ref_m = [s * pdf(est_d, [x, y]) for x in xr, y in yr]
        sum((a-b)^2 for (a,b) in zip(ref_m, m))
    catch
        sum(m)
    end
end

# test for an example starting point
result = optimize(objective, [1.0, 0.0, 1.0, 0.0, 1.0, 1.0])
decode(result.minimizer)

或者,您可以使用约束优化,例如像这样:

using Distributions, JuMP, NLopt

true_d = MvNormal([1.0, 0.0], [2.0  1.0; 1.0 3.0])
const xr = -3:0.1:3
const yr = -3:0.1:3
const s = 5.0
const Z = [s * pdf(true_d, [x, y]) for x in xr, y in yr]

m = Model(solver=NLoptSolver(algorithm=:LD_MMA))

@variable(m, m1)
@variable(m, m2)
@variable(m, sig11 >= 0.001)
@variable(m, sig12)
@variable(m, sig22 >= 0.001)
@variable(m, sc >= 0.001)

function obj(m1, m2, sig11, sig12, sig22, sc)
    est_d = MvNormal([m1, m2], [sig11 sig12; sig12 sig22])
    ref_Z = [sc * pdf(est_d, [x, y]) for x in xr, y in yr]
    sum((a-b)^2 for (a,b) in zip(ref_Z, Z))
end

JuMP.register(m, :obj, 6, obj, autodiff=true)
@NLobjective(m, Min, obj(m1, m2, sig11, sig12, sig22, sc))
@NLconstraint(m, sig12*sig12 + 0.001 <= sig11*sig22)

setvalue(m1, 0.0)
setvalue(m2, 0.0)
setvalue(sig11, 1.0)
setvalue(sig12, 0.0)
setvalue(sig22, 1.0)
setvalue(sc, 1.0)

status = solve(m)
getvalue.([m1, m2, sig11, sig12, sig22, sc])

答案 1 :(得分:1)

原则上,您有损失函数

loss(μ, Σ) = sum(dist(Z[i,j], N([x(i), y(j)], μ, Σ)) for i in Ri, j in Rj)

其中xy将索引转换为轴上的点(您需要了解这些点的网格距离和偏移位置),以及RiRj索引范围。 dist是您使用的距离量度,例如平方差。

通过将μΣ包装到单个向量中,您应该可以将其传递给优化器:

pack(μ, Σ) = [μ; vec(Σ)]
unpack(v) = @views v[1:N], reshape(v[N+1:end], N, N)
loss_packed(v) = loss(unpack(v)...)

在您的情况下为N = 2。 (也许解压缩应该进行一些优化以消除不必要的复制。)

另一件事是,我们必须确保Σ是半正定的(因此也是对称的)。一种方法是对打包损耗函数进行参数化,并在一些较低的三角矩阵L上进行优化,例如Σ = L * L'。对于N = 2,我们可以将其写为

unpack(v) = v[1:2], LowerTriangular([v[3] zero(v[3]); v[4] v[5]])
loss_packed(v) = let (μ, L) = unpack(v)
    loss(μ, L * L')
end

(这当然易于进一步优化,例如将乘法直接扩展到loss中)。另一种方法是将条件指定为优化器的约束。

要使优化程序正常工作,您可能必须获得loss_packed的派生词。要么必须找到手动计算的方法(最好选择dist),要么使用对数转换更容易(如果幸运的话,您可以找到一种方法来将其简化为线性问题。) )。或者,您可以尝试找到可以自动区分的优化程序。