Skip to content

Interface tests #230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0c35d77
Make block learning methods more modular
lorenzoh Dec 20, 2021
8d978ad
Add WIP VAE notebook
lorenzoh Dec 20, 2021
922ec9e
Comment VAE notebook
lorenzoh Dec 20, 2021
fab7a2c
Make `fitonecycle!` more flexible
lorenzoh Dec 22, 2021
cbe85de
Improve Block printing
lorenzoh Dec 22, 2021
ecd0171
Replace `BlockMethod` with `SupervisedMethod`,
lorenzoh Dec 22, 2021
c8b5490
Update vae notebook
lorenzoh Dec 22, 2021
f2ef154
Add block invariant interface
lorenzoh Dec 23, 2021
f1432ee
implement block invariants for basic blocks
lorenzoh Dec 23, 2021
55b1814
more invariants
lorenzoh Jan 3, 2022
2054ce1
Make UNet closer to fastai
lorenzoh Jan 3, 2022
b7ec4a1
Merge branch 'master' into lorenzoh/better-unet
lorenzoh Jan 3, 2022
738a822
Merge branch 'lorenzoh/better-unet' into lorenzoh/invariants
lorenzoh Jan 3, 2022
2b3eae5
Forward `usedefaultcallbacks` kwarg through `methodlearner`
lorenzoh Jan 22, 2022
29a2082
Add title to `showencodedsample`
lorenzoh Jan 22, 2022
939e8b3
Switch LearnBase + MLDataPattern + DataLoaders -> MLUtils
lorenzoh May 29, 2022
1c7c5db
Remove unneeded dependencies
lorenzoh May 29, 2022
e60e5db
Merge branch 'master' into lorenzoh/invariants
lorenzoh May 30, 2022
d30a4ce
Update to newer Invariants
lorenzoh May 31, 2022
9e241f2
Merge branch 'lo/mlutils' into lorenzoh/invariants
lorenzoh May 31, 2022
8396180
Fix submodule import
lorenzoh May 31, 2022
c77707a
Merge branch 'lo/mlutils' into lorenzoh/invariants
lorenzoh May 31, 2022
8384eba
One more import
lorenzoh May 31, 2022
8956b31
Merge branch 'lo/mlutils' into lorenzoh/invariants
lorenzoh May 31, 2022
c77d1c7
Add data container invariant
lorenzoh May 31, 2022
e9d0c45
Add encoding interface invariant
lorenzoh Jun 2, 2022
fa1a30d
Merge branch 'master' into lorenzoh/invariants
lorenzoh Jul 3, 2022
96c3dd7
Updat einvariants and merge #240
lorenzoh Jul 5, 2022
cc49c41
Merge branch 'master' into lorenzoh/invariants
lorenzoh Dec 11, 2022
c155196
Merge branch 'master' into lorenzoh/invariants
lorenzoh Dec 11, 2022
a230a78
Fix invariants for FastVision.jl
lorenzoh Dec 13, 2022
c0eea08
Improve docstring
lorenzoh Dec 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions FastTabular/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ FastAI = "5d0beca9-ade8-49ae-ad0b-a3cf890e669f"
FilePathsBase = "48062228-2e41-5def-b9a4-89aafe57970f"
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
InlineTest = "bd334432-b1e7-49c7-a2dc-dd9149e4ebd6"
Invariants = "115d0255-0791-41d2-b533-80bc4cbe6c10"
InvariantsCore = "69cbffe8-09de-43b1-81db-93034495284f"
MLUtils = "f1d291b0-491e-4a28-83b9-f70985020b54"
PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
ShowCases = "605ecd9f-84a6-4c9e-81e2-4798472b76a3"
Expand Down
7 changes: 7 additions & 0 deletions FastTabular/src/encodings/tabularpreprocessing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ function EncodedTableRow(catcols, contcols, categorydict)
EncodedTableRow{length(catcols), length(contcols)}(catcols, contcols, categorydict)
end

function mockblock(block::EncodedTableRow)
b = TableRow(block.catcols, block.contcols, block.categorydict)
obs = mockblock(b)
enc = setup(TabularPreprocessing, b, TableDataset(map(x -> [x], obs)))
return encode(enc, Validation(), b, obs)
end

