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

Примитивы нейронной сети из NNlib.jl

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

Внимание

Примитивы для слоя MultiHeadAttention.

# NNlib.dot_product_attentionFunction

dot_product_attention(query, key, value, [bias]; [fdrop, mask, nheads])

Внимание многоголового скалярного произведения, используемое в архитектурах трансформера.

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

Возвращает выходной массив внимания размером (v_dim, q_len, batch_size...) и оценки внимания размером (kv_len, q_len, nheads, batch_size...).

Если вам нужны только оценки внимания, см. описание dot_product_attention_scores.

Аргументы

  • query: массив запросов размером (qk_dim, q_len, batch_size...).

  • key: массив ключей размером (qk_dim, kv_len, batch_size...).

  • value: массив значений размером (v_dim, kv_len, batch_size...).

  • bias: либо nothing, либо массив, транслируемый в размер (kv_len, q_len, nheads, batch_size). Будет добавлен к оценкам внимания перед применением многопеременной логистической функции (softmax). Значение по умолчанию — nothing.

  • fdrop: слой или функция предотвращения переобучения, которые будут применяться к оценкам внимания сразу после softmax. Значение по умолчанию — identity (без предотвращения переобучения).

  • mask: либо nothing, либо логический массив, транслируемый в размер (kv_len, q_len, nheads, batch_size). Маска применяется к оценкам внимания непосредственно перед softmax. См. описание make_causal_mask для создания причинных масок. Значение по умолчанию — nothing.

  • nheads: количество голов, на которое будут разделены входные массивы. Значение по умолчанию — 1.

Примеры

q, k, v = rand(10, 20, 2), rand(10, 30, 2), rand(20, 30, 2)
y, α = dot_product_attention(q, k, v)

# NNlib.dot_product_attention_scoresFunction

dot_product_attention_scores(query, key, [bias]; [fdrop, mask])

Возвращает оценки внимания для dot_product_attention. Входные массивы должны иметь измерения (num_features ÷ nheads, nheads, sequence_length, batch_size).

Дополнительные сведения см. в документе dot_product_attention.

# NNlib.make_causal_maskFunction

make_causal_mask(x, dims=2)

Возвращает логическую квадратную матрицу m того же типа, что и x, со стороной size(x, dims). Ее элементы заданы таким образом, что m[i, j] == i ≤ j.

Можно использовать для маскирования оценок внимания в dot_product_attention.

Многопеременная логистическая функция (softmax)

Flux.logitcrossentropy во Flux использует NNlib.logsoftmax внутренним образом.

# NNlib.softmaxFunction

softmax(x; dims = 1)

Softmax преобразует входной массив x в распределения вероятностей, которые в сумме равны 1 по измерениям, указанным с помощью dims. Функция семантически эквивалентна следующему:

softmax(x; dims = 1) = exp.(x) ./ sum(exp.(x), dims = dims)

с дополнительными операциями, повышающими численную устойчивость.

Для входа матрицы x она по умолчанию (dims = 1) будет рассматриваться как пакет векторов, каждый столбец которого независим. Ключевое слово dims = 2 будет рассматривать строки независимо друг от друга и так далее.

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

Примеры

julia> softmax([1, 2, 3])
3-element Vector{Float64}:
 0.09003057317038046
 0.24472847105479764
 0.6652409557748218

julia> softmax([1 2 3; 2 2 2])  # dims=1
2×3 Matrix{Float64}:
 0.268941  0.5  0.731059
 0.731059  0.5  0.268941

julia> softmax([1 2 3; 2 2 2]; dims=2)
2×3 Matrix{Float64}:
 0.0900306  0.244728  0.665241
 0.333333   0.333333  0.333333

Обратите внимание, что при использовании Flux.jl softmax нельзя передавать слоям типа Dense, которые принимают функцию активации. Активация транслируется через результат, поэтому применяется к отдельным числам. Но softmax всегда нужно видеть весь столбец.

julia> using Flux

julia> x = randn(Float32, 4, 4, 3, 13);

julia> model = Chain(Conv((4, 4), 3 => 8, tanh), Flux.flatten, Dense(8 => 7), softmax);

julia> model(x) |> size
(7, 13)

julia> Dense(4 => 7, softmax)(x)
ERROR: `softmax(x)` called with a number, but it expects an array.

# NNlib.logsoftmaxFunction

logsoftmax(x; dims = 1)

Вычисляет логарифм softmax более численно устойчивым способом по сравнению с прямым взятием log.(softmax(xs)). Обычно используется при вычислении потери перекрестной энтропии.

Функция семантически эквивалентна следующему:

logsoftmax(x; dims = 1) = x .- log.(sum(exp.(x), dims = dims))

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

Подвыборка

AdaptiveMaxPool, AdaptiveMeanPool, GlobalMaxPool, GlobalMeanPool, MaxPool и MeanPool во Flux используют NNlib.PoolDims, NNlib.maxpool и NNlib.meanpool в качестве своего бэкенда.

# NNlib.PoolDimsType

PoolDims(x_size::NTuple{M}, k::Union{NTuple{L, Int}, Int};
        stride=k, padding=0, dilation=1)  where {M, L}

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

# NNlib.lpnormpoolFunction

lpnormpool(x, p::Real, k::NTuple{N, Integer}; pad=0, stride=k)

