From 313fa0971ec86735c57987cf5e50e5bfc27f3a0f Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Mon, 3 Jun 2024 05:46:45 +0000 Subject: [PATCH] build based on 9534622 --- dev/.documenter-siteinfo.json | 2 +- dev/convenience_methods/index.html | 12 ++++----- dev/document_strings/index.html | 2 +- dev/feature_importances/index.html | 2 +- dev/fitting_distributions/index.html | 2 +- dev/form_of_data/index.html | 2 +- dev/how_to_register/index.html | 2 +- dev/implementing_a_data_front_end/index.html | 2 +- dev/index.html | 2 +- dev/iterative_models/index.html | 2 +- dev/objects.inv | Bin 2561 -> 2562 bytes dev/outlier_detection_models/index.html | 2 +- dev/quick_start_guide/index.html | 2 +- dev/reference/index.html | 24 +++++++++--------- dev/serialization/index.html | 2 +- dev/static_models/index.html | 2 +- dev/summary_of_methods/index.html | 2 +- dev/supervised_models/index.html | 2 +- .../index.html | 2 +- dev/the_fit_method/index.html | 2 +- dev/the_fitted_params_method/index.html | 2 +- dev/the_model_type_hierarchy/index.html | 2 +- dev/the_predict_joint_method/index.html | 2 +- dev/the_predict_method/index.html | 2 +- dev/training_losses/index.html | 2 +- dev/trait_declarations/index.html | 4 +-- dev/type_declarations/index.html | 2 +- dev/unsupervised_models/index.html | 2 +- dev/where_to_put_code/index.html | 2 +- 29 files changed, 45 insertions(+), 45 deletions(-) diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index e218a05..a03bbc9 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.3","generation_timestamp":"2024-05-09T04:07:06","documenter_version":"1.4.1"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.3","generation_timestamp":"2024-06-03T05:46:41","documenter_version":"1.4.1"}} \ No newline at end of file diff --git a/dev/convenience_methods/index.html b/dev/convenience_methods/index.html index 7cc69a7..8344db0 100644 --- a/dev/convenience_methods/index.html +++ b/dev/convenience_methods/index.html @@ -1,5 +1,5 @@ -Convenience methods · MLJModelInterface

Convenience methods

MLJModelInterface.tableFunction
table(columntable; prototype=nothing)

Convert a named tuple of vectors or tuples columntable, into a table of the "preferred sink type" of prototype. This is often the type of prototype itself, when prototype is a sink; see the Tables.jl documentation. If prototype is not specified, then a named tuple of vectors is returned.

table(A::AbstractMatrix; names=nothing, prototype=nothing)

Wrap an abstract matrix A as a Tables.jl compatible table with the specified column names (a tuple of symbols). If names are not specified, names=(:x1, :x2, ..., :xn) is used, where n=size(A, 2).

If a prototype is specified, then the matrix is materialized as a table of the preferred sink type of prototype, rather than wrapped. Note that if prototype is not specified, then matrix(table(A)) is essentially a no-op.

source
MLJModelInterface.matrixFunction
matrix(X; transpose=false)

If X isa AbstractMatrix, return X or permutedims(X) if transpose=true. Otherwise if X is a Tables.jl compatible table source, convert X into a Matrix.

source
MLJModelInterface.intFunction
int(x)

The positional integer of the CategoricalString or CategoricalValue x, in the ordering defined by the pool of x. The type of int(x) is the reference type of x.

Not to be confused with x.ref, which is unchanged by reordering of the pool of x, but has the same type.

int(X::CategoricalArray)
+Convenience methods · MLJModelInterface

Convenience methods

MLJModelInterface.tableFunction
table(columntable; prototype=nothing)

Convert a named tuple of vectors or tuples columntable, into a table of the "preferred sink type" of prototype. This is often the type of prototype itself, when prototype is a sink; see the Tables.jl documentation. If prototype is not specified, then a named tuple of vectors is returned.

table(A::AbstractMatrix; names=nothing, prototype=nothing)

Wrap an abstract matrix A as a Tables.jl compatible table with the specified column names (a tuple of symbols). If names are not specified, names=(:x1, :x2, ..., :xn) is used, where n=size(A, 2).

If a prototype is specified, then the matrix is materialized as a table of the preferred sink type of prototype, rather than wrapped. Note that if prototype is not specified, then matrix(table(A)) is essentially a no-op.

source
MLJModelInterface.matrixFunction
matrix(X; transpose=false)

If X isa AbstractMatrix, return X or permutedims(X) if transpose=true. Otherwise if X is a Tables.jl compatible table source, convert X into a Matrix.

source
MLJModelInterface.intFunction
int(x)

The positional integer of the CategoricalString or CategoricalValue x, in the ordering defined by the pool of x. The type of int(x) is the reference type of x.

Not to be confused with x.ref, which is unchanged by reordering of the pool of x, but has the same type.

int(X::CategoricalArray)
 int(W::Array{<:CategoricalString})
 int(W::Array{<:CategoricalValue})

Broadcasted versions of int.

julia> v = categorical(["c", "b", "c", "a"])
 4-element CategoricalArrays.CategoricalArray{String,1,UInt32}:
@@ -19,7 +19,7 @@
  0x00000003
  0x00000002
  0x00000003
- 0x00000001

See also: decoder.

