Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
name = "IterationControl"
uuid = "b3c1a2ee-3fec-4384-bf48-272ea71de57c"
authors = ["Anthony D. Blaom <anthony.blaom@gmail.com>"]
version = "0.3.1"
version = "0.3.2"

[deps]
EarlyStopping = "792122b4-ca99-40de-a6bc-6742525f08b6"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[compat]
EarlyStopping = "0.1.6"
EarlyStopping = "0.1.8"
julia = "1"

[extras]
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Builds on
inspired by
[LearningStrategies.jl](https://github.com/JuliaML/LearningStrategies.jl).

Other related software:
[DynamicIterators.jl](https://github.com/mschauer/DynamicIterators.jl).

## Installation

Expand Down Expand Up @@ -156,7 +158,7 @@ The `IterationControl.train!` method can be given the keyword argument
## Controls provided

Controls are repeatedly applied in sequence until a control triggers a
stop. Each control type has a detailed doc-string. Below is a short
stop. Each control type has a detailed doc-string. sBelow is a short
summary, with some advanced options omitted.

control | description | enabled if these are overloaded | can trigger a stop | notation in Prechelt
Expand All @@ -170,9 +172,9 @@ control | description
`NumberLimit(n=100)` | Stop after `n` applications of the control |`train!` | yes |
`NumberSinceBest(n=6)` | Stop when best loss occurred `n` control applications ago |`train!` | yes |
`WithNumberDo(f=n->@info(n))` | Call `f(n + 1)` where `n` is number of previous applications of control |`train!` | yes |
`WithLossDo(f=x->@info(x))` | Call `f(loss)` where `loss` is the current loss |`train!`, `loss` | yes |
`WithLossDo(f=x->@info("loss: $x"))` | Call `f(loss)` where `loss` is the current loss |`train!`, `loss` | yes |
`WithTrainingLossesDo(f=v->@info(v))`| Call `f(v)` where `v` is the current batch of training losses |`train!`, `training_loss` | yes |
`NotANumber()` | Stop when `NaN` encountered |`train!`, `loss` | yes |
`InvalidValue()` | Stop when `NaN`, `Inf` or `-Inf` loss/training loss encountered |`train!` | yes |
`Threshold(value=0.0)` | Stop when `loss < value` |`train!`, `loss` | yes |
`GL(alpha=2.0)` | Stop after "Generalization Loss" exceeds `alpha` |`train!`, `loss` | yes | ``GL_α``
`Patience(n=5)` | Stop after `n` consecutive loss increases |`train!`, `loss` | yes | ``UP_s``
Expand Down
7 changes: 4 additions & 3 deletions test/api.jl
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
model = Particle()
invalid = InvalidValue()

@test_throws IC.ERR_TRAIN IterationControl.train!(model)
@test_throws IC.err_train(model) IterationControl.train!(model, 1)

# lifting train!:
IC.train!(model::Particle, n) = train!(model, n)

@test_throws(IC.err_getter(NotANumber(), :loss, model),
IC.train!(model, NotANumber(), NumberLimit(1)))
@test_throws(IC.err_getter(invalid, :loss, model),
IC.train!(model, invalid, NumberLimit(1)))

# lifting loss!:
IterationControl.loss(m::Particle) = loss(m)

IC.train!(model, NotANumber(), NumberLimit(1), verbosity=0)
IC.train!(model, invalid, NumberLimit(1), verbosity=0)

@test_throws(IC.err_getter(PQ(), :training_losses, model),
IC.train!(model, PQ(), NumberLimit(1)))
Expand Down
4 changes: 2 additions & 2 deletions test/composite_controls.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@testset "constructors, IC.flat" begin
@test IC.composite() == Never()
c1 = Patience(1)
c2 = NotANumber()
c2 = InvalidValue()
c3 = Step(1)

@test IC.composite(c1) == c1
Expand All @@ -19,7 +19,7 @@ end
@testset "behaviour" begin
a = Step(4)
b = NumberLimit(2)
c = NotANumber()
c = InvalidValue()
d = IC.composite(a, b, c)

# separated:
Expand Down
6 changes: 3 additions & 3 deletions test/stopping_controls.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
@testset "loss getters" begin
model=SquareRooter(4)
@test IterationControl.get_loss(NotANumber(), model) ==
@test IterationControl.get_loss(InvalidValue(), model) ==
IterationControl.loss(model)
@test_throws(IterationControl.err_getter(NotANumber(), :loss, :junk),
IterationControl.get_loss(NotANumber(), :junk))
@test_throws(IterationControl.err_getter(InvalidValue(), :loss, :junk),
IterationControl.get_loss(InvalidValue(), :junk))
IterationControl.train!(model, 2)
@test IterationControl.get_training_losses(PQ(), model) ==
IterationControl.training_losses(model)
Expand Down
8 changes: 4 additions & 4 deletions test/train.jl
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
@testset "basic integration" begin
m = SquareRooter(4)
report = IC.train!(m, Step(2), NotANumber(), NumberLimit(3); verbosity=0);
report = IC.train!(m, Step(2), InvalidValue(), NumberLimit(3); verbosity=0);
@test report[1] == (Step(2), NamedTuple())
@test report[2] == (NotANumber(), (done=false, log=""))
@test report[2] == (InvalidValue(), (done=false, log=""))
report[3] == (NumberLimit(3),
(done=true,
log="Stop triggered by NumberLimit(3) "*
"stopping criterion. "))

m = SquareRooter(4)
@test_logs((:info, r"Stop triggered by Num"),
IC.train!(m, Step(2), NotANumber(), NumberLimit(3)));
IC.train!(m, Step(2), InvalidValue(), NumberLimit(3)));
@test_logs((:info, r"Using these controls"),
(:info, r"Stepping model for 2 iterations"),
(:info, r"Stepping model for 2 iterations"),
(:info, r"Stepping model for 2 iterations"),
(:info, r"Stop triggered by NumberLimit"),
IC.train!(m, Step(2),
NotANumber(),
InvalidValue(),
NumberLimit(3);
verbosity=2));
end
Expand Down
8 changes: 4 additions & 4 deletions test/wrapped_controls.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
@testset "debug" begin
m = SquareRooter(4)
test_controls = [Step(2), NotANumber(), GL(), Callback(println)]
test_controls = [Step(2), InvalidValue(), GL(), Callback(println)]
_info = fill((:info, r""), 2*length(test_controls))

@test_logs(_info...,
for c in [Step(2), NotANumber(), GL(), Callback()]
for c in [Step(2), InvalidValue(), GL(), Callback()]
d = IC.debug(c)
state = IC.update!(c, m, 1)
@test state == IC.update!(d, m, 1)
Expand All @@ -17,11 +17,11 @@ end

@testset "skip" begin
m = SquareRooter(4)
test_controls = [Step(2), NotANumber(), GL(), Callback(println)]
test_controls = [Step(2), InvalidValue(), GL(), Callback(println)]
_info = fill((:info, r""), 2)

@test_logs(_info...,
for c in [Step(2), NotANumber(), GL(), Callback()]
for c in [Step(2), InvalidValue(), GL(), Callback()]
s = IC.skip(c, predicate=2)
@test !s.predicate(0)
@test s.predicate(1)
Expand Down