Выполняет операцию подвыборки Lp со значением Lp-нормы p и размером окна k на входном тензоре x, также известную как LPPool в PyTorch. Этот оператор подвыборки взят из документа Learned-Norm Pooling for Deep Feedforward and Recurrent Neural Networks.

Аргументы

  • x и k: ожидает ndim(x) ∈ 3:5, and alwayslength(k) == ndim(x) - 2`

  • p ограничен 0 < p < Inf.

  • pad: дополнительные сведения см. в описании pad_zeros.

  • stride: либо кортеж той же длины, что и k, либо одно целое число для всех направлений. Значение по умолчанию — k.

Для всех элементов x в окне размером k lpnormpool вычисляет (∑ᵢ xᵢ^p)^(1 / p) как элемент вывода.

Таким образом, lpnormpool(x, 1, k) ./ prod(k) ≈ meanpool(x, k) и lpnormpool(x, 2, k).^2 ./ prod(k) ≈ meanpool(x.^2, k).

# NNlib.maxpoolFunction

maxpool(x, k::NTuple{N, Integer}; pad=0, stride=k)

Выполняет операцию подвыборки максимума с размером окна k во входном тензоре x.

Аргументы

  • x и k: ожидает ndim(x) ∈ 3:5 и всегда length(k) == ndim(x) - 2.

  • pad: дополнительные сведения см. в описании pad_zeros.

  • stride: либо кортеж той же длины, что и k, либо одно целое число для всех направлений. Значение по умолчанию — k.

# NNlib.meanpoolFunction

meanpool(x, k::NTuple{N, Integer}; pad=0, stride=k)

Выполняет операцию подвыборки среднего с размером окна k во входном тензоре x.

Аргументы

  • x и k: ожидает ndim(x) ∈ 3:5, and alwayslength(k) == ndim(x) - 2`

  • pad: дополнительные сведения см. в описании pad_zeros.

  • stride: либо кортеж той же длины, что и k, либо одно целое число для всех направлений. Значение по умолчанию — k.

Заполнение

# NNlib.pad_circularFunction

pad_circular(x, pad::Tuple; [dims])
pad_circular(x, pad::Int; [dims])

Заполняет массив x кругообразно через границу, заключая в оболочку значения с противоположной стороны x.

pad может быть кортежем целых чисел (l1, r1, ..., ln, rn) некоторой длины 2n, задающим размер левого и правого заполнения для каждого измерения в dims. Если значение dims не задано, по умолчанию используются первые n измерений.

Если pad является целым числом, он применяется к обеим сторонам каждого измерения в dims. В данном случае для dims по умолчанию используются первые ndims(x)-2 измерений (т. е. исключаются измерение канала и пакетное измерение).

Длина заполнения с каждой стороны в любом измерении не должна превышать размер x в этом измерении, т. е. pad_circular не может создавать мозаики произвольного размера x.

См. также описание pad_repeat, pad_reflect, pad_symmetric и pad_constant.

julia> r = reshape(1:9, 3, 3)
3×3 reshape(::UnitRange{Int64}, 3, 3) with eltype Int64:
 1  4  7
 2  5  8
 3  6  9

julia> pad_circular(r, (1,2,1,2))
6×6 Matrix{Int64}:
 9  3  6  9  3  6
 7  1  4  7  1  4
 8  2  5  8  2  5
 9  3  6  9  3  6
 7  1  4  7  1  4
 8  2  5  8  2  5

# NNlib.pad_constantFunction

pad_constant(x, pad::Tuple, val = 0; [dims = :])
pad_constant(x, pad::Int, val = 0; [dims = :])

Заполняет массив x постоянным значением val.

pad может быть кортежем целых чисел. Если он имеет некоторую длину 2 * length(dims), которая задает размер левого и правого заполнения для каждого измерения в dims в виде (l1, r1, ..., ln, rn). Если указан с кортежем длиной length(dims), применяется симметричное заполнение. Если значение dims не задано, по умолчанию используются все измерения.

Если pad является целым числом, он применяется к обеим сторонам каждого измерения в dims.

См. также описание pad_zeros, pad_repeat, pad_reflect, pad_symmetric и pad_circular.

julia> r = reshape(1:4, 2, 2)
2×2 reshape(::UnitRange{Int64}, 2, 2) with eltype Int64:
 1  3
 2  4

julia> pad_constant(r, (1, 2, 3, 4), 8)
5×9 Matrix{Int64}:
 8  8  8  8  8  8  8  8  8
 8  8  8  1  3  8  8  8  8
 8  8  8  2  4  8  8  8  8
 8  8  8  8  8  8  8  8  8
 8  8  8  8  8  8  8  8  8

julia> pad_constant(r, 1, 8)
4×4 Matrix{Int64}:
 8  8  8  8
 8  1  3  8
 8  2  4  8
 8  8  8  8

julia> r = reshape(1:27, 3, 3, 3)
3×3×3 reshape(::UnitRange{Int64}, 3, 3, 3) with eltype Int64:
[:, :, 1] =
 1  4  7
 2  5  8
 3  6  9

[:, :, 2] =
 10  13  16
 11  14  17
 12  15  18

[:, :, 3] =
 19  22  25
 20  23  26
 21  24  27

julia> pad_constant(r, (2,1), dims = 1) # ассиметричное заполнение
6×3×3 Array{Int64, 3}:
[:, :, 1] =
 0  0  0
 0  0  0
 1  4  7
 2  5  8
 3  6  9
 0  0  0

