如何为神经 ODE 的损失函数添加 L1 惩罚?

How to add an L1 penalty to the loss function for Neural ODEs?

我一直在尝试将微分方程组拟合到我拥有的一些数据,并且有 18 个参数可以拟合,但是理想情况下,其中一些参数应该 zero/go 到零。在谷歌搜索时,我遇到的是将 DE 层构建到神经网络中,并且我发现了一些带有 Julia 代码示例的 Github 回购协议,但是我对 Julia 和神经 ODE 都是新手。特别是,我一直在修改这个例子中的代码:

https://computationalmindset.com/en/neural-networks/experiments-with-neural-odes-in-julia.html

差异:我有一个包含 3 个 DE 的系统,而不是 2 个,我有 18 个参数,并且我导入了两个 CSV 文件来适应它,而不是生成一个玩具数据集来适应它。

我的困境:在使用 gooling 时,我遇到了 LASSO/L1 正则化,并希望通过向成本函数添加 L1 惩罚,我可以将一些参数“清零”。问题是我不明白如何修改成本函数来合并它。我现在的损失函数只是

function loss_func()
 pred = net()

 sum(abs2, truth[1] .- pred[1,:]) +
 sum(abs2, truth[2] .- pred[2,:]) +
 sum(abs2, truth[3] .- pred[3,:])
end

但我想将 L1 处罚纳入其中。对于 L1 回归,我遇到了成本函数的方程式:J′(θ;X,y) = J(θ;X,y)+aΩ(θ),其中“其中 θ 表示可训练参数,X 输入... y [the ] 目标标签。a 是一个超参数,对范数惩罚的贡献进行加权”,对于 L1 正则化,惩罚是 Ω(θ) = ∣∣w∣∣ = ∑∣w∣(来源:https://theaisummer.com/regularization/)。我知道 RHS 的第一项是损失 J(θ;X,y) 并且是我已经拥有的,a 是我选择的超参数,可以是 0.001、0.1、1、100000000 等,并且L1惩罚是参数绝对值的总和。我不明白的是我如何将 a∑∣w∣ 项添加到我当前的函数中 - 我想将其编辑为如下所示:

function cost_func(lambda)
 pred = net()
 penalty(lambda) = lambda * (sum(abs(param[1])) + 
                             sum(abs(param[2])) + 
                             sum(abs(param[3]))
                            )
 sum(abs2, truth[1] .- pred[1,:]) +
 sum(abs2, truth[2] .- pred[2,:]) +
 sum(abs2, truth[3] .- pred[3,:]) +
 penalty(lambda)
end

其中 param[1], param[2], param[3] 指的是我正在尝试学习的 DE u[1], u[2], u[3] 的参数。我不知道这个逻辑是否正确或实现它的正确方法,而且我也不知道 how/where 我会访问学习到的参数。我怀疑答案可能就在这段代码中的某处

callback_func = function ()
 loss_value = loss_func()
 println("Loss: ", loss_value)
end
fparams = Flux.params(p)
Flux.train!(loss_func, fparams, data, optimizer, cb = callback_func);

但我不确定甚至不知道如何使用它,如果它是答案的话。

我一直在弄乱这个,并查看了其他一些 NODE 实现 (this one in particular) 并调整了我的成本函数,使其成为:

function cost_fnct(param)
   prob = ODEProblem(model, u0, tspan, param)
   prediction = Array(concrete_solve(prob, Tsit5(), p = param, saveat = trange))

   loss = Flux.mae(prediction, data)
   penalty = sum(abs, param)
   loss + lambda*penalty
 end;

其中lambda为调优参数,使用L1惩罚为参数绝对值之和的定义。然后,进行训练:

lambda = 0.01
resinit = DiffEqFlux.sciml_train(cost_fnct, p, ADAM(), maxiters = 3000)
res = DiffEqFlux.sciml_train(cost_fnct, resinit.minimizer, BFGS(initial_stepnorm = 1e-5))

其中 p 只是我的参数“猜测”的初始值,即与我尝试拟合的参数数量长度相同的矢量。

如果你看的是第一个link我原来的post(here),你可以重新定义损失函数加上这个惩罚项然后定义lambda回调函数前及后续训练:

lambda = 0.01
callback_func = function ()
     loss_value = cost_fnct()
     println("Loss: ", loss_value)
     println("\nLearned parameters: ", p)
end
fparams = Flux.params(p)
Flux.train!(cost_fnct, fparams, data, optimizer, cb = callback_func);

None 当然,其中包括任何类型的交叉验证和调整参数优化!我会继续接受我对我的问题的回答,因为据我所知,未回答的问题会被推送以鼓励回答,我想避免堵塞标签,但如果有人有不同的解决方案,或想发表评论,请随时继续这样做。