source
MLJModelInterface.UnivariateFiniteFunction
UnivariateFinite(
     support,
     probs;
     pool=nothing,
@@ -79,7 +79,7 @@
  UnivariateFinite{Multiclass{4}}(x=>0.727, y=>0.234, z=>0.0391)
  UnivariateFinite{Multiclass{4}}(x=>0.674, y=>0.00535, z=>0.321)
  ⋮
- UnivariateFinite{Multiclass{4}}(x=>0.292, y=>0.339, z=>0.369)

Probability augmentation

If augment=true the provided array is augmented by inserting appropriate elements ahead of those provided, along the last dimension of the array. This means the user only provides probabilities for the classes c2, c3, ..., cn. The class c1 probabilities are chosen so that each UnivariateFinite distribution in the returned array is a bona fide probability distribution.


UnivariateFinite(prob_given_class; pool=nothing, ordered=false)

Construct a discrete univariate distribution whose finite support is the set of keys of the provided dictionary, prob_given_class, and whose values specify the corresponding probabilities.

The type requirements on the keys of the dictionary are the same as the elements of support given above with this exception: if non-categorical elements (raw labels) are used as keys, then pool=... must be specified and cannot be missing.

If the values (probabilities) are arrays instead of scalars, then an abstract array of UnivariateFinite elements is created, with the same size as the array.

source
MLJModelInterface.classesFunction
classes(x)

All the categorical elements with the same pool as x (including x), returned as a list, with an ordering consistent with the pool. Here x has CategoricalValue type, and classes(x) is a vector of the same eltype. Note that x in classes(x) is always true.

Not to be confused with levels(x.pool). See the example below.

julia> v = categorical(["c", "b", "c", "a"])
+ UnivariateFinite{Multiclass{4}}(x=>0.292, y=>0.339, z=>0.369)

Probability augmentation

If augment=true the provided array is augmented by inserting appropriate elements ahead of those provided, along the last dimension of the array. This means the user only provides probabilities for the classes c2, c3, ..., cn. The class c1 probabilities are chosen so that each UnivariateFinite distribution in the returned array is a bona fide probability distribution.


UnivariateFinite(prob_given_class; pool=nothing, ordered=false)

Construct a discrete univariate distribution whose finite support is the set of keys of the provided dictionary, prob_given_class, and whose values specify the corresponding probabilities.

The type requirements on the keys of the dictionary are the same as the elements of support given above with this exception: if non-categorical elements (raw labels) are used as keys, then pool=... must be specified and cannot be missing.

If the values (probabilities) are arrays instead of scalars, then an abstract array of UnivariateFinite elements is created, with the same size as the array.

source
MLJModelInterface.classesFunction
classes(x)

All the categorical elements with the same pool as x (including x), returned as a list, with an ordering consistent with the pool. Here x has CategoricalValue type, and classes(x) is a vector of the same eltype. Note that x in classes(x) is always true.

Not to be confused with levels(x.pool). See the example below.

julia> v = categorical(["c", "b", "c", "a"])
 4-element CategoricalArrays.CategoricalArray{String,1,UInt32}:
  "c"
  "b"
@@ -105,7 +105,7 @@
 3-element Vector{String}:
  "a"
  "b"
- "c"
source
MLJModelInterface.decoderFunction
decoder(x)

Return a callable object for decoding the integer representation of a CategoricalValue sharing the same pool the CategoricalValue x. Specifically, one has decoder(x)(int(y)) == y for all CategoricalValues y having the same pool as x. One can also call decoder(x) on integer arrays, in which case decoder(x) is broadcast over all elements.

Examples

julia> v = categorical(["c", "b", "c", "a"])
+ "c"
source
MLJModelInterface.decoderFunction
decoder(x)

Return a callable object for decoding the integer representation of a CategoricalValue sharing the same pool the CategoricalValue x. Specifically, one has decoder(x)(int(y)) == y for all CategoricalValues y having the same pool as x. One can also call decoder(x) on integer arrays, in which case decoder(x) is broadcast over all elements.

Examples

julia> v = categorical(["c", "b", "c", "a"])
 4-element CategoricalArrays.CategoricalArray{String,1,UInt32}:
  "c"
  "b"
@@ -122,7 +122,7 @@
 julia> d = decoder(v[3]);
 
 julia> d(int(v)) == v
-true

Warning:

It is not true that int(d(u)) == u always holds.

See also: int.

source
MLJModelInterface.selectFunction
select(X, r, c)

Select element(s) of a table or matrix at row(s) r and column(s) c. An object of the sink type of X (or a matrix) is returned unless c is a single integer or symbol. In that case a vector is returned, unless r is a single integer, in which case a single element is returned.

See also: selectrows, selectcols.

source
MLJModelInterface.selectrowsFunction
selectrows(X, r)

Select single or multiple rows from a table, abstract vector or matrix X. If X is tabular, the object returned is a table of the preferred sink type of typeof(X), even if only a single row is selected.

If the object is neither a table, abstract vector or matrix, X is returned and r is ignored.

source
MLJModelInterface.selectcolsFunction
selectcols(X, c)

Select single or multiple columns from a matrix or table X. If c is an abstract vector of integers or symbols, then the object returned is a table of the preferred sink type of typeof(X). If c is a single integer or column, then an AbstractVector is returned.

source
MLJModelInterface.selectFunction
select(X, r, c)

Select element(s) of a table or matrix at row(s) r and column(s) c. An object of the sink type of X (or a matrix) is returned unless c is a single integer or symbol. In that case a vector is returned, unless r is a single integer, in which case a single element is returned.

See also: selectrows, selectcols.

source
MLJModelInterface.selectrowsFunction
selectrows(X, r)

Select single or multiple rows from a table, abstract vector or matrix X. If X is tabular, the object returned is a table of the preferred sink type of typeof(X), even if only a single row is selected.

If the object is neither a table, abstract vector or matrix, X is returned and r is ignored.

source
MLJModelInterface.selectcolsFunction
selectcols(X, c)

Select single or multiple columns from a matrix or table X. If c is an abstract vector of integers or symbols, then the object returned is a table of the preferred sink type of typeof(X). If c is a single integer or column, then an AbstractVector is returned.

source
MLJModelInterface.UnivariateFiniteFunction
UnivariateFinite(
     support,
     probs;
     pool=nothing,
@@ -182,4 +182,4 @@
  UnivariateFinite{Multiclass{4}}(x=>0.727, y=>0.234, z=>0.0391)
  UnivariateFinite{Multiclass{4}}(x=>0.674, y=>0.00535, z=>0.321)
  ⋮
- UnivariateFinite{Multiclass{4}}(x=>0.292, y=>0.339, z=>0.369)

Probability augmentation

If augment=true the provided array is augmented by inserting appropriate elements ahead of those provided, along the last dimension of the array. This means the user only provides probabilities for the classes c2, c3, ..., cn. The class c1 probabilities are chosen so that each UnivariateFinite distribution in the returned array is a bona fide probability distribution.


UnivariateFinite(prob_given_class; pool=nothing, ordered=false)

Construct a discrete univariate distribution whose finite support is the set of keys of the provided dictionary, prob_given_class, and whose values specify the corresponding probabilities.

The type requirements on the keys of the dictionary are the same as the elements of support given above with this exception: if non-categorical elements (raw labels) are used as keys, then pool=... must be specified and cannot be missing.

If the values (probabilities) are arrays instead of scalars, then an abstract array of UnivariateFinite elements is created, with the same size as the array.

source
+ UnivariateFinite{Multiclass{4}}(x=>0.292, y=>0.339, z=>0.369)

Probability augmentation

If augment=true the provided array is augmented by inserting appropriate elements ahead of those provided, along the last dimension of the array. This means the user only provides probabilities for the classes c2, c3, ..., cn. The class c1 probabilities are chosen so that each UnivariateFinite distribution in the returned array is a bona fide probability distribution.


UnivariateFinite(prob_given_class; pool=nothing, ordered=false)

Construct a discrete univariate distribution whose finite support is the set of keys of the provided dictionary, prob_given_class, and whose values specify the corresponding probabilities.

The type requirements on the keys of the dictionary are the same as the elements of support given above with this exception: if non-categorical elements (raw labels) are used as keys, then pool=... must be specified and cannot be missing.

If the values (probabilities) are arrays instead of scalars, then an abstract array of UnivariateFinite elements is created, with the same size as the array.

source
diff --git a/dev/document_strings/index.html b/dev/document_strings/index.html index 4cf9f00..5586e74 100644 --- a/dev/document_strings/index.html +++ b/dev/document_strings/index.html @@ -22,4 +22,4 @@ """ FooRegressor -

Variation to augment existing document string

For models that have a native API with separate documentation, one may want to call doc_header(FooRegressor, augment=true) instead. In that case, the output will look like this:

From MLJ, the FooRegressor type can be imported using

FooRegressor = @load FooRegressor pkg=FooRegressorPkg

Construct an instance with default hyper-parameters using the syntax model = FooRegressor(). Provide keyword arguments to override hyper-parameter defaults, as in FooRegressor(a=...).

source

The document string standard

Your document string must include the following components, in order:

+

Variation to augment existing document string

For models that have a native API with separate documentation, one may want to call doc_header(FooRegressor, augment=true) instead. In that case, the output will look like this:

From MLJ, the FooRegressor type can be imported using

FooRegressor = @load FooRegressor pkg=FooRegressorPkg

Construct an instance with default hyper-parameters using the syntax model = FooRegressor(). Provide keyword arguments to override hyper-parameter defaults, as in FooRegressor(a=...).

source

The document string standard

Your document string must include the following components, in order:

diff --git a/dev/feature_importances/index.html b/dev/feature_importances/index.html index 0e3e0ac..4638084 100644 --- a/dev/feature_importances/index.html +++ b/dev/feature_importances/index.html @@ -1,2 +1,2 @@ -Feature importances · MLJModelInterface

Feature importances

MLJModelInterface.feature_importancesFunction
feature_importances(model::M, fitresult, report)

For a given model of model type M supporting intrinsic feature importances, calculate the feature importances from the model's fitresult and report as an abstract vector of feature::Symbol => importance::Real pairs (e.g [:gender =>0.23, :height =>0.7, :weight => 0.1]).

New model implementations

The following trait overload is also required: MLJModelInterface.reports_feature_importances(::Type{<:M}) = true

If for some reason a model is sometimes unable to report feature importances then feature_importances should return all importances as 0.0, as in [:gender =>0.0, :height =>0.0, :weight => 0.0].

source

Trait values can also be set using the metadata_model method, see below.

+Feature importances · MLJModelInterface

Feature importances

MLJModelInterface.feature_importancesFunction
feature_importances(model::M, fitresult, report)

For a given model of model type M supporting intrinsic feature importances, calculate the feature importances from the model's fitresult and report as an abstract vector of feature::Symbol => importance::Real pairs (e.g [:gender =>0.23, :height =>0.7, :weight => 0.1]).

New model implementations

The following trait overload is also required: MLJModelInterface.reports_feature_importances(::Type{<:M}) = true

If for some reason a model is sometimes unable to report feature importances then feature_importances should return all importances as 0.0, as in [:gender =>0.0, :height =>0.0, :weight => 0.0].

source

Trait values can also be set using the metadata_model method, see below.

diff --git a/dev/fitting_distributions/index.html b/dev/fitting_distributions/index.html index 25ccfd3..aaab38a 100644 --- a/dev/fitting_distributions/index.html +++ b/dev/fitting_distributions/index.html @@ -1,2 +1,2 @@ -Models that learn a probability distribution · MLJModelInterface

Models that learn a probability distribution

Experimental

The following API is experimental. It is subject to breaking changes during minor or major releases without warning. Models implementing this interface will not work with MLJBase versions earlier than 0.17.5.

Models that fit a probability distribution to some data should be regarded as Probabilistic <: Supervised models with target y = data and X = nothing.

The predict method should return a single distribution.

A working implementation of a model that fits a UnivariateFinite distribution to some categorical data using Laplace smoothing controlled by a hyperparameter alpha is given here.

+Models that learn a probability distribution · MLJModelInterface

Models that learn a probability distribution

Experimental

The following API is experimental. It is subject to breaking changes during minor or major releases without warning. Models implementing this interface will not work with MLJBase versions earlier than 0.17.5.

Models that fit a probability distribution to some data should be regarded as Probabilistic <: Supervised models with target y = data and X = nothing.

The predict method should return a single distribution.

A working implementation of a model that fits a UnivariateFinite distribution to some categorical data using Laplace smoothing controlled by a hyperparameter alpha is given here.

diff --git a/dev/form_of_data/index.html b/dev/form_of_data/index.html index 17381b4..7c0a845 100644 --- a/dev/form_of_data/index.html +++ b/dev/form_of_data/index.html @@ -1,2 +1,2 @@ -The form of data for fitting and predicting · MLJModelInterface

The form of data for fitting and predicting

The model implementer does not have absolute control over the types of data X, y and Xnew appearing in the fit and predict methods they must implement. Rather, they can specify the scientific type of this data by making appropriate declarations of the traits input_scitype and target_scitype discussed later under Trait declarations.

Important Note. Unless it genuinely makes little sense to do so, the MLJ recommendation is to specify a Table scientific type for X (and hence Xnew) and an AbstractVector scientific type (e.g., AbstractVector{Continuous}) for targets y. Algorithms requiring matrix input can coerce their inputs appropriately; see below.

Additional type coercions

If the core algorithm being wrapped requires data in a different or more specific form, then fit will need to coerce the table into the form desired (and the same coercions applied to X will have to be repeated for Xnew in predict). To assist with common cases, MLJ provides the convenience method MMI.matrix. MMI.matrix(Xtable) has type Matrix{T} where T is the tightest common type of elements of Xtable, and Xtable is any table. (If Xtable is itself just a wrapped matrix, Xtable=Tables.table(A), then A=MMI.table(Xtable) will be returned without any copying.)

Alternatively, a more performant option is to implement a data front-end for your model; see Implementing a data front-end.

Other auxiliary methods provided by MLJModelInterface for handling tabular data are: selectrows, selectcols, select and schema (for extracting the size, names and eltypes of a table's columns). See Convenience methods below for details.

Important convention

It is to be understood that the columns of table X correspond to features and the rows to observations. So, for example, the predict method for a linear regression model might look like predict(model, w, Xnew) = MMI.matrix(Xnew)*w, where w is the vector of learned coefficients.

+The form of data for fitting and predicting · MLJModelInterface

The form of data for fitting and predicting

The model implementer does not have absolute control over the types of data X, y and Xnew appearing in the fit and predict methods they must implement. Rather, they can specify the scientific type of this data by making appropriate declarations of the traits input_scitype and target_scitype discussed later under Trait declarations.

Important Note. Unless it genuinely makes little sense to do so, the MLJ recommendation is to specify a Table scientific type for X (and hence Xnew) and an AbstractVector scientific type (e.g., AbstractVector{Continuous}) for targets y. Algorithms requiring matrix input can coerce their inputs appropriately; see below.

Additional type coercions

If the core algorithm being wrapped requires data in a different or more specific form, then fit will need to coerce the table into the form desired (and the same coercions applied to X will have to be repeated for Xnew in predict). To assist with common cases, MLJ provides the convenience method MMI.matrix. MMI.matrix(Xtable) has type Matrix{T} where T is the tightest common type of elements of Xtable, and Xtable is any table. (If Xtable is itself just a wrapped matrix, Xtable=Tables.table(A), then A=MMI.table(Xtable) will be returned without any copying.)

Alternatively, a more performant option is to implement a data front-end for your model; see Implementing a data front-end.

Other auxiliary methods provided by MLJModelInterface for handling tabular data are: selectrows, selectcols, select and schema (for extracting the size, names and eltypes of a table's columns). See Convenience methods below for details.

Important convention

It is to be understood that the columns of table X correspond to features and the rows to observations. So, for example, the predict method for a linear regression model might look like predict(model, w, Xnew) = MMI.matrix(Xnew)*w, where w is the vector of learned coefficients.

diff --git a/dev/how_to_register/index.html b/dev/how_to_register/index.html index b2c0d56..74f8d26 100644 --- a/dev/how_to_register/index.html +++ b/dev/how_to_register/index.html @@ -1,2 +1,2 @@ -How to add models to the MLJ Model Registry · MLJModelInterface

How to add models to the MLJ model registry

The MLJ model registry is located in the MLJModels.jl repository. To add a model, you need to follow these steps

  • Ensure your model conforms to the interface defined above

  • Raise an issue at MLJModels.jl and point out where the MLJ-interface implementation is, e.g. by providing a link to the code.

  • An administrator will then review your implementation and work with you to add the model to the registry

+How to add models to the MLJ Model Registry · MLJModelInterface

How to add models to the MLJ model registry

The MLJ model registry is located in the MLJModels.jl repository. To add a model, you need to follow these steps

  • Ensure your model conforms to the interface defined above

  • Raise an issue at MLJModels.jl and point out where the MLJ-interface implementation is, e.g. by providing a link to the code.

  • An administrator will then review your implementation and work with you to add the model to the registry

diff --git a/dev/implementing_a_data_front_end/index.html b/dev/implementing_a_data_front_end/index.html index ba095ad..f612de2 100644 --- a/dev/implementing_a_data_front_end/index.html +++ b/dev/implementing_a_data_front_end/index.html @@ -15,4 +15,4 @@ # for predict: MMI.reformat(::SomeSupervised, X) = (MMI.matrix(X)',) -MMI.selectrows(::SomeSupervised, I, Xmatrix) = (view(Xmatrix, :, I),)

With these additions, fit and predict are refactored, so that X and Xnew represent matrices with features as rows.

+MMI.selectrows(::SomeSupervised, I, Xmatrix) = (view(Xmatrix, :, I),)

With these additions, fit and predict are refactored, so that X and Xnew represent matrices with features as rows.

diff --git a/dev/index.html b/dev/index.html index eb9b004..52b77a5 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -Home · MLJModelInterface

Adding Models for General Use

The machine learning tools provided by MLJ can be applied to the models in any package that imports MLJModelInterface and implements the API defined there, as outlined in this document.

Tip

This is a reference document, which has become rather sprawling over the evolution of the MLJ project. We recommend starting with Quick start guide, which covers the main points relevant to most new model implementations.

Interface code can be hosted by the package providing the core machine learning algorithm, or by a stand-alone "interface-only" package, using the template MLJExampleInterface.jl (see Where to place code implementing new models below). For a list of packages implementing the MLJ model API (natively, and in interface packages) see here.

Important

MLJModelInterface is a very light-weight interface allowing you to define your interface, but does not provide the functionality required to use or test your interface; this requires MLJBase. So, while you only need to add MLJModelInterface to your project's [deps], for testing purposes you need to add MLJBase to your project's [extras] and [targets]. In testing, simply use MLJBase in place of MLJModelInterface.

It is assumed the reader has read the Getting Started section of the MLJ manual. To implement the API described here, some familiarity with the following packages is also helpful:

  • ScientificTypes.jl (for specifying model requirements of data)

  • Distributions.jl (for probabilistic predictions)

  • CategoricalArrays.jl (essential if you are implementing a model handling data of Multiclass or OrderedFactor scitype; familiarity with CategoricalPool objects required)

  • Tables.jl (if your algorithm needs input data in a novel format).

In MLJ, the basic interface exposed to the user, built atop the model interface described here, is the machine interface. After a first reading of this document, the reader may wish to refer to MLJ Internals for context.

+Home · MLJModelInterface

Adding Models for General Use

The machine learning tools provided by MLJ can be applied to the models in any package that imports MLJModelInterface and implements the API defined there, as outlined in this document.

Tip

This is a reference document, which has become rather sprawling over the evolution of the MLJ project. We recommend starting with Quick start guide, which covers the main points relevant to most new model implementations.

Interface code can be hosted by the package providing the core machine learning algorithm, or by a stand-alone "interface-only" package, using the template MLJExampleInterface.jl (see Where to place code implementing new models below). For a list of packages implementing the MLJ model API (natively, and in interface packages) see here.

Important

MLJModelInterface is a very light-weight interface allowing you to define your interface, but does not provide the functionality required to use or test your interface; this requires MLJBase. So, while you only need to add MLJModelInterface to your project's [deps], for testing purposes you need to add MLJBase to your project's [extras] and [targets]. In testing, simply use MLJBase in place of MLJModelInterface.

It is assumed the reader has read the Getting Started section of the MLJ manual. To implement the API described here, some familiarity with the following packages is also helpful:

  • ScientificTypes.jl (for specifying model requirements of data)

  • Distributions.jl (for probabilistic predictions)

  • CategoricalArrays.jl (essential if you are implementing a model handling data of Multiclass or OrderedFactor scitype; familiarity with CategoricalPool objects required)

  • Tables.jl (if your algorithm needs input data in a novel format).

In MLJ, the basic interface exposed to the user, built atop the model interface described here, is the machine interface. After a first reading of this document, the reader may wish to refer to MLJ Internals for context.

diff --git a/dev/iterative_models/index.html b/dev/iterative_models/index.html index be133e0..effae2c 100644 --- a/dev/iterative_models/index.html +++ b/dev/iterative_models/index.html @@ -2,4 +2,4 @@ Iterative models and the update! method · MLJModelInterface

Iterative models and the update! method

An update method may be optionally overloaded to enable a call by MLJ to retrain a model (on the same training data) to avoid repeating computations unnecessarily.

MMI.update(model::SomeSupervisedModel, verbosity, old_fitresult, old_cache, X, y) -> fit
 result, cache, report
 MMI.update(model::SomeSupervisedModel, verbosity, old_fitresult, old_cache, X, y, w=nothing) -> fit
-result, cache, report

Here the second variation applies if SomeSupervisedModel supports sample weights.

If an MLJ Machine is being fit! and it is not the first time, then update is called instead of fit, unless the machine fit! has been called with a new rows keyword argument. However, MLJModelInterface defines a fallback for update which just calls fit. For context, see the Internals section of the MLJ manual.

Learning networks wrapped as models constitute one use case (see the Composing Models section of the MLJ manual): one would like each component model to be retrained only when hyperparameter changes "upstream" make this necessary. In this case, MLJ provides a fallback (specifically, the fallback is for any subtype of SupervisedNetwork = Union{DeterministicNetwork,ProbabilisticNetwork}). A second more generally relevant use case is iterative models, where calls to increase the number of iterations only restarts the iterative procedure if other hyperparameters have also changed. (A useful method for inspecting model changes in such cases is MLJModelInterface.is_same_except. ) For an example, see MLJEnsembles.jl.

A third use case is to avoid repeating the time-consuming preprocessing of X and y required by some models.

If the argument fitresult (returned by a preceding call to fit) is not sufficient for performing an update, the author can arrange for fit to output in its cache return value any additional information required (for example, pre-processed versions of X and y), as this is also passed as an argument to the update method.

+result, cache, report

Here the second variation applies if SomeSupervisedModel supports sample weights.

If an MLJ Machine is being fit! and it is not the first time, then update is called instead of fit, unless the machine fit! has been called with a new rows keyword argument. However, MLJModelInterface defines a fallback for update which just calls fit. For context, see the Internals section of the MLJ manual.

Learning networks wrapped as models constitute one use case (see the Composing Models section of the MLJ manual): one would like each component model to be retrained only when hyperparameter changes "upstream" make this necessary. In this case, MLJ provides a fallback (specifically, the fallback is for any subtype of SupervisedNetwork = Union{DeterministicNetwork,ProbabilisticNetwork}). A second more generally relevant use case is iterative models, where calls to increase the number of iterations only restarts the iterative procedure if other hyperparameters have also changed. (A useful method for inspecting model changes in such cases is MLJModelInterface.is_same_except. ) For an example, see MLJEnsembles.jl.

A third use case is to avoid repeating the time-consuming preprocessing of X and y required by some models.

If the argument fitresult (returned by a preceding call to fit) is not sufficient for performing an update, the author can arrange for fit to output in its cache return value any additional information required (for example, pre-processed versions of X and y), as this is also passed as an argument to the update method.

diff --git a/dev/objects.inv b/dev/objects.inv index f814b03ea3d7a482d9fedcd65a2661887148a417..89faa1abbdb24cb35edde462a73b45ae1868f7f5 100644 GIT binary patch delta 15 WcmZn^X%d;>&SGewXRy(0J0}1mPXvJg delta 14 VcmZn?X%v~@&TOe?w$Xb#CjcNC1YZCE diff --git a/dev/outlier_detection_models/index.html b/dev/outlier_detection_models/index.html index f6cd5f6..6694fb1 100644 --- a/dev/outlier_detection_models/index.html +++ b/dev/outlier_detection_models/index.html @@ -1,2 +1,2 @@ -Outlier detection models · MLJModelInterface

Outlier detection models

Experimental API

The Outlier Detection API is experimental and may change in future releases of MLJ.

Outlier detection or anomaly detection is predominantly an unsupervised learning task, transforming each data point to an outlier score quantifying the level of "outlierness". However, because detectors can also be semi-supervised or supervised, MLJModelInterface provides a collection of abstract model types, that enable the different characteristics, namely:

  • MLJModelInterface.SupervisedDetector
  • MLJModelInterface.UnsupervisedDetector
  • MLJModelInterface.ProbabilisticSupervisedDetector
  • MLJModelInterface.ProbabilisticUnsupervisedDetector
  • MLJModelInterface.DeterministicSupervisedDetector
  • MLJModelInterface.DeterministicUnsupervisedDetector

All outlier detection models subtyping from any of the above supertypes have to implement MLJModelInterface.fit(model, verbosity, X, [y]). Models subtyping from either SupervisedDetector or UnsupervisedDetector have to implement MLJModelInterface.transform(model, fitresult, Xnew), which should return the raw outlier scores (<:Continuous) of all points in Xnew.

Probabilistic and deterministic outlier detection models provide an additional option to predict a normalized estimate of outlierness or a concrete outlier label and thus enable evaluation of those models. All corresponding supertypes have to implement (in addition to the previously described fit and transform) MLJModelInterface.predict(model, fitresult, Xnew), with deterministic predictions conforming to OrderedFactor{2}, with the first class being the normal class and the second class being the outlier. Probabilistic models predict a UnivariateFinite estimate of those classes.

It is typically possible to automatically convert an outlier detection model to a probabilistic or deterministic model if the training scores are stored in the model's report. Below mentioned OutlierDetection.jl package, for example, stores the training scores under the scores key in the report returned from fit. It is then possible to use model wrappers such as OutlierDetection.ProbabilisticDetector to automatically convert a model to enable predictions of the required output type.

External outlier detection packages

OutlierDetection.jl provides an opinionated interface on top of MLJ for outlier detection models, standardizing things like class names, dealing with training scores, score normalization and more.

+Outlier detection models · MLJModelInterface

Outlier detection models

Experimental API

The Outlier Detection API is experimental and may change in future releases of MLJ.

Outlier detection or anomaly detection is predominantly an unsupervised learning task, transforming each data point to an outlier score quantifying the level of "outlierness". However, because detectors can also be semi-supervised or supervised, MLJModelInterface provides a collection of abstract model types, that enable the different characteristics, namely:

  • MLJModelInterface.SupervisedDetector
  • MLJModelInterface.UnsupervisedDetector
  • MLJModelInterface.ProbabilisticSupervisedDetector
  • MLJModelInterface.ProbabilisticUnsupervisedDetector
  • MLJModelInterface.DeterministicSupervisedDetector
  • MLJModelInterface.DeterministicUnsupervisedDetector

All outlier detection models subtyping from any of the above supertypes have to implement MLJModelInterface.fit(model, verbosity, X, [y]). Models subtyping from either SupervisedDetector or UnsupervisedDetector have to implement MLJModelInterface.transform(model, fitresult, Xnew), which should return the raw outlier scores (<:Continuous) of all points in Xnew.

Probabilistic and deterministic outlier detection models provide an additional option to predict a normalized estimate of outlierness or a concrete outlier label and thus enable evaluation of those models. All corresponding supertypes have to implement (in addition to the previously described fit and transform) MLJModelInterface.predict(model, fitresult, Xnew), with deterministic predictions conforming to OrderedFactor{2}, with the first class being the normal class and the second class being the outlier. Probabilistic models predict a UnivariateFinite estimate of those classes.

It is typically possible to automatically convert an outlier detection model to a probabilistic or deterministic model if the training scores are stored in the model's report. Below mentioned OutlierDetection.jl package, for example, stores the training scores under the scores key in the report returned from fit. It is then possible to use model wrappers such as OutlierDetection.ProbabilisticDetector to automatically convert a model to enable predictions of the required output type.

External outlier detection packages

OutlierDetection.jl provides an opinionated interface on top of MLJ for outlier detection models, standardizing things like class names, dealing with training scores, score normalization and more.

diff --git a/dev/quick_start_guide/index.html b/dev/quick_start_guide/index.html index dfdfd29..cf11984 100644 --- a/dev/quick_start_guide/index.html +++ b/dev/quick_start_guide/index.html @@ -47,4 +47,4 @@ supports_weights = false, # does the model support sample weights? descr = "A short description of your model" load_path = "YourPackage.SubModuleContainingModelStructDefinition.YourModel1" -)

Important. Do not omit the load_path specification. Without a correct load_path MLJ will be unable to import your model.

Examples:

Adding a model to the model registry

See How to add models to the MLJ model registry.

+)

Important. Do not omit the load_path specification. Without a correct load_path MLJ will be unable to import your model.

Examples:

Adding a model to the model registry

See How to add models to the MLJ model registry.

diff --git a/dev/reference/index.html b/dev/reference/index.html index 279fcb5..7a80991 100644 --- a/dev/reference/index.html +++ b/dev/reference/index.html @@ -1,5 +1,5 @@ -Reference · MLJModelInterface

Reference

MLJModelInterface.UnivariateFiniteFunction
UnivariateFinite(
+Reference · MLJModelInterface

Reference

MLJModelInterface.UnivariateFiniteFunction
UnivariateFinite(
     support,
     probs;
     pool=nothing,
@@ -59,7 +59,7 @@
  UnivariateFinite{Multiclass{4}}(x=>0.727, y=>0.234, z=>0.0391)
  UnivariateFinite{Multiclass{4}}(x=>0.674, y=>0.00535, z=>0.321)
  ⋮
- UnivariateFinite{Multiclass{4}}(x=>0.292, y=>0.339, z=>0.369)

Probability augmentation

If augment=true the provided array is augmented by inserting appropriate elements ahead of those provided, along the last dimension of the array. This means the user only provides probabilities for the classes c2, c3, ..., cn. The class c1 probabilities are chosen so that each UnivariateFinite distribution in the returned array is a bona fide probability distribution.


UnivariateFinite(prob_given_class; pool=nothing, ordered=false)

Construct a discrete univariate distribution whose finite support is the set of keys of the provided dictionary, prob_given_class, and whose values specify the corresponding probabilities.

The type requirements on the keys of the dictionary are the same as the elements of support given above with this exception: if non-categorical elements (raw labels) are used as keys, then pool=... must be specified and cannot be missing.

If the values (probabilities) are arrays instead of scalars, then an abstract array of UnivariateFinite elements is created, with the same size as the array.

source
MLJModelInterface.classesMethod
classes(x)

All the categorical elements with the same pool as x (including x), returned as a list, with an ordering consistent with the pool. Here x has CategoricalValue type, and classes(x) is a vector of the same eltype. Note that x in classes(x) is always true.

Not to be confused with levels(x.pool). See the example below.

julia> v = categorical(["c", "b", "c", "a"])
+ UnivariateFinite{Multiclass{4}}(x=>0.292, y=>0.339, z=>0.369)

Probability augmentation

If augment=true the provided array is augmented by inserting appropriate elements ahead of those provided, along the last dimension of the array. This means the user only provides probabilities for the classes c2, c3, ..., cn. The class c1 probabilities are chosen so that each UnivariateFinite distribution in the returned array is a bona fide probability distribution.


UnivariateFinite(prob_given_class; pool=nothing, ordered=false)

Construct a discrete univariate distribution whose finite support is the set of keys of the provided dictionary, prob_given_class, and whose values specify the corresponding probabilities.

The type requirements on the keys of the dictionary are the same as the elements of support given above with this exception: if non-categorical elements (raw labels) are used as keys, then pool=... must be specified and cannot be missing.

If the values (probabilities) are arrays instead of scalars, then an abstract array of UnivariateFinite elements is created, with the same size as the array.

source
MLJModelInterface.classesMethod
classes(x)

All the categorical elements with the same pool as x (including x), returned as a list, with an ordering consistent with the pool. Here x has CategoricalValue type, and classes(x) is a vector of the same eltype. Note that x in classes(x) is always true.

Not to be confused with levels(x.pool). See the example below.

julia> v = categorical(["c", "b", "c", "a"])
 4-element CategoricalArrays.CategoricalArray{String,1,UInt32}:
  "c"
  "b"
@@ -85,7 +85,7 @@
 3-element Vector{String}:
  "a"
  "b"
- "c"
source
MLJModelInterface.decoderMethod
decoder(x)

Return a callable object for decoding the integer representation of a CategoricalValue sharing the same pool the CategoricalValue x. Specifically, one has decoder(x)(int(y)) == y for all CategoricalValues y having the same pool as x. One can also call decoder(x) on integer arrays, in which case decoder(x) is broadcast over all elements.

Examples

julia> v = categorical(["c", "b", "c", "a"])
+ "c"
source
MLJModelInterface.decoderMethod
decoder(x)

Return a callable object for decoding the integer representation of a CategoricalValue sharing the same pool the CategoricalValue x. Specifically, one has decoder(x)(int(y)) == y for all CategoricalValues y having the same pool as x. One can also call decoder(x) on integer arrays, in which case decoder(x) is broadcast over all elements.

Examples

julia> v = categorical(["c", "b", "c", "a"])
 4-element CategoricalArrays.CategoricalArray{String,1,UInt32}:
  "c"
  "b"
@@ -102,7 +102,7 @@
 julia> d = decoder(v[3]);
 
 julia> d(int(v)) == v
-true

Warning:

It is not true that int(d(u)) == u always holds.

See also: int.

source
MLJModelInterface.fitFunction
MLJModelInterface.fit(model, verbosity, data...) -> fitresult, cache, report

All models must implement a fit method. Here data is the output of reformat on user-provided data, or some some resampling thereof. The fallback of reformat returns the user-provided data (eg, a table).

source
MLJModelInterface.fitted_paramsMethod
fitted_params(model, fitresult) -> human_readable_fitresult # named_tuple

Models may overload fitted_params. The fallback returns (fitresult=fitresult,).

Other training-related outcomes should be returned in the report part of the tuple returned by fit.

source
MLJModelInterface.intMethod
int(x)

The positional integer of the CategoricalString or CategoricalValue x, in the ordering defined by the pool of x. The type of int(x) is the reference type of x.

Not to be confused with x.ref, which is unchanged by reordering of the pool of x, but has the same type.

int(X::CategoricalArray)
+true

Warning:

It is not true that int(d(u)) == u always holds.

See also: int.

source
MLJModelInterface.fitFunction
MLJModelInterface.fit(model, verbosity, data...) -> fitresult, cache, report

All models must implement a fit method. Here data is the output of reformat on user-provided data, or some some resampling thereof. The fallback of reformat returns the user-provided data (eg, a table).

source
MLJModelInterface.fitted_paramsMethod
fitted_params(model, fitresult) -> human_readable_fitresult # named_tuple

Models may overload fitted_params. The fallback returns (fitresult=fitresult,).

Other training-related outcomes should be returned in the report part of the tuple returned by fit.

source
MLJModelInterface.intMethod
int(x)

The positional integer of the CategoricalString or CategoricalValue x, in the ordering defined by the pool of x. The type of int(x) is the reference type of x.

Not to be confused with x.ref, which is unchanged by reordering of the pool of x, but has the same type.

int(X::CategoricalArray)
 int(W::Array{<:CategoricalString})
 int(W::Array{<:CategoricalValue})

Broadcasted versions of int.

julia> v = categorical(["c", "b", "c", "a"])
 4-element CategoricalArrays.CategoricalArray{String,1,UInt32}:
@@ -122,23 +122,23 @@
  0x00000003
  0x00000002
  0x00000003
- 0x00000001

See also: decoder.

source
MLJModelInterface.is_same_exceptMethod
is_same_except(m1, m2, exceptions::Symbol...; deep_properties=Symbol[])

If both m1 and m2 are of MLJType, return true if the following conditions all hold, and false otherwise:

  • typeof(m1) === typeof(m2)

  • propertynames(m1) === propertynames(m2)

  • with the exception of properties listed as exceptions or bound to an AbstractRNG, each pair of corresponding property values is either "equal" or both undefined. (If a property appears as a propertyname but not a fieldname, it is deemed as always defined.)

The meaining of "equal" depends on the type of the property value:

  • values that are themselves of MLJType are "equal" if they are equal in the sense of is_same_except with no exceptions.

  • values that are not of MLJType are "equal" if they are ==.

In the special case of a "deep" property, "equal" has a different meaning; see deep_properties) for details.

If m1 or m2 are not MLJType objects, then return ==(m1, m2).

source
MLJModelInterface.isrepresentedMethod
isrepresented(object::MLJType, objects)

Test if object has a representative in the iterable objects. This is a weaker requirement than object in objects.

Here we say m1 represents m2 if is_same_except(m1, m2) is true.

source
MLJModelInterface.matrixMethod
matrix(X; transpose=false)

If X isa AbstractMatrix, return X or permutedims(X) if transpose=true. Otherwise if X is a Tables.jl compatible table source, convert X into a Matrix.

source
MLJModelInterface.metadata_modelMethod
metadata_model(T; args...)

Helper function to write the metadata for a model T.

Keywords

  • input_scitype=Unknown: allowed scientific type of the input data
  • target_scitype=Unknown: allowed scitype of the target (supervised)
  • output_scitype=Unknown: allowed scitype of the transformed data (unsupervised)
  • supports_weights=false: whether the model supports sample weights
  • supports_class_weights=false: whether the model supports class weights
  • load_path="unknown": where the model is (usually PackageName.ModelName)
  • human_name=nothing: human name of the model
  • supports_training_losses=nothing: whether the (necessarily iterative) model can report training losses
  • reports_feature_importances=nothing: whether the model reports feature importances

Example

metadata_model(KNNRegressor,
+ 0x00000001

See also: decoder.

source
MLJModelInterface.is_same_exceptMethod
is_same_except(m1, m2, exceptions::Symbol...; deep_properties=Symbol[])

If both m1 and m2 are of MLJType, return true if the following conditions all hold, and false otherwise:

  • typeof(m1) === typeof(m2)

  • propertynames(m1) === propertynames(m2)

  • with the exception of properties listed as exceptions or bound to an AbstractRNG, each pair of corresponding property values is either "equal" or both undefined. (If a property appears as a propertyname but not a fieldname, it is deemed as always defined.)

The meaining of "equal" depends on the type of the property value:

  • values that are themselves of MLJType are "equal" if they are equal in the sense of is_same_except with no exceptions.

  • values that are not of MLJType are "equal" if they are ==.

In the special case of a "deep" property, "equal" has a different meaning; see deep_properties) for details.

If m1 or m2 are not MLJType objects, then return ==(m1, m2).

source
MLJModelInterface.isrepresentedMethod
isrepresented(object::MLJType, objects)

Test if object has a representative in the iterable objects. This is a weaker requirement than object in objects.

Here we say m1 represents m2 if is_same_except(m1, m2) is true.

source
MLJModelInterface.matrixMethod
matrix(X; transpose=false)

If X isa AbstractMatrix, return X or permutedims(X) if transpose=true. Otherwise if X is a Tables.jl compatible table source, convert X into a Matrix.

source
MLJModelInterface.metadata_modelMethod
metadata_model(T; args...)

Helper function to write the metadata for a model T.

Keywords

  • input_scitype=Unknown: allowed scientific type of the input data
  • target_scitype=Unknown: allowed scitype of the target (supervised)
  • output_scitype=Unknown: allowed scitype of the transformed data (unsupervised)
  • supports_weights=false: whether the model supports sample weights
  • supports_class_weights=false: whether the model supports class weights
  • load_path="unknown": where the model is (usually PackageName.ModelName)
  • human_name=nothing: human name of the model
  • supports_training_losses=nothing: whether the (necessarily iterative) model can report training losses
  • reports_feature_importances=nothing: whether the model reports feature importances

Example

metadata_model(KNNRegressor,
     input_scitype=MLJModelInterface.Table(MLJModelInterface.Continuous),
     target_scitype=AbstractVector{MLJModelInterface.Continuous},
     supports_weights=true,
-    load_path="NearestNeighbors.KNNRegressor")
source
MLJModelInterface.metadata_pkgMethod
metadata_pkg(T; args...)

Helper function to write the metadata for a package providing model T. Use it with broadcasting to define the metadata of the package providing a series of models.

Keywords

  • package_name="unknown" : package name
  • package_uuid="unknown" : package uuid
  • package_url="unknown" : package url
  • is_pure_julia=missing : whether the package is pure julia
  • package_license="unknown": package license
  • is_wrapper=false : whether the package is a wrapper

Example

metadata_pkg.((KNNRegressor, KNNClassifier),
+    load_path="NearestNeighbors.KNNRegressor")
source
MLJModelInterface.metadata_pkgMethod
metadata_pkg(T; args...)

Helper function to write the metadata for a package providing model T. Use it with broadcasting to define the metadata of the package providing a series of models.

Keywords

  • package_name="unknown" : package name
  • package_uuid="unknown" : package uuid
  • package_url="unknown" : package url
  • is_pure_julia=missing : whether the package is pure julia
  • package_license="unknown": package license
  • is_wrapper=false : whether the package is a wrapper

Example

metadata_pkg.((KNNRegressor, KNNClassifier),
     package_name="NearestNeighbors",
     package_uuid="b8a86587-4115-5ab1-83bc-aa920d37bbce",
     package_url="https://github.com/KristofferC/NearestNeighbors.jl",
     is_pure_julia=true,
     package_license="MIT",
-    is_wrapper=false)
source
MLJModelInterface.paramsMethod
params(m::MLJType)

Recursively convert any transparent object m into a named tuple, keyed on the fields of m. An object is transparent if MLJModelInterface.istransparent(m) == true. The named tuple is possibly nested because params is recursively applied to the field values, which themselves might be transparent.

Most objects of type MLJType are transparent.

julia> params(EnsembleModel(model=ConstantClassifier()))
+    is_wrapper=false)
source
MLJModelInterface.paramsMethod
params(m::MLJType)

Recursively convert any transparent object m into a named tuple, keyed on the fields of m. An object is transparent if MLJModelInterface.istransparent(m) == true. The named tuple is possibly nested because params is recursively applied to the field values, which themselves might be transparent.

Most objects of type MLJType are transparent.

julia> params(EnsembleModel(model=ConstantClassifier()))
 (model = (target_type = Bool,),
  weights = Float64[],
  bagging_fraction = 0.8,
  rng_seed = 0,
  n = 100,
- parallel = true,)
source
MLJModelInterface.predictFunction
predict(model, fitresult, new_data...)

Supervised and SupervisedAnnotator models must implement the predict operation. Here new_data is the output of reformat called on user-specified data.

source
MLJModelInterface.reformatMethod
MLJModelInterface.reformat(model, args...) -> data

Models optionally overload reformat to define transformations of user-supplied data into some model-specific representation (e.g., from a table to a matrix). When implemented, the MLJ user can avoid repeating such transformations unnecessarily, and can additionally make use of more efficient row subsampling, which is then based on the model-specific representation of data, rather than the user-representation. When reformat is overloaded, selectrows(::Model, ...) must be as well (see selectrows). Furthermore, the model fit method(s), and operations, such as predict and transform, must be refactored to act on the model-specific representations of the data.

To implement the reformat data front-end for a model, refer to "Implementing a data front-end" in the MLJ manual.

source
MLJModelInterface.scitypeMethod
scitype(X)

The scientific type (interpretation) of X, distinct from its machine type.

Examples

julia> scitype(3.14)
+ parallel = true,)
source
MLJModelInterface.predictFunction
predict(model, fitresult, new_data...)

Supervised and SupervisedAnnotator models must implement the predict operation. Here new_data is the output of reformat called on user-specified data.

source
MLJModelInterface.reformatMethod
MLJModelInterface.reformat(model, args...) -> data

Models optionally overload reformat to define transformations of user-supplied data into some model-specific representation (e.g., from a table to a matrix). When implemented, the MLJ user can avoid repeating such transformations unnecessarily, and can additionally make use of more efficient row subsampling, which is then based on the model-specific representation of data, rather than the user-representation. When reformat is overloaded, selectrows(::Model, ...) must be as well (see selectrows). Furthermore, the model fit method(s), and operations, such as predict and transform, must be refactored to act on the model-specific representations of the data.

To implement the reformat data front-end for a model, refer to "Implementing a data front-end" in the MLJ manual.

source
MLJModelInterface.scitypeMethod
scitype(X)

The scientific type (interpretation) of X, distinct from its machine type.

Examples

julia> scitype(3.14)
 Continuous
 
 julia> scitype([1, 2, missing])
@@ -153,13 +153,13 @@
             ndevices = [1, 3, 2, 3, 2]);
 
 julia> scitype(X)
-Table{Union{AbstractVector{Count}, AbstractVector{Multiclass{2}}}}
source
MLJModelInterface.selectFunction
select(X, r, c)

Select element(s) of a table or matrix at row(s) r and column(s) c. An object of the sink type of X (or a matrix) is returned unless c is a single integer or symbol. In that case a vector is returned, unless r is a single integer, in which case a single element is returned.

See also: selectrows, selectcols.

source
MLJModelInterface.selectcolsFunction
selectcols(X, c)

Select single or multiple columns from a matrix or table X. If c is an abstract vector of integers or symbols, then the object returned is a table of the preferred sink type of typeof(X). If c is a single integer or column, then an AbstractVector is returned.

source
MLJModelInterface.selectrowsFunction
selectrows(X, r)

Select single or multiple rows from a table, abstract vector or matrix X. If X is tabular, the object returned is a table of the preferred sink type of typeof(X), even if only a single row is selected.

If the object is neither a table, abstract vector or matrix, X is returned and r is ignored.

source
MLJModelInterface.selectrowsMethod
MLJModelInterface.selectrows(::Model, I, data...) -> sampled_data

A model overloads selectrows whenever it buys into the optional reformat front-end for data preprocessing. See reformat for details. The fallback assumes data is a tuple and calls selectrows(X, I) for each X in data, returning the results in a new tuple of the same length. This call makes sense when X is a table, abstract vector or abstract matrix. In the last two cases, a new object and not a view is returned.

source
MLJModelInterface.tableMethod
table(columntable; prototype=nothing)

Convert a named tuple of vectors or tuples columntable, into a table of the "preferred sink type" of prototype. This is often the type of prototype itself, when prototype is a sink; see the Tables.jl documentation. If prototype is not specified, then a named tuple of vectors is returned.

table(A::AbstractMatrix; names=nothing, prototype=nothing)

Wrap an abstract matrix A as a Tables.jl compatible table with the specified column names (a tuple of symbols). If names are not specified, names=(:x1, :x2, ..., :xn) is used, where n=size(A, 2).

If a prototype is specified, then the matrix is materialized as a table of the preferred sink type of prototype, rather than wrapped. Note that if prototype is not specified, then matrix(table(A)) is essentially a no-op.

source
MLJModelInterface.training_lossesMethod
MLJModelInterface.training_losses(model::M, report)

If M is an iterative model type which calculates training losses, implement this method to return an AbstractVector of the losses in historical order. If the model calculates scores instead, then the sign of the scores should be reversed.

The following trait overload is also required: MLJModelInterface.supports_training_losses(::Type{<:M}) = true.

source
MLJModelInterface.updateMethod
MLJModelInterface.update(model, verbosity, fitresult, cache, data...)

Models may optionally implement an update method. The fallback calls fit.

source
StatisticalTraits.deep_propertiesFunction
deep_properties(::Type{<:MLJType})

Given an MLJType subtype M, the value of this trait should be a tuple of any properties of M to be regarded as "deep".

When two instances of type M are to be tested for equality, in the sense of == or is_same_except, then the values of a "deep" property (whose values are assumed to be of composite type) are deemed to agree if all corresponding properties of those property values are ==.

Any property of M whose values are themselves of MLJType are "deep" automatically, and should not be included in the trait return value.

See also is_same_except

Example

Consider an MLJType subtype Foo, with a single field of type Bar which is not a subtype of MLJType:

mutable struct Bar
+Table{Union{AbstractVector{Count}, AbstractVector{Multiclass{2}}}}
source
MLJModelInterface.selectFunction
select(X, r, c)

Select element(s) of a table or matrix at row(s) r and column(s) c. An object of the sink type of X (or a matrix) is returned unless c is a single integer or symbol. In that case a vector is returned, unless r is a single integer, in which case a single element is returned.

See also: selectrows, selectcols.

source
MLJModelInterface.selectcolsFunction
selectcols(X, c)

Select single or multiple columns from a matrix or table X. If c is an abstract vector of integers or symbols, then the object returned is a table of the preferred sink type of typeof(X). If c is a single integer or column, then an AbstractVector is returned.

source
MLJModelInterface.selectrowsFunction
selectrows(X, r)

Select single or multiple rows from a table, abstract vector or matrix X. If X is tabular, the object returned is a table of the preferred sink type of typeof(X), even if only a single row is selected.

If the object is neither a table, abstract vector or matrix, X is returned and r is ignored.

source
MLJModelInterface.selectrowsMethod
MLJModelInterface.selectrows(::Model, I, data...) -> sampled_data

A model overloads selectrows whenever it buys into the optional reformat front-end for data preprocessing. See reformat for details. The fallback assumes data is a tuple and calls selectrows(X, I) for each X in data, returning the results in a new tuple of the same length. This call makes sense when X is a table, abstract vector or abstract matrix. In the last two cases, a new object and not a view is returned.

source
MLJModelInterface.tableMethod
table(columntable; prototype=nothing)

Convert a named tuple of vectors or tuples columntable, into a table of the "preferred sink type" of prototype. This is often the type of prototype itself, when prototype is a sink; see the Tables.jl documentation. If prototype is not specified, then a named tuple of vectors is returned.

table(A::AbstractMatrix; names=nothing, prototype=nothing)

Wrap an abstract matrix A as a Tables.jl compatible table with the specified column names (a tuple of symbols). If names are not specified, names=(:x1, :x2, ..., :xn) is used, where n=size(A, 2).

If a prototype is specified, then the matrix is materialized as a table of the preferred sink type of prototype, rather than wrapped. Note that if prototype is not specified, then matrix(table(A)) is essentially a no-op.

source
MLJModelInterface.training_lossesMethod
MLJModelInterface.training_losses(model::M, report)

If M is an iterative model type which calculates training losses, implement this method to return an AbstractVector of the losses in historical order. If the model calculates scores instead, then the sign of the scores should be reversed.

The following trait overload is also required: MLJModelInterface.supports_training_losses(::Type{<:M}) = true.

source
MLJModelInterface.updateMethod
MLJModelInterface.update(model, verbosity, fitresult, cache, data...)

Models may optionally implement an update method. The fallback calls fit.

source
StatisticalTraits.deep_propertiesFunction
deep_properties(::Type{<:MLJType})

Given an MLJType subtype M, the value of this trait should be a tuple of any properties of M to be regarded as "deep".

When two instances of type M are to be tested for equality, in the sense of == or is_same_except, then the values of a "deep" property (whose values are assumed to be of composite type) are deemed to agree if all corresponding properties of those property values are ==.

Any property of M whose values are themselves of MLJType are "deep" automatically, and should not be included in the trait return value.

See also is_same_except

Example

Consider an MLJType subtype Foo, with a single field of type Bar which is not a subtype of MLJType:

mutable struct Bar
     x::Int
 end
 
 mutable struct Foo <: MLJType
     bar::Bar
-end

Then the mutability of Foo implies Foo(1) != Foo(1) and so, by the definition == for MLJType objects (see is_same_except) we have

Bar(Foo(1)) != Bar(Foo(1))

However after the declaration

MLJModelInterface.deep_properties(::Type{<:Foo}) = (:bar,)

We have

Bar(Foo(1)) == Bar(Foo(1))
source
MLJModelInterface._model_cleanerMethod
_model_cleaner(modelname, defaults, constraints)

Build the expression of the cleaner associated with the constraints specified in a model def.

source
MLJModelInterface._model_constructorMethod
_model_constructor(modelname, params, defaults)

Build the expression of the keyword constructor associated with a model definition. When the constructor is called, the clean! function is called as well to check that parameter assignments are valid.

source
MLJModelInterface._process_model_defMethod
_process_model_def(modl, ex)

Take an expression defining a model (mutable struct Model ...) and unpack key elements for further processing:

  • Model name (modelname)
  • Names of parameters (params)
  • Default values (defaults)
  • Constraints (constraints)

When no default field value is given a heuristic is to guess an appropriate default (eg, zero for a Float64 parameter). To this end, the specified type expression is evaluated in the module modl.

source
MLJModelInterface._unpack!Method
_unpack!(ex, rep)

Internal function to allow to read a constraint given after a default value for a parameter and transform it in an executable condition (which is returned to be executed later). For instance if we have

alpha::Int = 0.5::(arg > 0.0)

Then it would transform the (arg > 0.0) in (alpha > 0.0) which is executable.

source
MLJModelInterface.doc_headerMethod
MLJModelInterface.doc_header(SomeModelType; augment=false)

Return a string suitable for interpolation in the document string of an MLJ model type. In the example given below, the header expands to something like this:

FooRegressor

A model type for constructing a foo regressor, based on FooRegressorPkg.jl.

From MLJ, the type can be imported using

FooRegressor = @load FooRegressor pkg=FooRegressorPkg

Construct an instance with default hyper-parameters using the syntax model = FooRegressor(). Provide keyword arguments to override hyper-parameter defaults, as in FooRegressor(a=...).

Ordinarily, doc_header is used in document strings defined after the model type definition, as doc_header assumes model traits (in particular, package_name and package_url) to be defined; see also MLJModelInterface.metadata_pkg.

Example

Suppose a model type and traits have been defined by:

mutable struct FooRegressor
+end

Then the mutability of Foo implies Foo(1) != Foo(1) and so, by the definition == for MLJType objects (see is_same_except) we have

Bar(Foo(1)) != Bar(Foo(1))

However after the declaration

MLJModelInterface.deep_properties(::Type{<:Foo}) = (:bar,)

We have

Bar(Foo(1)) == Bar(Foo(1))
source
MLJModelInterface._model_cleanerMethod
_model_cleaner(modelname, defaults, constraints)

Build the expression of the cleaner associated with the constraints specified in a model def.

source
MLJModelInterface._model_constructorMethod
_model_constructor(modelname, params, defaults)

Build the expression of the keyword constructor associated with a model definition. When the constructor is called, the clean! function is called as well to check that parameter assignments are valid.

source
MLJModelInterface._process_model_defMethod
_process_model_def(modl, ex)

Take an expression defining a model (mutable struct Model ...) and unpack key elements for further processing:

  • Model name (modelname)
  • Names of parameters (params)
  • Default values (defaults)
  • Constraints (constraints)

When no default field value is given a heuristic is to guess an appropriate default (eg, zero for a Float64 parameter). To this end, the specified type expression is evaluated in the module modl.

source
MLJModelInterface._unpack!Method
_unpack!(ex, rep)

Internal function to allow to read a constraint given after a default value for a parameter and transform it in an executable condition (which is returned to be executed later). For instance if we have

alpha::Int = 0.5::(arg > 0.0)

Then it would transform the (arg > 0.0) in (alpha > 0.0) which is executable.

source
MLJModelInterface.doc_headerMethod
MLJModelInterface.doc_header(SomeModelType; augment=false)

Return a string suitable for interpolation in the document string of an MLJ model type. In the example given below, the header expands to something like this:

FooRegressor

A model type for constructing a foo regressor, based on FooRegressorPkg.jl.

From MLJ, the type can be imported using

FooRegressor = @load FooRegressor pkg=FooRegressorPkg

Construct an instance with default hyper-parameters using the syntax model = FooRegressor(). Provide keyword arguments to override hyper-parameter defaults, as in FooRegressor(a=...).

Ordinarily, doc_header is used in document strings defined after the model type definition, as doc_header assumes model traits (in particular, package_name and package_url) to be defined; see also MLJModelInterface.metadata_pkg.

Example

Suppose a model type and traits have been defined by:

mutable struct FooRegressor
     a::Int
     b::Float64
 end
@@ -182,7 +182,7 @@
 
 """
 FooRegressor
-

Variation to augment existing document string

For models that have a native API with separate documentation, one may want to call doc_header(FooRegressor, augment=true) instead. In that case, the output will look like this:

From MLJ, the FooRegressor type can be imported using

FooRegressor = @load FooRegressor pkg=FooRegressorPkg

Construct an instance with default hyper-parameters using the syntax model = FooRegressor(). Provide keyword arguments to override hyper-parameter defaults, as in FooRegressor(a=...).

source
MLJModelInterface.feature_importancesFunction
feature_importances(model::M, fitresult, report)

For a given model of model type M supporting intrinsic feature importances, calculate the feature importances from the model's fitresult and report as an abstract vector of feature::Symbol => importance::Real pairs (e.g [:gender =>0.23, :height =>0.7, :weight => 0.1]).

New model implementations

The following trait overload is also required: MLJModelInterface.reports_feature_importances(::Type{<:M}) = true

If for some reason a model is sometimes unable to report feature importances then feature_importances should return all importances as 0.0, as in [:gender =>0.0, :height =>0.0, :weight => 0.0].

source
MLJModelInterface.flat_paramsMethod
flat_params(m::Model)

Deconstruct any Model instance model as a flat named tuple, keyed on property names. Properties of nested model instances are recursively exposed,.as shown in the example below. For most Model objects, properties are synonymous with fields, but this is not a hard requirement.

julia> using MLJModels
+

Variation to augment existing document string

For models that have a native API with separate documentation, one may want to call doc_header(FooRegressor, augment=true) instead. In that case, the output will look like this:

From MLJ, the FooRegressor type can be imported using

FooRegressor = @load FooRegressor pkg=FooRegressorPkg

Construct an instance with default hyper-parameters using the syntax model = FooRegressor(). Provide keyword arguments to override hyper-parameter defaults, as in FooRegressor(a=...).

source
MLJModelInterface.feature_importancesFunction
feature_importances(model::M, fitresult, report)

For a given model of model type M supporting intrinsic feature importances, calculate the feature importances from the model's fitresult and report as an abstract vector of feature::Symbol => importance::Real pairs (e.g [:gender =>0.23, :height =>0.7, :weight => 0.1]).

New model implementations

The following trait overload is also required: MLJModelInterface.reports_feature_importances(::Type{<:M}) = true

If for some reason a model is sometimes unable to report feature importances then feature_importances should return all importances as 0.0, as in [:gender =>0.0, :height =>0.0, :weight => 0.0].

source
MLJModelInterface.flat_paramsMethod
flat_params(m::Model)

Deconstruct any Model instance model as a flat named tuple, keyed on property names. Properties of nested model instances are recursively exposed,.as shown in the example below. For most Model objects, properties are synonymous with fields, but this is not a hard requirement.

julia> using MLJModels
 julia> using EnsembleModels
 julia> tree = (@load DecisionTreeClassifier pkg=DecisionTree)();
 
@@ -202,4 +202,4 @@
  rng = Random._GLOBAL_RNG(),
  n = 100,
  acceleration = CPU1{Nothing}(nothing),
- out_of_bag_measure = Any[],)
source
MLJModelInterface.reportMethod
MLJModelInterface.report(model, report_given_method)

Merge the reports in the dictionary report_given_method into a single property-accessible object. It is supposed that each key of the dictionary is either :fit or the name of an operation, such as :predict or :transform. Each value will be the report component returned by a training method (fit or update) dispatched on the model type, in the case of :fit, or the report component returned by an operation that supports reporting.

New model implementations

Overloading this method is optional, unless the model generates reports that are neither named tuples nor nothing.

Assuming each value in the report_given_method dictionary is either a named tuple or nothing, and there are no conflicts between the keys of the dictionary values (the individual reports), the fallback returns the usual named tuple merge of the dictionary values, ignoring any nothing value. If there is a key conflict, all operation reports are first wrapped in a named tuple of length one, as in (predict=predict_report,). A :fit report is never wrapped.

If any dictionary value is neither a named tuple nor nothing, it is first wrapped as (report=value, ) before merging.

source
MLJModelInterface.schemaMethod
schema(X)

Inspect the column types and scitypes of a tabular object. returns nothing if the column types and scitypes can't be inspected.

source
+ out_of_bag_measure = Any[],)
source
MLJModelInterface.reportMethod
MLJModelInterface.report(model, report_given_method)