[:, :, 2] =
  0   0   0
  0   0   0
 10  13  16
 11  14  17
 12  15  18
  0   0   0

[:, :, 3] =
  0   0   0
  0   0   0
 19  22  25
 20  23  26
 21  24  27
  0   0   0

julia> pad_constant(r, (2,1, 3), dims = (1,2)) # заполнение всегда должно быть либо такой же длины, как измерения, либо в два раза больше
ERROR: ArgumentError: Could not parse padding (2, 1, 3) and dims (1, 2)
Stacktrace:
[...]

# NNlib.pad_reflectFunction

pad_reflect(x, pad::Tuple; [dims])
pad_reflect(x, pad::Int; [dims])

Заполняет массив x, отражая его значения через границу.

pad может быть кортежем целых чисел (l1, r1, ..., ln, rn) некоторой длины 2n, задающим размер левого и правого заполнения для каждого измерения в dims. Если значение dims не задано, по умолчанию используются первые n измерений.

Если pad является целым числом, он применяется к обеим сторонам каждого измерения в dims. В данном случае для dims по умолчанию используются первые ndims(x)-2 измерений (т. е. исключаются измерение канала и пакетное измерение).

julia> r = reshape(1:9, 3, 3)
3×3 reshape(::UnitRange{Int64}, 3, 3) with eltype Int64:
 1  4  7
 2  5  8
 3  6  9

julia> pad_reflect(r, (1,2,1,2))
6×6 Matrix{Int64}:
 5  2  5  8  5  2
 4  1  4  7  4  1
 5  2  5  8  5  2
 6  3  6  9  6  3
 5  2  5  8  5  2
 4  1  4  7  4  1

# NNlib.pad_repeatFunction

pad_repeat(x, pad::Tuple; [dims])
pad_repeat(x, pad::Int; [dims])

Заполняет массив x, повторяя значения на границе.

pad может быть кортежем целых чисел (l1, r1, ..., ln, rn) некоторой длины 2n, задающим размер левого и правого заполнения для каждого измерения в dims. Если значение dims не задано, по умолчанию используются первые n измерений.

Если pad является целым числом, он применяется к обеим сторонам каждого измерения в dims. В данном случае для dims по умолчанию используются первые ndims(x)-2 измерений (т. е. исключаются измерение канала и пакетное измерение).

julia> r = reshape(1:9, 3, 3)
3×3 reshape(::UnitRange{Int64}, 3, 3) with eltype Int64:
 1  4  7
 2  5  8
 3  6  9

julia> pad_repeat(r, (1,2,3,4))
6×10 Matrix{Int64}:
 1  1  1  1  4  7  7  7  7  7
 1  1  1  1  4  7  7  7  7  7
 2  2  2  2  5  8  8  8  8  8
 3  3  3  3  6  9  9  9  9  9
 3  3  3  3  6  9  9  9  9  9
 3  3  3  3  6  9  9  9  9  9

# NNlib.pad_symmetricFunction

pad_symmetric(x, pad::Tuple; [dims])
pad_symmetric(x, pad::Int; [dims])

Заполняет массив x, симметрично отражая его значения через границу, т. е. граничные значения x присутствуют в значениях заполнения, в отличие от pad_reflect.

pad может быть кортежем целых чисел (l1, r1, ..., ln, rn) некоторой длины 2n, задающим размер левого и правого заполнения для каждого измерения в dims. Если значение dims не задано, по умолчанию используются первые n измерений.

Если pad является целым числом, он применяется к обеим сторонам каждого измерения в dims. В данном случае для dims по умолчанию используются первые ndims(x)-2 измерений (т. е. исключаются измерение канала и пакетное измерение).

julia> r = reshape(1:9, 3, 3)
3×3 reshape(::UnitRange{Int64}, 3, 3) with eltype Int64:
 1  4  7
 2  5  8
 3  6  9

julia> pad_symmetric(r, (1,2,1,2))
6×6 Matrix{Int64}:
 1  1  4  7  7  4
 1  1  4  7  7  4
 2  2  5  8  8  5
 3  3  6  9  9  6
 3  3  6  9  9  6
 2  2  5  8  8  5

# NNlib.pad_zerosFunction

pad_zeros(x, pad::Tuple; [dims])
pad_zeros(x, pad::Int; [dims])

Заполняет массив x нулями. Эквивалентна pad_constant с константой, равной 0.

Свертка

Слои Conv и CrossCor во Flux используют NNlib.DenseConvDims и NNlib.conv внутренним образом.

# NNlib.convFunction

conv(x, w; stride = 1, pad = 0, dilation = 1, flipped = false, groups = 1)

Применяет сверточный фильтр w к входному x. x и w являются трех-, четырех- и пятимерными тензорами в одно-, дву- и трехмерных свертках, соответственно. x и w могут иметь вещественные или комплексные типы элементов.

# NNlib.ConvDimsType

ConvDims

Вводит информацию системного уровня об измерениях свертки. Имеет критически важное значение для таких вещей, как im2col!(), чтобы генерировать эффективный код, и полезна для уменьшения количества передаваемых именованных аргументов.

# NNlib.depthwiseconvFunction

depthwiseconv(x, w; stride=1, pad=0, dilation=1, flipped=false)

Операция глубинной свертки с фильтром w на входном x. x и w являются трех-, четырех- и пятимерными тензорами в одно-, дву- и трехмерных свертках, соответственно.

