Álv*_*gas 6 closures function julia
我正在学习如何编写最大似然实现Julia
,目前,我正在关注此材料(强烈推荐顺便说一句!)。\n所以问题是我不完全理解Julia 中的闭包是什么,也不知道什么时候应该实际使用它。即使在阅读了官方文档之后,这个概念对我来说仍然有点模糊。
例如,在教程中,我提到作者将对数似然函数定义为:
\nfunction log_likelihood(X, y, \xce\xb2)\n ll = 0.0\n @inbounds for i in eachindex(y)\n z\xe1\xb5\xa2 = dot(X[i, :], \xce\xb2)\n c = -log1pexp(-z\xe1\xb5\xa2) # Conceptually equivalent to log(1 / (1 + exp(-z\xe1\xb5\xa2))) == -log(1 + exp(-z\xe1\xb5\xa2))\n ll += y[i] * c + (1 - y[i]) * (-z\xe1\xb5\xa2 + c) # Conceptually equivalent to log(exp(-z\xe1\xb5\xa2) / (1 + exp(-z\xe1\xb5\xa2)))\n end\n ll\nend\n
Run Code Online (Sandbox Code Playgroud)\n不过,后来他声称
\n\n\n我们写的对数似然是数据和参数的函数,但从数学上讲它应该只取决于参数。除了创建新函数的数学原因之外,我们只需要参数的函数,因为Optim 中的优化算法假设输入具有该属性。为了实现这两个目标,我们将构建一个闭包,该闭包部分地为我们应用对数似然函数,并对其求反以给出我们想要最小化的负对数似然。
\n
# Creating the closure\nmake_closures(X, y) = \xce\xb2 -> -log_likelihood(X, y, \xce\xb2)\nnll = make_closures(X, y)\n\n# Define Initial Values equal to zero\n\xce\xb2\xe2\x82\x80 = zeros(2 + 1)\n# Ignite the optimization routine using `nll`\nres = optimize(nll, \xce\xb2\xe2\x82\x80, LBFGS(), autodiff=:forward)\n
Run Code Online (Sandbox Code Playgroud)\n从这段文字中,我明白我们需要使用它,因为这就是Optim
算法的工作原理,但我仍然不明白什么是更广泛意义上的闭包。如果有人能阐明这一点,我将不胜感激。非常感谢。
在您询问的上下文中,您可以认为闭包是一个引用其外部作用域中定义的某些变量的函数(对于其他情况,请参阅@phipsgabler的答案)。这是一个最小的例子:
julia> function est_mean(x)
function fun(m)
return m - mean(x)
end
val = find_zero(fun, 0.0)
@show val, mean(x)
return fun # explicitly return the inner function to inspect it
end
est_mean (generic function with 1 method)
julia> x = rand(10)
10-element Vector{Float64}:
0.6699650145575134
0.8208379672036165
0.4299946498764684
0.1321653923513042
0.5552854476018734
0.8729613266067378
0.5423030870674236
0.15751882823315777
0.4227087678654101
0.8594042895489912
julia> fun = est_mean(x)
(val, mean(x)) = (0.5463144770912497, 0.5463144770912497)
fun (generic function with 1 method)
julia> dump(fun)
fun (function of type var"#fun#3"{Vector{Float64}})
x: Array{Float64}((10,)) [0.6699650145575134, 0.8208379672036165, 0.4299946498764684, 0.1321653923513042, 0.5552854476018734, 0.8729613266067378, 0.5423030870674236, 0.15751882823315777, 0.4227087678654101, 0.8594042895489912]
julia> fun.x
10-element Vector{Float64}:
0.6699650145575134
0.8208379672036165
0.4299946498764684
0.1321653923513042
0.5552854476018734
0.8729613266067378
0.5423030870674236
0.15751882823315777
0.4227087678654101
0.8594042895489912
julia> fun(10)
9.453685522908751
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,fun
保存了x
外部作用域(在本例中为est_mean
函数引入的作用域)对变量的引用。此外,我已经向您展示了您甚至可以从外部检索该值fun
作为其字段(通常不建议这样做,但我向您展示这一点是为了证明确实fun
存储了对其外部范围中定义的对象的引用x
;它需要存储此引用,因为变量x
在函数体内使用fun
)。
正如您所指出的,在估计的上下文中,这很有用,因为find_zero
在我的情况下,要求函数仅采用一个参数 -m
在我的情况下是变量,而您希望返回值同时取决于 passm
和 on x
。
重要的是,一旦x
在闭包中捕获,fun
它不必在当前范围内。例如,当我调用时,fun(10)
代码会正确执行,尽管我们超出了 function 的范围est_mean
。但这不是问题,因为fun
函数已经捕获了x
变量。
我再举一个例子:
julia> function gen()
x = []
return v -> push!(x, v)
end
gen (generic function with 1 method)
julia> fun2 = gen()
#4 (generic function with 1 method)
julia> fun2.x
Any[]
julia> fun2(1)
1-element Vector{Any}:
1
julia> fun2.x
1-element Vector{Any}:
1
julia> fun2(100)
2-element Vector{Any}:
1
100
julia> fun2.x
2-element Vector{Any}:
1
100
Run Code Online (Sandbox Code Playgroud)
在这里您可以看到函数x
中定义的变量被我绑定到该变量gen
的匿名函数捕获。稍后,当您调用绑定到变量的对象时,它会被更新(并且可以被引用),尽管它是在函数作用域中定义的。尽管我们离开了作用域,但绑定到变量的对象的寿命比作用域长,因为它是由我们定义的匿名函数捕获的。v -> push!(x, v)
fun2
fun2
x
gen
gen
x
如果有不清楚的地方请评论。
归档时间: |
|
查看次数: |
1698 次 |
最近记录: |