function checkblock(::EncodedTableRow{M, N}, x::Tuple{Vector, Vector}) where {M, N}
length(x[1]) == M && length(x[2]) == N
end
Expand Down
1 change: 1 addition & 0 deletions FastVision/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ImageIO = "82e4d734-157c-48bb-816b-45c225c6df19"
ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254"
IndirectArrays = "9b13fd28-a010-5f03-acff-a1bbcff69959"
InlineTest = "bd334432-b1e7-49c7-a2dc-dd9149e4ebd6"
Invariants = "115d0255-0791-41d2-b533-80bc4cbe6c10"
MLUtils = "f1d291b0-491e-4a28-83b9-f70985020b54"
MakieCore = "20f20a25-4f0e-4fdf-b5d1-57303727442b"
ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca"
Expand Down
2 changes: 2 additions & 0 deletions FastVision/src/FastVision.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,13 @@ import DataAugmentation: apply, Identity, ToEltype, ImageToTensor, Normalize,
AdjustBrightness, AdjustContrast, Maybe, FlipX, FlipY, WarpAffine,
Rotate, Zoom,
ResizePadDivisible, itemdata
import Invariants: Invariants, md, invariant, check
import ImageInTerminal
import IndirectArrays: IndirectArray
import MakieCore
import MakieCore: @recipe
import MakieCore.Observables: @map

