This section compares the beta loss when directly applying the classica MLP to train $(λ, β)$

using MonotoneSplines
using Plots
using Random
__init_pytorch__() # initialize supports for PyTorch backend
PyObject <module 'boot' from '/home/runner/work/MonotoneSplines.jl/MonotoneSplines.jl/src/boot.py'>

Generate samples

seed = 1
n = 100
σ = 0.2
prop_nknots = 0.2
f = x -> x^3
x = rand(MersenneTwister(seed), n) * 2 .- 1
y = f.(x) + randn(MersenneTwister(seed+1), n) * σ
100-element Vector{Float64}:
  0.0007819503546631634
 -0.17782624307112058
 -0.1742614506793042
 -1.2979823247474416
 -0.13513493385996378
 -0.08183503123964558
  0.5660362347385295
  1.1030450840101351
  0.12712269579175728
  1.155935257401418
  ⋮
 -0.06035131206452983
  0.37117086314660136
 -0.12565509945611203
 -0.14361458475165484
 -0.07610037738674293
 -0.4362535693694123
  0.08783960166858745
  0.029232900958345298
  0.02312389902322433

construct B spline basis matrix

B, L, J = MonotoneSplines.build_model(x, prop_nknots = prop_nknots)
([0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0], [101.9428539030352 0.0 … 0.0 0.0; -139.08167285801295 31.71316023009978 … 0.0 0.0; … ; 0.0 0.0 … 0.15896851969011258 0.0; 0.0 0.0 … -0.15118239117609084 0.004914483718312649], 14, -0.9841814332188785, 1.9839907510161057, [4, 33, 91, 38, 100, 12, 78, 11, 43, 77, 42, 8], [4, 57, 18, 35, 21, 27, 29, 97, 55, 33  …  42, 69, 7, 85, 54, 34, 10, 74, 80, 8], [1.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 1.0], [-28.595566037824863 28.595566037824863 … 0.0 0.0; 0.0 0.0 … -28.144709657570832 28.144709657570832], [0.0, 0.10491136968688579, 0.21899472817772042, 0.27367650493628326, 0.3338442475676866, 0.4326619879646609, 0.5157437085725072, 0.5522624575275193, 0.6453099284911253, 0.7665855741024367, 0.8934080316869423, 1.0])

Range of the smoothness parameter λ

λs = 10 .^ (range(-6, 0, length = 10))
λmin = minimum(λs)
λmax = maximum(λs)
1.0

Set number of λ for training and validation

N = 100
ngrid = 100
arr_λs = rand(N) * (λmax - λmin) .+ λmin
βs = [mono_ss(x, y, λ, prop_nknots = prop_nknots).β for λ in arr_λs]
βs_mat = vcat(βs'...)'
λs_mat = vcat(MonotoneSplines.aug.(arr_λs)'...)'
8×100 adjoint(::Matrix{Float64}) with eltype Float64:
  0.84318    0.431223    0.727837  …   0.678795   0.618857   0.593
  0.944728   0.755499    0.899521      0.878846   0.852178   0.84014
  2.32374    1.53914     2.0706        1.9715     1.8568     1.80941
  0.918248   0.656676    0.853134      0.82389    0.786675   0.770065
 -0.170575  -0.841129   -0.317678     -0.387437  -0.479881  -0.522561
  8.4318     4.31223     7.27837   …   6.78795    6.18857    5.93
  0.710952   0.185954    0.529747      0.460762   0.382984   0.351649
  0.599461   0.0801876   0.385569      0.312763   0.237012   0.208528

for integrative loss

grid_λs = range(λmin, λmax, length=ngrid)
grid_βs = [mono_ss(x, y, λ, prop_nknots = prop_nknots).β for λ in grid_λs]
βs_grid_mat = vcat(grid_βs'...)'
λs_grid_mat = vcat(MonotoneSplines.aug.(grid_λs)'...)'
8×100 adjoint(::Matrix{Float64}) with eltype Float64:
   1.0e-6    0.010102     0.020203     …   0.979798    0.989899    1.0
   0.01      0.216174     0.272357         0.99322     0.996622    1.0
   1.0       1.01015      1.02041          2.66392     2.69096     2.71828
   0.001     0.100509     0.142137         0.989847    0.994937    1.0
 -13.8155   -4.59502     -3.90192         -0.0204089  -0.0101524   0.0
   1.0e-5    0.10102      0.20203      …   9.79798     9.89899    10.0
   1.0e-12   0.00010205   0.000408161      0.960004    0.9799      1.0
   1.0e-18   1.03091e-6   8.24608e-6       0.94061     0.970002    1.0

train GpBS

G, Loss, Loss1, bfun = MonotoneSplines.py_train_G_lambda(y, B, L, nepoch = 0, nepoch0 = 3, K = N,
            λl = λmin, λu = λmax, gpu_id = -1,
            nhidden = 1000,
            λs_opt_train = λs, λs_opt_val = grid_λs,
            βs_opt_train = βs_mat', βs_opt_val = βs_grid_mat',
            disable_tqdm = true,
            niter_per_epoch = 200)
(MonotoneSplines.var"#79#81"{Matrix{Float64}, PyCall.PyObject}([0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0], PyObject <function train_G_lambda.<locals>.<lambda> at 0x7f16df8e4550>), [0.20706725120544434, 24.154579162597656, 21.34688377380371, 2.2098512649536133, 1.5733797550201416, 5.085545063018799, 1.252031683921814, 1.6796458959579468, 1.081223487854004, 0.4646139442920685  …  0.21427270770072937, 0.17265203595161438, 0.17469000816345215, 0.15882530808448792, 0.1710147261619568, 0.12286616116762161, 0.17516601085662842, 0.11560828983783722, 0.1396656185388565, 0.15126356482505798], Float32[0.086014666 0.076909244; 0.09443208 0.08820638; … ; 0.03986398 0.034542393; 0.03972225 0.035326876], MonotoneSplines.var"#80#82"{PyCall.PyObject}(PyObject <function train_G_lambda.<locals>.<lambda> at 0x7f16df8e4550>))

Check training loss

plot(Loss, label = "training loss")

Check beta loss

plot(Loss1[:, 1], label = "training L2 beta Loss")
plot!(Loss1[:, 2], label = "expected L2 beta loss")