Skip to content

Commit

Permalink
update mix.exs docs
Browse files Browse the repository at this point in the history
  • Loading branch information
acalejos committed Jan 25, 2024
1 parent 53ed029 commit 4b83629
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 43 deletions.
20 changes: 20 additions & 0 deletions lib/exgboost.ex
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ defmodule EXGBoost do
Returns a map containing information about the build.
"""
@spec xgboost_build_info() :: map()
@doc type: :system
def xgboost_build_info,
do: EXGBoost.NIF.xgboost_build_info() |> Internal.unwrap!() |> Jason.decode!()

Expand All @@ -166,6 +167,7 @@ defmodule EXGBoost do
Returns a 3-tuple in the form of `{major, minor, patch}`.
"""
@spec xgboost_version() :: {integer(), integer(), integer()} | {:error, String.t()}
@doc type: :system
def xgboost_version, do: EXGBoost.NIF.xgboost_version() |> Internal.unwrap!()

@doc """
Expand All @@ -176,6 +178,7 @@ defmodule EXGBoost do
for the full list of parameters supported in the global configuration.
"""
@spec set_config(map()) :: :ok | {:error, String.t()}
@doc type: :system
def set_config(%{} = config) do
config = EXGBoost.Parameters.validate_global!(config)
EXGBoost.NIF.set_global_config(Jason.encode!(config)) |> Internal.unwrap!()
Expand All @@ -189,6 +192,7 @@ defmodule EXGBoost do
for the full list of parameters supported in the global configuration.
"""
@spec get_config() :: map()
@doc type: :system
def get_config do
EXGBoost.NIF.get_global_config() |> Internal.unwrap!() |> Jason.decode!()
end
Expand Down Expand Up @@ -237,6 +241,7 @@ defmodule EXGBoost do
* `opts` - Refer to `EXGBoost.Parameters` for the full list of options.
"""
@spec train(Nx.Tensor.t(), Nx.Tensor.t(), Keyword.t()) :: EXGBoost.Booster.t()
@doc type: :train_pred
def train(x, y, opts \\ []) do
x = Nx.concatenate(x)
y = Nx.concatenate(y)
Expand Down Expand Up @@ -301,6 +306,7 @@ defmodule EXGBoost do
Returns an Nx.Tensor containing the predictions.
"""
@doc type: :train_pred
def predict(%Booster{} = bst, x, opts \\ []) do
x = Nx.concatenate(x)
{dmat_opts, opts} = Keyword.split(opts, Internal.dmatrix_feature_opts())
Expand Down Expand Up @@ -331,6 +337,7 @@ defmodule EXGBoost do
Returns an Nx.Tensor containing the predictions.
"""
@doc type: :train_pred
def inplace_predict(%Booster{} = boostr, data, opts \\ []) do
opts =
Keyword.validate!(opts,
Expand Down Expand Up @@ -457,6 +464,7 @@ defmodule EXGBoost do
## Options
#{NimbleOptions.docs(@write_schema)}
"""
@doc type: :serialization
@spec write_model(Booster.t(), String.t()) :: :ok | {:error, String.t()}
def write_model(%Booster{} = booster, path, opts \\ []) do
opts = NimbleOptions.validate!(opts, @write_schema)
Expand All @@ -466,6 +474,7 @@ defmodule EXGBoost do
@doc """
Read a model from a file and return the Booster.
"""
@doc type: :serialization
@spec read_model(String.t()) :: EXGBoost.Booster.t()
def read_model(path) do
EXGBoost.Booster.load(path, deserialize: :model)
Expand All @@ -478,6 +487,7 @@ defmodule EXGBoost do
#{NimbleOptions.docs(@dump_schema)}
"""
@spec dump_model(Booster.t()) :: binary()
@doc type: :serialization
def dump_model(%Booster{} = booster, opts \\ []) do
opts = NimbleOptions.validate!(opts, @dump_schema)
EXGBoost.Booster.save(booster, opts ++ [serialize: :model, to: :buffer])
Expand All @@ -487,6 +497,7 @@ defmodule EXGBoost do
Read a model from a buffer and return the Booster.
"""
@spec load_model(binary()) :: EXGBoost.Booster.t()
@doc type: :serialization
def load_model(buffer) do
EXGBoost.Booster.load(buffer, deserialize: :model, from: :buffer)
end
Expand All @@ -498,6 +509,7 @@ defmodule EXGBoost do
#{NimbleOptions.docs(@write_schema)}
"""
@spec write_config(Booster.t(), String.t()) :: :ok | {:error, String.t()}
@doc type: :serialization
def write_config(%Booster{} = booster, path, opts \\ []) do
opts = NimbleOptions.validate!(opts, @write_schema)
EXGBoost.Booster.save(booster, opts ++ [path: path, serialize: :config])
Expand All @@ -510,6 +522,7 @@ defmodule EXGBoost do
#{NimbleOptions.docs(@dump_schema)}
"""
@spec dump_config(Booster.t()) :: binary()
@doc type: :serialization
def dump_config(%Booster{} = booster, opts \\ []) do
opts = NimbleOptions.validate!(opts, @dump_schema)
EXGBoost.Booster.save(booster, opts ++ [serialize: :config, to: :buffer])
Expand All @@ -522,6 +535,7 @@ defmodule EXGBoost do
#{NimbleOptions.docs(@load_schema)}
"""
@spec read_config(String.t()) :: EXGBoost.Booster.t()
@doc type: :serialization
def read_config(path, opts \\ []) do
opts = NimbleOptions.validate!(opts, @load_schema)
EXGBoost.Booster.load(path, opts ++ [deserialize: :config])
Expand All @@ -534,6 +548,7 @@ defmodule EXGBoost do
#{NimbleOptions.docs(@load_schema)}
"""
@spec load_config(binary()) :: EXGBoost.Booster.t()
@doc type: :serialization
def load_config(buffer, opts \\ []) do
opts = NimbleOptions.validate!(opts, @load_schema)
EXGBoost.Booster.load(buffer, opts ++ [deserialize: :config, from: :buffer])
Expand All @@ -546,6 +561,7 @@ defmodule EXGBoost do
#{NimbleOptions.docs(@write_schema)}
"""
@spec write_weights(Booster.t(), String.t()) :: :ok | {:error, String.t()}
@doc type: :serialization
def write_weights(%Booster{} = booster, path, opts \\ []) do
opts = NimbleOptions.validate!(opts, @write_schema)
EXGBoost.Booster.save(booster, opts ++ [path: path, serialize: :weights])
Expand All @@ -558,6 +574,7 @@ defmodule EXGBoost do
#{NimbleOptions.docs(@dump_schema)}
"""
@spec dump_weights(Booster.t()) :: binary()
@doc type: :serialization
def dump_weights(%Booster{} = booster, opts \\ []) do
opts = NimbleOptions.validate!(opts, @dump_schema)
EXGBoost.Booster.save(booster, opts ++ [serialize: :weights, to: :buffer])
Expand All @@ -567,6 +584,7 @@ defmodule EXGBoost do
Read a model's trained parameters from a file and return the Booster.
"""
@spec read_weights(String.t()) :: EXGBoost.Booster.t()
@doc type: :serialization
def read_weights(path) do
EXGBoost.Booster.load(path, deserialize: :weights)
end
Expand All @@ -575,6 +593,7 @@ defmodule EXGBoost do
Read a model's trained parameters from a buffer and return the Booster.
"""
@spec load_weights(binary()) :: EXGBoost.Booster.t()
@doc type: :serialization
def load_weights(buffer) do
EXGBoost.Booster.load(buffer, deserialize: :weights, from: :buffer)
end
Expand All @@ -588,6 +607,7 @@ defmodule EXGBoost do
* `:path` - the path to save the graphic to. If not provided, the graphic is returned as a VegaLite spec.
* `:opts` - additional options to pass to `EXGBoost.Plotting.plot/2`. See `EXGBoost.Plotting` for more information.
"""
@doc type: :plotting
def plot_tree(booster, opts \\ []) do
{path, opts} = Keyword.pop(opts, :path)
{save_opts, opts} = Keyword.split(opts, [:format, :local_npm_prefix])
Expand Down
15 changes: 15 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,21 @@ defmodule EXGBoost.MixProject do
groups_for_extras: [
Notebooks: Path.wildcard("notebooks/*.livemd")
],
groups_for_functions: [
"System / Native Config": &(&1[:type] == :system),
"Training & Prediction": &(&1[:type] == :train_pred),
Serialization: &(&1[:type] == :serialization),
Plotting: &(&1[:type] == :plotting)
],
groups_for_modules: [
Plotting: [EXGBoost.Plotting, EXGBoost.Plotting.Styles],
Training: [
EXGBoost.Training,
EXGBoost.Training.Callback,
EXGBoost.Booster,
EXGBoost.Parameters
]
],
before_closing_body_tag: &before_closing_body_tag/1
]
end
Expand Down
78 changes: 35 additions & 43 deletions notebooks/plotting.livemd
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
# Plotting in EXGBoost

