如何在神经 ODE 的损失函数中添加 L1 惩罚?
Posted
技术标签:
【中文标题】如何在神经 ODE 的损失函数中添加 L1 惩罚?【英文标题】:How to add an L1 penalty to the loss function for Neural ODEs? 【发布时间】:2021-12-18 08:01:38 【问题描述】:我一直在尝试将微分方程系统拟合到我拥有的一些数据中,并且有 18 个参数要拟合,但理想情况下,其中一些参数应该为零/归零。在谷歌搜索时,我遇到的一件事是将 DE 层构建到神经网络中,并且我发现了一些带有 Julia 代码示例的 Github 存储库,但是我对 Julia 和神经 ODE 都是新手。特别是,我一直在修改此示例中的代码:
https://computationalmindset.com/en/neural-networks/experiments-with-neural-odes-in-julia.html
差异:我有一个由 3 个 DE 组成的系统,而不是 2 个,我有 18 个参数,并且我导入了两个带有数据的 CSV 来适应它,而不是生成一个玩具数据集来适应。
我的困境:在搜索时,我遇到了 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]
指的是我正在尝试学习的 DEs u[1], u[2], u[3]
的参数。我不知道这个逻辑是否正确或实现它的正确方法,而且我也不知道如何/在哪里访问学习的参数。我怀疑答案可能就在这段代码的某个地方
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);
但我不确定甚至不知道如何使用它,如果它是答案。
【问题讨论】:
万一有人来看这个,我尝试了penalty() = lambda* sum(abs, p)
并在成本函数上方定义了 lambda。
什么意思?它解决了你的问题吗?如果是,请将其发布为答案。
它可以运行,但我没有在生成的玩具数据集上测试它,其中一些参数真正为零,而不是在不确定的真实数据上测试它。我一直在更改调整参数的值并获得最差的模型,但不是最小的模型/没有将参数归零。我不想明确地说“这就是答案”,直到我确认它做了它应该做的事情。
【参考方案1】:
我一直在搞砸这个,并查看了其他一些 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
最初只是我的参数“猜测”,即长度与我尝试拟合的参数数量相同的向量。
如果您正在查看我在原始帖子中的第一个链接(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);
当然,这些都不包括任何形式的交叉验证和调优参数优化!我会继续接受我对我的问题的回答,因为我的理解是,未回答的问题会被推送以鼓励回答,我想避免堵塞标签,但如果有人有不同的解决方案,或者想发表评论,请随意继续这样做。
【讨论】:
以上是关于如何在神经 ODE 的损失函数中添加 L1 惩罚?的主要内容,如果未能解决你的问题,请参考以下文章