【问题标题】:Julia Flux: writing a regularizer depending on the provided regularization coefficientsJulia Flux:根据提供的正则化系数编写正则化器
【发布时间】:2020-06-22 20:29:37
【问题描述】:

我正在编写一个脚本,将 Python 的 Keras (v1.1.0) 模型转换为 Julia 的 Flux 模型,并且我正在努力实现正则化(我已阅读 https://fluxml.ai/Flux.jl/stable/models/regularisation/)作为了解 Julia 的一种方式。

所以,在Keras 的 json 模型中,我有类似的内容:"W_regularizer": {"l2": 0.0010000000474974513, "name": "WeightRegularizer", "l1": 0.0} 对应每个 Dense 层。我想使用这些系数在Flux 模型中创建正则化。问题是,在Flux 中,它被直接添加到损失中,而不是被定义为层本身的属性。

为避免在此处发布过多代码,我已将其添加到存储库中。这是一个小脚本,它采用 json 并创建 FluxChainhttps://github.com/iegorval/Keras2Flux.jl/blob/master/Keras2Flux/src/Keras2Flux.jl

现在,我想使用预定义的l1/l2 系数为每个Dense 层创建一个惩罚。我试着这样做:

using Pkg
pkg"activate /home/username/.julia/dev/Keras2Flux"

using Flux
using Keras2Flux
using LinearAlgebra

function get_penalty(model::Chain, regs::Array{Any, 1})
    index_model = 1
    index_regs = 1
    penalties = []
    for layer in model
        if layer isa Dense
            println(regs[index_regs](layer.W))   
            penalty(m) = regs[index_regs](m[index_model].W)
            push!(penalties, penalty)
            #println(regs[i])
            index_regs += 1
        end
        index_model += 1
    end
    total_penalty(m) = sum([p(m) for p in penalties])
    println(total_penalty)
    println(total_penalty(model))
    return total_penalty
end

model, regs = convert_keras2flux("examples/keras_1_1_0.json")
penalty = get_penalty(model, regs)

所以,我为每个Dense 层创建了一个惩罚函数,然后将其相加为总惩罚。但是,它给了我这个错误: ERROR: LoadError: BoundsError: attempt to access 3-element Array{Any,1} at index [4]

我明白这意味着什么,但我真的不明白如何解决它。因此,似乎当我调用total_penalty(model) 时,它使用index_regs == 4(因此,index_regsindex_model 的值在for-cycle 之后)。相反,我想使用他们的实际指数,同时将给定的惩罚推到惩罚列表中。

另一方面,如果我不是将其作为函数列表而是作为值列表,那么它也是不正确的,因为我将损失定义为: loss(x, y) = binarycrossentropy(model(x), y) + total_penalty(model)。如果我只是将它用作值列表,那么我将有一个静态的total_penalty,而在模型训练期间每次都应该为每个Dense 层重新计算它。

如果有 Julia 经验的人给我一些建议,我将不胜感激,因为我肯定无法理解它在 Julia 中是如何工作的,特别是在 Flux 中。如何创建在训练期间自动重新计算的total_penalty

【问题讨论】:

    标签: julia flux.jl


    【解决方案1】:

    您的问题有几个部分,由于您是 Flux(和 Julia?)的新手,我将逐步回答。但我建议最后将解决方案作为一种更清洁的方式来处理这个问题。

    首先,存在p(m) 使用index_regsindex_model 作为for 循环后的值来计算惩罚的问题。这是因为 Julia 中的 the scoping rules。当您定义闭包penalty(m) = regs[index_regs](m[index_model].W) 时,index_regs 将绑定到get_penalty 中定义的变量。所以,随着index_regs 的变化,p(m) 的输出也会发生变化。另一个问题是将函数命名为penalty(m)。每次运行此行时,您都在重新定义 penalty 以及您推送到 penalties 上的所有对它的引用。相反,您应该更喜欢创建一个匿名函数。以下是我们整合这些更改的方式:

    function get_penalty(model::Chain, regs::Array{Any, 1})
        index_model = 1
        index_regs = 1
        penalties = []
        for layer in model
            if layer isa Dense
                println(regs[index_regs](layer.W))   
                penalty = let i = index_regs, index_model = index_model
                    m -> regs[i](m[index_model].W)
                end
                push!(penalties, penalty)
                index_regs += 1
            end
            index_model += 1
        end
        total_penalty(m) = sum([p(m) for p in penalties])
        return total_penalty
    end
    

    我在 let 块中使用了 iindex_model 来驱动范围规则。我鼓励您将 let 块中的匿名函数替换为 global penalty(m) = ...(并在 let 块之前删除对 penalty 的赋值),看看使用匿名函数与命名函数的区别。


    但是,如果我们回到您最初的问题,您想使用存储的系数计算模型的正则化惩罚。理想情况下,这些将与 Keras 中的每个 Dense 层一起存储。您可以在 Flux 中重新创建相同的功能:

    using Flux, Functor
    
    struct RegularizedDense{T, LT<:Dense}
        layer::LT
        w_l1::T
        w_l2::T
    end
    
    @functor RegularizedDense
    
    (l::RegularizedDense)(x) = l.layer(x)
    
    penalty(l) = 0
    penalty(l::RegularizedDense) =
      l.w_l1 * norm(l.layer.W, 1) + l.w_l2 * norm(l.layer.W, 2)
    penalty(model::Chain) = sum(penalty(layer) for layer in model)
    

    然后,在您的 Keras2Flux 源中,您可以重新定义 get_regularization 以返回 w_l1_regw_l2_reg 而不是函数。在create_dense 你可以这样做:

    function create_dense(config::Dict{String,Any}, prev_out_dim::Int64=-1)
        # ... code you have already written
        dense = Dense(in, out, activation; initW = init, initb = zeros)
        w_l1, w_l2 = get_regularization(config)
        return RegularizedDense(dense, w_l1, w_l2)
    end
    

    最后,您可以像这样计算损失函数:

    loss(x, y, m) = binarycrossentropy(m(x), y) + penalty(m)
    # ... later for training
    train!((x, y) -> loss(x, y, m), training_data, params)
    

    我们将loss 定义为(x, y, m) 的函数以避免performance issues

    因此,最后,这种方法更简洁,因为在模型构建之后,您不需要传递一组正则化函数并弄清楚如何使用相应的密集层正确索引每个函数。

    如果您希望将正则化器和模型分开(即在您的模型链中有标准的Dense 层),那么您也可以这样做。如果您需要该解决方案,请告诉我,但我暂时将其省略。

    【讨论】:

    • 感谢您提供如此详细的答复。我确实是 Julia 的新手,这是我第一次在里面写一些模块,我从你的回复中学到了很多。
    • 如果我正确理解了 let 块,它不应该是 m -&gt; regs[i](m[index_model].W) 而不是 m -&gt; regs[index_regs](m[index_model].W) 吗?
    • 是的,没错!为了说明一点,我打错了。
    猜你喜欢
    • 2021-03-04
    • 1970-01-01
    • 1970-01-01
    • 2018-07-20
    • 2017-11-28
    • 2012-12-17
    • 2021-04-06
    • 2017-08-26
    • 2013-03-15
    相关资源
    最近更新 更多