```elixir
# Mix.install(
# [
# {:exgboost, "~> 0.5", env: :dev}
# ]
# )

Mix.install([
{:exgboost, path: "/Users/andres/Documents/exgboost"},
{:nx, "~> 0.5"},
{:scidata, "~> 0.1"},
{:scholar, "~> 0.1"},
{:kino_vega_lite, "~> 0.1"}
])
Mix.install(
[
{:exgboost, "~> 0.5", env: :dev}
]
)

# This assumed you launch this livebook from its location in the exgboost/notebooks folder
```
Expand All @@ -32,24 +24,24 @@ This notebook will go over some of the details of the `EXGBoost.Plotting` module

There are 2 main APIs exposed to control plotting in `EXGBoost`:

* Top-level API (`EXGBoost.plot_tree/2`)
- Top-level API (`EXGBoost.plot_tree/2`)

* Using predefined styles
* Defining custom styles
* Mix of the first 2
- Using predefined styles
- Defining custom styles
- Mix of the first 2

* `EXBoost.Plotting` module API
- `EXBoost.Plotting` module API

* Use the Vega `data` spec defined in `EXGBoost.get_data_spec/2`
* Define your own Vega spec using the data from either `EXGBoost.Plotting.to_tabular/1` or some other means
- Use the Vega `data` spec defined in `EXGBoost.Plotting.get_data_spec/2`
- Define your own Vega spec using the data from either `EXGBoost.Plotting.to_tabular/1` or some other means