Merge the reports in the dictionary report_given_method into a single property-accessible object. It is supposed that each key of the dictionary is either :fit or the name of an operation, such as :predict or :transform. Each value will be the report component returned by a training method (fit or update) dispatched on the model type, in the case of :fit, or the report component returned by an operation that supports reporting.

New model implementations

Overloading this method is optional, unless the model generates reports that are neither named tuples nor nothing.

Assuming each value in the report_given_method dictionary is either a named tuple or nothing, and there are no conflicts between the keys of the dictionary values (the individual reports), the fallback returns the usual named tuple merge of the dictionary values, ignoring any nothing value. If there is a key conflict, all operation reports are first wrapped in a named tuple of length one, as in (predict=predict_report,). A :fit report is never wrapped.

If any dictionary value is neither a named tuple nor nothing, it is first wrapped as (report=value, ) before merging.

source
MLJModelInterface.schemaMethod
schema(X)

Inspect the column types and scitypes of a tabular object. returns nothing if the column types and scitypes can't be inspected.

source
diff --git a/dev/serialization/index.html b/dev/serialization/index.html index 85cb00d..1b46318 100644 --- a/dev/serialization/index.html +++ b/dev/serialization/index.html @@ -1,2 +1,2 @@ -Serialization · MLJModelInterface