# NNlib.DepthwiseConvDimsType

DepthwiseConvDims

Конкретный подкласс ConvDims для глубинной свертки. Отличается в первую очередь тем, что характеризуется Cin, Cmult, а не Cin, Cout. Рекомендуется использовать отдельно от DenseConvDims в первую очередь из-за различий в вычислении каналов.

# NNlib.DenseConvDimsType

DenseConvDims

Конкретный подкласс ConvDims для нормальной плотной двумерной или трехмерной свертки.

Прекращение переобучения

# NNlib.dropoutFunction

dropout([rng], A, p; [dims])

Возвращает массив, в котором каждый элемент A либо заменен нулем с вероятностью p, либо умножен на 1/(1-p).

По умолчанию каждый массив обрабатывается независимо. С помощью ключевого слова dims=1 выбор осуществляется для каждого значения 1-го индекса, т. е. каждая строка матрицы либо нулевая, либо нет.

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

Примеры

julia> dropout(ones(2, 10), 0.2)
2×10 Matrix{Float64}:
 1.25  1.25  0.0   1.25  1.25  1.25  1.25  1.25  1.25  1.25
 1.25  1.25  1.25  0.0   1.25  1.25  0.0   1.25  1.25  1.25

julia> mean(dropout(ones(10^4, 5), 0.2), dims=1)
1×5 Matrix{Float64}:
 0.998  1.00075  0.99125  0.99575  1.00075

julia> dropout(ones(5, 5), 0.7, dims=1)  # вся строка та же
5×5 Matrix{Float64}:
 3.33333  3.33333  3.33333  3.33333  3.33333
 0.0      0.0      0.0      0.0      0.0
 0.0      0.0      0.0      0.0      0.0
 3.33333  3.33333  3.33333  3.33333  3.33333
 0.0      0.0      0.0      0.0      0.0

julia> mean(dropout(ones(10^4, 5), 0.3, dims=1), dims=1)
1×5 Matrix{Float64}:
 1.00571  1.00571  1.00571  1.00571  1.00571

# NNlib.dropout!Function

dropout!(B, A, p; [dims])

Это делает именно B .= dropout(A, p; dims), точнее, это реализация dropout не на месте.

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

Слой Upsample во Flux использует NNlib.upsample_nearest, NNlib.upsample_bilinear и NNlib.upsample_trilinear в качестве своего бэкенда. Кроме того, слой PixelShuffle во Flux использует NNlib.pixel_shuffle в качестве своего бэкенда.

# NNlib.upsample_nearestFunction

upsample_nearest(x, scale::NTuple{S,Int})
upsample_nearest(x; size::NTuple{S,Int})

Повышает дискретизацию массива x на целые кратные в первых S измерениях. Последующие измерения x не изменяются.

Можно указать либо множители scale, либо конечный результат size.

Кроме того, см. описание upsample_bilinear со сведениями о двух измерениях массива N=4.

Пример

julia> upsample_nearest([1 2 3; 4 5 6], (2, 3))
4×9 Matrix{Int64}:
 1  1  1  2  2  2  3  3  3
 1  1  1  2  2  2  3  3  3
 4  4  4  5  5  5  6  6  6
 4  4  4  5  5  5  6  6  6

julia> ans == upsample_nearest([1 2 3; 4 5 6]; size=(4, 9))  # эквивалентно
true

julia> upsample_nearest([1 2 3; 4 5 6], (2,))
4×3 Matrix{Int64}:
 1  2  3
 1  2  3
 4  5  6
 4  5  6

julia> ans == upsample_nearest([1 2 3; 4 5 6], size=(4,))
true

# NNlib.upsample_linearFunction

upsample_linear(x::AbstractArray{T,3}, scale::Real; align_corners::Bool = true)
upsample_linear(x::AbstractArray{T,3}; size::Integer, align_corners::Bool = true)

Повышает дискретизацию массива x на величину, заданную scale, с помощью линейной интерполяции. В качестве альтернативы использования scale результирующий массив size можно указать напрямую с помощью именованного аргумента.

Размер вывода равен (scale*S1, S2, S3), где S1, S2, S3 = size(x).

# NNlib.∇upsample_linearFunction

∇upsample_linear(Δ::AbstractArray{T,3}; size::Integer, align_corners::Bool = true) where T

Аргументы

  • Δ: входящий массив градиентов, распространяющийся с нижележащих слоев в обратном направлении.

  • size: размер изображения, подвергнутого повышающей дискретизации в первую очередь.

Выводы

  • dx: подвергнутая понижающей дискретизации версия Δ.

# NNlib.upsample_bilinearFunction

upsample_bilinear(x::AbstractArray{T,4}, scale::NTuple{2,Real}; align_corners::Bool = true)
upsample_bilinear(x::AbstractArray{T,4}; size::NTuple{2,Integer}, align_corners::Bool = true)

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

Размер вывода равен (scale[1]*S1, scale[2]*S2, S3, S4), где S1, S2, S3, S4 = size(x).

Примеры

julia> x = reshape(Float32[1 2 3; 4 5 6], (2,3,1,1))
2×3×1×1 Array{Float32, 4}:
[:, :, 1, 1] =
 1.0  2.0  3.0
 4.0  5.0  6.0

