1

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

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

为避免在此处发布太多代码,我已将其添加到 repo 中。这是一个采用 json 和 createa 的小脚本FluxChainhttps: //github.com/iegorval/Keras2Flux.jl/blob/master/Keras2Flux/src/Keras2Flux.jl

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

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_modelfor 循环之后)。相反,我想使用他们的实际指数,同时将给定的惩罚推到惩罚列表中。

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

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

4

1 回答 1

3

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

首先,存在使用和作为for循环后的值p(m)来计算惩罚的问题。这是因为 Julia 中的范围规则。当你定义闭包时,会绑定到定义在. 因此,随着变化, 的输出也会变化。另一个问题是将函数命名为. 每次运行此行时,您都在重新定义您推送的所有对它的引用。相反,您应该更喜欢创建一个匿名函数。以下是我们如何合并这些更改:index_regsindex_modelpenalty(m) = regs[index_regs](m[index_model].W)index_regsget_penaltyindex_regsp(m)penalty(m)penaltypenalties

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 块中使用iand来驱动范围规则。index_model我鼓励您将 let 块中的匿名函数替换为global penalty(m) = ...(并删除penaltylet 块之前的赋值),以查看使用匿名函数与命名函数的区别。


但是,如果我们回到您最初的问题,您想使用存储的系数计算模型的正则化惩罚。理想情况下,这些将与DenseKeras 中的每一层一起存储。您可以在 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)以避免性能问题

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

如果您希望将正则化器和模型分开(即Dense模型链中有标准层),那么您也可以这样做。如果你想要那个解决方案,请告诉我,但我暂时不考虑它。

于 2020-06-22T22:46:21.720 回答