Serialization

New in MLJBase 0.20

The following API is incompatible with versions of MLJBase < 0.20, even for model implementations compatible with MLJModelInterface 1^

This section may be occasionally relevant when wrapping models implemented in languages other than Julia.

The MLJ user can serialize and deserialize machines, as she would any other julia object. (This user has the option of first removing data from the machine. See the Saving machines section of the MLJ manual for details.) However, a problem can occur if a model's fitresult (see The fit method) is not a persistent object. For example, it might be a C pointer that would have no meaning in a new Julia session.

If that is the case a model implementation needs to implement a save and restore method for switching between a fitresult and some persistent, serializable representation of that result.

The save method

MMI.save(model::SomeModel, fitresult; kwargs...) -> serializable_fitresult

Implement this method to return a persistent serializable representation of the fitresult component of the MMI.fit return value.

The fallback of save performs no action and returns fitresult.

The restore method

MMI.restore(model::SomeModel, serializable_fitresult) -> fitresult

Implement this method to reconstruct a valid fitresult (as would be returned by MMI.fit) from a persistent representation constructed using MMI.save as described above.

The fallback of restore performs no action and returns serializable_fitresult.

Example

Refer to the model implementations at MLJXGBoostInterface.jl.

+Serialization · MLJModelInterface

Serialization

