Документация Engee

Встроенные типы слоев

Если вы начали изучать это руководство с самого начала, то вам уже встречался базовый слой Dense и вы уже видели, как объединять слои с помощью Chain. Эти базовые слои служат основой почти для всех нейронных сетей.

На примере слоя Dense хорошо виден ряд особенностей.

  • Он содержит функцию активации, которая транслируется через выход. Поскольку данная трансляция может совмещаться с другими операциями, такой подход более эффективен по сравнению с отдельным применением функции активации.

  • Он имеет именованный аргумент init, который принимает функцию, действующую как rand. То есть вызов init(2,3,4) должен создавать массив соответствующего размера. Во Flux есть множество таких встроенных функций. Все они создают массив в памяти ЦП, который затем при желании можно перенести с помощью функции gpu.

  • Вектор смещений всегда инициализируется с помощью Flux.zeros32. Именованный аргумент bias=false отключает это поведение, то есть смещения всегда будут оставаться равными нулю.

  • Он аннотируется с помощью @functor, то есть функции params будет доступно содержимое, а функция gpu будет перемещать массивы в память GPU.

В свою очередь, объект Chain сам по себе не содержит параметров, но связывает другие слои. В разделе о слоях потока данных представлены другие слои наподобие этого.

Полносвязные слои

# Flux.DenseType

Dense(in => out, σ=identity; bias=true, init=glorot_uniform)
Dense(W::AbstractMatrix, [bias, σ])

Создает традиционный полносвязный слой, прямой проход которого задается следующим образом:

y = σ.(W * x .+ bias)

Входной параметр x должен представлять собой вектор длиной in или пакет векторов, представленный в виде матрицы in × N либо массива с size(x,1) == in. Результат y будет вектором длиной out или пакетом с size(y) == (out, size(x)[2:end]...)

Именованный аргумент bias=false отключает обучаемое смещение для слоя. Весовая матрица инициализируется следующим образом: W = init(out, in). При этом вызывается функция, переданная в именованном аргументе init. По умолчанию это функция glorot_uniform. Весовую матрицу и (или) вектор смещений (длиной out) также можно предоставить явным образом.

Примеры

julia> d = Dense(5 => 2)
Dense(5 => 2)       # 12 параметров

julia> d(rand32(5, 64)) |> size
(2, 64)

julia> d(rand32(5, 6, 4, 64)) |> size  # обрабатывается как три пакетных измерения
(2, 6, 4, 64)

julia> d1 = Dense(ones(2, 5), false, tanh)  # используется предоставленная весовая матрица
Dense(5 => 2, tanh; bias=false)  # 10 параметров

julia> d1(ones(5))
2-element Vector{Float64}:
 0.9999092042625951
 0.9999092042625951

julia> Flux.params(d1)  # обучаемое смещение отсутствует
Params([[1.0 1.0 … 1.0 1.0; 1.0 1.0 … 1.0 1.0]])

# Flux.BilinearType

Bilinear((in1, in2) => out, σ=identity; bias=true, init=glorot_uniform)
Bilinear(W::AbstractArray, [bias, σ])

Создает слой с полной связью между двумя входами и выходом, который в остальном аналогичен Dense. Если x и y — векторы, результат представляет собой еще один вектор z, для всех i ∈ 1:out в котором верно следующее:

