Julia中的二维曲线拟合

Julia中的二维曲线拟合,julia,curve-fitting,data-fitting,Julia,Curve Fitting,Data Fitting,我在Julia中有一个数组Z,它表示二维高斯函数的图像。即,Z[I,j]是像素I,j处高斯的高度。我想确定高斯分布的参数(均值和协方差),大概是通过某种曲线拟合 我研究了各种拟合Z的方法:我首先尝试了发行版软件包,但它是为不同的情况设计的(随机选择的点)。然后我尝试了LsqFit包,但它似乎是为一维拟合量身定做的,因为当我尝试拟合二维数据时,它会抛出错误,而且我找不到任何文档来引导我找到解决方案 如何将高斯分布拟合到Julia中的2D数组 最简单的方法是使用Optim.jl。下面是一个示例代码(

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

我研究了各种拟合
Z
的方法:我首先尝试了
发行版
软件包,但它是为不同的情况设计的(随机选择的点)。然后我尝试了
LsqFit
包,但它似乎是为一维拟合量身定做的,因为当我尝试拟合二维数据时,它会抛出错误,而且我找不到任何文档来引导我找到解决方案

如何将高斯分布拟合到Julia中的2D数组


最简单的方法是使用Optim.jl。下面是一个示例代码(它没有针对速度进行优化,但应该向您展示如何处理该问题):

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

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])
使用分布、跳转、NLopt
真d=MvNormal([1.0,0.0],[2.01.0;1.03.0])
常数xr=-3:0.1:3
常数年=-3:0.1:3
常数s=5.0
常数Z=[s*pdf(真值[x,y]),表示x在xr中,y在yr中]
m=模型(解算器=NLoptSolver(算法=:LD_MMA))
@变量(m,m1)
@变量(m,m2)
@变量(m,sig11>=0.001)
@变量(m,sig12)
@变量(m,sig22>=0.001)
@变量(m,sc>=0.001)
功能obj(m1、m2、sig11、sig12、sig22、sc)
est_d=MvNormal([m1,m2],[sig11 sig12;sig12 sig22])
ref_Z=[sc*pdf(est_d[x,y]),表示x在xr中,y在yr中]
总额((a-b)^2(a,b)(参考Z,Z))
结束
跳转寄存器(m,:obj,6,obj,autodiff=true)
@n目标(m,Min,obj(m1,m2,sig11,sig12,sig22,sc))

@NLconstraint(m,sig12*sig12+0.001原则上,你有一个损失函数

loss(μ, Σ) = sum(dist(Z[i,j], N([x(i), y(j)], μ, Σ)) for i in Ri, j in Rj)
其中,
x
y
将索引转换为轴上的点(您需要知道网格距离和偏移位置),以及
Ri
Rj
索引范围。
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
)。另一种方法是将条件指定为优化器中的约束


要使Optimizer发挥作用,您可能必须获得
损耗的导数。\u packeted
。或者必须手动查找并计算它(通过选择
dist
),或者使用日志转换更容易(如果幸运的话,您可以找到将其简化为线性问题的方法…)。或者,您可以尝试找到一个进行自动微分的优化器。

一个很好的解释-这与我上面给出的实现一致。这个问题中唯一的附加问题是检查协方差矩阵是否有效。哦,是的,这是一个很好的观点。可以通过使用参数
s
∑=S^2(对吗?)。这只会使微分更复杂。而且,
也必须是半正定的。我认为西格玛有平方根意味着它是半正定的?还是我读错了?(我的线性代数可能有点生疏,我不得不承认…)这确实是一个很好的观点。您必须设置
的∑=S*转置
,然后最简单的方法是将
S
设置为三角形,这样您就有三个参数需要估计。您对LsqFit做了哪些尝试?当然可以做您想做的事情。
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