New in MLJBase 0.20

The following API is incompatible with versions of MLJBase < 0.20, even for model implementations compatible with MLJModelInterface 1^

This section may be occasionally relevant when wrapping models implemented in languages other than Julia.

The MLJ user can serialize and deserialize machines, as she would any other julia object. (This user has the option of first removing data from the machine. See the Saving machines section of the MLJ manual for details.) However, a problem can occur if a model's fitresult (see The fit method) is not a persistent object. For example, it might be a C pointer that would have no meaning in a new Julia session.

If that is the case a model implementation needs to implement a save and restore method for switching between a fitresult and some persistent, serializable representation of that result.

The save method

MMI.save(model::SomeModel, fitresult; kwargs...) -> serializable_fitresult

Implement this method to return a persistent serializable representation of the fitresult component of the MMI.fit return value.

The fallback of save performs no action and returns fitresult.

The restore method

MMI.restore(model::SomeModel, serializable_fitresult) -> fitresult

Implement this method to reconstruct a valid fitresult (as would be returned by MMI.fit) from a persistent representation constructed using MMI.save as described above.

The fallback of restore performs no action and returns serializable_fitresult.

Example

Refer to the model implementations at MLJXGBoostInterface.jl.

diff --git a/dev/static_models/index.html b/dev/static_models/index.html index bc488ad..a20804c 100644 --- a/dev/static_models/index.html +++ b/dev/static_models/index.html @@ -1,2 +1,2 @@ -Static models · MLJModelInterface