julia> upsample_bilinear(x, (2, 3))
4×9×1×1 Array{Float32, 4}:
[:, :, 1, 1] =
 1.0  1.25  1.5  1.75  2.0  2.25  2.5  2.75  3.0
 2.0  2.25  2.5  2.75  3.0  3.25  3.5  3.75  4.0
 3.0  3.25  3.5  3.75  4.0  4.25  4.5  4.75  5.0
 4.0  4.25  4.5  4.75  5.0  5.25  5.5  5.75  6.0

julia> ans == upsample_bilinear(x; size=(4, 9))  # укажите размер вывода
true

julia> upsample_bilinear(x, (2.5, 3.5))  # допускаются нецелочисленные масштабирующие множители
5×10×1×1 Array{Float32, 4}:
[:, :, 1, 1] =
 1.0   1.22222  1.44444  1.66667  1.88889  …  2.33333  2.55556  2.77778  3.0
 1.75  1.97222  2.19444  2.41667  2.63889     3.08333  3.30556  3.52778  3.75
 2.5   2.72222  2.94444  3.16667  3.38889     3.83333  4.05556  4.27778  4.5
 3.25  3.47222  3.69444  3.91667  4.13889     4.58333  4.80556  5.02778  5.25
 4.0   4.22222  4.44444  4.66667  4.88889     5.33333  5.55556  5.77778  6.0

# NNlib.∇upsample_bilinearFunction

∇upsample_bilinear(Δ::AbstractArray{T,4}; size::NTuple{2,Integer}, align_corners::Bool = true) where T

Аргументы

  • Δ: входящий массив градиентов, распространяющийся с нижележащих слоев в обратном направлении.

  • size: боковой (W,H) размер изображения, подвергнутого повышающей дискретизации в первую очередь.

Выводы

  • dx: подвергнутая понижающей дискретизации версия Δ.

# NNlib.upsample_trilinearFunction

upsample_trilinear(x::AbstractArray{T,5}, scale::NTuple{3,Real}; align_corners::Bool = true)
upsample_trilinear(x::AbstractArray{T,5}; size::NTuple{3,Integer}, align_corners::Bool = true)

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

Размер вывода равен (scale[1]*S1, scale[2]*S2, scale[3]*S3, S4, S5), где S1, S2, S3, S4, S5 = size(x).

Примеры

upsample_trilinear(x, (2, 3, 4))
upsample_trilinear(x; size=(4, 9, 11))  # укажите размер вывода
upsample_trilinear(x, (2.5, 3.5, pi))  # допускаются нецелочисленные масштабирующие множители

# NNlib.∇upsample_trilinearFunction

∇upsample_trilinear(Δ::AbstractArray{T,5}; size::NTuple{3,Integer}, align_corners::Bool = true) where T

Аргументы

  • Δ: входящий массив градиентов, распространяющийся с нижележащих слоев в обратном направлении.

  • size: боковой размер и глубина (W,H,D) изображения, подвергнутого повышающей дискретизации в первую очередь.

Выводы

  • dx: подвергнутая понижающей дискретизации версия Δ.

# NNlib.pixel_shuffleFunction

pixel_shuffle(x, r::Integer)

Операция перестановки пикселей, повышение масштаба на коэффициент r.

Для четырехмерных массивов, представляющих N изображение, операция преобразует входной size(x) == (W, H, r^2*C, N) в вывод размером (r*W, r*H, C, N). Для D-мерных данных она ожидает ndims(x) == D+2 с измерениями канала и пакета и делит количество каналов на r^D.

Используется в сетях с суперразрешением для повышения дискретизации до высокого разрешения. Справка: Shi et. al., Real-Time Single Image and Video Super-Resolution …​, CVPR 2016, https://arxiv.org/abs/1609.05158

Примеры

julia> x = [10i + j + channel/10 for i in 1:2, j in 1:3, channel in 1:4, batch in 1:1]
2×3×4×1 Array{Float64, 4}:
[:, :, 1, 1] =
 11.1  12.1  13.1
 21.1  22.1  23.1

[:, :, 2, 1] =
 11.2  12.2  13.2
 21.2  22.2  23.2

[:, :, 3, 1] =
 11.3  12.3  13.3
 21.3  22.3  23.3

[:, :, 4, 1] =
 11.4  12.4  13.4
 21.4  22.4  23.4

julia> pixel_shuffle(x, 2)  # 4 канала используются для 2-кратного увеличения измерений изображения
4×6×1×1 Array{Float64, 4}:
[:, :, 1, 1] =
 11.1  11.3  12.1  12.3  13.1  13.3
 11.2  11.4  12.2  12.4  13.2  13.4
 21.1  21.3  22.1  22.3  23.1  23.3
 21.2  21.4  22.2  22.4  23.2  23.4

julia> y = [i + channel/10 for i in 1:3, channel in 1:6, batch in 1:1]
3×6×1 Array{Float64, 3}:
[:, :, 1] =
 1.1  1.2  1.3  1.4  1.5  1.6
 2.1  2.2  2.3  2.4  2.5  2.6
 3.1  3.2  3.3  3.4  3.5  3.6

julia> pixel_shuffle(y, 2)  # одномерное изображение, в котором 6 каналов сокращены до 3
6×3×1 Array{Float64, 3}:
[:, :, 1] =
 1.1  1.3  1.5
 1.2  1.4  1.6
 2.1  2.3  2.5
 2.2  2.4  2.6
 3.1  3.3  3.5
 3.2  3.4  3.6

