Examples
Fitting a PSF
Here is a brief example which shows how to construct a loss function for fitting a PSFModel
to some data.
using PSFModels: Gaussian
using HCIDatasets: BetaPictoris
using Plots
# convenience function for plotting
function imshow(data; kwargs...)
xlim = extrema(axes(data, 2))
ylim = extrema(axes(data, 1))
heatmap(data; xlim=xlim, ylim=ylim, aspect_ratio=1, kwargs...)
end
# get a PSF from HCIDatasets.jl;
# you may be prompted to download the file
psf = BetaPictoris[:psf]
imshow(psf)
using LossFunctions
# generative model
function model(X::AbstractVector{T}) where T
position = @view X[1:2] # x, y position
fwhm = @view X[3:4] # fwhm_x, fwhm_y
amp = X[5] # amplitude
return amp * Gaussian{T}(position, fwhm)
end
# objective function
function loss(X::AbstractVector{T}, target) where T
# cheap way to enforce positivity
all(>(0), X) || return T(Inf)
# get generative model
m = model(X)
# l2-distance loss (χ² loss) (LossFunctions.jl)
stamp = @view m[axes(target)...]
return value(L2DistLoss(), target, stamp, AggMode.Sum())
end
# params are [x, y, fwhm_x, fwhm_y, amp]
test_params = Float32[20, 20, 5, 5, 1]
loss(test_params, psf)
11.579905f0
The objective function can then be used with an optimization library like Optim.jl to find best-fitting parameters
using Optim
# Fit our data using test_params as a starting point
# uses Nelder-Mead optimization
res = optimize(P -> loss(P, psf), test_params)
* Status: success * Candidate solution Final objective value: 1.026439e-02 * Found with Algorithm: Nelder-Mead * Convergence measures √(Σ(yᵢ-ȳ)²)/n ≤ 1.0e-08 * Work counters Seconds run: 0 (vs limit Inf) Iterations: 375 f(x) calls: 645
# utilize automatic differentiation (AD) to enable
# advanced algorithms, like LBFGS
res_ad = optimize(P -> loss(P, psf), test_params, LBFGS(); autodiff=:forward)
* Status: success * Candidate solution Final objective value: 1.026439e-02 * Found with Algorithm: L-BFGS * Convergence measures |x - x'| = 9.54e-07 ≰ 0.0e+00 |x - x'|/|x'| = 4.76e-08 ≰ 0.0e+00 |f(x) - f(x')| = 0.00e+00 ≤ 0.0e+00 |f(x) - f(x')|/|f(x')| = 0.00e+00 ≤ 0.0e+00 |g(x)| = 7.22e-08 ≰ 1.0e-08 * Work counters Seconds run: 1 (vs limit Inf) Iterations: 12 f(x) calls: 52 ∇f(x) calls: 52
we can see which result has the better loss, and then use the generative model to create a model that we can use elsewhere
best_res = minimum(res) < minimum(res_ad) ? res : res_ad
best_fit_params = Optim.minimizer(best_res)
5-element Array{Float32,1}: 20.018291 20.009983 4.767066 4.8366284 0.10024304
synth_psf = model(best_fit_params)
plot(
imshow(psf, title="Data"),
plot(synth_psf, axes(psf); title="Model"),
cbar=false,
ticks=false,
layout=2,
size=(600, 300)
)