Static models

A model type subtypes Static <: Unsupervised if it does not generalize to new data but nevertheless has hyperparameters. See the Static transformers section of the MLJ manual for examples. In the Static case, transform can have multiple arguments and input_scitype refers to the allowed scitype of the slurped data, even if there is only a single argument. For example, if the signature is transform(static_model, X1, X2), then the allowed input_scitype might be Tuple{Table(Continuous), Table(Continuous)}; if the signature is transform(static_model, X), the allowed input_scitype might be Tuple{Table(Continuous)}. The other traits are as for regular Unsupervised models.

Reporting byproducts of a static transformation

As a static transformer does not implement fit, the usual mechanism for creating a report is not available. Instead, byproducts of the computation performed by transform can be returned by transform itself by returning a pair (output, report) instead of just output. Here report should be a named tuple. In fact, any operation, (e.g., predict) can do this for any model type. However, this exceptional behavior must be flagged with an appropriate trait declaration, as in

MLJModelInterface.reporting_operations(::Type{<:SomeModelType}) = (:transform,)

If mach is a machine wrapping a model of this kind, then the report(mach) will include the report item form transform's output. For sample implementations, see this issue or the code for DBSCAN clustering.

+Static models · MLJModelInterface

Static models

A model type subtypes Static <: Unsupervised if it does not generalize to new data but nevertheless has hyperparameters. See the Static transformers section of the MLJ manual for examples. In the Static case, transform can have multiple arguments and input_scitype refers to the allowed scitype of the slurped data, even if there is only a single argument. For example, if the signature is transform(static_model, X1, X2), then the allowed input_scitype might be Tuple{Table(Continuous), Table(Continuous)}; if the signature is transform(static_model, X), the allowed input_scitype might be Tuple{Table(Continuous)}. The other traits are as for regular Unsupervised models.

Reporting byproducts of a static transformation

As a static transformer does not implement fit, the usual mechanism for creating a report is not available. Instead, byproducts of the computation performed by transform can be returned by transform itself by returning a pair (output, report) instead of just output. Here report should be a named tuple. In fact, any operation, (e.g., predict) can do this for any model type. However, this exceptional behavior must be flagged with an appropriate trait declaration, as in

MLJModelInterface.reporting_operations(::Type{<:SomeModelType}) = (:transform,)

If mach is a machine wrapping a model of this kind, then the report(mach) will include the report item form transform's output. For sample implementations, see this issue or the code for DBSCAN clustering.

diff --git a/dev/summary_of_methods/index.html b/dev/summary_of_methods/index.html index 6344189..d0c987c 100644 --- a/dev/summary_of_methods/index.html +++ b/dev/summary_of_methods/index.html @@ -12,4 +12,4 @@ MMI.is_pure_julia(::Type{<:SomeSupervisedModel}) = false MMI.package_license(::Type{<:SomeSupervisedModel}) = "unknown"

If SomeSupervisedModel supports sample weights or class weights, then instead of the fit above, one implements

MMI.fit(model::SomeSupervisedModel, verbosity, X, y, w=nothing) -> fitresult, cache, report

and, if appropriate

MMI.update(model::SomeSupervisedModel, verbosity, old_fitresult, old_cache, X, y, w=nothing) =
    MMI.fit(model, verbosity, X, y, w)

Additionally, if SomeSupervisedModel supports sample weights, one must declare

MMI.supports_weights(model::Type{<:SomeSupervisedModel}) = true

Optionally, an implementation may add a data front-end, for transforming user data (such as a table) into some model-specific format (such as a matrix), and/or add methods to specify how reformatted data is resampled. This alters the interpretation of the data arguments of fit, update and predict, whose number may also change. See Implementing a data front-end for details). A data front-end provides the MLJ user certain performance advantages when retraining a machine.

Third-party packages that interact directly with models using the MLJModelInterface.jl API, rather than through the machine interface, will also need to understand how the data front-end works, so they incorporate reformat into their fit/update/predict calls. See also this issue.

MLJModelInterface.reformat(model::SomeSupervisedModel, args...) = args
-MLJModelInterface.selectrows(model::SomeSupervisedModel, I, data...) = data

Optionally, to customized support for serialization of machines (see Serialization), overload

MMI.save(filename, model::SomeModel, fitresult; kwargs...) = fitresult

and possibly

MMI.restore(filename, model::SomeModel, serializable_fitresult) -> serializable_fitresult

These last two are unlikely to be needed if wrapping pure Julia code.

+MLJModelInterface.selectrows(model::SomeSupervisedModel, I, data...) = data

Optionally, to customized support for serialization of machines (see Serialization), overload

MMI.save(filename, model::SomeModel, fitresult; kwargs...) = fitresult

and possibly

MMI.restore(filename, model::SomeModel, serializable_fitresult) -> serializable_fitresult

These last two are unlikely to be needed if wrapping pure Julia code.