Пакетные операции

Слой Flux.Bilinear во Flux использует NNlib.batched_mul внутренним образом.

# NNlib.batched_mulFunction

batched_mul(A, B) -> C
A ⊠ B  # \boxtimes

Пакетное умножение матриц. В результате имеем C[:,:,k...] == A[:,:,k...] * B[:,:,k...], где k... представляет любые индексы в последних измерениях.

Если ndims(A) == ndims(B) == 3 и size(B,3) == 1, то вместо C[:,:,k] == A[:,:,k] * B[:,:,1] и аналогично для A.

Чтобы транспонировать каждую матрицу, примените batched_transpose к массиву, или batched_adjoint для сопряженного транспонирования:

julia> A, B = randn(2,5,17), randn(5,9,17);

julia> A ⊠ B |> size
(2, 9, 17)

julia> batched_adjoint(A) |> size
(5, 2, 17)

julia> batched_mul(A, batched_adjoint(randn(9,5,17))) |> size
(2, 9, 17)

julia> A ⊠ randn(5,9,1) |> size
(2, 9, 17)

julia> batched_transpose(A) == PermutedDimsArray(A, (2,1,3))
true

Вместо batched_transpose можно использовать эквивалентный PermutedDimsArray. Другие перестановки также обрабатываются BLAS при условии, что пакетный индекс kне является первым измерением базового массива. Таким образом, PermutedDimsArray(::Array, (1,3,2)) и PermutedDimsArray(::Array, (3,1,2)) вполне подходят.

Однако A = PermutedDimsArray(::Array, (3,2,1)) неприемлемо для BLAS, поскольку пакетное измерение является непрерывным: stride(A,3) == 1. Это будет скопировано, так как это быстрее, чем выполнение batched_mul_generic!.

И copy, и batched_mul_generic! выдают сообщения @debug, и при задании, например, ENV["JULIA_DEBUG"] = NNlib они будут выведены на экран.

batched_mul(A::Array{T,3}, B::Matrix)
batched_mul(A::Matrix, B::Array{T,3})
A ⊠ B

Это всегда умножение матрицы на матрицу, но у A или B может отсутствовать индекс пакета.

  • Если B является матрицей, результат имеет вид C[:,:,k] == A[:,:,k] * B[:,:] для всех k.

  • Если A является матрицей, то C[:,:,k] == A[:,:] * B[:,:,k]. Это также можно сделать, изменив форму и вызвав *, например A ⊡ B, используя TensorCore.jl, но здесь это реализовано с помощью batched_gemm вместо gemm.

julia> randn(16,8,32) ⊠ randn(8,4) |> size
(16, 4, 32)

julia> randn(16,8,32) ⊠ randn(8,4,1) |> size  # эквивалентно
(16, 4, 32)

julia> randn(16,8) ⊠ randn(8,4,32) |> size
(16, 4, 32)

См. также описание batched_vec, где B рассматривается как пакет векторов A[:,:,k] * B[:,k].

# NNlib.batched_mul!Function

batched_mul!(C, A, B) -> C
batched_mul!(C, A, B, α=1, β=0)

Пакетное умножение матриц на месте, функция эквивалентна mul!(C[:,:,k], A[:,:,k], B[:,:,k], α, β) для всех k. Если size(B,3) == 1, каждый пакет использует B[:,:,1].

При любой возможности будет вызываться batched_gemm!. Для вещественных массивов это означает, что для X ∈ [A,B,C] либо strides(X,1)==1, либо strides(X,2)==1. Последнее может быть вызвано с помощью batched_transpose или с помощью PermutedDimsArray(::Array, (3,1,2)), к примеру. В отличие от batched_mul, функция никогда не будет создавать копию.

Для сложных массивов оболочка, созданная с помощью batched_adjoint, должна быть самой дальней, чтобы ее можно было увидеть. В этом случае принимаемые BLAS массивы с заданным шагом более ограничены, если stride(C,1)==1, принимается только stride(AorB::BatchedAdjoint,2) == 1.

# NNlib.batched_adjointFunction

batched_transpose(A::AbstractArray{T,3})
batched_adjoint(A)

Эквивалентна применению транспонирования (transpose) или сопряжения (adjoint) к каждой матрице A[:,:,k].

Они существуют для того, чтобы контролировать поведение функции batched_mul, поскольку она работает с такими матричными срезами массива, где ndims(A)==3.

PermutedDimsArray(A, (2,1,3)) эквивалентен batched_transpose(A) и также понятен для batched_mul (и более широко поддерживается в других местах).

BatchedTranspose{T, S} <: AbstractBatchedMatrix{T, 3}
BatchedAdjoint{T, S}

Ленивые оболочки, аналогичные Transpose и Adjoint, возвращаемые batched_transpose и т. д.

# NNlib.batched_transposeFunction

batched_transpose(A::AbstractArray{T,3})
batched_adjoint(A)

Эквивалентна применению транспонирования (transpose) или сопряжения (adjoint) к каждой матрице A[:,:,k].

Они существуют для того, чтобы контролировать поведение функции batched_mul, поскольку она работает с такими матричными срезами массива, где ndims(A)==3.

PermutedDimsArray(A, (2,1,3)) эквивалентен batched_transpose(A) и также понятен для batched_mul (и более широко поддерживается в других местах).

