Как тренировать комбинацию моделей во Flux? - PullRequest
2 голосов
/ 30 апреля 2020

Я пытаюсь построить модель глубокого обучения в Юлии. У меня есть две модели m1 и m2, которые являются нейронными сетями. Вот мой код:

using Flux

function even_mask(x)
    s1, s2 = size(x)
    weight_mask = zeros(s1, s2)
    weight_mask[2:2:s1,:] = ones(Int(s1/2), s2)
    return weight_mask
end

function odd_mask(x)
    s1, s2 = size(x)
    weight_mask = zeros(s1, s2)
    weight_mask[1:2:s1,:] = ones(Int(s1/2), s2)
    return weight_mask
end

function even_duplicate(x)
    s1, s2 = size(x)
    x_ = zeros(s1, s2)
    x_[1:2:s1,:] = x[1:2:s1,:]
    x_[2:2:s1,:] = x[1:2:s1,:]
    return x_
end

function odd_duplicate(x)
    s1, s2 = size(x)
    x_ = zeros(s1, s2)
    x_[1:2:s1,:] = x[2:2:s1,:]
    x_[2:2:s1,:] = x[2:2:s1,:]
    return x_
end

function Even(m)
    x -> x .+ even_mask(x).*m(even_duplicate(x))
end

function InvEven(m)
    x -> x .- even_mask(x).*m(even_duplicate(x))
end

function Odd(m)
    x -> x .+ odd_mask(x).*m(odd_duplicate(x))
end

function InvOdd(m)
    x -> x .- odd_mask(x).*m(odd_duplicate(x))
end

m1 = Chain(Dense(4,6,relu), Dense(6,5,relu), Dense(5,4))
m2 = Chain(Dense(4,7,relu), Dense(7,4))

forward = Chain(Even(m1), Odd(m2))
inverse = Chain(InvOdd(m2), InvEven(m1))

function loss(x)
    z = forward(x)
    return 0.5*sum(z.*z)
end

opt = Flux.ADAM()

x = rand(4,100)

for i=1:100
    Flux.train!(loss, Flux.params(forward), x, opt)
    println(loss(x))
end

Передняя модель представляет собой комбинацию m1 и m2. Мне нужно оптимизировать m1 и m2, чтобы я мог оптимизировать как прямую, так и обратную модели. Но похоже, что params (forward) пуст. Как я могу тренировать свою модель?

1 Ответ

2 голосов
/ 01 мая 2020

Я не думаю, что простые функции можно использовать в качестве слоев в Flux. Вам нужно использовать макрос @functor, чтобы добавить дополнительную функциональность для сбора параметров: https://fluxml.ai/Flux.jl/stable/models/basics/#Layer -helpers-1

В вашем случае, переписав Even, InvEven, Odd и InvOdd, как это должно помочь:

struct Even
    model
end

(e::Even)(x) = x .+ even_mask(x).*e.model(even_duplicate(x))

Flux.@functor Even

После добавления этого определения

Flux.params(Even(m1))

Должен возвращать непустой список

EDIT

Еще более простой способ реализовать Even и друзей - использовать встроенный слой SkipConnection :

 Even(m) = SkipConnection(Chain(even_duplicate, m),                         
                          (mx, x) -> x .+ even_mask(x) .* mx)

Я подозреваю, что это это разница в версии, но с Julia 1.4.1 и Flux v0.10.4 я получаю ошибку BoundsError: attempt to access () at index [1] при запуске тренировки l oop, мне нужно заменить данные на

x = [(rand(4,100), 0)]

В противном случае потеря применяется к каждой записи в массиве x. поскольку train! разбивает loss более x.

Следующая ошибка mutating arrays is not supported связана с реализацией *_mask и *_duplicate. Эти функции создают массив нулей и затем изменяют его, заменяя значения из ввода.

Вы можете использовать Zygote.Buffer для реализации этого кода способом, который можно дифференцировать.

using Flux
using Zygote: Buffer

function even_mask(x)
    s1, s2 = size(x)
    weight_mask = Buffer(x)
    weight_mask[2:2:s1,:] = ones(Int(s1/2), s2)
    weight_mask[1:2:s1,:] = zeros(Int(s1/2), s2)
    return copy(weight_mask)
end

function odd_mask(x)
    s1, s2 = size(x)
    weight_mask = Buffer(x)
    weight_mask[2:2:s1,:] = zeros(Int(s1/2), s2)
    weight_mask[1:2:s1,:] = ones(Int(s1/2), s2)
    return copy(weight_mask)
end

function even_duplicate(x)
    s1, s2 = size(x)
    x_ = Buffer(x)
    x_[1:2:s1,:] = x[1:2:s1,:]
    x_[2:2:s1,:] = x[1:2:s1,:]
    return copy(x_)
end

function odd_duplicate(x)
    s1, s2 = size(x)
    x_ = Buffer(x)
    x_[1:2:s1,:] = x[2:2:s1,:]
    x_[2:2:s1,:] = x[2:2:s1,:]
    return copy(x_)
end

Even(m) = SkipConnection(Chain(even_duplicate, m),
                         (mx, x) -> x .+ even_mask(x) .* mx)

InvEven(m) = SkipConnection(Chain(even_duplicate, m),
                            (mx, x) -> x .- even_mask(x) .* mx)

Odd(m) = SkipConnection(Chain(odd_duplicate, m),
                        (mx, x) -> x .+ odd_mask(x) .* mx)

InvOdd(m) = SkipConnection(Chain(odd_duplicate, m),
                           (mx, x) -> x .- odd_mask(x) .* mx)

m1 = Chain(Dense(4,6,relu), Dense(6,5,relu), Dense(5,4))
m2 = Chain(Dense(4,7,relu), Dense(7,4))

forward = Chain(Even(m1), Odd(m2))
inverse = Chain(InvOdd(m2), InvEven(m1))

function loss(x, y)
    z = forward(x)
    return 0.5*sum(z.*z)
end

opt = Flux.ADAM(1e-6)

x = [(rand(4,100), 0)]

function train!()
    for i=1:100
        Flux.train!(loss, Flux.params(forward), x, opt)
        println(loss(x[1]...))
    end
end

На этом этапе вы получите настоящее удовольствие от глубоких сетей. После одного шага обучения обучение расходится до NaN со скоростью обучения по умолчанию. Снижение начальной скорости тренировки до 1e-6 помогает, и потеря выглядит так, как будто она уменьшается.

...