diff --git a/dev/supervised_models/index.html b/dev/supervised_models/index.html index e5ade05..f60f8c9 100644 --- a/dev/supervised_models/index.html +++ b/dev/supervised_models/index.html @@ -1,2 +1,2 @@ -Supervised models · MLJModelInterface

Supervised models

Mathematical assumptions

At present, MLJ's performance estimate functionality (resampling using evaluate/evaluate!) tacitly assumes that feature-label pairs of observations (X1, y1), (X2, y2), (X2, y2), ... are being modelled as identically independent random variables (i.i.d.), and constructs some kind of representation of an estimate of the conditional probability p(y | X) (y and X single observations). It may be that a model implementing the MLJ interface has the potential to make predictions under weaker assumptions (e.g., time series forecasting models). However the output of the compulsory predict method described below should be the output of the model under the i.i.d assumption.

In the future, newer methods may be introduced to handle weaker assumptions (see, e.g., The predict_joint method below).

The following sections were written with Supervised models in mind, but also cover material relevant to general models:

+Supervised models · MLJModelInterface

Supervised models

Mathematical assumptions

At present, MLJ's performance estimate functionality (resampling using evaluate/evaluate!) tacitly assumes that feature-label pairs of observations (X1, y1), (X2, y2), (X2, y2), ... are being modelled as identically independent random variables (i.i.d.), and constructs some kind of representation of an estimate of the conditional probability p(y | X) (y and X single observations). It may be that a model implementing the MLJ interface has the potential to make predictions under weaker assumptions (e.g., time series forecasting models). However the output of the compulsory predict method described below should be the output of the model under the i.i.d assumption.

In the future, newer methods may be introduced to handle weaker assumptions (see, e.g., The predict_joint method below).

The following sections were written with Supervised models in mind, but also cover material relevant to general models:

diff --git a/dev/supervised_models_with_transform/index.html b/dev/supervised_models_with_transform/index.html index 85f4351..b81f758 100644 --- a/dev/supervised_models_with_transform/index.html +++ b/dev/supervised_models_with_transform/index.html @@ -1,2 +1,2 @@ -Supervised models with a transform method · MLJModelInterface

Supervised models with a transform method

A supervised model may optionally implement a transform method, whose signature is the same as predict. In that case, the implementation should define a value for the output_scitype trait. A declaration

output_scitype(::Type{<:SomeSupervisedModel}) = T

is an assurance that scitype(transform(model, fitresult, Xnew)) <: T always holds, for any model of type SomeSupervisedModel.

A use-case for a transform method for a supervised model is a neural network that learns feature embeddings for categorical input features as part of overall training. Such a model becomes a transformer that other supervised models can use to transform the categorical features (instead of applying the higher-dimensional one-hot encoding representations).

+Supervised models with a transform method · MLJModelInterface

Supervised models with a transform method

A supervised model may optionally implement a transform method, whose signature is the same as predict. In that case, the implementation should define a value for the output_scitype trait. A declaration

output_scitype(::Type{<:SomeSupervisedModel}) = T

is an assurance that scitype(transform(model, fitresult, Xnew)) <: T always holds, for any model of type SomeSupervisedModel.

A use-case for a transform method for a supervised model is a neural network that learns feature embeddings for categorical input features as part of overall training. Such a model becomes a transformer that other supervised models can use to transform the categorical features (instead of applying the higher-dimensional one-hot encoding representations).

diff --git a/dev/the_fit_method/index.html b/dev/the_fit_method/index.html index e7479cf..fc6cd3b 100644 --- a/dev/the_fit_method/index.html +++ b/dev/the_fit_method/index.html @@ -1,2 +1,2 @@ -The fit method · MLJModelInterface

The fit method

A compulsory fit method returns three objects:

MMI.fit(model::SomeSupervisedModel, verbosity, X, y) -> fitresult, cache, report
  1. fitresult is the fitresult in the sense above (which becomes an argument for predict discussed below).

  2. report is a (possibly empty) NamedTuple, for example, report=(deviance=..., dof_residual=..., stderror=..., vcov=...). Any training-related statistics, such as internal estimates of the generalization error, and feature rankings, should be returned in the report tuple. How, or if, these are generated should be controlled by hyperparameters (the fields of model). Fitted parameters, such as the coefficients of a linear model, do not go in the report as they will be extractable from fitresult (and accessible to MLJ through the fitted_params method described below).

  3. The value of cache can be nothing, unless one is also defining an update method (see below). The Julia type of cache is not presently restricted.

Note

The fit (and update) methods should not mutate the model. If necessary, fit can create a deepcopy of model first.

It is not necessary for fit to provide type or dimension checks on X or y or to call clean! on the model; MLJ will carry out such checks.

The types of X and y are constrained by the input_scitype and target_scitype trait declarations; see Trait declarations below. (That is, unless a data front-end is implemented, in which case these traits refer instead to the arguments of the overloaded reformat method, and the types of X and y are determined by the output of reformat.)

The method fit should never alter hyperparameter values, the sole exception being fields of type <:AbstractRNG. If the package is able to suggest better hyperparameters, as a byproduct of training, return these in the report field.

The verbosity level (0 for silent) is for passing to the learning algorithm itself. A fit method wrapping such an algorithm should generally avoid doing any of its own logging.

Sample weight support. If supports_weights(::Type{<:SomeSupervisedModel}) has been declared true, then one instead implements the following variation on the above fit:

MMI.fit(model::SomeSupervisedModel, verbosity, X, y, w=nothing) -> fitresult, cache, report
+The fit method · MLJModelInterface

The fit method

A compulsory fit method returns three objects:

MMI.fit(model::SomeSupervisedModel, verbosity, X, y) -> fitresult, cache, report
  1. fitresult is the fitresult in the sense above (which becomes an argument for predict discussed below).

  2. report is a (possibly empty) NamedTuple, for example, report=(deviance=..., dof_residual=..., stderror=..., vcov=...). Any training-related statistics, such as internal estimates of the generalization error, and feature rankings, should be returned in the report tuple. How, or if, these are generated should be controlled by hyperparameters (the fields of model). Fitted parameters, such as the coefficients of a linear model, do not go in the report as they will be extractable from fitresult (and accessible to MLJ through the fitted_params method described below).

  3. The value of cache can be nothing, unless one is also defining an update method (see below). The Julia type of cache is not presently restricted.

Note

The fit (and update) methods should not mutate the model. If necessary, fit can create a deepcopy of model first.

It is not necessary for fit to provide type or dimension checks on X or y or to call clean! on the model; MLJ will carry out such checks.

The types of X and y are constrained by the input_scitype and target_scitype trait declarations; see Trait declarations below. (That is, unless a data front-end is implemented, in which case these traits refer instead to the arguments of the overloaded reformat method, and the types of X and y are determined by the output of reformat.)

The method fit should never alter hyperparameter values, the sole exception being fields of type <:AbstractRNG. If the package is able to suggest better hyperparameters, as a byproduct of training, return these in the report field.

The verbosity level (0 for silent) is for passing to the learning algorithm itself. A fit method wrapping such an algorithm should generally avoid doing any of its own logging.

Sample weight support. If supports_weights(::Type{<:SomeSupervisedModel}) has been declared true, then one instead implements the following variation on the above fit:

MMI.fit(model::SomeSupervisedModel, verbosity, X, y, w=nothing) -> fitresult, cache, report
diff --git a/dev/the_fitted_params_method/index.html b/dev/the_fitted_params_method/index.html index 81033e2..14aad51 100644 --- a/dev/the_fitted_params_method/index.html +++ b/dev/the_fitted_params_method/index.html @@ -1,2 +1,2 @@ -The fitted_params method · MLJModelInterface

The fitted_params method

A fitted_params method may be optionally overloaded. Its purpose is to provide MLJ access to a user-friendly representation of the learned parameters of the model (as opposed to the hyperparameters). They must be extractable from fitresult.

MMI.fitted_params(model::SomeSupervisedModel, fitresult) -> friendly_fitresult::NamedTuple

For a linear model, for example, one might declare something like friendly_fitresult=(coefs=[...], bias=...).

The fallback is to return (fitresult=fitresult,).

+The fitted_params method · MLJModelInterface

The fitted_params method

A fitted_params method may be optionally overloaded. Its purpose is to provide MLJ access to a user-friendly representation of the learned parameters of the model (as opposed to the hyperparameters). They must be extractable from fitresult.

MMI.fitted_params(model::SomeSupervisedModel, fitresult) -> friendly_fitresult::NamedTuple

For a linear model, for example, one might declare something like friendly_fitresult=(coefs=[...], bias=...).

The fallback is to return (fitresult=fitresult,).

diff --git a/dev/the_model_type_hierarchy/index.html b/dev/the_model_type_hierarchy/index.html index aa2c08c..598c98c 100644 --- a/dev/the_model_type_hierarchy/index.html +++ b/dev/the_model_type_hierarchy/index.html @@ -1,4 +1,4 @@ The model type hierarchy · MLJModelInterface

The model type hierarchy

A model is an object storing hyperparameters associated with some machine learning algorithm, and that is all. In MLJ, hyperparameters include configuration parameters, like the number of threads, and special instructions, such as "compute feature rankings", which may or may not affect the final learning outcome. However, the logging level (verbosity below) is excluded. Learned parameters (such as the coefficients in a linear model) have no place in the model struct.

The name of the Julia type associated with a model indicates the associated algorithm (e.g., DecisionTreeClassifier). The outcome of training a learning algorithm is called a fitresult. For ordinary multivariate regression, for example, this would be the coefficients and intercept. For a general supervised model, it is the (generally minimal) information needed to make new predictions.

The ultimate supertype of all models is MLJModelInterface.Model, which has two abstract subtypes:

abstract type Supervised <: Model end
 abstract type Unsupervised <: Model end

Supervised models are further divided according to whether they are able to furnish probabilistic predictions of the target (which they will then do by default) or directly predict "point" estimates, for each new input pattern:

abstract type Probabilistic <: Supervised end
-abstract type Deterministic <: Supervised end

Further division of model types is realized through Trait declarations.

Associated with every concrete subtype of Model there must be a fit method, which implements the associated algorithm to produce the fitresult. Additionally, every Supervised model has a predict method, while Unsupervised models must have a transform method. More generally, methods such as these, that are dispatched on a model instance and a fitresult (plus other data), are called operations. Probabilistic supervised models optionally implement a predict_mode operation (in the case of classifiers) or a predict_mean and/or predict_median operations (in the case of regressors) although MLJModelInterface also provides fallbacks that will suffice in most cases. Unsupervised models may implement an inverse_transform operation.

+abstract type Deterministic <: Supervised end

Further division of model types is realized through Trait declarations.

Associated with every concrete subtype of Model there must be a fit method, which implements the associated algorithm to produce the fitresult. Additionally, every Supervised model has a predict method, while Unsupervised models must have a transform method. More generally, methods such as these, that are dispatched on a model instance and a fitresult (plus other data), are called operations. Probabilistic supervised models optionally implement a predict_mode operation (in the case of classifiers) or a predict_mean and/or predict_median operations (in the case of regressors) although MLJModelInterface also provides fallbacks that will suffice in most cases. Unsupervised models may implement an inverse_transform operation.

diff --git a/dev/the_predict_joint_method/index.html b/dev/the_predict_joint_method/index.html index 4609348..82d00ab 100644 --- a/dev/the_predict_joint_method/index.html +++ b/dev/the_predict_joint_method/index.html @@ -1,2 +1,2 @@ -The predict_joint method · MLJModelInterface

The predict_joint method

Experimental

The following API is experimental. It is subject to breaking changes during minor or major releases without warning.

MMI.predict_joint(model::SomeSupervisedModel, fitresult, Xnew) -> yhat

Any Probabilistic model type SomeModelmay optionally implement a predict_joint method, which has the same signature as predict, but whose predictions are a single distribution (rather than a vector of per-observation distributions).

Specifically, the output yhat of predict_joint should be an instance of Distributions.Sampleable{<:Multivariate,V}, where scitype(V) = target_scitype(SomeModel) and samples have length n, where n is the number of observations in Xnew.

If a new model type subtypes JointProbabilistic <: Probabilistic then implementation of predict_joint is compulsory.

+The predict_joint method · MLJModelInterface

The predict_joint method

Experimental

The following API is experimental. It is subject to breaking changes during minor or major releases without warning.

MMI.predict_joint(model::SomeSupervisedModel, fitresult, Xnew) -> yhat

Any Probabilistic model type SomeModelmay optionally implement a predict_joint method, which has the same signature as predict, but whose predictions are a single distribution (rather than a vector of per-observation distributions).

Specifically, the output yhat of predict_joint should be an instance of Distributions.Sampleable{<:Multivariate,V}, where scitype(V) = target_scitype(SomeModel) and samples have length n, where n is the number of observations in Xnew.

