One-Hot Encoding
It's common to encode categorical variables (like true
, false
or cat
, dog
) in "one-of-k" or "one-hot" form. Flux provides the onehot
function to make this easy.
julia> using Flux: onehot, onecold
julia> onehot(:b, [:a, :b, :c])
3-element OneHotVector(::UInt32) with eltype Bool:
⋅
1
⋅
julia> onehot(:c, [:a, :b, :c])
3-element OneHotVector(::UInt32) with eltype Bool:
⋅
⋅
1
The inverse is onecold
(which can take a general probability distribution, as well as just booleans).
julia> onecold(ans, [:a, :b, :c])
:c
julia> onecold([true, false, false], [:a, :b, :c])
:a
julia> onecold([0.3, 0.2, 0.5], [:a, :b, :c])
:c
For multiple samples at once, onehotbatch
creates a batch (matrix) of one-hot vectors, and onecold
treats matrices as batches.
julia> using Flux: onehotbatch
julia> onehotbatch([:b, :a, :b], [:a, :b, :c])
3×3 OneHotMatrix(::Vector{UInt32}) with eltype Bool:
⋅ 1 ⋅
1 ⋅ 1
⋅ ⋅ ⋅
julia> onecold(ans, [:a, :b, :c])
3-element Vector{Symbol}:
:b
:a
:b
Note that these operations returned OneHotVector
and OneHotMatrix
rather than Array
s. OneHotVector
s behave like normal vectors but avoid any unnecessary cost compared to using an integer index directly. For example, multiplying a matrix with a one-hot vector simply slices out the relevant row of the matrix under the hood.
Flux.onehot
— Functiononehot(x, labels, [default])
Return a OneHotVector
which is roughly a sparse representation of x .== labels
.
Instead of storing say Vector{Bool}
, it stores the index of the first occurrence of x
in labels
. If x
is not found in labels, then it either returns onehot(default, labels)
, or gives an error if no default is given.
See also onehotbatch
to apply this to many x
s, and onecold
to reverse either of these, as well as to generalise argmax
.
Examples
julia> β = Flux.onehot(:b, [:a, :b, :c])
3-element OneHotVector(::UInt32) with eltype Bool:
⋅
1
⋅
julia> αβγ = (Flux.onehot(0, 0:2), β, Flux.onehot(:z, [:a, :b, :c], :c)) # uses default
(Bool[1, 0, 0], Bool[0, 1, 0], Bool[0, 0, 1])
julia> hcat(αβγ...) # preserves sparsity
3×3 OneHotMatrix(::Vector{UInt32}) with eltype Bool:
1 ⋅ ⋅
⋅ 1 ⋅
⋅ ⋅ 1
Flux.onecold
— Functiononecold(y::AbstractArray, labels = 1:size(y,1))
Roughly the inverse operation of onehot
or onehotbatch
: This finds the index of the largest element of y
, or each column of y
, and looks them up in labels
.
If labels
are not specified, the default is integers 1:size(y,1)
– the same operation as argmax(y, dims=1)
but sometimes a different return type.
Examples
julia> Flux.onecold([false, true, false])
2
julia> Flux.onecold([0.3, 0.2, 0.5], [:a, :b, :c])
:c
julia> Flux.onecold([ 1 0 0 1 0 1 0 1 0 0 1
0 1 0 0 0 0 0 0 1 0 0
0 0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0 0
0 0 1 0 0 0 0 0 0 1 0 ], 'a':'e') |> String
"abeacadabea"
Flux.onehotbatch
— Functiononehotbatch(xs, labels, [default])
Returns a OneHotMatrix
where k
th column of the matrix is onehot(xs[k], labels)
. This is a sparse matrix, which stores just a Vector{UInt32}
containing the indices of the nonzero elements.
If one of the inputs in xs
is not found in labels
, that column is onehot(default, labels)
if default
is given, else an error.
If xs
has more dimensions, M = ndims(xs) > 1
, then the result is an AbstractArray{Bool, M+1}
which is one-hot along the first dimension, i.e. result[:, k...] == onehot(xs[k...], labels)
.
Examples
julia> oh = Flux.onehotbatch(collect("abracadabra"), 'a':'e', 'e')
5×11 OneHotMatrix(::Vector{UInt32}) with eltype Bool:
1 ⋅ ⋅ 1 ⋅ 1 ⋅ 1 ⋅ ⋅ 1
⋅ 1 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1 ⋅ ⋅
⋅ ⋅ ⋅ ⋅ 1 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1 ⋅ ⋅ ⋅ ⋅
⋅ ⋅ 1 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1 ⋅
julia> reshape(1:15, 3, 5) * oh # this matrix multiplication is done efficiently
3×11 Matrix{Int64}:
1 4 13 1 7 1 10 1 4 13 1
2 5 14 2 8 2 11 2 5 14 2
3 6 15 3 9 3 12 3 6 15 3