z[i] = σ(x' * W[i,:,:] * y + bias[i])

Если x и y — матрицы, каждый столбец в результате z = B(x, y) имеет такую форму, причем B — это билинейный слой.

Если второй вход y не задан, он принимается равным x, то есть B(x) == B(x, x).

Два входа можно также предоставить в виде кортежа, Bx, y == B(x, y), который принимается в качестве входных данных Chain.

Если два входа имеют одинаковый размер (in1 == in2), вызов можно записать так: Bilinear(in => out, σ).

Инициализация происходит так же, как для слоя Dense, с W = init(out, in1, in2). Вектор смещений по умолчанию имеет вид zeros(Float32, out); аргумент bias=false отключает обучаемое смещение. Каждый из этих параметров можно задать явным образом.

Примеры

julia> x, y = randn(Float32, 5, 32), randn(Float32, 5, 32);

julia> B = Flux.Bilinear((5, 5) => 7)
Bilinear(5 => 7)    # 182 параметра

julia> B(x) |> size  # взаимодействия на основе одного входа
(7, 32)

julia> B(x,y) == B((x,y))  # два входа, могут быть заданы в виде кортежа
true

julia> sc = SkipConnection(
                Chain(Dense(5 => 20, tanh), Dense(20 => 9, tanh)),
                Flux.Bilinear((9, 5) => 3, bias=false),
            );  # используется в качестве рекомбинатора, второй вход пропущен

julia> sc(x) |> size
(3, 32)

julia> Flux.Bilinear(rand(4,8,16), false, tanh)  # выходом является первое измерение веса
Bilinear((8, 16) => 4, tanh; bias=false)  # 512 параметров

# Flux.ScaleType

Scale(size::Integer..., σ=identity; bias=true, init=ones32)
Scale(scale::AbstractArray, [bias, σ])

Создает поэлементный слой, прямой проход которого задается следующим образом:

y = σ.(scale .* x .+ bias)

При этом используется операция .* вместо умножения матриц * в Dense. Обучаемый масштаб и смещение инициализируются посредством init(size...) и zeros32(size...); по умолчанию init=ones32. Вы можете указать функцию init, отключить обучаемое смещение с помощью bias=false или предоставить массивы явным образом.

Используется LayerNorm с affine=true.

Примеры

julia> a = Flux.Scale(2)
Scale(2)            # 4 параметра

julia> Flux.params(a)
Params([Float32[1.0, 1.0], Float32[0.0, 0.0]])

julia> a([1 2 3])
2×3 Matrix{Float32}:
 1.0  2.0  3.0
 1.0  2.0  3.0

julia> b = Flux.Scale([1 2 3 4], false, abs2)
Scale(1, 4, abs2; bias=false)  # 4 параметра

julia> b([1, 10])
2×4 Matrix{Int64}:
   1    4    9    16
 100  400  900  1600

julia> Flux.params(b)
Params([[1 2 3 4]])

Слой Scale, возможно, и не является полносвязным, но его можно представить как Dense(Diagonal(s.weights), s.bias), а тип Diagonal из LinearAlgebra — это матрица, в которой содержится множество нулей.

Совместимость: Flux ≤ 0.12

В старых версиях Flux допускались только вызовы Dense(in, out, act), но не Dense(in => out, act). Такая нотация создает объект Pair. Если возникает ошибка наподобие MethodError: no method matching Dense(::Pair{Int64,Int64}), это означает, что необходимо выполнить обновление до более новой версии Flux.

Сверточные модели

Эти слои служат для построения сверточных нейронных сетей (CNN).

Все они принимают изображения в так называемом порядке WHCN: пакет из 32 цветных изображений, каждое размером 50 x 50 пикселей, будет иметь размер size(x) == (50, 50, 3, 32). У одного изображения в оттенках серого размер будет size(x) == (28, 28, 1, 1).

Помимо изображений, то есть двумерных данных, они также работают с одномерными данными. Например, стереофоническая запись с 1000 точек дискретизации может иметь размер size(x) == (1000, 2, 1). Эти слои также работают с трехмерными данными, ndims(x) == 5, где последние два измерения опять же соответствуют каналу и пакету.

Работа с шагами и заполнением хорошо проиллюстрирована в статье Дюмулена (Dumoulin) и Визина (Visin).

# Flux.ConvType

Conv(filter, in => out, σ = identity;
     stride = 1, pad = 0, dilation = 1, groups = 1, [bias, init])

Стандартный сверточный слой. filter — это кортеж целых чисел, определяющий размер сверточного ядра, а in и out задают количество входных и выходных каналов.

Данные изображения должны храниться в порядке WHCN (ширина, высота, каналы, пакет). То есть изображение размером 100×100 в модели RGB будет представлять собой массив 100×100×3×1, а пакет из 50 таких изображений — массив 100×100×3×50. При этом количество пространственных измерений N = 2, поэтому ядро должно иметь размер, например, (5,5), то есть кортеж из двух целых чисел.

Для сверток по N измерениям признаков этот слой требует на входе массива с ndims(x) == N+2, где size(x, N+1) == in — это количество входных каналов, а size(x, ndims(x)) — это (как и всегда) количество наблюдений в пакете. В таком случае:

  • filter должно быть кортежем из N целых чисел.

  • Значениями именованных аргументов stride и dilation должны быть одновременно либо целые числа, либо кортежи из N целых чисел.

  • Именованный аргумент pad определяет количество элементов, добавляемых к границам массива данных. Его значением может быть:

    • одно целое число для равномерного заполнения со всех сторон;

    • кортеж из N целых чисел для одинакового заполнения в начале и конце каждого пространственного измерения;

    • кортеж из 2*N целых чисел для асимметричного заполнения;

    • одинарное значение SamePad() для расчета заполнения таким образом, чтобы size(output,d) == size(x,d) / stride (possibly rounded) for each spatial dimension.

  • Именованный аргумент groups должен иметь значение типа Int. Он задает количество групп, на которые делится свертка.

Именованные аргументы для управления инициализацией слоя:

  • init - функция для генерирования начальных весов. Значение по умолчанию — glorot_uniform.

  • bias - начальный вектор смещений по умолчанию содержит одни нули. Можно полностью отключить обучаемое смещение, присвоив этому аргументу значение false, либо передать другой вектор, например bias = randn(Float32, out).

См. также описание ConvTranspose, DepthwiseConv и CrossCor.

Примеры

julia> xs = rand32(100, 100, 3, 50); # пакет из 50 изображений в модели RGB

julia> layer = Conv((5,5), 3 => 7, relu; bias = false)
Conv((5, 5), 3 => 7, relu, bias=false)  # 525 параметров

julia> layer(xs) |> size
(96, 96, 7, 50)

julia> Conv((5,5), 3 => 7; stride = 2)(xs) |> size
(48, 48, 7, 50)

julia> Conv((5,5), 3 => 7; stride = 2, pad = SamePad())(xs) |> size
(50, 50, 7, 50)

julia> Conv((1,1), 3 => 7; pad = (20,10,0,0))(xs) |> size
(130, 100, 7, 50)

julia> Conv((5,5), 3 => 7; stride = 2, dilation = 4)(xs) |> size
(42, 42, 7, 50)

# Flux.ConvMethod

Conv(weight::AbstractArray, [bias, activation; stride, pad, dilation])

Создает сверточный слой с указанными весом и смещением. Принимает те же именованные аргументы и имеет те же значения по умолчанию, что и Conv(k::NTuple{N,Integer}, ch::Pair{<:Integer,<:Integer}, σ; ...).

julia> weight = rand(3, 4, 5);

julia> bias = zeros(5);

julia> layer = Conv(weight, bias, sigmoid)  # ожидает 1 пространственное измерение
Conv((3,), 4 => 5, σ)  # 65 параметров

julia> layer(randn(100, 4, 64)) |> size
(98, 5, 64)

julia> Flux.params(layer) |> length
2

# Flux.ConvTransposeType

ConvTranspose(filter, in => out, σ=identity; stride=1, pad=0, dilation=1, [bias, init])

Стандартный транспонированный сверточный слой. filter — это кортеж целых чисел, определяющий размер сверточного ядра, а in и out задают количество входных и выходных каналов.

Обратите внимание: чтобы попытаться обеспечить соблюдение условия size(output,d) == size(x,d) * stride в данном случае, можно задать pad=SamePad().

Параметры контролируются дополнительными именованными аргументами со значениями по умолчанию init=glorot_uniform и bias=true.

Более подробное описание именованных аргументов см. в описании Conv.

Примеры

julia> xs = rand32(100, 100, 3, 50);  # пакет из 50 изображений в модели RGB

julia> layer = ConvTranspose((5,5), 3 => 7, relu)
ConvTranspose((5, 5), 3 => 7, relu)  # 532 parameters

julia> layer(xs) |> size
(104, 104, 7, 50)

julia> ConvTranspose((5,5), 3 => 7, stride=2)(xs) |> size
(203, 203, 7, 50)

julia> ConvTranspose((5,5), 3 => 7, stride=3, pad=SamePad())(xs) |> size
(300, 300, 7, 50)

# Flux.ConvTransposeMethod

ConvTranspose(weight::AbstractArray, [bias, activation; stride, pad, dilation, groups])

Создает слой ConvTranspose с указанными весом и смещением. Принимает те же именованные аргументы и имеет те же значения по умолчанию, что и ConvTranspose(k::NTuple{N,Integer}, ch::Pair{<:Integer,<:Integer}, σ; ...).

Примеры

julia> weight = rand(3, 4, 5);

julia> bias = zeros(4);

julia> layer = ConvTranspose(weight, bias, sigmoid)
ConvTranspose((3,), 5 => 4, σ)  # 64 parameters

julia> layer(randn(100, 5, 64)) |> size  # транспонированная свертка увеличивает размер измерения (повышающая дискретизация)
(102, 4, 64)

julia> Flux.params(layer) |> length
2

# Flux.CrossCorType

CrossCor(filter, in => out, σ=identity; stride=1, pad=0, dilation=1, [bias, init])

Стандартный взаимнокорреляционный слой. filter — это кортеж целых чисел, определяющий размер сверточного ядра, а in и out задают количество входных и выходных каналов.

Параметры контролируются дополнительными именованными аргументами со значениями по умолчанию init=glorot_uniform и bias=true.

Более подробное описание именованных аргументов см. в описании Conv.

Примеры

julia> xs = rand(Float32, 100, 100, 3, 50);  # пакет из 50 изображений в модели RGB

julia> layer = CrossCor((5,5), 3 => 6, relu; bias=false)
CrossCor((5, 5), 3 => 6, relu, bias=false)  # 450 параметров

julia> layer(xs) |> size
(96, 96, 6, 50)

julia> CrossCor((5,5), 3 => 7, stride=3, pad=(2,0))(xs) |> size
(34, 32, 7, 50)

# Flux.CrossCorMethod

CrossCor(weight::AbstractArray, [bias, activation; stride, pad, dilation])

Создает слой CrossCor с указанными весом и смещением. Принимает те же именованные аргументы и имеет те же значения по умолчанию, что и CrossCor(k::NTuple{N,Integer}, ch::Pair{<:Integer,<:Integer}, σ; ...).

Примеры

julia> weight = rand(3, 4, 5);

julia> bias = zeros(5);

julia> layer = CrossCor(weight, bias, relu)
CrossCor((3,), 4 => 5, relu)  # 65 параметров

julia> layer(randn(100, 4, 64)) |> size
(98, 5, 64)

# Flux.DepthwiseConvFunction

DepthwiseConv(filter, in => out, σ=identity; stride=1, pad=0, dilation=1, [bias, init])
DepthwiseConv(weight::AbstractArray, [bias, activation; stride, pad, dilation])

Возвращает поканальный сверточный слой, то есть слой Conv с количеством групп, равным количеству входных каналов.

Описание аргументов см. в описании Conv.

Примеры

julia> xs = rand(Float32, 100, 100, 3, 50);  # пакет из 50 изображений в модели RGB

julia> layer = DepthwiseConv((5,5), 3 => 6, relu; bias=false)
Conv((5, 5), 3 => 6, relu, groups=3, bias=false)  # 150 параметров

julia> layer(xs) |> size
(96, 96, 6, 50)

julia> DepthwiseConv((5, 5), 3 => 9, stride=2, pad=2)(xs) |> size
(50, 50, 9, 50)

# Flux.SamePadType

SamePad()

При передаче в качестве параметра в сверточные (и подобные им) слои приводит к выбору такого заполнения, что размеры входа и выхода будут согласованы (для первых N измерений, ядра или окна) при stride==1. При stride≠1 размер выхода равен ceil(input_size/stride).

См. также описание Conv и MaxPool.

Примеры

julia> xs = rand32(100, 100, 3, 50);  # пакет изображений

julia> layer = Conv((2,2), 3 => 7, pad=SamePad())
Conv((2, 2), 3 => 7, pad=(1, 0, 1, 0))  # 91 параметр

julia> layer(xs) |> size  # обратите внимание, что при таком заполнении измерения остаются такими же
(100, 100, 7, 50)

julia> layer2 = Conv((2,2), 3 => 7)
Conv((2, 2), 3 => 7)  # 91 параметр

julia> layer2(xs) |> size  # выходное измерение изменяется, так как заполнение не было аналогичным
(99, 99, 7, 50)

julia> layer3 = Conv((5, 5), 3 => 7, stride=2, pad=SamePad())
Conv((5, 5), 3 => 7, pad=2, stride=2)  # 532 parameters

julia> layer3(xs) |> size  # размер выхода = `ceil(input_size/stride)` = 50
(50, 50, 7, 50)

# Flux.flattenFunction

flatten(x)

Действует аналогично функции MLUtils.flatten, которая является предпочтительной, так как данный метод существует только в целях обратной совместимости.

MultiHeadAttention

Базовые блоки, необходимые для реализации архитектур преобразователей. См. также описание функциональных аналогов в разделе, посвященном примитиву Attention из NNlib.

Подвыборка

Данные слои часто используются после сверточного слоя и сокращают размер его выходных данных. У них нет обучаемых параметров.

# Flux.AdaptiveMaxPoolType

AdaptiveMaxPool(out::NTuple)

Адаптивный пулинговый слой со взятием максимума. Необходимый размер окна вычисляется так, что для выхода size(y)[1:N] == out.

На входе ожидается массив с ndims(x) == N+2, то есть с измерениями каналов и пакетов после N измерений признаков, где N = length(out).

См. также описание MaxPool и AdaptiveMeanPool.

Примеры

julia> xs = rand(Float32, 100, 100, 3, 50);  # пакет из 50 изображений в модели RGB

julia> AdaptiveMaxPool((25, 25))(xs) |> size
(25, 25, 3, 50)

julia> MaxPool((4,4))(xs) ≈ AdaptiveMaxPool((25, 25))(xs)
true

# Flux.MaxPoolType

MaxPool(window::NTuple; pad=0, stride=window)

Пулинговый слой со взятием максимума, в котором все пиксели в блоке размером window заменяются единицей.

На входе ожидается массив с ndims(x) == N+2, то есть с измерениями каналов и пакетов после N измерений признаков, где N = length(window).

По умолчанию размер окна — это также шаг в каждом измерении. Именованный аргумент pad принимает те же значения, что и для слоя Conv, включая SamePad().

См. также описание Conv, MeanPool, AdaptiveMaxPool и GlobalMaxPool.

Примеры

julia> xs = rand(Float32, 100, 100, 3, 50);  # пакет из 50 изображений в модели RGB

julia> m = Chain(Conv((5, 5), 3 => 7, pad=SamePad()), MaxPool((5, 5), pad=SamePad()))
Chain(
  Conv((5, 5), 3 => 7, pad=2),          # 532 parameters
  MaxPool((5, 5), pad=2),
)

julia> m[1](xs) |> size
(100, 100, 7, 50)

julia> m(xs) |> size
(20, 20, 7, 50)

julia> layer = MaxPool((5,), pad=2, stride=(3,))  # одномерное окно
MaxPool((5,), pad=2, stride=3)

julia> layer(rand(Float32, 100, 7, 50)) |> size
(34, 7, 50)

# Flux.GlobalMaxPoolType

GlobalMaxPool()

Глобальный пулинговый слой со взятием максимума.

Преобразует входные данные в форме (w,h,c,b) в форму (1,1,c,b), выполняя подвыборку со взятием максимума применительно к полным картам признаков в форме (w,h).

См. также описание MaxPool и GlobalMeanPool.

julia> xs = rand(Float32, 100, 100, 3, 50);

julia> m = Chain(Conv((3,3), 3 => 7), GlobalMaxPool());

julia> m(xs) |> size
(1, 1, 7, 50)

julia> GlobalMaxPool()(rand(3,5,7)) |> size  # сохраняет 2 измерения
(1, 5, 7)

# Flux.AdaptiveMeanPoolType

AdaptiveMeanPool(out::NTuple)

Адаптивный пулинговый слой со взятием среднего значения. Необходимый размер окна вычисляется так, что для выхода size(y)[1:N] == out.

На входе ожидается массив с ndims(x) == N+2, то есть с измерениями каналов и пакетов после N измерений признаков, где N = length(out).

См. также описание MaxPool и AdaptiveMaxPool.

Примеры

julia> xs = rand(Float32, 100, 100, 3, 50);  # пакет из 50 изображений в модели RGB

julia> AdaptiveMeanPool((25, 25))(xs) |> size
(25, 25, 3, 50)

julia> MeanPool((4,4))(xs) ≈ AdaptiveMeanPool((25, 25))(xs)
true

# Flux.MeanPoolType

MeanPool(window::NTuple; pad=0, stride=window)

Пулинговый слой со взятием среднего значения, в котором все пиксели в блоке размера window усредняются.

На входе ожидается массив с ndims(x) == N+2, то есть с измерениями каналов и пакетов после N измерений признаков, где N = length(window).

По умолчанию размер окна — это также шаг в каждом измерении. Именованный аргумент pad принимает те же значения, что и для слоя Conv, включая SamePad().

См. также описание функций Conv, MaxPool и AdaptiveMeanPool.

Примеры

julia> xs = rand(Float32, 100, 100, 3, 50);

julia> m = Chain(Conv((5,5), 3 => 7), MeanPool((5,5), pad=SamePad()))
Chain(
  Conv((5, 5), 3 => 7),                 # 532 parameters
  MeanPool((5, 5), pad=2),
)

julia> m[1](xs) |> size
(96, 96, 7, 50)

julia> m(xs) |> size
(20, 20, 7, 50)

# Flux.GlobalMeanPoolType

GlobalMeanPool()

Глобальный пулинговый слой со взятием среднего значения.

Преобразует входные данные в форме (w,h,c,b) в форму (1,1,c,b), выполняя подвыборку со взятием среднего значения применительно к полным картам признаков в форме (w,h).

julia> xs = rand(Float32, 100, 100, 3, 50);

julia> m = Chain(Conv((3,3), 3 => 7), GlobalMeanPool());

julia> m(xs) |> size
(1, 1, 7, 50)

Повышающая дискретизация

Такие слои, действующие противоположно слоям подвыборки, увеличивают размер массива. У них нет обучаемых параметров.

# Flux.UpsampleType

Upsample(mode = :nearest; [scale, size])
Upsample(scale, mode = :nearest)

Слой повышающей дискретизации. Необходимо задать один из двух именованных аргументов:

Если scale — число, оно применяется ко всем измерениям входных данных, кроме двух последних (измерений каналов и пакетов). Значением также может быть кортеж для управления измерениями по отдельности. Именованный аргумент size также может принимать кортеж для указания начальных измерений результата напрямую.

В настоящее время поддерживаются следующие режимы (mode) повышающей дискретизации и соответствующие методы NNlib:

Примеры

julia> m = Upsample(scale = (2, 3))
Upsample(:nearest, scale = (2, 3))

julia> m(ones(2, 2, 1, 1)) |> size
(4, 6, 1, 1)

julia> m = Upsample(:bilinear, size = (4, 5))
Upsample(:bilinear, size = (4, 5))

julia> m(ones(2, 2, 1, 1)) |> size
(4, 5, 1, 1)

# Flux.PixelShuffleType

PixelShuffle(r::Int)

Слой перемешивания пикселей с коэффициентом увеличения масштаба r. Обычно применяется для генерирования изображений в более высоком разрешении при увеличении их масштаба.

См. описание NNlib.pixel_shuffle.

Примеры

julia> p = PixelShuffle(2);

julia> xs = [2row + col + channel/10 for row in 1:2, col in 1:2, channel in 1:4, n in 1:1]
2×2×4×1 Array{Float64, 4}:
[:, :, 1, 1] =
 3.1  4.1
 5.1  6.1

[:, :, 2, 1] =
 3.2  4.2
 5.2  6.2

[:, :, 3, 1] =
 3.3  4.3
 5.3  6.3

[:, :, 4, 1] =
 3.4  4.4
 5.4  6.4

julia> p(xs)
4×4×1×1 Array{Float64, 4}:
[:, :, 1, 1] =
 3.1  3.3  4.1  4.3
 3.2  3.4  4.2  4.4
 5.1  5.3  6.1  6.3
 5.2  5.4  6.2  6.4

julia> xs = [3row + col + channel/10 for row in 1:2, col in 1:3, channel in 1:4, n in 1:1]
2×3×4×1 Array{Float64, 4}:
[:, :, 1, 1] =
 4.1  5.1  6.1
 7.1  8.1  9.1

[:, :, 2, 1] =
 4.2  5.2  6.2
 7.2  8.2  9.2

[:, :, 3, 1] =
 4.3  5.3  6.3
 7.3  8.3  9.3

[:, :, 4, 1] =
 4.4  5.4  6.4
 7.4  8.4  9.4

julia> p(xs)
4×6×1×1 Array{Float64, 4}:
[:, :, 1, 1] =
 4.1  4.3  5.1  5.3  6.1  6.3
 4.2  4.4  5.2  5.4  6.2  6.4
 7.1  7.3  8.1  8.3  9.1  9.3
 7.2  7.4  8.2  8.4  9.2  9.4

Векторные представления

Эти слои принимают индекс (или несколько индексов) и возвращают вектор (или несколько векторов). Одними из возможных векторных представлений являются обучаемые параметры.

# Flux.EmbeddingType

Embedding(in => out; init=randn32)

Таблица поиска, в которой хранятся векторные представления измерения out для словаря размером in в виде обучаемой матрицы.

Этот слой часто применяется для хранения векторных представлений слов и их извлечения по индексам. Входными данными для этого слоя могут быть индекс словаря в 1:in, массив индексов или соответствующая кодировка onehot encoding.

Для индексов x результат имеет размер (out, size(x)...), благодаря чему допустимо несколько измерений пакетов. Для прямой унитарной кодировки ohx результат имеет размер (out, size(ohx)[2:end]...).

Примеры

julia> emb = Embedding(26 => 4, init=Flux.identity_init(gain=22))
Embedding(26 => 4)  # 104 параметра

julia> emb(2)  # один столбец примерных весов (в данном случае не случайных)
4-element Vector{Float32}:
  0.0
 22.0
  0.0
  0.0

julia> emb([3, 1, 20, 14, 4, 15, 7])  # индексы словаря в диапазоне 1:26
4×7 Matrix{Float32}:
  0.0  22.0  0.0  0.0   0.0  0.0  0.0
  0.0   0.0  0.0  0.0   0.0  0.0  0.0
 22.0   0.0  0.0  0.0   0.0  0.0  0.0
  0.0   0.0  0.0  0.0  22.0  0.0  0.0

julia> ans == emb(Flux.onehotbatch("cat&dog", 'a':'z', 'n'))
true

julia> emb(rand(1:26, (10, 1, 12))) |> size  # три измерения пакетов
(4, 10, 1, 12)

# Flux.EmbeddingBagType

EmbeddingBag(in => out, reduction=mean; init=Flux.randn32)

Таблица поиска, в которой хранятся векторные представления измерения out для словаря размером in. Отличается от Embedding тем, что вместо применения к одному индексу словаря всегда применяется к вектору индексов, который называется мультимножеством. Отдельные векторные представления сводятся к единице с помощью mean или какой-либо иной функции.

Вместо применения к одному мультимножеству, например x::Vector{Int}, этот слой также может работать с несколькими:

  • При применении к вектору мультимножеств создается матрица, столбцы которой представляют собой приведенные векторы. В более общем случае при применении к x::Array{Vector{Int}} результат имеет размер (out, size(x)...).

  • Любой массив целых чисел более высокого ранга рассматривается как коллекция мультимножеств, каждое из которых располагается по первому измерению. Таким образом, при e::EmbeddingBag и x::Array{Int,N} результатом будет mapslices(e, x; dims=1). Этот метод эффективнее, но требует, чтобы все мультимножества были одинаковой длины.

  • Вектор мультимножеств можно также получить путем разделения вектора индексов в заданных точках. В таком случае слой принимает два входных объекта, каждый из которых представляет собой вектор целых чисел. Подробные сведения см. ниже.

Мультимножество можно эквивалентно представить как OneHotMatrix. Их коллекция или один массив OneHotArray более высокого ранга также создают стек векторных представлений. Подробные сведения см. ниже.

Примеры

julia> vocab_size = 26;  # внедрение в 3 измерения с неслучайными векторами:

julia> eb = EmbeddingBag(vocab_size => 3, init=Flux.identity_init(gain=100))
EmbeddingBag(26 => 3)  # 78 параметров

julia> eb([2])  # одно мультимножество из 1 элемента
3-element Vector{Float32}:
   0.0
 100.0
   0.0

julia> eb([3,3,1])  # одно мультимножество из 3 элементов, одно усредненное векторное представление
3-element Vector{Float32}:
 33.333332
  0.0
 66.666664

julia> eb([[3,1,3], [2,1]])  # два мультимножества
3×2 Matrix{Float32}:
 33.3333  50.0
  0.0     50.0
 66.6667   0.0

julia> eb([1 1 1 1; 1 2 3 4])  # 4 мультимножества по 2 элемента, eachcol([1 1 1 1; 1 2 3 4])
3×4 Matrix{Float32}:
 100.0  50.0  50.0  50.0
   0.0  50.0   0.0   0.0
   0.0   0.0  50.0   0.0

julia> eb(rand(1:26, 10, 5, 5)) |> size  # 25 мультимножество по 10 элементов
(3, 5, 5)

Другим способом задания «множества мультимножеств из множества элементов» является передача вектора data (в диапазоне 1:in) и вектора at, указывающего, в каких точках его следует разделить на мультимножества. Первое мультимножество начинается с data[at[1]], второе — с data[at[2]] и т. д., без перекрытий и оставшихся элементов (поэтому требуется at[1]==1).

julia> data = [11, 1, 12, 2, 13, 3, 14];

julia> Flux._splitat(data, [1, 4]) |> println  # внутренняя функция, выполняет data[1:3], data[4:end]
[[11, 1, 12], [2, 13, 3, 14]]

julia> eb(data, [1, 4])  # два мультимножества из 3 и 4 элементов
3×2 Matrix{Float32}:
 33.3333   0.0
  0.0     25.0
  0.0     25.0

Наконец, каждое мультимножество можно также представить как OneHotMatrix.

julia> eb(Flux.onehotbatch("bba", 'a':'z'))  # то же, что и [2,2,1], одно мультимножество из 3 элементов
3-element Vector{Float32}:
 33.333332
 66.666664
  0.0

julia> eb([Flux.onehotbatch("bba", 'a':'z'), Flux.onehotbatch("cc", 'a':'z')])  # два мультимножества
3×2 Matrix{Float32}:
 33.3333    0.0
 66.6667    0.0
  0.0     100.0

Слои потока данных или контейнеры

Базовый объект Chain(F, G, H) применяет содержащиеся в нем слои по порядку, что равносильно H ∘ G ∘ F. Во Flux есть и другие слои, которые также содержат слои, но соединенные более сложным образом: SkipConnection обеспечивает остаточную связь ResNet.

# Flux.ChainType

Chain(layers...)
Chain(name = layer, ...)

Объединяет несколько слоев или функций для поочередного вызова применительно к указанным входным данным. Поддерживает индексирование и создание срезов (m[2], m[1:end-1]), а если заданы имена, то m[:name] == m[1] и т. д.

Примеры

julia> m = Chain(x -> x^2, x -> x+1);

julia> m(5) == 26
true

julia> m = Chain(Dense(10 => 5, tanh), Dense(5 => 2));

julia> x = rand32(10, 32);

julia> m(x) == m[2](m[1](x))
true

julia> m2 = Chain(enc = Chain(Flux.flatten, Dense(10 => 5, tanh)),
                  dec = Dense(5 => 2));

julia> m2(x) == (m2[:dec] ∘ m2[:enc])(x)
true

Для больших моделей есть специальный нестабильный по типу путь, позволяющий ускорить компиляцию. Для его использования можно предоставить вектор слоев Chain([layer1, layer2, ...]). Однако будьте осторожны: эта функция пока имеет экспериментальный характер.

# Flux.activationsFunction

activations(c::Chain, input)

Действует аналогично вызову Chain, но сохраняет результат каждого слоя в выходных данных.

Примеры

julia> using Flux: activations

julia> c = Chain(x -> x + 1, x -> x * 2, x -> x ^ 3);

julia> activations(c, 1)
(2, 4, 64)

# Flux.MaxoutType

Maxout(layers...)
Maxout(f, n_alts)

Содержит несколько внутренних слоев, каждый из которых принимает одни и те же входные данные. Результатом является поэлементный максимум выходов внутренних слоев.

Можно не определять слои по отдельности, а предоставить создающую их функцию без аргументов и количество создаваемых слоев.

Результат применения функции Maxout к линейным плотным слоям соответствует теореме об универсальной аппроксимации. См. работу Гудфеллоу (Goodfellow), Варда-Фарли (Warde-Farley), Мирзы (Mirza), Курвиля (Courville) и Бенжио (Bengio) — Maxout Networks https://arxiv.org/abs/1302.4389.

См. также сведения о приведении с помощью других операторов в описании Parallel.

Примеры

julia> m = Maxout(x -> abs2.(x), x -> x .* 3);

julia> m([-2 -1 0 1 2])
1×5 Matrix{Int64}:
 4  1  0  3  6

julia> m3 = Maxout(() -> Dense(5 => 7, tanh), 3)
Maxout(
  Dense(5 => 7, tanh),                  # 42 параметра
  Dense(5 => 7, tanh),                  # 42 параметра
  Dense(5 => 7, tanh),                  # 42 параметра
)                   # Всего: 6 массивов, 126 параметров, 888 байтов.

julia> Flux.outputsize(m3, (5, 11))
(7, 11)

# Flux.SkipConnectionType

SkipConnection(layer, connection)

Создает связь с пропуском, которая состоит из слоя или объекта Chain с последовательностью слоев и связи напрямую между входом и выходом блока, посредством предоставленного пользователем вызова с двумя аргументами. Первый аргумент вызова распространяется через слой layer, а второй представляет неизменяемый, «пропускаемый» вход.

Простейший тип связи ResNet — SkipConnection(layer, +). Вот более сложный пример:

julia> m = Conv((3,3), 4 => 7, pad=(1,1));

julia> x = ones(Float32, 5, 5, 4, 10);

julia> size(m(x)) == (5, 5, 7, 10)
true

julia> sm = SkipConnection(m, (mx, x) -> cat(mx, x, dims=3));

julia> size(sm(x)) == (5, 5, 11, 10)
true

См. также описание Parallel и Maxout.

# Flux.ParallelType

Parallel(connection, layers...)
Parallel(connection; name = layer, ...)

Создает слой, который передает входной массив по каждому пути в layers перед приведением выхода с помощью connection.

Вызов с одним входом x эквивалентен connection([l(x) for l in layers]...). При вызове с несколькими входами в каждый слой передается один из них, то есть Parallel(+, f, g)(x, y) = f(x) + g(y).

Как и в случае с Chain, подслоям можно присвоить имена с помощью конструктора с именованными аргументами. Обращаться к ним можно по индексам: m[1] == m[:name] — это первый слой.

См. также описание типа SkipConnection, который аналогичен identity, но имеет один аргумент Parallel, и типа Maxout, который выполняет приведение путем трансляции max.

Примеры

julia> model = Chain(Dense(3 => 5),
                     Parallel(vcat, Dense(5 => 4), Chain(Dense(5 => 7), Dense(7 => 4))),
                     Dense(8 => 17));

julia> model(rand32(3)) |> size
(17,)

julia> model2 = Parallel(+; α = Dense(10, 2, tanh), β = Dense(5, 2))
Parallel(
  +,
  α = Dense(10 => 2, tanh),             # 22 параметра
  β = Dense(5 => 2),                    # 12 параметров
)                   # Всего: 4 массива, 34 параметра, 392 байта.

julia> model2(rand32(10), rand32(5)) |> size
(2,)

julia> model2[:α](rand32(10)) |> size
(2,)

julia> model2[:β] == model2[2]
true

# Flux.PairwiseFusionType

PairwiseFusion(connection, layers...)

Аргументы

  • connection: функция, принимающая два входа и объединяющая их в один выход

  • layers: слои, выходы которых следует объединить

Входные данные

Поведение этого слоя зависит от типа входных данных.

  1. Если вход x — это кортеж длиной N, соответствующей количеству слоев layers (либо если на входе передан объект xs из N x),

каждый слой принимает новый вход x[i], объединенный с предыдущим выходом y[i-1] с помощью connection. Таким образом, выражение (y1, y2, y3) = PairwiseFusion(connection, layer1, layer2, layer3)x1, x2, x3 можно представить такой схемой:

x1 → layer1 → y1 ↘
                  connection → layer2 → y2 ↘
              x2 ↗                          connection → layer3 → y3
                                        x3 ↗
  1. или записать в следующем виде:

y1 = layer1(x1)
y2 = layer2(connection(y1, x2))
y3 = layer3(connection(y2, x3))
  1. Если вход только один, каждый слой принимает один и тот же вход x, объединенный с предыдущим выходом. Таким образом, выражение y = PairwiseFusion(connection, layers...)(x) соответствует следующему коду:

y[1] == layers[1](x)
for i in 2:length(layers)
    y[i] == connection(layers[i](y[i-1]), x)
end

Возвращаемые значения

Кортеж длиной N с результатом каждого слияния (в примере выше это будет (y1, y2, …​, yN)).

Рекуррентные модели

Во многом похожи на описанные выше базовые слои, но могут применяться для обработки данных последовательностей (а также других структурированных данных).

# Flux.RNNFunction

RNN(in => out, σ = tanh)

Самый базовый рекуррентный слой; по сути действует как слой Dense, но на каждом временном шаге выход подается обратно на вход.

Аргументы in и out описывают размер векторов признаков, передаваемых в качестве входа и выхода. Таким образом, функция принимает вектор длиной in или пакет векторов, представленный в виде матрицы in x B, и выдает вектор длиной out или пакет векторов размером out x B.

Данный конструктор представляет собой синтаксический сахар для Recur(RNNCell(a...)), то есть вызовы RNN производятся с сохранением состояния. Обратите внимание, что форма состояния может изменяться в зависимости от входов, поэтому при изменении размера пакета между вызовами вывода желательно сбрасывать модель с помощью reset!. См. примеры ниже.

Примеры

julia> r = RNN(3 => 5)
Recur(
  RNNCell(3 => 5, tanh),                # 50 параметров
)         # Всего: 4 обучаемых массива, 50 параметров,
          # плюс 1 необучаемый, 5 параметров, суммарный размер 432 байта.

julia> r(rand(Float32, 3)) |> size
(5,)

julia> Flux.reset!(r);

julia> r(rand(Float32, 3, 10)) |> size # размер пакета 10
(5, 10)

Если не вызвать reset! в случае изменения размера входного пакета, результат может быть непредсказуемым. См. следующий пример:

julia> r = RNN(3 => 5)
Recur(
  RNNCell(3 => 5, tanh),                # 50 параметров
)         # Всего: 4 обучаемых массива, 50 параметров,
          # плюс 1 необучаемый, 5 параметров, суммарный размер 432 байта.

julia> r.state |> size
(5, 1)

julia> r(rand(Float32, 3)) |> size
(5,)

julia> r.state |> size
(5, 1)

julia> r(rand(Float32, 3, 10)) |> size # размер пакета 10
(5, 10)

julia> r.state |> size # форма состояния изменилась
(5, 10)

julia> r(rand(Float32, 3)) |> size # ошибочно выдает вектор длиной 5*10 = 50.
(50,)

Примечание.

RNNCell можно создать напрямую, задав нелинейную функцию, внутренние матрицы Wi и Wh, вектор смещений b и обучаемое исходное состояние state0. Матрицы Wi и Wh могут быть разных типов, но если Wh имеет форму dxd, матрица Wi должна иметь форму dxN.

julia> using LinearAlgebra

julia> r = Flux.Recur(Flux.RNNCell(tanh, rand(5, 4), Tridiagonal(rand(5, 5)), rand(5), rand(5, 1)))

julia> r(rand(4, 10)) |> size # размер пакета 10
(5, 10)

# Flux.LSTMFunction

LSTM(in => out)

Рекуррентный слой сети с долговременной и кратковременной памятью . Похож на RNN, но, как правило, требует большего объема памяти по сравнению с последовательностями.

Аргументы in и out описывают размер векторов признаков, передаваемых в качестве входа и выхода. Таким образом, функция принимает вектор длиной in или пакет векторов, представленный в виде матрицы in x B, и выдает вектор длиной out или пакет векторов размером out x B.

Данный конструктор представляет собой синтаксический сахар для Recur(LSTMCell(a...)), то есть вызовы LSTM производятся с сохранением состояния. Обратите внимание, что форма состояния может изменяться в зависимости от входов, поэтому при изменении размера пакета между вызовами вывода желательно сбрасывать модель с помощью reset!. См. примеры ниже.

В этой статье дается хороший обзор внутренних принципов работы.

Примеры

julia> l = LSTM(3 => 5)
Recur(
  LSTMCell(3 => 5),                     # 190 параметров
)         # Всего: 5 обучаемых массивов, 190 параметров,
          # плюс 2 необучаемых, 10 параметров, суммарный размер 1,062 КиБ.

julia> l(rand(Float32, 3)) |> size
(5,)

julia> Flux.reset!(l);

julia> l(rand(Float32, 3, 10)) |> size # размер пакета 10
(5, 10)

Если не вызвать reset! в случае изменения размера входного пакета, результат может быть непредсказуемым. См. пример в описании RNN.

Примечание.

LSTMCell можно создать напрямую, задав нелинейную функцию, внутренние матрицы Wi и Wh, вектор смещений b и обучаемое исходное состояние state0. Матрицы Wi и Wh могут быть разных типов. См. пример в описании RNN.

# Flux.GRUFunction

GRU(in => out)

Слой управляемого рекуррентного блока. Похож на RNN, но, как правило, требует большего объема памяти по сравнению с последовательностями. Реализует вариант, предложенный в версии 1 работы по ссылке.

Целочисленные аргументы in и out описывают размер векторов признаков, передаваемых в качестве входа и выхода. Таким образом, функция принимает вектор длиной in или пакет векторов, представленный в виде матрицы in x B, и выдает вектор длиной out или пакет векторов размером out x B.

Данный конструктор представляет собой синтаксический сахар для Recur(GRUCell(a...)), то есть вызовы GRU производятся с сохранением состояния. Обратите внимание, что форма состояния может изменяться в зависимости от входов, поэтому при изменении размера пакета между вызовами вывода желательно сбрасывать модель с помощью reset!. См. примеры ниже.

В этой статье дается хороший обзор внутренних принципов работы.

Примеры

julia> g = GRU(3 => 5)
Recur(
  GRUCell(3 => 5),                      # 140 параметров
)         # Всего: 4 обучаемых массива, 140 параметров,
          # плюс 1 необучаемый, 5 параметров, суммарный размер 792 байта.

julia> g(rand(Float32, 3)) |> size
(5,)

julia> Flux.reset!(g);

julia> g(rand(Float32, 3, 10)) |> size # размер пакета 10
(5, 10)

Если не вызвать reset! в случае изменения размера входного пакета, результат может быть непредсказуемым. См. пример в описании RNN.

Примечание.

GRUCell можно создать напрямую, задав нелинейную функцию, внутренние матрицы Wi и Wh, вектор смещений b и обучаемое исходное состояние state0. Матрицы Wi и Wh могут быть разных типов. См. пример в описании RNN.

# Flux.GRUv3Function

GRUv3(in => out)

Слой управляемого рекуррентного блока. Похож на RNN, но, как правило, требует большего объема памяти по сравнению с последовательностями. Реализует вариант, предложенный в версии 3 работы по ссылке.

Аргументы in и out описывают размер векторов признаков, передаваемых в качестве входа и выхода. Таким образом, функция принимает вектор длиной in или пакет векторов, представленный в виде матрицы in x B, и выдает вектор длиной out или пакет векторов размером out x B.

Данный конструктор представляет собой синтаксический сахар для Recur(GRUv3Cell(a...)), то есть вызовы GRUv3 производятся с сохранением состояния. Обратите внимание, что форма состояния может изменяться в зависимости от входов, поэтому при изменении размера пакета между вызовами вывода желательно сбрасывать модель с помощью reset!. См. примеры ниже.

В этой статье дается хороший обзор внутренних принципов работы.

Примеры

julia> g = GRUv3(3 => 5)
Recur(
  GRUv3Cell(3 => 5),                    # 140 параметров
)         # Всего: 5 обучаемых массивов, 140 параметров,
          # плюс 1 необучаемый, 5 параметров, суммарный размер 848 байтов.

julia> g(rand(Float32, 3)) |> size
(5,)

julia> Flux.reset!(g);

julia> g(rand(Float32, 3, 10)) |> size # размер пакета 10
(5, 10)

Если не вызвать reset! в случае изменения размера входного пакета, результат может быть непредсказуемым. См. пример в описании RNN.

Примечание.

GRUv3Cell можно создать напрямую, задав нелинейную функцию, внутренние матрицы Wi, Wh и Wh_h, вектор смещений b и обучаемое исходное состояние state0. Матрицы Wi, Wh и Wh_h могут быть разных типов. См. пример в описании RNN.

# Flux.RecurType

Recur(cell)

Recur принимает рекуррентную ячейку и делает ее ячейкой с сохранением состояния, управляя скрытым состоянием в фоновом режиме. Аргумент cell должен представлять собой модель в следующей форме:

h, y = cell(h, x...)

Например, вот рекуррентная сеть, сохраняющая промежуточную сумму входов:

Примеры

julia> accum(h, x) = (h + x, x)
accum (generic function with 1 method)

julia> rnn = Flux.Recur(accum, 0)
Recur(accum)

julia> rnn(2)
2

julia> rnn(3)
3

julia> rnn.state
5

Также поддерживается свертка трехмерного массива с измерениями (features, batch, time):

julia> accum(h, x) = (h .+ x, x)
accum (generic function with 1 method)

julia> rnn = Flux.Recur(accum, zeros(Int, 1, 1))
Recur(accum)

julia> rnn([2])
1-element Vector{Int64}:
 2

julia> rnn([3])
1-element Vector{Int64}:
 3

julia> rnn.state
1×1 Matrix{Int64}:
 5

julia> out = rnn(reshape(1:10, 1, 1, :));  # применяется к последовательности (признаки, пакет, время)

julia> out |> size
(1, 1, 10)

julia> vec(out)
10-element Vector{Int64}:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10

julia> rnn.state
1×1 Matrix{Int64}:
 60

# Flux.reset!Function

reset!(rnn)

Сбрасывает скрытое состояние рекуррентного слоя в исходное значение.

Для слоя rnn типа Recur это будет примерно соответствовать следующему выражению:

rnn.state = hidden(rnn.cell)

Примеры

julia> r = Flux.RNNCell(relu, ones(1,1), zeros(1,1), ones(1,1), zeros(1,1));  # вместо этого следует использовать структуру оболочки RNN

julia> y = Flux.Recur(r, ones(1,1));

julia> y.state
1×1 Matrix{Float64}:
 1.0

julia> y(ones(1,1))  # relu(1*1 + 1)
1×1 Matrix{Float64}:
 2.0

julia> y.state
1×1 Matrix{Float64}:
 2.0

julia> Flux.reset!(y)
1×1 Matrix{Float64}:
 0.0

julia> y.state
1×1 Matrix{Float64}:
 0.0

Нормализация и регуляризация

Эти слои не влияют на структуру сети, но могут ускорять обучение или предотвращать чрезмерное обучение. Некоторые из них содержат обучаемые параметры, а другие — нет.

# Flux.BatchNormType

BatchNorm(channels::Integer, λ=identity;
          initβ=zeros32, initγ=ones32,
          affine=true, track_stats=true, active=nothing,
          eps=1f-5, momentum= 0.1f0)

Слой пакетной нормализации. Аргумент channels должен содержать размер измерения канала в данных (см. ниже).

Для массива с N измерениями следует вызвать N-1-е измерение канала. Для пакета векторов признаков это просто измерение данных; для изображений WHCN это обычное измерение канала.

BatchNorm вычисляет среднее значение и дисперсию для каждого входного среза D_1×...×D_{N-2}×1×D_N и соответствующим образом нормализует входные данные.

При affine=true также применяет сдвиг и коэффициент масштаба к входным данным посредством обучаемых параметров поканального смещения β и масштаба γ.

После нормализации применяется поэлементная активация λ.

При track_stats=true на этапе обучения накапливается статистика по средним значениям и дисперсии, которая будет использоваться для повторной нормализации входных данных на этапе проверки.

Во время вывода используйте testmode!.

Примеры

julia> using Statistics

julia> xs = rand(3, 3, 3, 2);  # пакет из 2 изображений по 3 канала в каждом

julia> m = BatchNorm(3);

julia> Flux.trainmode!(m);

julia> isapprox(std(m(xs)), 1, atol=0.1) && std(xs) != std(m(xs))
true

# Flux.DropoutType

Dropout(p; [dims, rng, active])

Слой, реализующий предотвращение переобучения с заданной вероятностью. Используется в целях регуляризации, то есть для предотвращения чрезмерного обучения.

При обучении каждый вход устанавливается в значение 0 (с вероятностью p) либо масштабируется в отношении 1 / (1 - p)с помощью функции NNlib.dropout. При проверке не действует.

По умолчанию режим активируется автоматически, но им также можно управлять вручную с помощью Flux.testmode! или путем передачи именованного аргумента active=true для режима обучения.

По умолчанию каждый вход обрабатывается независимо. При задании именованного аргумента dims вместо этого случайный выбор делается только по данному измерению. Например, при вызове Dropout(p; dims = 3) случайным образом обнуляются целые каналы на входе WHCN (это также называется двумерным исключением).

Именованный аргумент rng позволяет указать пользовательский генератор случайных чисел. (Поддерживается только при выполнении на ЦП.)

Примеры

julia> m = Chain(Dense(ones(3,2)), Dropout(0.4))
Chain(
  Dense(2 => 3),                        # 9 параметров
  Dropout(0.4),
)

julia> m(ones(2, 7))  # тестовый режим, не оказывает эффекта
3×7 Matrix{Float64}:
 2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0

julia> Flux.trainmode!(m)  # равносильно использованию в градиенте
Chain(
  Dense(2 => 3),                        # 9 параметров
  Dropout(0.4, active=true),
)

julia> m(ones(2, 7))
3×7 Matrix{Float64}:
 0.0      0.0      3.33333  0.0      0.0      0.0  0.0
 3.33333  0.0      3.33333  0.0      3.33333  0.0  3.33333
 3.33333  3.33333  0.0      3.33333  0.0      0.0  3.33333

julia> y = m(ones(2, 10_000));

julia> using Statistics

julia> mean(y)  # примерно равно 2,0, как и в режиме проверки
1.9989999999999961

julia> mean(iszero, y)  # примерно равно 0,4
0.4003

# Flux.AlphaDropoutType

AlphaDropout(p; [rng, active])

Слой исключения. Используется в самонормализующихся нейронных сетях. Слой AlphaDropout гарантирует сохранение среднего значения и дисперсии активаций.

Если testmode! имеет значение true, со входом ничего не происходит.

Примеры

julia> using Statistics

julia> x = randn32(1000,1);

julia> m = Chain(Dense(1000 => 1000, selu), AlphaDropout(0.2));

julia> Flux.trainmode!(m);

julia> y = m(x);

julia> isapprox(std(x), std(y), atol=0.2)
true

# Flux.LayerNormType

LayerNorm(size..., λ=identity; affine=true, eps=1f-5)

Слой нормализации, предназначенный для использования с рекуррентными скрытыми состояниями. Значением аргумента size должно быть целое число или их кортеж.

При прямом проходе слой нормализует среднее значение и среднеквадратичное отклонение входа, а затем применяет поэлементную активацию λ. Вход нормализуется по первым length(size) измерениям для кортежа size или по первому измерению для целочисленного значения size. Размер первых измерений входа должен быть равен size.

При affine=true также применяются обучаемый сдвиг и коэффициент масштаба с помощью слоя Scale.

См. также описание BatchNorm, InstanceNorm, GroupNorm и normalise.

Примеры

julia> using Statistics

julia> xs = rand(3, 3, 3, 2);  # пакет из 2 изображений по 3 канала в каждом

julia> m = LayerNorm(3);

julia> y = m(xs);

julia> isapprox(std(y, dims=1:3), ones(1, 1, 1, 2), atol=0.1) && std(y, dims=1:3) != std(xs, dims=1:3)
true

# Flux.InstanceNormType

InstanceNorm(channels::Integer, λ=identity;
             initβ=zeros32, initγ=ones32,
             affine=false, track_stats=false,
             eps=1f-5, momentum=0.1f0)

Слой индивидуальной нормализации. Аргумент channels должен содержать размер измерения канала в данных (см. ниже).

Для массива с N > 2 измерениями следует вызвать N-1-е измерение канала. Для изображений WHCN это обычное измерение канала.

InstanceNorm вычисляет среднее значение и дисперсию для каждого входного среза D_1×...×D_{N-2}×1×1 и соответствующим образом нормализует входные данные.

При affine=true также применяет сдвиг и коэффициент масштаба к входным данным посредством обучаемых параметров поканального смещения β и масштаба γ.

При track_stats=true на этапе обучения накапливается статистика по средним значениям и дисперсии, которая будет использоваться для повторной нормализации входных данных на этапе проверки.

Внимание! Для параметров affine и track_stats значением по умолчанию было true в предыдущих версиях Flux (до версии 0.12).

Примеры

julia> using Statistics

julia> xs = rand(3, 3, 3, 2);  # пакет из 2 изображений по 3 канала в каждом

julia> m = InstanceNorm(3);

julia> y = m(xs);

julia> isapprox(std(y, dims=1:2), ones(1, 1, 3, 2), atol=0.2) && std(y, dims=1:2) != std(xs, dims=1:2)
true

# Flux.GroupNormType

GroupNorm(channels::Int, G::Int, λ = identity;
          initβ = zeros32,
          initγ = ones32,
          affine = true,
          eps = 1f-5,
          momentum = 0.1f0)

chs — это количество каналов в измерении каналов входа. Для массива с N измерениями измерение каналов имеет индекс N-1.

G — это количество групп, по которым вычисляется статистика. Количество каналов должно быть целым числом, кратным количеству групп.

Аргумент channels должен содержать размер измерения канала в данных (см. ниже).

Для массива с N > 2 измерениями следует вызвать N-1-е измерение канала. Для изображений WHCN это обычное измерение канала.

При affine=true также применяет сдвиг и коэффициент масштаба к входным данным посредством обучаемых параметров поканального смещения β и масштаба γ.

Примеры

julia> using Statistics

julia> xs = rand(3, 3, 4, 2);  # пакет из 2 изображений по 4 канала в каждом

julia> m = GroupNorm(4, 2);

julia> y = m(xs);

julia> isapprox(std(y[:, :, 1:2, 1]), 1, atol=0.1) && std(xs[:, :, 1:2, 1]) != std(y[:, :, 1:2, 1])
true

julia> isapprox(std(y[:, :, 3:4, 2]), 1, atol=0.1) && std(xs[:, :, 3:4, 2]) != std(y[:, :, 3:4, 2])
true

# Flux.normaliseFunction

normalise(x; dims=ndims(x), eps=1e-5)

Нормализует x к среднему значению 0 и среднеквадратичному отклонению 1 по измерениям, указанным в dims. По умолчанию dims — это последнее измерение. eps — это небольшой член, который добавляется в знаменатель для численной устойчивости.

Примеры

julia> using Statistics

julia> x = [90, 100, 110, 130, 70];

julia> mean(x), std(x; corrected=false)
(100.0, 20.0)

julia> y = Flux.normalise(x)
5-element Vector{Float64}:
 -0.49999975000012503
  0.0
  0.49999975000012503
  1.499999250000375
 -1.499999250000375

julia> isapprox(std(y; corrected=false), 1, atol=1e-5)
true

julia> x = rand(10:100, 10, 10);

julia> y = Flux.normalise(x, dims=1);

julia> isapprox(std(y; dims=1, corrected=false), ones(1, 10), atol=1e-5)
true

Проверка и обучение

Некоторые нормализационные слои ведут себя по-разному в режиме обучения и вывода (проверки). По умолчанию Flux автоматически определяет, вычисляется ли слой в рамках обучения или вывода.

Автоматическое определение режима обучения или проверки лучше всего работает с Zygote, пакетом по умолчанию для автоматического дифференцирования. С другими пакетами, такими как Tracker, Yota или ForwardDiff, оно может не работать.

С помощью функций Flux.trainmode! и Flux.testmode! можно вручную указать требуемое поведение. При вызове для модели они переводят все ее слои в заданный режим.

# Flux.testmode!Method

testmode!(model, [mode]) -> model

Переводит слой или все слои модели в режим проверки. При этом перестают действовать Dropout и некоторые другие слои регуляризации.

Если модель была переведена в режим проверки вручную, ее необходимо также вручную перевести обратно в режим обучения на этапе обучения с помощью trainmode!.

Кроме того, имеется необязательный второй аргумент, который принимает символ :auto для перевода всех слоев обратно в автоматический режим по умолчанию.

Пример

julia> d = Dropout(0.3)
Dropout(0.3)

julia> testmode!(d)   # исключение теперь всегда отключено
Dropout(0.3, active=false)

julia> trainmode!(d)  # исключение теперь всегда включено
Dropout(0.3, active=true)

julia> testmode!(d, :auto)  # восстановление режима по умолчанию
Dropout(0.3)

# Flux.testmode!Method

testmode!(model, inactive)

Этот метод с двумя аргументами предназначен преимущественно для внутреннего использования. Он выполняет рекурсию по model, пока метод, например testmode!(d::Dropout, inactive), не изменит активность слоя. Пользовательские слои могут поддерживать переключение вручную между testmode! и trainmode! путем определения такого метода.

Возможные значения inactive:

  • true для тестирования, то есть active=false;

  • false для обучения; равносильно xref:./layers.adoc#Flux.trainmode!(m);

  • :auto или nothing для автоматического обнаружения обучения библиотекой Flux.

compat Этот метод может быть упразднен при внесении следующего критического изменения для отделения доступной пользователю функции testmode! от внутренней рекурсии.

# Flux.trainmode!Function

trainmode!(model) -> model

Переводит слой или все слои модели в режим обучения. Функция, обратная функции testmode!. Дополнительные сведения см. в ее описании.

trainmode!(m, active)

Этот метод с двумя аргументами устарел.

Возможные значения active:

  • true для обучения;

  • false для тестирования; равносильно testmode!(m);

  • :auto или nothing для автоматического обнаружения обучения библиотекой Flux.