BatchedTranspose{T, S} <: AbstractBatchedMatrix{T, 3}
BatchedAdjoint{T, S}

Ленивые оболочки, аналогичные Transpose и Adjoint, возвращаемые batched_transpose и т. д.

# NNlib.batched_vecFunction

batched_vec(A::Array{T,3}, B::Matrix)
batched_vec(A::Array{T,3}, b::Vector)

Пакетное матрично-векторное умножение: результат имеет вид C[:,:,k] == A[:,:,k] * B[:,k] для всех k, или же C[:,:,k] == A[:,:,k] * b для b::Vector.

При тех же типах аргументов batched_mul(A, B) будет рассматривать B как фиксированную матрицу, а не как пакет векторов. Изменяет форму, а затем вызывает batched_mul(::Array{T,3}, ::Array{T,3}).

julia> A, B, b = randn(16,8,32), randn(8,32), randn(8);

julia> batched_vec(A,B) |> size
(16, 32)

julia> batched_vec(A,b) |> size
(16, 32)

Дефрагментация и фрагментация

Слой Flux во Embedding использует NNlib.gather в качестве своего бэкенда.

# NNlib.gatherFunction

NNlib.gather(src, idx) -> dst

Операция, обратная scatter. Собирает данные из источника src и записывает их в место назначения dst в соответствии с массивом индексов idx. Для каждого k в CartesianIndices(idx) присваивает значения dst в соответствии со следующим выражением:

dst[:, ... , k] .= src[:, ... , idx[k]...]

Обратите внимание, что если idx — это вектор, содержащий целые числа, а src является матрицей, предыдущее выражение упрощается до следующего

dst[:, k] .= src[:, idx[k]]

и k будет превышать 1:length(idx).

Элементы idx могут быть целыми числами или кортежами целых чисел и могут повторяться. Один столбец src может быть скопирован в ноль, один или несколько столбцов dst.

См. описание gather! со сведениями о версии на месте.

Примеры

julia> NNlib.gather([1,20,300,4000], [2,4,2])
3-element Vector{Int64}:
   20
 4000
   20

julia> NNlib.gather([1 2 3; 4 5 6], [1,3,1,3,1])
2×5 Matrix{Int64}:
 1  3  1  3  1
 4  6  4  6  4
gather(src, IJK...)

Преобразует кортеж целочисленных векторов IJK в кортеж CartesianIndex и вызывает gather для него: gather(src, CartesianIndex.(IJK...)).

Примеры

julia> src = reshape([1:15;], 3, 5)
3×5 Matrix{Int64}:
 1  4  7  10  13
 2  5  8  11  14
 3  6  9  12  15

julia> NNlib.gather(src, [1, 2], [2, 4])
2-element Vector{Int64}:
  4
 11

# NNlib.gather!Function

NNlib.gather!(dst, src, idx)

Операция, обратная scatter!. Собирает данные из источника src и записывает их в место назначения dst в соответствии с массивом индексов idx. Для каждого k в CartesianIndices(idx) присваивает значения dst в соответствии со следующим выражением:

dst[:, ... , k] .= src[:, ... , idx[k]...]

Обратите внимание, что если idx — это вектор, содержащий целые числа, а dst и src являются матрицами, предыдущее выражение упрощается до следующего

dst[:, k] .= src[:, idx[k]]

и k будет превышать 1:length(idx).

Элементы idx могут быть целыми числами или кортежами целых чисел и могут повторяться. Один столбец src может быть скопирован в ноль, один или несколько столбцов dst.

См. описание gather со сведениями о выделяющей версии.

# NNlib.scatterFunction

NNlib.scatter(op, src, idx; [init, dstsize])

Операция фрагментации, выделяющая целевой массив dst и вызывающая для него scatter!(op, dst, src, idx).

  • Если указано ключевое слово init, оно используется для инициализации содержимого dst. В противном случае значения инициализации выводятся из оператора редукции op для некоторых общих операторов (например, init = 0 для op = +).

  • Если указано значение dstsize, оно будет использоваться для определения размера массива назначения, в противном случае он будет определяться по src и idx.

Подробные сведения о работе idx см. в описании scatter!.

Примеры

julia> NNlib.scatter(+, [10,100,1000], [3,1,2])
3-element Vector{Int64}:
  100
 1000
   10

julia> NNlib.scatter(+, [1 2 3 4; 5 6 7 8], [2,1,1,5])
2×5 Matrix{Int64}:
  5  1  0  0  4
 13  5  0  0  8

julia> NNlib.scatter(*, [10,200,3000], [1,4,2]; init = 10, dstsize = 6)
6-element Vector{Int64}:
   100
 30000
    10
  2000
    10
    10

# NNlib.scatter!Function

NNlib.scatter!(op, dst, src, idx)

Операция фрагментации, которая записывает данные из источника (src) в место назначения (dst) в расположениях индексов (idx). Во время фрагментации применяется бинарный оператор редукции op. Для каждого индекса k в idx накапливает значения в dst в соответствии со следующим выражением:

dst[:, ..., idx[k]...] = (op).(dst[:, ..., idx[k]...], src[:, ..., k...])

См. также описание scatter и gather.

Аргументы

  • op: операции, применяемые к dst и src, например +, -, *, /, max, min и mean.

  • dst: место назначения src для агрегирования. Этот аргумент будет изменен.

  • src: исходные данные для агрегирования.

  • idx: сопоставление для агрегирования из источника (индекс) в место назначения (значение). Массив idx может содержать либо целые числа, либо кортежи.

