如何在神经 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 惩罚?的主要内容,如果未能解决你的问题,请参考以下文章

正则化项L1和L2

正则化项L1和L2的直观理解及L1不可导处理

正则化系数

机器学习中正则化项L1和L2的直观理解

自定义损失函数 Tensorflow / Keras 惩罚相对距离

人脸神经网络及算法整理