We will walk through each of these in detail.

Regardless of which API you choose to use, it is helpful to understand how the plotting module works (althought the higher-level API you choose to work with the less important it becomes).

## Implementation Details

The plotting functionality provided in `EXGBoost` is powered by the [Vega](https://vega.github.io/vega/) JavaScript library and the Elixir [`VegaLite`](https://hexdocs.pm/vega_lite/VegaLite.html) library which provides the piping to interop with the JavaScript libraries. **We do not actually much use the Elixir API provided by the Elixir VegaLite library. It is mainly used for the purposes of rendering.**
The plotting functionality provided in `EXGBoost` is powered by the [Vega](https://vega.github.io/vega/) JavaScript library and the Elixir [`VegaLite`](https://hexdocs.pm/vega_lite/VegaLite.html) library which provides the piping to interop with the JavaScript libraries. **We do not actually much use the Elixir API provided by the Elixir VegaLite library. It is mainly used for the purposes of rendering.**

Vega is a plotting library built on top of the very powerful [D3](https://d3js.org/) JavaScript library. Vega visualizations are defined according to the respective JSON Schema specification. Vega-Lite offers a [reduced schema](https://vega.github.io/schema/vega-lite/v5.json) compared to the [full Vega spec](https://vega.github.io/schema/vega/v5.json). `EXGBoost.Plotting` leverages several transforms which are not available in the reduced Vega-Lite schema, which is the reason for targeting the lower-level API.

Expand Down Expand Up @@ -77,7 +69,7 @@ y_test = Nx.tensor(y_test)

## Train Your Booster

Now go ahead and train your booster. We will use `early_stopping_rounds: 1` because we're not interested in the accuracy of the booster for this demonstration (*Note that we need to set `evals` to use early stopping*).
Now go ahead and train your booster. We will use `early_stopping_rounds: 1` because we're not interested in the accuracy of the booster for this demonstration (_Note that we need to set `evals` to use early stopping_).

You will notice that `EXGBoost` also provides an implementation for `Kino.Render` so that `EXGBoost.Booster`s are rendered as a plot by default.

Expand Down Expand Up @@ -120,20 +112,20 @@ This API uses [Vega `Mark`s](https://vega.github.io/vega/docs/marks/) to describ

The plot is composed of the following parts:

* Top-level keys: Options controlling parts of the plot outside of direct control of a `Mark`, such as `:padding`, `:autosize`, etc. Accepts any Vega top-level [top-level key](https://vega.github.io/vega/docs/specification/) in addition to several specific to this API (scuh as `:style` and `:depth`).
* `:leaves`: `Mark` specifying the leaf nodes of the tree
* `:text`: [Text Mark](https://vega.github.io/vega/docs/marks/text/)
* `:rect`: [Rect Mark](https://vega.github.io/vega/docs/marks/rect/)
* `:splits` `Mark` specifying the split (or inner / decision) nodes of the tree
* `:text`: [Text Mark](https://vega.github.io/vega/docs/marks/text/)
* `:rect`: [Rect Mark](https://vega.github.io/vega/docs/marks/rect/)
* `:children`: [Text Mark](https://vega.github.io/vega/docs/marks/text/) for the child count
* `:yes`
* `:path`: [Path Mark](https://vega.github.io/vega/docs/marks/path/)
* `:text`: [Text Mark](https://vega.github.io/vega/docs/marks/text/)
* `:no`
* `:path`: [Path Mark](https://vega.github.io/vega/docs/marks/path/)
* `:text`: [Text Mark](https://vega.github.io/vega/docs/marks/text/)
- Top-level keys: Options controlling parts of the plot outside of direct control of a `Mark`, such as `:padding`, `:autosize`, etc. Accepts any Vega top-level [top-level key](https://vega.github.io/vega/docs/specification/) in addition to several specific to this API (scuh as `:style` and `:depth`).
- `:leaves`: `Mark` specifying the leaf nodes of the tree
- `:text`: [Text Mark](https://vega.github.io/vega/docs/marks/text/)
- `:rect`: [Rect Mark](https://vega.github.io/vega/docs/marks/rect/)
- `:splits` `Mark` specifying the split (or inner / decision) nodes of the tree
- `:text`: [Text Mark](https://vega.github.io/vega/docs/marks/text/)
- `:rect`: [Rect Mark](https://vega.github.io/vega/docs/marks/rect/)
- `:children`: [Text Mark](https://vega.github.io/vega/docs/marks/text/) for the child count
- `:yes`
- `:path`: [Path Mark](https://vega.github.io/vega/docs/marks/path/)
- `:text`: [Text Mark](https://vega.github.io/vega/docs/marks/text/)
- `:no`
- `:path`: [Path Mark](https://vega.github.io/vega/docs/marks/path/)
- `:text`: [Text Mark](https://vega.github.io/vega/docs/marks/text/)

`EXGBoost.plot_tree/2` defaults to outputting a `VegaLite` struct. If you pass the `:path` option it will save to a file instead.

Expand Down Expand Up @@ -348,7 +340,7 @@ For example, if you just want to change the default pre-configured style you can
Mix.install([
{:exgboost, path: Path.join(__DIR__, ".."), env: :dev},
],
config:
config:
[
exgboost: [
plotting: [
Expand All @@ -363,7 +355,7 @@ You can also make one-off changes to any of the settings with this method. In ef
<!-- livebook:{"force_markdown":true} -->

```elixir
default_style =
default_style =
[
style: nil,
background: "#3f3f3f",
Expand Down Expand Up @@ -398,7 +390,7 @@ You can also make one-off changes to any of the settings with this method. In ef
Mix.install([
{:exgboost, path: Path.join(__DIR__, ".."), env: :dev},
],
config:
config:
[
exgboost: [
plotting: default_style,
Expand All @@ -407,7 +399,7 @@ config:
)
```

**NOTE: When you specify a parameter in the configuration, it is merged with the defaults which is different from runtime behavior.**
**NOTE: When you specify a parameter in the configuration, it is merged with the defaults which is different from runtime behavior.**

At any point, you can check what your default settings are by using `EXGBoost.Plotting.get_defaults/0`

Expand Down Expand Up @@ -445,8 +437,8 @@ EXGBoost.Plotting.get_data_spec(booster, rankdir: :bt)

The Vega fields which are not included with `get_data_spec/2` and are included in `plot/2` are:

* [Marks](https://vega.github.io/vega/docs/marks/)
* [Scales](https://vega.github.io/vega/docs/scales/)
* [Signals](https://vega.github.io/vega/docs/signals/)
- [Marks](https://vega.github.io/vega/docs/marks/)
- [Scales](https://vega.github.io/vega/docs/scales/)
- [Signals](https://vega.github.io/vega/docs/signals/)

You can make a completely valid plot using only the Data from `get_data_specs/2` and adding the marks you need.

0 comments on commit 4b83629

Please sign in to comment.