import ProgressMeter: Progress, next!
import StaticArrays: SVector
import Statistics: mean, std
Expand Down
24 changes: 21 additions & 3 deletions FastVision/src/blocks/bounded.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ will update the bounds:
block = Image{2}()
Bounded(Bounded(block, (16, 16)), (8, 8)) == Bounded(block, (8, 8))
```



"""
struct Bounded{N, B <: AbstractBlock} <: WrapperBlock
block::B
Expand All @@ -67,6 +64,18 @@ function checkblock(bounded::Bounded{N}, a::AbstractArray{N}) where {N}
return checksize(bounded.size, size(a)) && checkblock(parent(bounded), a)
end


function FastAI.invariant_checkblock(block::Bounded{N}; blockvar = "block", obsvar = "obs", kwargs...) where N
return invariant(
FastAI.__inv_checkblock_title(block, blockvar, obsvar),
[
FastAI.invariant_checkblock(parent(block)),
];
kwargs...
)
end


@testset "Bounded [block, wrapper]" begin
@test_nowarn Bounded(Image{2}(), (16, 16))
bounded = Bounded(Image{2}(), (16, 16))
Expand All @@ -75,3 +84,12 @@ end
# composition
@test Bounded(bounded, (16, 16)) == bounded
end

@testset "checksize" begin
@test checksize((10, 1), (10, 1))
@test !checksize((100, 1), (10, 1))
@test checksize((:, :, :), (1, 2, 3))
@test !checksize((:, :, :), (1, 2))
@test checksize((10, :, 1), (10, 20, 1))
@test !checksize((10, :, 2), (10, 20, 1))
end
61 changes: 60 additions & 1 deletion FastVision/src/blocks/image.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,65 @@ setup(::Type{Image}, data) = Image{ndims(getobs(data, 1))}()

# Visualization

function showblock!(io, ::ShowText, block::Image{2}, obs)
showblock!(io, ::ShowText, block::Image{2}, obs::AbstractMatrix{<:Colorant}) =
ImageInTerminal.imshow(io, obs)
showblock!(io, ::ShowText, block::Image{2}, obs::AbstractMatrix{<:Real}) =
ImageInTerminal.imshow(io, colorview(Gray, obs))


function FastAI.invariant_checkblock(block::Image{N}; blockvar = "block", obsvar = "obs", kwargs...) where N
return invariant(
FastAI.__inv_checkblock_title(block, blockvar, obsvar),
[
invariant("`$obsvar` is an `AbstractArray`",
description = md("`$obsvar` should be of type `AbstractArray`.")) do obs
if !(obs isa AbstractArray)
return "Instead, got invalid type `$(nameof(typeof(obs)))`." |> md
end
end,
invariant("`$obsvar` is `$N`-dimensional") do obs
if ndims(obs) != N
return "Instead, got invalid dimensionality `$N`." |> md
end
end,
invariant("`$obsvar` should have a color or numerical element type") do obs
if !((eltype(obs) <: Color) ||(eltype(obs) <: Real))
return "Instead, got invalid element type `$(eltype(obs))`." |> md
end
end,
];
kwargs...
)
end

#=

function isblockinvariant(block::Image{N}; obsvar = "data", blockvar = "block") where {N}
return SequenceInvariant(
[
BooleanInvariant(
obs -> obs isa AbstractArray,
name = "Image data is an array",
messagefn = obs -> """Expected `$obsvar` to be a subtype of
`AbstractArray`, but instead got type `$(typeof(obs))`.""",
),
BooleanInvariant(
obs -> ndims(obs) == N,
name = "Image data is `$N`-dimensional",
messagefn = obs -> """Expected `$obsvar` to be an `$N`-dimensional array,
but instead got a `$(ndims(obs))`-dimensional array.""",
),
BooleanInvariant(
obs -> eltype(obs) <: Color || eltype(obs) <: Number,
name = "Image data has a color or numerical type.",
messagefn = obs -> """Expected `$obsvar` to have an element type that is a
color (`eltype($obsvar) <: Color`) or a number (`eltype($obsvar)
<: Color`), but instead found `eltype($obsvar) == $(eltype(obs)).`
"""
)
],
"`$obsvar` is a valid `$(typeof(block))`",
""
)
end
=#
35 changes: 33 additions & 2 deletions FastVision/src/blocks/mask.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,41 @@ function checkblock(block::Mask{N, T}, a::AbstractArray{T, N}) where {N, T}
return all(map(x -> x ∈ block.classes, a))
end

function mockblock(mask::Mask{N, T}) where {N, T}
rand(mask.classes, ntuple(_ -> 16, N))::AbstractArray{T, N}
mockblock(mask::Mask{N, T}) where {N, T} = rand(mask.classes, ntuple(_ -> 16, N))::AbstractArray{T, N}

function FastAI.invariant_checkblock(block::Mask{N}; blockvar = "block", obsvar = "obs", kwargs...) where N
return invariant(
FastAI.__inv_checkblock_title(block, blockvar, obsvar),
[
invariant("`$obsvar` is an `AbstractArray`",
description = md("`$obsvar` should be of type `AbstractArray`.")) do obs
if !(obs isa AbstractArray)
return "Instead, got invalid type `$(nameof(typeof(obs)))`." |> md
end
end,
invariant("`$obsvar` is `$N`-dimensional") do obs
if ndims(obs) != N
return "Instead, got invalid dimensionality `$N`." |> md
end
end,
invariant("All elements are valid labels") do obs
valid = ∈(block.classes).(obs)
if !(all(valid))
unknown = unique(obs[valid .== false])
return md("""`$obsvar` should contain only valid labels,
i.e. `∀ y ∈ $obsvar: y ∈ $blockvar.classes`, but `$obsvar` includes
unknown labels: `$(sprint(show, unknown))`.

Valid classes are:
`$(sprint(show, block.classes, context=:limit => true))`""")
end
end,
];
kwargs...
)
end


# Visualization

function showblock!(io, ::ShowText, block::Mask{2}, obs)
Expand Down
14 changes: 13 additions & 1 deletion FastVision/src/encodings/imagepreprocessing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,19 @@ function checkblock(block::ImageTensor{N}, a::AbstractArray{T, M}) where {M, N,
return (N + 1 == M) && (size(a, M) == block.nchannels)
end

Base.summary(io::IO, ::ImageTensor{N}) where {N} = print(io, "ImageTensor{$N}")
FastAI.blockname(io::IO, ::ImageTensor{N}) where {N} = "ImageTensor{$N}"

function FastAI.mockblock(block::ImageTensor{N}) where {N}
return randn(Float32, ntuple(n -> n == N+1 ? block.nchannels : 16, N + 1))
end

function FastAI.invariant_checkblock(block::ImageTensor{N}; blockvar = "block", obsvar = "obs", kwargs...) where N
return invariant(
Invariants.hastype_invariant(AbstractArray{<:Number, N+1}),
title = FastAI.__inv_checkblock_title(block, blockvar, obsvar);
kwargs...
)
end

"""
ImagePreprocessing([; kwargs...]) <: Encoding
Expand Down
10 changes: 7 additions & 3 deletions FastVision/src/encodings/keypointpreprocessing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ struct KeypointTensor{N, T, M} <: Block
sz::NTuple{M, Int}
end

mockblock(block::KeypointTensor{N}) where {N} = rand(SVector{N, Float32}, block.sz)
function checkblock(block::KeypointTensor{N, T}, obs::AbstractArray{T}) where {N, T}
return length(obs) == (prod(block.sz) * N)
function mockblock(block::KeypointTensor{N}) where {N}
enc = KeypointPreprocessing((16, 16))
b = Keypoints{N}(block.sz)
return encode(enc, Validation(), b, mockblock(b))
end
function checkblock(block::KeypointTensor{N, T, M}, obs::AbstractArray{U, M}) where {N, T, U, M}
return length(obs) == prod(block.sz) * N
end

"""
Expand Down
2 changes: 2 additions & 0 deletions FastVision/src/tasks/segmentation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ _tasks["imagesegmentation"] = (id = "vision/imagesegmentation",
@testset "taskdataloaders" begin
data, blocks = load(datarecipes()["camvid_tiny"])
traindl, _ = taskdataloaders(data, ImageSegmentation(blocks))
# Iterate once so that precompilation does not print when testing
for batch in traindl end
@test_nowarn for batch in traindl
end
end
Expand Down
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ FilePathsBase = "48062228-2e41-5def-b9a4-89aafe57970f"
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
FluxTraining = "7bf95e4d-ca32-48da-9824-f0dc5310474f"
InlineTest = "bd334432-b1e7-49c7-a2dc-dd9149e4ebd6"
Invariants = "115d0255-0791-41d2-b533-80bc4cbe6c10"
InvariantsCore = "69cbffe8-09de-43b1-81db-93034495284f"
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
MLDatasets = "eb30cadb-4394-5ae3-aed4-317e484a6458"
MLUtils = "f1d291b0-491e-4a28-83b9-f70985020b54"
Expand Down
5 changes: 4 additions & 1 deletion src/FastAI.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ using Flux.Optimise
import Flux.Optimise: apply!, Optimiser, WeightDecay
using FluxTraining: Learner, handle
using FluxTraining.Events
import Invariants: Invariants, invariant, check, check_throw, md
using JLD2: jldsave, jldopen
using Markdown
using PrettyTables
Expand Down Expand Up @@ -79,11 +80,13 @@ include("serialization.jl")

# submodules
include("datasets/Datasets.jl")
@reexport using .Datasets
using .Datasets

include("Registries/Registries.jl")
@reexport using .Registries

include("invariants.jl")

export
# submodules
Datasets,
Expand Down
32 changes: 32 additions & 0 deletions src/blocks/continuous.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,35 @@ function blocklossfn(outblock::Continuous, yblock::Continuous)
outblock.size == yblock.size || error("Sizes of $outblock and $yblock differ!")
return Flux.Losses.mse
end


function invariant_checkblock(block::Continuous; blockvar = "block", obsvar = "obs", kwargs...)
return invariant(
__inv_checkblock_title(block, blockvar, obsvar),
[
Invariants.hastype_invariant(AbstractVector; var = obsvar),
invariant("length(`$obsvar`) should be $(block.size)") do obs
if !(length(obs) == block.size)
return """`$obsvar` should have `$(block.size)` features, instead
found a vector with `$(length(obs))` features.""" |> md
end
end,
Invariants.hastype_invariant(
Number,
title = "`eltype($obsvar)` should be a subtype of number",
inputfn = eltype,
),
];
kwargs...
)
end


@testset "Continuous [block]" begin
inv = invariant_checkblock(Continuous(5))

@test check(Bool, inv, zeros(5))
@test !check(Bool, inv, "hi")
@test !check(Bool, inv, ["hi"])
@test !check(Bool, inv, [5])
end
Loading