Skip to content

Commit 9803f64

Browse files
committed
polish
1 parent fb28df8 commit 9803f64

File tree

10 files changed

+240
-157
lines changed

10 files changed

+240
-157
lines changed

README.md

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
# LearnAPI.jl
22

3-
A Julia interface for training and applying machine learning models.
3+
A Julia interface for training and applying machine learning models.
44

5-
**Status:** Proposal.
65

6+
**Devlopement Status:**
77

8-
🚧
8+
- [X] Detailed proposal stage ([this
9+
documentation](https://juliaai.github.io/LearnAPI.jl/dev/))
10+
- [ ] Initial feedback stage (opened mid-January, 2023)
11+
- [ ] Implement feedback and finish "To do" list (below)
12+
- [ ] Proof of concept implementation
13+
- [ ] Polish
14+
- [ ] Registration
915

16+
To do:
1017

11-
[![Build Status](https://github.com/JuliaAI/LearnAPI.jl/workflows/CI/badge.svg)](https://github.com/JuliaAI/LearnAPI.jl/actions)
12-
[![Coverage](https://codecov.io/gh/JuliaAI/LearnAPI.jl/branch/master/graph/badge.svg)](https://codecov.io/github/JuliaAI/LearnAPI.jl?branch=master)
18+
- [ ] Add methods to create/save persistent representation of learned parameters
19+
- [ ] Add more repo tests
20+
- [ ] Add methods to test an implementation
21+
- [ ] Add user guide ("Common Implementation Patterns" section of manual)
22+
23+
[![Build Status](https://github.com/JuliaAI/LearnAPI.jl/workflows/CI/badge.svg)](https://github.com/JuliaAI/LearnAPI.jl/actions)
24+
[![Coverage](https://codecov.io/gh/JuliaAI/LearnAPI.jl/branch/master/graph/badge.svg)](https://codecov.io/github/JuliaAI/LearnAPI.jl?branch=master)
1325
[![Docs](https://img.shields.io/badge/docs-dev-blue.svg)](https://juliaai.github.io/LearnAPI.jl/dev/)
1426

15-
Please refer to the documentation for a detailed preview of what this package proposes to
16-
offer.

docs/src/fit_update_and_ingest.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# Fit, update! and ingest!
22

3-
> **Summary.** Models that learn, i.e., generalize to new data, must overload `fit`;
4-
> the fallback performs no operation and returns all `nothing`. Implement `update!` if
5-
> certain hyper-parameter changes do not necessitate retraining from scratch (e.g.,
6-
> increasing iteration parameters). Implement `ingest!` to implement incremental learning.
3+
> **Summary.** Models that learn, i.e., generalize to new data, must overload `fit`; the
4+
> fallback performs no operation and returns all `nothing`. Implement `update!` if certain
5+
> hyper-parameter changes do not necessitate retraining from scratch (e.g., increasing an
6+
> iteration parameter). Implement `ingest!` to implement incremental learning. All
7+
> training methods implemented must be named in the return value of the
8+
> `functions` trait.
79
810
| method | fallback | compulsory? | requires |
911
|:---------------------------|:---------------------------------------------------|-------------|-------------------|

docs/src/model_traits.md

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
# Model Traits
22

3-
Traits are often called on instances but are frequently *defined* on model *types*, as in
3+
> **Summary.** Traits allow one to promise particular behaviour for a model, such as:
4+
> *This model supports per-observation weights, which must appear as the third argument of
5+
> `fit`*, or *This model predicts probability distributions for the target*, or *This
6+
> model's `transform` method predicts `Real` vectors*.
7+
8+
Traits are optional, except where required by the implementation of some LearnAPI method
9+
and documented in that method's docstring. Unless a model has no functionality whatsoever,
10+
[`LearnAPI.functions`](@ref)`(model)` will need to be overloaded.
11+
12+
Traits are often called on instances but are frequently *defined* on model *types*, as in
413

514
```julia
615
LearnAPI.is_pure_julia(::Type{<:MyModelType}) = true
@@ -19,11 +28,13 @@ t(model) = t(typeof(model))
1928
```
2029

2130
This means `LearnAPI.is_pure_julia(model) = true` whenever `model isa MyModelType` in the
22-
above example.
31+
above example.
32+
33+
Every trait has a global fallback implementation for `::Type`.
2334

24-
Traits that vary from instance to instance of the same type are discouraged, except in
25-
the case of composite models (`is_wrapper(model) = true`) where this is unavoidable. One
26-
reason for this so one can associate with each model type a unique set of trait-based
35+
Traits that vary from instance to instance of the same type are discouraged, except in the
36+
case of composite models (`is_wrapper(model) = true`) where this is unavoidable. One
37+
reason for this is so one can associate with each model type a unique set of trait-based
2738
"model metadata" for inclusion in searchable model databases. This requirement
2839
occasionally requires that an existing model implementation be split into several separate
2940
LearnAPI implementations (e.g., one for regression and another for classification).
@@ -33,46 +44,46 @@ traits are not.
3344

3445
## Ordinary traits
3546

36-
In the examples column of the table below, `Table` and `Continuous` are names owned by the
47+
In the examples column of the table below, `Table`, `Continuous`, `Sampleable` are names owned by the
3748
package [ScientificTypesBase.jl](https://github.com/JuliaAI/ScientificTypesBase.jl/).
3849

3950
| trait | fallback value | return value | example |
4051
|:-------------------------------------------------|:----------------------|:--------------|:--------|
4152
| [`LearnAPI.functions`](@ref)`(model)` | `()` | implemented LearnAPI functions (traits excluded) | `(:fit, :predict)` |
42-
| [`LearnAPI.predict_proxy`](@ref)`(model)` | `NamedTuple()` | form of target proxy output by `predict` | `LearnAPI.Distribution()` |
43-
| [`LearnAPI.predict_joint_proxy`](@ref)`(model)` | `NamedTuple()` | form of target proxy output by `predict_joint` | `LearnAPI.Distribution()` |
53+
| [`LearnAPI.predict_proxy`](@ref)`(model)` | `LearnAPI.None()` | form of target proxy output by `predict` | `LearnAPI.Distribution()` |
54+
| [`LearnAPI.predict_joint_proxy`](@ref)`(model)` | `LearnAPI.None()` | form of target proxy output by `predict_joint` | `LearnAPI.Distribution()` |
4455
| [`LearnAPI.position_of_target`](@ref)`(model)` | `0` | † the positional index of the **target** in `data` in `fit(..., data...; metadata)` calls | 2 |
4556
| [`LearnAPI.position_of_weights`](@ref)`(model)` | `0` | † the positional index of **per-observation weights** in `data` in `fit(..., data...; metadata)` | 3 |
4657
| [`LearnAPI.descriptors`](@ref)`(model)` | `()` | lists one or more suggestive model descriptors from `LearnAPI.descriptors()` | (:classifier, :probabilistic) |
4758
| [`LearnAPI.is_pure_julia`](@ref)`(model)` | `false` | is `true` if implementation is 100% Julia code | `true` |
4859
| [`LearnAPI.pkg_name`](@ref)`(model)` | `"unknown"` | name of package providing core algorithm (may be different from package providing LearnAPI.jl implementation) | `"DecisionTree"` |
4960
| [`LearnAPI.pkg_license`](@ref)`(model)` | `"unknown"` | name of license of package providing core algorithm | `"MIT"` |
5061
| [`LearnAPI.doc_url`](@ref)`(model)` | `"unknown"` | url providing documentation of the core algorithm | `"https://en.wikipedia.org/wiki/Decision_tree_learning"` |
51-
| [`LearnAPI.load_path`](@ref)`(model)` | `"unknown"` | a string indicating where the struct `typeof(model)` is defined, beginning with name of package providing implementation | `FastTrees.LearnAPI.DecisionTreeClassifier` |
52-
| [`LearnAPI.is_wrapper`](@ref)`(model)` | `false` | is `true` if one or more properties (fields) are themselves models | `true` |
62+
| [`LearnAPI.load_path`](@ref)`(model)` | `"unknown"` | a string indicating where the struct for `typeof(model)` is defined, beginning with name of package providing implementation | `FastTrees.LearnAPI.DecisionTreeClassifier` |
63+
| [`LearnAPI.is_wrapper`](@ref)`(model)` | `false` | is `true` if one or more properties (fields) of `model` may be a model | `true` |
5364
| [`LearnAPI.human_name`](@ref)`(model)` | type name with spaces | human name for the model; should be a noun | "elastic net regressor" |
54-
| [`LearnAPI.iteration_parameter`](@ref)`(model)` | nothing | symbolic name of an iteration parameter | :epochs |
55-
| [`LearnAPI.fit_keywords`](@ref)`(model)` | `()` | tuple of symbols for keyword arguments accepted by `fit` (metadata) | `(:class_weights,)` |
65+
| [`LearnAPI.iteration_parameter`](@ref)`(model)` | `nothing` | symbolic name of an iteration parameter | :epochs |
66+
| [`LearnAPI.fit_keywords`](@ref)`(model)` | `()` | tuple of symbols for keyword arguments accepted by `fit` (corresponding to metadata) | `(:class_weights,)` |
5667
| [`LearnAPI.fit_scitype`](@ref)`(model)` | `Union{}` | upper bound on `scitype(data)` in `fit(model, verbosity, data...)`†† | `Tuple{Table(Continuous), AbstractVector{Continuous}}` |
68+
| [`LearnAPI.fit_observation_scitype`](@ref)`(model)` | `Union{}`| upper bound on `scitype(observation)` for `observation` in `data` and `data` in `fit(model, verbosity, data...)`†† | `Tuple{AbstractVector{Continuous}, Continuous}` |
5769
| [`LearnAPI.fit_type`](@ref)`(model)` | `Union{}` | upper bound on `type(data)` in `fit(model, verbosity, data...)`†† | `Tuple{AbstractMatrix{<:Real}, AbstractVector{<:Real}}` |
58-
| [`LearnAPI.fit_observation_scitype`](@ref)`(model)` | `Union{}`| upper bound on `scitype(data)` in `fit(model, verbosity, data...)`†† | `Tuple{AbstractVector{Continuous}, Continuous}` |
59-
| [`LearnAPI.fit_observation_type`](@ref)`(model)` | `Union{}`| upper bound on `type(data)` in `fit(model, verbosity, data...)`* | `Tuple{AbstractVector{<:Real}, Real}` |
60-
| [`LearnAPI.predict_input_scitype`](@ref)`(model)` | `Union{}` | upper bound on `scitype(data)` in `predict(model, fitted_params, data...)`†† | `Tuple{AbstractVector{Continuous}}` |
70+
| [`LearnAPI.fit_observation_type`](@ref)`(model)` | `Union{}`| upper bound on `type(observation)` for `observation` in `data` and `data` in `fit(model, verbosity, data...)`* | `Tuple{AbstractVector{<:Real}, Real}` |
71+
| [`LearnAPI.predict_input_scitype`](@ref)`(model)` | `Union{}` | upper bound on `scitype(data)` in `predict(model, fitted_params, data...)`†† | `Table(Continuous)` |
6172
| [`LearnAPI.predict_output_scitype`](@ref)`(model)` | `Any` | upper bound on `scitype(first(predict(model, ...)))` | `AbstractVector{Continuous}` |
62-
| [`LearnAPI.predict_input_type`](@ref)`(model)` | `Union{}` | upper bound on `typeof(data)` in `predict(model, fitted_params, data...)`†† | `Tuple{AbstractVector{<:Real}}` |
73+
| [`LearnAPI.predict_input_type`](@ref)`(model)` | `Union{}` | upper bound on `typeof(data)` in `predict(model, fitted_params, data...)`†† | `AbstractMatrix{<:Real}` |
6374
| [`LearnAPI.predict_output_type`](@ref)`(model)` | `Any` | upper bound on `typeof(first(predict(model, ...)))` | `AbstractVector{<:Real}` |
64-
| [`LearnAPI.predict_joint_input_scitype`](@ref)`(model)` | `Union{}` | upper bound on `scitype(data)` in `predict_joint(model, fitted_params, data...)`†† | `Tuple{AbstractVector{Continuous}}` |
65-
| [`LearnAPI.predict_joint_output_scitype`](@ref)`(model)` | `Any` | upper bound on `scitype(first(predict_joint(model, ...)))` | `AbstractVector{Continuous}` |
66-
| [`LearnAPI.predict_joint_input_type`](@ref)`(model)` | `Union{}` | upper bound on `typeof(data)` in `predict_joint(model, fitted_params, data...)`†† | `Tuple{AbstractVector{<:Real}}` |
67-
| [`LearnAPI.predict_joint_output_type`](@ref)`(model)` | `Any` | upper bound on `typeof(first(predict_joint(model, ...)))` | `AbstractVector{<:Real}` |
68-
| [`LearnAPI.transform_input_scitype`](@ref)`(model)` | `Union{}` | upper bound on `scitype(data)` in `transform(model, fitted_params, data...)`†† | `Tuple{AbstractVector{Continuous}}` |
69-
| [`LearnAPI.transform_output_scitype`](@ref)`(model)` | `Any` | upper bound on `scitype(first(transform(model, ...)))` | `AbstractVector{Continuous}` |
70-
| [`LearnAPI.transform_input_type`](@ref)`(model)` | `Union{}` | upper bound on `typeof(data)` in `transform(model, fitted_params, data...)`†† | `Tuple{AbstractVector{<:Real}}` |
71-
| [`LearnAPI.transform_output_type`](@ref)`(model)` | `Any` | upper bound on `typeof(first(transform(model, ...)))` | `AbstractVector{<:Real}` |
72-
| [`LearnAPI.inverse_transform_input_scitype`](@ref)`(model)` | `Union{}` | upper bound on `scitype(data)` in `inverse_transform(model, fitted_params, data...)`†† | `Tuple{AbstractVector{Continuous}}` |
73-
| [`LearnAPI.inverse_transform_output_scitype`](@ref)`(model)` | `Any` | upper bound on `scitype(first(inverse_transform(model, ...)))` | `AbstractVector{Continuous}` |
74-
| [`LearnAPI.inverse_transform_input_type`](@ref)`(model)` | `Union{}` | upper bound on `typeof(data)` in `inverse_transform(model, fitted_params, data...)`†† | `Tuple{AbstractVector{<:Real}}` |
75-
| [`LearnAPI.inverse_transform_output_type`](@ref)`(model)` | `Any` | upper bound on `typeof(first(inverse_transform(model, ...)))` | `AbstractVector{<:Real}` |
75+
| [`LearnAPI.predict_joint_input_scitype`](@ref)`(model)` | `Union{}` | upper bound on `scitype(data)` in `predict_joint(model, fitted_params, data...)`†† |`Table(Continuous)` |
76+
| [`LearnAPI.predict_joint_output_scitype`](@ref)`(model)` | `Any` | upper bound on `scitype(first(predict_joint(model, ...)))` | `Sampleable{<:AbstractVector{Continuous}}` |
77+
| [`LearnAPI.predict_joint_input_type`](@ref)`(model)` | `Union{}` | upper bound on `typeof(data)` in `predict_joint(model, fitted_params, data...)`†† | `AbstractMatrix{<:Real}` |
78+
| [`LearnAPI.predict_joint_output_type`](@ref)`(model)` | `Any` | upper bound on `typeof(first(predict_joint(model, ...)))` | `Distributions.Sampleable{Distributions.Multivariate,Distributions.Continuous}` |
79+
| [`LearnAPI.transform_input_scitype`](@ref)`(model)` | `Union{}` | upper bound on `scitype(data)` in `transform(model, fitted_params, data...)`†† | `Table(Continuous)` |
80+
| [`LearnAPI.transform_output_scitype`](@ref)`(model)` | `Any` | upper bound on `scitype(first(transform(model, ...)))` | `Table(Continuous)` |
81+
| [`LearnAPI.transform_input_type`](@ref)`(model)` | `Union{}` | upper bound on `typeof(data)` in `transform(model, fitted_params, data...)`†† | `AbstractMatrix{<:Real}}` |
82+
| [`LearnAPI.transform_output_type`](@ref)`(model)` | `Any` | upper bound on `typeof(first(transform(model, ...)))` | `AbstractMatrix{<:Real}` |
83+
| [`LearnAPI.inverse_transform_input_scitype`](@ref)`(model)` | `Union{}` | upper bound on `scitype(data)` in `inverse_transform(model, fitted_params, data...)`†† | `Table(Continuous)` |
84+
| [`LearnAPI.inverse_transform_output_scitype`](@ref)`(model)` | `Any` | upper bound on `scitype(first(inverse_transform(model, ...)))` | `Table(Continuous)` |
85+
| [`LearnAPI.inverse_transform_input_type`](@ref)`(model)` | `Union{}` | upper bound on `typeof(data)` in `inverse_transform(model, fitted_params, data...)`†† | `AbstractMatrix{<:Real}` |
86+
| [`LearnAPI.inverse_transform_output_type`](@ref)`(model)` | `Any` | upper bound on `typeof(first(inverse_transform(model, ...)))` | `AbstractMatrix{<:Real}` |
7687

7788

7889
† If the value is `0`, then the variable in boldface type is not supported and not

0 commit comments

Comments
 (0)