Примеры

julia> NNlib.scatter!(+, ones(3), [10,100], [1,3])
3-element Vector{Float64}:
  11.0
   1.0
 101.0

julia> NNlib.scatter!(*, fill(0.5, 2, 4), [1 10; 100 1000], [3,2])
2×4 Matrix{Float64}:
 0.5    5.0   0.5  0.5
 0.5  500.0  50.0  0.5

Дискретизация

# NNlib.grid_sampleFunction

grid_sample(input::AbstractArray{T, 4}, grid::AbstractArray{T, 4}; padding_mode = :zeros)

При заданных входных данных (input) вычисляет вывод выборки входных (input) значения путем в местах расположения пикселей из сетки (grid). Использует билинейную интерполяцию для вычисления выходных значений.

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

Аргументы

  • input: входной массив в форме (W_in, H_in, C, N).

  • grid: входная сетка в форме (2, W_out, H_out, N). Где для каждой формы (W_out, H_out, N) сетка содержит координаты (x, y), которые определяют места выборки, нормализованные по форме input.

    Таким образом, x и y должны иметь значения в диапазоне [-1, 1]. Например, (x = -1, y = -1) — это левый верхний пиксель входного изображения (input), а (x = 1, y = 1) — правый нижний пиксель входного изображения (input).

    Выходящие за границы значения обрабатываются в соответствии с режимом padding_mode.

  • padding_mode: заполнение за границами. :zeros чтобы использовать 0 для мест, расположенных за пределами сетки. :border чтобы использовать граничные значения для мест, расположенных за пределами сетки. Значение по умолчанию — :zeros.

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

Сетка с выборкой (W_out, H_out, C, N) из входных данных (input).

Примеры

В приведенном ниже примере сетка содержит два выходящих за границы расположения выборки, которые обрабатываются по-разному в зависимости от padding_mode.

julia> x = reshape(collect(1.0:4.0), (2, 2, 1, 1))
2×2×1×1 Array{Float64, 4}:
[:, :, 1, 1] =
 1.0  3.0
 2.0  4.0

julia> grid = Array{Float64}(undef, 2, 3, 2, 1);

julia> grid[:, 1, 1, 1] .= (-3, -1);

julia> grid[:, 2, 1, 1] .= (0, -1);

julia> grid[:, 3, 1, 1] .= (1, -1);

julia> grid[:, 1, 2, 1] .= (-1, 1);

julia> grid[:, 2, 2, 1] .= (0, 1);

julia> grid[:, 3, 2, 1] .= (3, 1);

julia> grid_sample(x, grid; padding_mode=:zeros)
3×2×1×1 Array{Float64, 4}:
[:, :, 1, 1] =
 0.0  3.0
 1.5  3.5
 2.0  0.0

julia> grid_sample(x, grid; padding_mode=:border)
3×2×1×1 Array{Float64, 4}:
[:, :, 1, 1] =
 1.0  3.0
 1.5  3.5
 2.0  4.0

# NNlib.∇grid_sampleFunction

∇grid_sample(Δ::AbstractArray{T, 4}, input::AbstractArray{T, 4}, grid::AbstractArray{T, 4}; padding_mode = :zeros) where T

Аргументы

  • Δ: входной градиент в форме (W_out, H_out, C, N) (то же самое, что и результат первичного вычисления).

  • input: входные данные из первичного вычисления в форме (W_in, H_in, C, N).

  • grid: сетка из первичного вычисления в форме (2, W_out, H_out, N).

  • padding_mode: заполнение за границами. :zeros чтобы использовать 0 для мест, расположенных за пределами сетки. :border чтобы использовать граничные значения для мест, расположенных за пределами сетки. Должно быть таким же, как и при первичном вычислении. Значение по умолчанию — :zeros.

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

Градиенты dinput (той же формы, что и input) и dgrid (той же формы, что и grid).

Потери

# NNlib.ctc_lossFunction

ctc_loss(ŷ, y)

Вычисляет потери временной классификации на основе связей между и y. должны быть матрицами классов и времени, т. е. каждая строка представляет класс, а каждый столбец — временной шаг. Кроме того, к будет применена функция logsoftmax, поэтому должны быть необработанными значениями активации нейронной сети, а не, например, активациями, переданными через функцию активации softmax. y должен быть одномерным массивом меток, связанным с . Предполагается, что пустая метка является последней категорией меток в , поэтому она эквивалентна size(ŷ, 1). Используется для решения задач классификации последовательности в последовательность, таких как распознавание речи и распознавание рукописного ввода, где для решения задачи не требуется точное выравнивание по времени выходных данных (например, букв). См. Graves et al.https://www.cs.toronto.edu/graves/icml_2006.pdf[]https://www.cs.toronto.edu/graves/icml_2006.pdf[(2006)] или Graves (2012) с описанием математических подробностей.

Прочее

# NNlib.logsumexpFunction

logsumexp(x; dims = :)

Вычисляет log.(sum(exp.(x); dims)) численно устойчивым способом. Без ключевого слова dims возвращается скаляр.

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

# NNlib.gluFunction

glu(x, dim = 1)

Управляемый линейный блок из статьи Language Modeling with Gated Convolutional Networks.

Вычисляет a .* sigmoid(b), где x делится пополам в заданном измерении dim, образуя a и b.