If a new model type subtypes JointProbabilistic <: Probabilistic then implementation of predict_joint is compulsory.

diff --git a/dev/the_predict_method/index.html b/dev/the_predict_method/index.html index 96f5fac..e743302 100644 --- a/dev/the_predict_method/index.html +++ b/dev/the_predict_method/index.html @@ -18,4 +18,4 @@ y = ybig[1:6]

Your fit method has bundled the first element of y with the fitresult to make it available to predict for purposes of tracking the complete pool of classes. Let's call this an_element = y[1]. Then, supposing the corresponding probabilities of the observed classes [:a, :b] are in an n x 2 matrix probs (where n the number of rows of Xnew) then you return

yhat = MLJModelInterface.UnivariateFinite([:a, :b], probs, pool=an_element)

This object automatically assigns zero-probability to the unseen class :rare (i.e., pdf.(yhat, :rare) works and returns a zero vector). If you would like to assign :rare non-zero probabilities, simply add it to the first vector (the support) and supply a larger probs matrix.

In a binary classification problem, it suffices to specify a single vector of probabilities, provided you specify augment=true, as in the following example, and note carefully that these probabilities are associated with the last (second) class you specify in the constructor:

y = categorical([:TRUE, :FALSE, :FALSE, :TRUE, :TRUE])
 an_element = y[1]
 probs = rand(10)
-yhat = MLJModelInterface.UnivariateFinite([:FALSE, :TRUE], probs, augment=true, pool=an_element)

The constructor has a lot of options, including passing a dictionary instead of vectors. See CategoricalDistributions.UnivariateFinite for details.

See LinearBinaryClassifier for an example of a Probabilistic classifier implementation.

Important note on binary classifiers. There is no "Binary" scitype distinct from Multiclass{2} or OrderedFactor{2}; Binary is just an alias for Union{Multiclass{2},OrderedFactor{2}}. The target_scitype of a binary classifier will generally be AbstractVector{<:Binary} and according to the mlj scitype convention, elements of y have type CategoricalValue, and not Bool. See BinaryClassifier for an example.

Report items returned by predict

A predict method, or other operation such as transform, can contribute to the report accessible in any machine associated with a model. See Reporting byproducts of a static transformation below for details.

+yhat = MLJModelInterface.UnivariateFinite([:FALSE, :TRUE], probs, augment=true, pool=an_element)

The constructor has a lot of options, including passing a dictionary instead of vectors. See CategoricalDistributions.UnivariateFinite for details.

See LinearBinaryClassifier for an example of a Probabilistic classifier implementation.

Important note on binary classifiers. There is no "Binary" scitype distinct from Multiclass{2} or OrderedFactor{2}; Binary is just an alias for Union{Multiclass{2},OrderedFactor{2}}. The target_scitype of a binary classifier will generally be AbstractVector{<:Binary} and according to the mlj scitype convention, elements of y have type CategoricalValue, and not Bool. See BinaryClassifier for an example.

Report items returned by predict

A predict method, or other operation such as transform, can contribute to the report accessible in any machine associated with a model. See Reporting byproducts of a static transformation below for details.

diff --git a/dev/training_losses/index.html b/dev/training_losses/index.html index cbdebc9..dccfc18 100644 --- a/dev/training_losses/index.html +++ b/dev/training_losses/index.html @@ -1,2 +1,2 @@ -Training losses · MLJModelInterface

Training losses

MLJModelInterface.training_lossesFunction
MLJModelInterface.training_losses(model::M, report)

If M is an iterative model type which calculates training losses, implement this method to return an AbstractVector of the losses in historical order. If the model calculates scores instead, then the sign of the scores should be reversed.

The following trait overload is also required: MLJModelInterface.supports_training_losses(::Type{<:M}) = true.

source

Trait values can also be set using the metadata_model method, see below.

+Training losses · MLJModelInterface

Training losses

MLJModelInterface.training_lossesFunction
MLJModelInterface.training_losses(model::M, report)

If M is an iterative model type which calculates training losses, implement this method to return an AbstractVector of the losses in historical order. If the model calculates scores instead, then the sign of the scores should be reversed.

The following trait overload is also required: MLJModelInterface.supports_training_losses(::Type{<:M}) = true.

source

Trait values can also be set using the metadata_model method, see below.

diff --git a/dev/trait_declarations/index.html b/dev/trait_declarations/index.html index 684b07c..62f066f 100644 --- a/dev/trait_declarations/index.html +++ b/dev/trait_declarations/index.html @@ -24,8 +24,8 @@ package_url="https://github.com/KristofferC/NearestNeighbors.jl", is_pure_julia=true, package_license="MIT", - is_wrapper=false)source
MLJModelInterface.metadata_modelFunction
metadata_model(T; args...)

Helper function to write the metadata for a model T.

Keywords

  • input_scitype=Unknown: allowed scientific type of the input data
  • target_scitype=Unknown: allowed scitype of the target (supervised)
  • output_scitype=Unknown: allowed scitype of the transformed data (unsupervised)
  • supports_weights=false: whether the model supports sample weights
  • supports_class_weights=false: whether the model supports class weights
  • load_path="unknown": where the model is (usually PackageName.ModelName)
  • human_name=nothing: human name of the model
  • supports_training_losses=nothing: whether the (necessarily iterative) model can report training losses
  • reports_feature_importances=nothing: whether the model reports feature importances

Example

metadata_model(KNNRegressor,
+    is_wrapper=false)
source
MLJModelInterface.metadata_modelFunction
metadata_model(T; args...)

Helper function to write the metadata for a model T.

Keywords

  • input_scitype=Unknown: allowed scientific type of the input data
  • target_scitype=Unknown: allowed scitype of the target (supervised)
  • output_scitype=Unknown: allowed scitype of the transformed data (unsupervised)
  • supports_weights=false: whether the model supports sample weights
  • supports_class_weights=false: whether the model supports class weights
  • load_path="unknown": where the model is (usually PackageName.ModelName)
  • human_name=nothing: human name of the model
  • supports_training_losses=nothing: whether the (necessarily iterative) model can report training losses
  • reports_feature_importances=nothing: whether the model reports feature importances

Example

metadata_model(KNNRegressor,
     input_scitype=MLJModelInterface.Table(MLJModelInterface.Continuous),
     target_scitype=AbstractVector{MLJModelInterface.Continuous},
     supports_weights=true,
-    load_path="NearestNeighbors.KNNRegressor")
source
+ load_path="NearestNeighbors.KNNRegressor")source diff --git a/dev/type_declarations/index.html b/dev/type_declarations/index.html index 721e9e3..9cc938f 100644 --- a/dev/type_declarations/index.html +++ b/dev/type_declarations/index.html @@ -29,4 +29,4 @@ a::Int = -1::(_ > -2) end

But this does:

@mlj_model mutable struct Bar
     a::Int = (-)(1)::(_ > -2)
-end
+end diff --git a/dev/unsupervised_models/index.html b/dev/unsupervised_models/index.html index 3e36895..5e1cb41 100644 --- a/dev/unsupervised_models/index.html +++ b/dev/unsupervised_models/index.html @@ -1,2 +1,2 @@ -Unsupervised models · MLJModelInterface

Unsupervised models

Unsupervised models implement the MLJ model interface in a very similar fashion. The main differences are:

  • The fit method, which still returns (fitresult, cache, report) will typically have only one training argument X, as in MLJModelInterface.fit(model, verbosity, X), although this is not a hard requirement. For example, a feature selection tool (wrapping some supervised model) might also include a target y as input. Furthermore, in the case of models that subtype Static <: Unsupervised (see Static models) fit has no training arguments at all, but does not need to be implemented as a fallback returns (nothing, nothing, nothing).

  • A transform and/or predict method is implemented, and has the same signature as predict does in the supervised case, as in MLJModelInterface.transform(model, fitresult, Xnew). However, it may only have one data argument Xnew, unless model <: Static, in which case there is no restriction. A use-case for predict is K-means clustering that predicts labels and transforms input features into a space of lower dimension. See the Transformers that also predict section of the MLJ manual for an example.

  • The target_scitype refers to the output of predict, if implemented. A new trait, output_scitype, is for the output of transform. Unless the model is Static (see Static models) the trait input_scitype is for the single data argument of transform (and predict, if implemented). If fit has more than one data argument, you must overload the trait fit_data_scitype, which bounds the allowed data passed to fit(model, verbosity, data...) and will always be a Tuple type.

  • An inverse_transform can be optionally implemented. The signature is the same as transform, as in MLJModelInterface.inverse_transform(model, fitresult, Xout), which:

    • must make sense for any Xout for which scitype(Xout) <: output_scitype(SomeSupervisedModel) (see below); and
    • must return an object Xin satisfying scitype(Xin) <: input_scitype(SomeSupervisedModel).

For sample implementatations, see MLJ's built-in transformers and the clustering models at MLJClusteringInterface.jl.

+Unsupervised models · MLJModelInterface

Unsupervised models

Unsupervised models implement the MLJ model interface in a very similar fashion. The main differences are:

  • The fit method, which still returns (fitresult, cache, report) will typically have only one training argument X, as in MLJModelInterface.fit(model, verbosity, X), although this is not a hard requirement. For example, a feature selection tool (wrapping some supervised model) might also include a target y as input. Furthermore, in the case of models that subtype Static <: Unsupervised (see Static models) fit has no training arguments at all, but does not need to be implemented as a fallback returns (nothing, nothing, nothing).

  • A transform and/or predict method is implemented, and has the same signature as predict does in the supervised case, as in MLJModelInterface.transform(model, fitresult, Xnew). However, it may only have one data argument Xnew, unless model <: Static, in which case there is no restriction. A use-case for predict is K-means clustering that predicts labels and transforms input features into a space of lower dimension. See the Transformers that also predict section of the MLJ manual for an example.

  • The target_scitype refers to the output of predict, if implemented. A new trait, output_scitype, is for the output of transform. Unless the model is Static (see Static models) the trait input_scitype is for the single data argument of transform (and predict, if implemented). If fit has more than one data argument, you must overload the trait fit_data_scitype, which bounds the allowed data passed to fit(model, verbosity, data...) and will always be a Tuple type.

  • An inverse_transform can be optionally implemented. The signature is the same as transform, as in MLJModelInterface.inverse_transform(model, fitresult, Xout), which:

    • must make sense for any Xout for which scitype(Xout) <: output_scitype(SomeSupervisedModel) (see below); and
    • must return an object Xin satisfying scitype(Xin) <: input_scitype(SomeSupervisedModel).

For sample implementatations, see MLJ's built-in transformers and the clustering models at MLJClusteringInterface.jl.

diff --git a/dev/where_to_put_code/index.html b/dev/where_to_put_code/index.html index 179dbc8..85fcca9 100644 --- a/dev/where_to_put_code/index.html +++ b/dev/where_to_put_code/index.html @@ -1,2 +1,2 @@ -Where to place code implementing new models · MLJModelInterface

Where to place code implementing new models

Note that different packages can implement models having the same name without causing conflicts, although an MLJ user cannot simultaneously load two such models.

There are two options for making a new model implementation available to all MLJ users:

  1. Native implementations (preferred option). The implementation code lives in the same package that contains the learning algorithms implementing the interface. An example is EvoTrees.jl. In this case, it is sufficient to open an issue at MLJ requesting the package to be registered with MLJ. Registering a package allows the MLJ user to access its models' metadata and to selectively load them.

  2. Separate interface package. Implementation code lives in a separate interface package, which has the algorithm-providing package as a dependency. See the template repository MLJExampleInterface.jl.

Additionally, one needs to ensure that the implementation code defines the package_name and load_path model traits appropriately, so that MLJ's @load macro can find the necessary code (see MLJModels/src for examples).

+Where to place code implementing new models · MLJModelInterface

Where to place code implementing new models

Note that different packages can implement models having the same name without causing conflicts, although an MLJ user cannot simultaneously load two such models.

There are two options for making a new model implementation available to all MLJ users:

  1. Native implementations (preferred option). The implementation code lives in the same package that contains the learning algorithms implementing the interface. An example is EvoTrees.jl. In this case, it is sufficient to open an issue at MLJ requesting the package to be registered with MLJ. Registering a package allows the MLJ user to access its models' metadata and to selectively load them.

  2. Separate interface package. Implementation code lives in a separate interface package, which has the algorithm-providing package as a dependency. See the template repository MLJExampleInterface.jl.

Additionally, one needs to ensure that the implementation code defines the package_name and load_path model traits appropriately, so that MLJ's @load macro can find the necessary code (see MLJModels/src for examples).