diff --git a/Project.toml b/Project.toml index 97640a6..4b58b20 100644 --- a/Project.toml +++ b/Project.toml @@ -4,9 +4,11 @@ authors = ["Patrick Altmeyer"] version = "0.1.3" [deps] +CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" MLJ = "add582a8-e3ab-11e8-2d5e-e98b27df1bc7" MLJBase = "a7f614a8-145f-11e9-1d2a-a57a1082229d" MLJModelInterface = "e80e1ace-859a-464e-9ed9-23947d8ae3ea" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [compat] diff --git a/_freeze/docs/src/classification/execute-results/md.json b/_freeze/docs/src/classification/execute-results/md.json index 938a281..2216aa0 100644 --- a/_freeze/docs/src/classification/execute-results/md.json +++ b/_freeze/docs/src/classification/execute-results/md.json @@ -1,9 +1,9 @@ { - "hash": "a5e7b7c00cea9c148884a97a62ddf49b", + "hash": "a9026c438717b315a2ac33b3ad7cabcd", "result": { - "markdown": "\n\n::: {.cell execution_count=2}\n``` {.julia .cell-code}\nusing MLJ\nX, y = MLJ.make_blobs(1000, 2; centers=3, cluster_std=1.0)\ntrain, test = partition(eachindex(y), 0.4, 0.4, shuffle=true)\n```\n:::\n\n\n::: {.cell execution_count=3}\n``` {.julia .cell-code}\nEvoTreeClassifier = @load EvoTreeClassifier pkg=EvoTrees\nmodel = EvoTreeClassifier() \n```\n:::\n\n\n::: {.cell execution_count=4}\n``` {.julia .cell-code}\nusing ConformalPrediction\nconf_model = conformal_model(model)\nmach = machine(conf_model, X, y)\nfit!(mach, rows=train)\n```\n:::\n\n\n::: {.cell execution_count=5}\n``` {.julia .cell-code}\nrows = rand(test, 10)\nXtest = selectrows(X, rows)\nytest = y[rows]\npredict(mach, Xtest)\n```\n\n::: {.cell-output .cell-output-display execution_count=6}\n```\n╭───────────────────────────────────────────────────────────────────╮\n│ │\n│ (1) UnivariateFinite{Multiclass {#90CAF9}3} (1=>0.82{/#90CAF9}) │\n│ (2) UnivariateFinite{Multiclass {#90CAF9}3} (3=>0.82{/#90CAF9}) │\n│ (3) UnivariateFinite{Multiclass {#90CAF9}3} (1=>0.82{/#90CAF9}) │\n│ (4) UnivariateFinite{Multiclass {#90CAF9}3} (1=>0.82{/#90CAF9}) │\n│ (5) UnivariateFinite{Multiclass {#90CAF9}3} (1=>0.82{/#90CAF9}) │\n│ (6) UnivariateFinite{Multiclass {#90CAF9}3} (3=>0.82{/#90CAF9}) │\n│ (7) UnivariateFinite{Multiclass {#90CAF9}3} (3=>0.82{/#90CAF9}) │\n│ (8) UnivariateFinite{Multiclass {#90CAF9}3} (2=>0.82{/#90CAF9}) │\n│ (9) UnivariateFinite{Multiclass {#90CAF9}3} (1=>0.82{/#90CAF9}) │\n│ (10) UnivariateFinite{Multiclass {#90CAF9}3} (3=>0.82{/#90CAF9}) │\n│ │\n│ │\n╰────────────────────────────────────────────────────── 10 items ───╯\n```\n:::\n:::\n\n\n", + "markdown": "# Classification \n\n```@meta\nCurrentModule = ConformalPrediction\n```\n\n\n\nThis tutorial is based in parts on this [blog post](https://www.paltmeyer.com/blog/posts/conformal-prediction/).\n\n### Split Conformal Classification {#sec-scp}\n\nWe consider a simple binary classification problem. Let $(X_i, Y_i), \\ i=1,...,n$ denote our feature-label pairs and let $\\mu: \\mathcal{X} \\mapsto \\mathcal{Y}$ denote the mapping from features to labels. For illustration purposes we will use the moons dataset 🌙. Using [`MLJ.jl`](https://alan-turing-institute.github.io/MLJ.jl/v0.18/) we first generate the data and split into into a training and test set:\n\n::: {.cell execution_count=2}\n``` {.julia .cell-code}\nusing MLJ\nusing Random\nRandom.seed!(123)\n\n# Data:\nX, y = make_moons(500; noise=0.15)\ntrain, test = partition(eachindex(y), 0.8, shuffle=true)\n```\n:::\n\n\nHere we will use a specific case of CP called *split conformal prediction* which can then be summarized as follows:^[In other places split conformal prediction is sometimes referred to as *inductive* conformal prediction.]\n\n1. Partition the training into a proper training set and a separate calibration set: $\\mathcal{D}_n=\\mathcal{D}^{\\text{train}} \\cup \\mathcal{D}^{\\text{cali}}$.\n2. Train the machine learning model on the proper training set: $\\hat\\mu_{i \\in \\mathcal{D}^{\\text{train}}}(X_i,Y_i)$.\n3. Compute nonconformity scores, $\\mathcal{S}$, using the calibration data $\\mathcal{D}^{\\text{cali}}$ and the fitted model $\\hat\\mu_{i \\in \\mathcal{D}^{\\text{train}}}$. \n4. For a user-specified desired coverage ratio $(1-\\alpha)$ compute the corresponding quantile, $\\hat{q}$, of the empirical distribution of nonconformity scores, $\\mathcal{S}$.\n5. For the given quantile and test sample $X_{\\text{test}}$, form the corresponding conformal prediction set: \n\n$$\nC(X_{\\text{test}})=\\{y:s(X_{\\text{test}},y) \\le \\hat{q}\\}\n$$ {#eq-set}\n\nThis is the default procedure used for classification and regression in [`ConformalPrediction.jl`](https://github.com/pat-alt/ConformalPrediction.jl). \n\nNow let's take this to our 🌙 data. To illustrate the package functionality we will demonstrate the envisioned workflow. We first define our atomic machine learning model following standard [`MLJ.jl`](https://alan-turing-institute.github.io/MLJ.jl/v0.18/) conventions. Using [`ConformalPrediction.jl`](https://github.com/pat-alt/ConformalPrediction.jl) we then wrap our atomic model in a conformal model using the standard API call `conformal_model(model::Supervised; kwargs...)`. To train and predict from our conformal model we can then rely on the conventional [`MLJ.jl`](https://alan-turing-institute.github.io/MLJ.jl/v0.18/) procedure again. In particular, we wrap our conformal model in data (turning it into a machine) and then fit it on the training set. Finally, we use our machine to predict the label for a new test sample `Xtest`:\n\n::: {.cell execution_count=3}\n``` {.julia .cell-code}\n# Model:\nKNNClassifier = @load KNNClassifier pkg=NearestNeighborModels\nmodel = KNNClassifier(;K=50) \n\n# Training:\nusing ConformalPrediction\nconf_model = conformal_model(model; coverage=.9)\nmach = machine(conf_model, X, y)\nfit!(mach, rows=train)\n\n# Conformal Prediction:\nXtest = selectrows(X, first(test))\nytest = y[first(test)]\npredict(mach, Xtest)[1]\n```\n\n::: {.cell-output .cell-output-stdout}\n```\nimport NearestNeighborModels ✔\n```\n:::\n\n::: {.cell-output .cell-output-stdout}\n```\n\n```\n:::\n\n::: {.cell-output .cell-output-display execution_count=4}\n```\n UnivariateFinite{Multiclass{2}} \n ┌ ┐ \n 0 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.94 \n └ ┘ \n```\n:::\n:::\n\n\nThe final predictions are set-valued. While the softmax output remains unchanged for the `SimpleInductiveClassifier`, the size of the prediction set depends on the chosen coverage rate, $(1-\\alpha)$. \n\n::: {.cell execution_count=4}\n\n::: {.cell-output .cell-output-display execution_count=5}\nWhen specifying a coverage rate very close to one, the prediction set will typically include many (in some cases all) of the possible labels. Below, for example, both classes are included in the prediction set when setting the coverage rate equal to $(1-\\alpha)$=1.0. This is intuitive, since high coverage quite literally requires that the true label is covered by the prediction set with high probability.\n\n:::\n:::\n\n\n::: {.cell execution_count=5}\n``` {.julia .cell-code}\nconf_model = conformal_model(model; coverage=coverage)\nmach = machine(conf_model, X, y)\nfit!(mach, rows=train)\n\n# Conformal Prediction:\nXtest = (x1=[1],x2=[0])\npredict(mach, Xtest)[1]\n```\n\n::: {.cell-output .cell-output-display execution_count=6}\n```\n UnivariateFinite{Multiclass{2}} \n ┌ ┐ \n 0 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.5 \n 1 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.5 \n └ ┘ \n```\n:::\n:::\n\n\n::: {.cell execution_count=6}\n\n::: {.cell-output .cell-output-display execution_count=7}\nConversely, for low coverage rates, prediction sets can also be empty. For a choice of $(1-\\alpha)$=0.1, for example, the prediction set for our test sample is empty. This is a bit difficult to think about intuitively and I have not yet come across a satisfactory, intuitive interpretation.^[Any thoughts/comments welcome!] When the prediction set is empty, the `predict` call currently returns `missing`:\n\n:::\n:::\n\n\n::: {.cell execution_count=7}\n``` {.julia .cell-code}\nconf_model = conformal_model(model; coverage=coverage)\nmach = machine(conf_model, X, y)\nfit!(mach, rows=train)\n\n# Conformal Prediction:\npredict(mach, Xtest)[1]\n```\n\n::: {.cell-output .cell-output-display execution_count=8}\n```\nmissing\n```\n:::\n:::\n\n\n::: {.cell execution_count=8}\n``` {.julia .cell-code}\ncov_ = .9\nconf_model = conformal_model(model; coverage=cov_)\nmach = machine(conf_model, X, y)\nfit!(mach, rows=train)\nMarkdown.parse(\"\"\"\nThe following chart shows the resulting predicted probabilities for ``y=1`` (left) and set size (right) for a choice of ``(1-\\\\alpha)``=$cov_.\n\"\"\")\n```\n\n::: {.cell-output .cell-output-display execution_count=9}\nThe following chart shows the resulting predicted probabilities for $y=1$ (left) and set size (right) for a choice of $(1-\\alpha)$=0.9.\n\n:::\n:::\n\n\n::: {.cell execution_count=9}\n``` {.julia .cell-code}\nusing Plots\np_proba = plot(mach.model, mach.fitresult, X, y)\np_set_size = plot(mach.model, mach.fitresult, X, y; plot_set_size=true)\nplot(p_proba, p_set_size, size=(800,250))\n```\n\n::: {.cell-output .cell-output-display execution_count=10}\n![](classification_files/figure-commonmark/cell-10-output-1.svg){}\n:::\n:::\n\n\nThe animation below should provide some more intuition as to what exactly is happening here. It illustrates the effect of the chosen coverage rate on the predicted softmax output and the set size in the two-dimensional feature space. Contours are overlayed with the moon data points (including test data). The two samples highlighted in red, $X_1$ and $X_2$, have been manually added for illustration purposes. Let's look at these one by one.\n\nFirstly, note that $X_1$ (red cross) falls into a region of the domain that is characterized by high predictive uncertainty. It sits right at the bottom-right corner of our class-zero moon 🌜 (orange), a region that is almost entirely enveloped by our class-one moon 🌛 (green). For low coverage rates the prediction set for $X_1$ is empty: on the left-hand side this is indicated by the missing contour for the softmax probability; on the right-hand side we can observe that the corresponding set size is indeed zero. For high coverage rates the prediction set includes both $y=0$ and $y=1$, indicative of the fact that the conformal classifier is uncertain about the true label.\n\nWith respect to $X_2$, we observe that while also sitting on the fringe of our class-zero moon, this sample populates a region that is not fully enveloped by data points from the opposite class. In this region, the underlying atomic classifier can be expected to be more certain about its predictions, but still not highly confident. How is this reflected by our corresponding conformal prediction sets? \n\n::: {.cell execution_count=10}\n``` {.julia .cell-code code-fold=\"true\"}\nXtest_2 = (x1=[-0.5],x2=[0.25])\np̂_2 = pdf(predict(mach, Xtest_2)[1], 0)\n```\n:::\n\n\n::: {.cell execution_count=11}\n\n::: {.cell-output .cell-output-display execution_count=12}\nWell, for low coverage rates (roughly $<0.9$) the conformal prediction set does not include $y=0$: the set size is zero (right panel). Only for higher coverage rates do we have $C(X_2)=\\{0\\}$: the coverage rate is high enough to include $y=0$, but the corresponding softmax probability is still fairly low. For example, for $(1-\\alpha)=0.9$ we have $\\hat{p}(y=0|X_2)=0.72.$\n\n:::\n:::\n\n\nThese two examples illustrate an interesting point: for regions characterized by high predictive uncertainty, conformal prediction sets are typically empty (for low coverage) or large (for high coverage). While set-valued predictions may be something to get used to, this notion is overall intuitive. \n\n::: {.cell execution_count=12}\n``` {.julia .cell-code}\n# Setup\ncoverages = range(0.75,1.0,length=5)\nn = 100\nx1_range = range(extrema(X.x1)...,length=n)\nx2_range = range(extrema(X.x2)...,length=n)\n\nanim = @animate for coverage in coverages\n conf_model = conformal_model(model; coverage=coverage)\n mach = machine(conf_model, X, y)\n fit!(mach, rows=train)\n # Probabilities:\n p1 = plot(mach.model, mach.fitresult, X, y)\n scatter!(p1, Xtest.x1, Xtest.x2, ms=6, c=:red, label=\"X₁\", shape=:cross, msw=6)\n scatter!(p1, Xtest_2.x1, Xtest_2.x2, ms=6, c=:red, label=\"X₂\", shape=:diamond, msw=6)\n p2 = plot(mach.model, mach.fitresult, X, y; plot_set_size=true)\n scatter!(p2, Xtest.x1, Xtest.x2, ms=6, c=:red, label=\"X₁\", shape=:cross, msw=6)\n scatter!(p2, Xtest_2.x1, Xtest_2.x2, ms=6, c=:red, label=\"X₂\", shape=:diamond, msw=6)\n plot(p1, p2, plot_title=\"(1-α)=$(round(coverage,digits=2))\", size=(800,300))\nend\n\ngif(anim, joinpath(www_path,\"classification.gif\"), fps=1)\n```\n\n::: {#fig-anim .cell-output .cell-output-display execution_count=13}\n```{=html}\n\n```\n\nThe effect of the coverage rate on the conformal prediction set. Softmax probabilities are shown on the left. The size of the prediction set is shown on the right.\n:::\n:::\n\n\n![](www/classification.gif)\n\n", "supporting": [ - "classification_files" + "classification_files/figure-commonmark" ], "filters": [] } diff --git a/_freeze/docs/src/classification/figure-commonmark/cell-10-output-1.svg b/_freeze/docs/src/classification/figure-commonmark/cell-10-output-1.svg new file mode 100644 index 0000000..de81765 --- /dev/null +++ b/_freeze/docs/src/classification/figure-commonmark/cell-10-output-1.svg @@ -0,0 +1,2671 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_freeze/docs/src/classification/figure-commonmark/cell-9-output-1.svg b/_freeze/docs/src/classification/figure-commonmark/cell-9-output-1.svg new file mode 100644 index 0000000..8791f3f --- /dev/null +++ b/_freeze/docs/src/classification/figure-commonmark/cell-9-output-1.svg @@ -0,0 +1,2671 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_freeze/docs/src/index/execute-results/md.json b/_freeze/docs/src/index/execute-results/md.json index 03a9018..bc25f51 100644 --- a/_freeze/docs/src/index/execute-results/md.json +++ b/_freeze/docs/src/index/execute-results/md.json @@ -1,7 +1,7 @@ { "hash": "c56dcfed5fce5fece3f8dd90b08af0bd", "result": { - "markdown": "```@meta\nCurrentModule = ConformalPrediction\n```\n\n# ConformalPrediction\n\nDocumentation for [ConformalPrediction.jl](https://github.com/pat-alt/ConformalPrediction.jl).\n\n\n\n`ConformalPrediction.jl` is a package for Uncertainty Quantification (UQ) through Conformal Prediction (CP) in Julia. It is designed to work with supervised models trained in [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) @blaom2020mlj. Conformal Prediction is distribution-free, easy-to-understand, easy-to-use and model-agnostic. \n\n# 📖 Background\n\nConformal Prediction is a scalable frequentist approach to uncertainty quantification and coverage control. It promises to be an easy-to-understand, distribution-free and model-agnostic way to generate statistically rigorous uncertainty estimates. Interestingly, it can even be used to complement Bayesian methods.\n\nThe animation below is lifted from a small blog post that introduces the topic and the package ([[TDS](https://towardsdatascience.com/conformal-prediction-in-julia-351b81309e30)], [[Quarto](https://www.paltmeyer.com/blog/posts/conformal-prediction/#fig-anim)]). It shows conformal prediction sets for two different samples and changing coverage rates. Standard conformal classifiers produce set-valued predictions: for ambiguous samples these sets are typically large (for high coverage) or empty (for low coverage).\n\n![Conformal Prediction in action: Prediction sets for two different samples and changing coverage rates. As coverage grows, so does the size of the prediction sets.](https://raw.githubusercontent.com/pat-alt/blog/main/posts/conformal-prediction/www/medium.gif)\n\n## 🚩 Installation \n\nYou can install the latest stable release from the general registry:\n\n```{.julia}\nusing Pkg\nPkg.add(\"ConformalPrediction\")\n```\n\nThe development version can be installed as follows:\n\n```{.julia}\nusing Pkg\nPkg.add(url=\"https://github.com/pat-alt/ConformalPrediction.jl\")\n```\n\n## 🔁 Status \n\nThis package is in its early stages of development and therefore still subject to changes to the core architecture and API. The following CP approaches have been implemented in the development version:\n\n**Regression**:\n\n- Inductive \n- Naive Transductive \n- Jackknife \n- Jackknife+ \n- Jackknife-minmax\n- CV+\n- CV-minmax\n\n**Classification**:\n\n- Inductive (LABEL [@sadinle2019least])\n- Adaptive Inductive\n\nThe package has been tested for the following supervised models offered by [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/).\n\n**Regression**:\n\n::: {.cell execution_count=2}\n``` {.julia .cell-code}\nusing ConformalPrediction\nkeys(tested_atomic_models[:regression])\n```\n\n::: {.cell-output .cell-output-display execution_count=12}\n```\nKeySet for a Dict{Symbol, Expr} with 4 entries. Keys:\n :nearest_neighbor\n :evo_tree\n :light_gbm\n :decision_tree\n```\n:::\n:::\n\n\n**Classification**:\n\n::: {.cell execution_count=3}\n``` {.julia .cell-code}\nkeys(tested_atomic_models[:classification])\n```\n\n::: {.cell-output .cell-output-display execution_count=13}\n```\nKeySet for a Dict{Symbol, Expr} with 4 entries. Keys:\n :nearest_neighbor\n :evo_tree\n :light_gbm\n :decision_tree\n```\n:::\n:::\n\n\n## 🔍 Usage Example \n\nTo illustrate the intended use of the package, let's have a quick look at a simple regression problem. Using [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) we first generate some synthetic data and then determine indices for our training, calibration and test data:\n\n::: {.cell execution_count=4}\n``` {.julia .cell-code}\nusing MLJ\nX, y = MLJ.make_regression(1000, 2)\ntrain, test = partition(eachindex(y), 0.4, 0.4)\n```\n:::\n\n\nWe then import a decision tree ([`EvoTrees.jl`](https://github.com/Evovest/EvoTrees.jl)) following the standard [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) procedure.\n\n::: {.cell execution_count=5}\n``` {.julia .cell-code}\nEvoTreeRegressor = @load EvoTreeRegressor pkg=EvoTrees\nmodel = EvoTreeRegressor() \n```\n:::\n\n\nTo turn our conventional model into a conformal model, we just need to declare it as such by using `conformal_model` wrapper function. The generated conformal model instance can wrapped in data to create a *machine*. Finally, we proceed by fitting the machine on training data using the generic `fit!` method:\n\n::: {.cell execution_count=6}\n``` {.julia .cell-code}\nusing ConformalPrediction\nconf_model = conformal_model(model)\nmach = machine(conf_model, X, y)\nfit!(mach, rows=train)\n```\n:::\n\n\nPredictions can then be computed using the generic `predict` method. The code below produces predictions for the first `n` samples. Each tuple contains the lower and upper bound for the prediction interval.\n\n::: {.cell execution_count=7}\n``` {.julia .cell-code}\nn = 10\nXtest = selectrows(X, first(test,n))\nytest = y[first(test,n)]\npredict(mach, Xtest)\n```\n\n::: {.cell-output .cell-output-display execution_count=17}\n```\n╭─────────────────────────────────────────────────────────────────╮\n│ │\n│ (1) ([0.14395897640483468], [1.5537237281612537]) │\n│ (2) ([-0.539687877793372], [0.8700768739630471]) │\n│ (3) ([-0.46442052745067525], [0.9453442243057439]) │\n│ (4) ([0.010529843675146089], [1.420294595431565]) │\n│ (5) ([0.07301045762431613], [1.4827752093807351]) │\n│ (6) ([-0.012020120998203487], [1.3977446307582158]) │\n│ (7) ([0.5297045560243977], [1.9394693077808167]) │\n│ (8) ([-0.46442052745067525], [0.9453442243057439]) │\n│ (9) ([-0.09600489213468855], [1.3137598596217306]) │\n│ (10) ([0.010529843675146089], [1.420294595431565]) │\n│ │\n│ │\n╰──────────────────────────────────────────────────── 10 items ───╯\n```\n:::\n:::\n\n\n## 🛠 Contribute \n\nContributions are welcome! Please follow the [SciML ColPrac guide](https://github.com/SciML/ColPrac).\n\n## 🎓 References \n\n", + "markdown": "```@meta\nCurrentModule = ConformalPrediction\n```\n\n# ConformalPrediction\n\nDocumentation for [ConformalPrediction.jl](https://github.com/pat-alt/ConformalPrediction.jl).\n\n\n\n`ConformalPrediction.jl` is a package for Uncertainty Quantification (UQ) through Conformal Prediction (CP) in Julia. It is designed to work with supervised models trained in [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) @blaom2020mlj. Conformal Prediction is distribution-free, easy-to-understand, easy-to-use and model-agnostic. \n\n# 📖 Background\n\nConformal Prediction is a scalable frequentist approach to uncertainty quantification and coverage control. It promises to be an easy-to-understand, distribution-free and model-agnostic way to generate statistically rigorous uncertainty estimates. Interestingly, it can even be used to complement Bayesian methods.\n\nThe animation below is lifted from a small blog post that introduces the topic and the package ([[TDS](https://towardsdatascience.com/conformal-prediction-in-julia-351b81309e30)], [[Quarto](https://www.paltmeyer.com/blog/posts/conformal-prediction/#fig-anim)]). It shows conformal prediction sets for two different samples and changing coverage rates. Standard conformal classifiers produce set-valued predictions: for ambiguous samples these sets are typically large (for high coverage) or empty (for low coverage).\n\n![Conformal Prediction in action: Prediction sets for two different samples and changing coverage rates. As coverage grows, so does the size of the prediction sets.](https://raw.githubusercontent.com/pat-alt/blog/main/posts/conformal-prediction/www/medium.gif)\n\n## 🚩 Installation \n\nYou can install the latest stable release from the general registry:\n\n```{.julia}\nusing Pkg\nPkg.add(\"ConformalPrediction\")\n```\n\nThe development version can be installed as follows:\n\n```{.julia}\nusing Pkg\nPkg.add(url=\"https://github.com/pat-alt/ConformalPrediction.jl\")\n```\n\n## 🔁 Status \n\nThis package is in its early stages of development and therefore still subject to changes to the core architecture and API. The following CP approaches have been implemented in the development version:\n\n**Regression**:\n\n- Inductive \n- Naive Transductive \n- Jackknife \n- Jackknife+ \n- Jackknife-minmax\n- CV+\n- CV-minmax\n\n**Classification**:\n\n- Inductive (LABEL [@sadinle2019least])\n- Adaptive Inductive\n\nThe package has been tested for the following supervised models offered by [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/).\n\n**Regression**:\n\n::: {.cell execution_count=2}\n``` {.julia .cell-code}\nusing ConformalPrediction\nkeys(tested_atomic_models[:regression])\n```\n\n::: {.cell-output .cell-output-display execution_count=3}\n```\nKeySet for a Dict{Symbol, Expr} with 4 entries. Keys:\n :nearest_neighbor\n :evo_tree\n :light_gbm\n :decision_tree\n```\n:::\n:::\n\n\n**Classification**:\n\n::: {.cell execution_count=3}\n``` {.julia .cell-code}\nkeys(tested_atomic_models[:classification])\n```\n\n::: {.cell-output .cell-output-display execution_count=4}\n```\nKeySet for a Dict{Symbol, Expr} with 4 entries. Keys:\n :nearest_neighbor\n :evo_tree\n :light_gbm\n :decision_tree\n```\n:::\n:::\n\n\n## 🔍 Usage Example \n\nTo illustrate the intended use of the package, let's have a quick look at a simple regression problem. Using [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) we first generate some synthetic data and then determine indices for our training, calibration and test data:\n\n::: {.cell execution_count=4}\n``` {.julia .cell-code}\nusing MLJ\nX, y = MLJ.make_regression(1000, 2)\ntrain, test = partition(eachindex(y), 0.4, 0.4)\n```\n:::\n\n\nWe then import a decision tree ([`EvoTrees.jl`](https://github.com/Evovest/EvoTrees.jl)) following the standard [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) procedure.\n\n::: {.cell execution_count=5}\n``` {.julia .cell-code}\nEvoTreeRegressor = @load EvoTreeRegressor pkg=EvoTrees\nmodel = EvoTreeRegressor() \n```\n:::\n\n\nTo turn our conventional model into a conformal model, we just need to declare it as such by using `conformal_model` wrapper function. The generated conformal model instance can wrapped in data to create a *machine*. Finally, we proceed by fitting the machine on training data using the generic `fit!` method:\n\n::: {.cell execution_count=6}\n``` {.julia .cell-code}\nusing ConformalPrediction\nconf_model = conformal_model(model)\nmach = machine(conf_model, X, y)\nfit!(mach, rows=train)\n```\n:::\n\n\nPredictions can then be computed using the generic `predict` method. The code below produces predictions for the first `n` samples. Each tuple contains the lower and upper bound for the prediction interval.\n\n::: {.cell execution_count=7}\n``` {.julia .cell-code}\nn = 5\nXtest = selectrows(X, first(test,n))\nytest = y[first(test,n)]\npredict(mach, Xtest)\n```\n\n::: {.cell-output .cell-output-display execution_count=8}\n```\n╭─────────────────────────────────────────────────────────╮\n│ │\n│ (1) (1.2801183281465092, 2.0024286641173816) │\n│ (2) (0.8012756658949756, 1.5235860018658482) │\n│ (3) (1.1850387604493555, 1.9073490964202282) │\n│ (4) (1.1185514282818692, 1.8408617642527418) │\n│ (5) (1.1651738766694149, 1.8874842126402875) │\n│ │\n│ │\n╰───────────────────────────────────────────── 5 items ───╯\n```\n:::\n:::\n\n\n## 🛠 Contribute \n\nContributions are welcome! Please follow the [SciML ColPrac guide](https://github.com/SciML/ColPrac).\n\n## 🎓 References \n\n", "supporting": [ "index_files" ], diff --git a/_freeze/docs/src/intro/execute-results/md.json b/_freeze/docs/src/intro/execute-results/md.json index 960f41c..966caac 100644 --- a/_freeze/docs/src/intro/execute-results/md.json +++ b/_freeze/docs/src/intro/execute-results/md.json @@ -1,7 +1,7 @@ { - "hash": "1b0ee553524705afa5795d1e05898476", + "hash": "4b0109965d9339b1ffd867a3c20a947b", "result": { - "markdown": "\n\n`ConformalPrediction.jl` is a package for Uncertainty Quantification (UQ) through Conformal Prediction (CP) in Julia. It is designed to work with supervised models trained in [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/). Conformal Prediction is distribution-free, easy-to-understand, easy-to-use and model-agnostic. \n\n## Installation 🚩\n\nYou can install the first stable release from the general registry:\n\n```{.julia}\nusing Pkg\nPkg.add(\"ConformalPrediction\")\n```\n\nThe development version can be installed as follows:\n\n```{.julia}\nusing Pkg\nPkg.add(url=\"https://github.com/pat-alt/ConformalPrediction.jl\")\n```\n\n## Status 🔁\n\nThis package is in its very early stages of development and therefore still subject to changes to the core architecture. The following approaches have been implemented in the development version:\n\n**Regression**:\n\n- Inductive \n- Naive Transductive \n- Jackknife \n- Jackknife+ \n- Jackknife-minmax\n- CV+\n- CV-minmax\n\n**Classification**:\n\n- Inductive (LABEL [@sadinle2019least])\n- Adaptive Inductive\n\nI have only tested it for a few of the supervised models offered by [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/).\n\n## Usage Example 🔍\n\nTo illustrate the intended use of the package, let's have a quick look at a simple regression problem. Using [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) we first generate some synthetic data and then determine indices for our training, calibration and test data:\n\n::: {.cell execution_count=2}\n``` {.julia .cell-code}\nusing MLJ\nX, y = MLJ.make_regression(1000, 2)\ntrain, test = partition(eachindex(y), 0.4, 0.4)\n```\n:::\n\n\nWe then import a decision tree ([`EvoTrees.jl`](https://github.com/Evovest/EvoTrees.jl)) following the standard [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) procedure.\n\n::: {.cell execution_count=3}\n``` {.julia .cell-code}\nEvoTreeRegressor = @load EvoTreeRegressor pkg=EvoTrees\nmodel = EvoTreeRegressor() \n```\n:::\n\n\nTo turn our conventional model into a conformal model, we just need to declare it as such by using `conformal_model` wrapper function. The generated conformal model instance can wrapped in data to create a *machine*. Finally, we proceed by fitting the machine on training data using the generic `fit!` method:\n\n::: {.cell execution_count=4}\n``` {.julia .cell-code}\nusing ConformalPrediction\nconf_model = conformal_model(model)\nmach = machine(conf_model, X, y)\nfit!(mach, rows=train)\n```\n:::\n\n\nPredictions can then be computed using the generic `predict` method. The code below produces predictions for the first `n` samples. Each tuple contains the lower and upper bound for the prediction interval.\n\n::: {.cell execution_count=5}\n``` {.julia .cell-code}\nn = 10\nXtest = selectrows(X, first(test,n))\nytest = y[first(test,n)]\npredict(mach, Xtest)\n```\n\n::: {.cell-output .cell-output-display execution_count=6}\n```\n╭─────────────────────────────────────────────────────────────────╮\n│ │\n│ (1) ([-0.20063113789390163], [1.323655530145934]) │\n│ (2) ([-0.061147489871723804], [1.4631391781681118]) │\n│ (3) ([-1.4486105066363675], [0.07567616140346822]) │\n│ (4) ([-0.7160881365817455], [0.8081985314580902]) │\n│ (5) ([-1.7173644161988695], [-0.19307774815903367]) │\n│ (6) ([-1.2158809697881832], [0.3084056982516525]) │\n│ (7) ([-1.7173644161988695], [-0.19307774815903367]) │\n│ (8) ([0.26510754559144056], [1.7893942136312764]) │\n│ (9) ([-0.8716996456392521], [0.6525870224005836]) │\n│ (10) ([0.43084861624955606], [1.9551352842893919]) │\n│ │\n│ │\n╰──────────────────────────────────────────────────── 10 items ───╯\n```\n:::\n:::\n\n\n## Contribute 🛠\n\nContributions are welcome! Please follow the [SciML ColPrac guide](https://github.com/SciML/ColPrac).\n\n## References 🎓\n\n", + "markdown": "\n\n`ConformalPrediction.jl` is a package for Uncertainty Quantification (UQ) through Conformal Prediction (CP) in Julia. It is designed to work with supervised models trained in [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) @blaom2020mlj. Conformal Prediction is distribution-free, easy-to-understand, easy-to-use and model-agnostic. \n\n# 📖 Background\n\nConformal Prediction is a scalable frequentist approach to uncertainty quantification and coverage control. It promises to be an easy-to-understand, distribution-free and model-agnostic way to generate statistically rigorous uncertainty estimates. Interestingly, it can even be used to complement Bayesian methods.\n\nThe animation below is lifted from a small blog post that introduces the topic and the package ([[TDS](https://towardsdatascience.com/conformal-prediction-in-julia-351b81309e30)], [[Quarto](https://www.paltmeyer.com/blog/posts/conformal-prediction/#fig-anim)]). It shows conformal prediction sets for two different samples and changing coverage rates. Standard conformal classifiers produce set-valued predictions: for ambiguous samples these sets are typically large (for high coverage) or empty (for low coverage).\n\n![Conformal Prediction in action: Prediction sets for two different samples and changing coverage rates. As coverage grows, so does the size of the prediction sets.](https://raw.githubusercontent.com/pat-alt/blog/main/posts/conformal-prediction/www/medium.gif)\n\n## 🚩 Installation \n\nYou can install the latest stable release from the general registry:\n\n```{.julia}\nusing Pkg\nPkg.add(\"ConformalPrediction\")\n```\n\nThe development version can be installed as follows:\n\n```{.julia}\nusing Pkg\nPkg.add(url=\"https://github.com/pat-alt/ConformalPrediction.jl\")\n```\n\n## 🔁 Status \n\nThis package is in its early stages of development and therefore still subject to changes to the core architecture and API. The following CP approaches have been implemented in the development version:\n\n**Regression**:\n\n- Inductive \n- Naive Transductive \n- Jackknife \n- Jackknife+ \n- Jackknife-minmax\n- CV+\n- CV-minmax\n\n**Classification**:\n\n- Inductive (LABEL [@sadinle2019least])\n- Adaptive Inductive\n\nThe package has been tested for the following supervised models offered by [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/).\n\n**Regression**:\n\n::: {.cell execution_count=2}\n``` {.julia .cell-code}\nusing ConformalPrediction\nkeys(tested_atomic_models[:regression])\n```\n\n::: {.cell-output .cell-output-display execution_count=3}\n```\nKeySet for a Dict{Symbol, Expr} with 4 entries. Keys:\n :nearest_neighbor\n :evo_tree\n :light_gbm\n :decision_tree\n```\n:::\n:::\n\n\n**Classification**:\n\n::: {.cell execution_count=3}\n``` {.julia .cell-code}\nkeys(tested_atomic_models[:classification])\n```\n\n::: {.cell-output .cell-output-display execution_count=4}\n```\nKeySet for a Dict{Symbol, Expr} with 4 entries. Keys:\n :nearest_neighbor\n :evo_tree\n :light_gbm\n :decision_tree\n```\n:::\n:::\n\n\n## 🔍 Usage Example \n\nTo illustrate the intended use of the package, let's have a quick look at a simple regression problem. Using [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) we first generate some synthetic data and then determine indices for our training, calibration and test data:\n\n::: {.cell execution_count=4}\n``` {.julia .cell-code}\nusing MLJ\nX, y = MLJ.make_regression(1000, 2)\ntrain, test = partition(eachindex(y), 0.4, 0.4)\n```\n:::\n\n\nWe then import a decision tree ([`EvoTrees.jl`](https://github.com/Evovest/EvoTrees.jl)) following the standard [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) procedure.\n\n::: {.cell execution_count=5}\n``` {.julia .cell-code}\nEvoTreeRegressor = @load EvoTreeRegressor pkg=EvoTrees\nmodel = EvoTreeRegressor() \n```\n:::\n\n\nTo turn our conventional model into a conformal model, we just need to declare it as such by using `conformal_model` wrapper function. The generated conformal model instance can wrapped in data to create a *machine*. Finally, we proceed by fitting the machine on training data using the generic `fit!` method:\n\n::: {.cell execution_count=6}\n``` {.julia .cell-code}\nusing ConformalPrediction\nconf_model = conformal_model(model)\nmach = machine(conf_model, X, y)\nfit!(mach, rows=train)\n```\n:::\n\n\nPredictions can then be computed using the generic `predict` method. The code below produces predictions for the first `n` samples. Each tuple contains the lower and upper bound for the prediction interval.\n\n::: {.cell execution_count=7}\n``` {.julia .cell-code}\nn = 5\nXtest = selectrows(X, first(test,n))\nytest = y[first(test,n)]\npredict(mach, Xtest)\n```\n\n::: {.cell-output .cell-output-display execution_count=8}\n```\n╭──────────────────────────────────────────────────────────╮\n│ │\n│ (1) (-0.9864061984981062, 2.2503222170961554) │\n│ (2) (-0.7192196826151477, 2.5175087329791137) │\n│ (3) (-0.33838267507136344, 2.898345740522898) │\n│ (4) (-2.838413186252051, 0.39831522934221053) │\n│ (5) (-0.7192196826151477, 2.5175087329791137) │\n│ │\n│ │\n╰────────────────────────────────────────────── 5 items ───╯\n```\n:::\n:::\n\n\n## 🛠 Contribute \n\nContributions are welcome! Please follow the [SciML ColPrac guide](https://github.com/SciML/ColPrac).\n\n## 🎓 References \n\n", "supporting": [ "intro_files" ], diff --git a/_freeze/docs/src/regression/execute-results/md.json b/_freeze/docs/src/regression/execute-results/md.json new file mode 100644 index 0000000..0499fa3 --- /dev/null +++ b/_freeze/docs/src/regression/execute-results/md.json @@ -0,0 +1,10 @@ +{ + "hash": "f2f3a2a6c8e2023197fe550406131d55", + "result": { + "markdown": "# Regression\n\n```@meta\nCurrentModule = ConformalPrediction\n```\n\n\n\nThis tutorial mostly replicates this [tutorial](https://mapie.readthedocs.io/en/latest/examples_regression/4-tutorials/plot_main-tutorial-regression.html#) from MAPIE.\n\n## Data\n\nWe begin by generating some synthetic regression data below:\n\n::: {#fig-data .cell execution_count=2}\n``` {.julia .cell-code}\n# Regression data:\n\n# Inputs:\nN = 600\nxmax = 3.0\nusing Distributions\nd = Uniform(-xmax, xmax)\nX = rand(d, N)\nX = reshape(X, :, 1)\n\n# Outputs:\nnoise = 0.5\nfun(X) = X * sin(X)\nε = randn(N) .* noise\ny = @.(fun(X)) + ε\nusing MLJ\ntrain, test = partition(eachindex(y), 0.4, 0.4, shuffle=true)\n\nusing Plots\nscatter(X, y, label=\"Observed\")\nxrange = range(-xmax,xmax,length=N)\nplot!(xrange, @.(fun(xrange)), lw=4, label=\"Ground truth\", ls=:dash, colour=:black)\n```\n:::\n\n\n## Model\n\nTo model this data we will use polynomial regression. There is currently no out-of-the-box support for polynomial feature transformations in `MLJ`, but it is easy enough to add a little helper function for this. Note how we define a linear pipeline `pipe` here. Since pipelines in `MLJ` are just models, we can use the generated object as an input to `conformal_model` below.\n\n::: {.cell execution_count=3}\n``` {.julia .cell-code}\nLinearRegressor = @load LinearRegressor pkg=MLJLinearModels\ndegree_polynomial = 10\npolynomial_features(X, degree::Int) = reduce(hcat, map(i -> X.^i, 1:degree))\npipe = (X -> MLJ.table(polynomial_features(MLJ.matrix(X), degree_polynomial))) |> LinearRegressor()\n```\n:::\n\n\nNext, we conformalize our polynomial regressor using every available approach (except the Naive approach):\n\n::: {.cell execution_count=4}\n``` {.julia .cell-code}\nusing ConformalPrediction\nconformal_models = merge(values(available_models[:regression])...)\ndelete!(conformal_models, :naive)\n# delete!(conformal_models, :jackknife)\nresults = Dict()\nfor _mod in keys(conformal_models) \n conf_model = conformal_model(pipe; method=_mod, coverage=0.95)\n mach = machine(conf_model, X, y)\n fit!(mach, rows=train)\n results[_mod] = mach\nend\n```\n:::\n\n\nFinally, let us look at the resulting conformal predictions in each case.\n\n::: {.cell execution_count=5}\n``` {.julia .cell-code}\nusing Plots\nzoom = -3\nxrange = range(-xmax+zoom,xmax-zoom,length=N)\nplt_list = []\n\nfor (_mod, mach) in results\n plt = plot(mach.model, mach.fitresult, X, y, zoom=zoom, title=_mod)\n plot!(plt, xrange, @.(fun(xrange)), lw=1, ls=:dash, colour=:black, label=\"Ground truth\")\n push!(plt_list, plt)\nend\n\nplot(plt_list..., size=(1600,1000))\n```\n\n::: {.cell-output .cell-output-display execution_count=6}\n![Conformal prediction regions.](regression_files/figure-commonmark/fig-cp-output-1.svg){#fig-cp}\n:::\n:::\n\n\n", + "supporting": [ + "regression_files" + ], + "filters": [] + } +} \ No newline at end of file diff --git a/_freeze/docs/src/regression/figure-commonmark/fig-cp-output-1.svg b/_freeze/docs/src/regression/figure-commonmark/fig-cp-output-1.svg new file mode 100644 index 0000000..a1fba3e --- /dev/null +++ b/_freeze/docs/src/regression/figure-commonmark/fig-cp-output-1.svg @@ -0,0 +1,4606 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_quarto.yml b/_quarto.yml index 3ce739e..c5443d7 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -1,16 +1,18 @@ project: title: "ConformalPrediction.jl" execute-dir: project - + crossref: fig-prefix: Figure tbl-prefix: Table bibliography: https://raw.githubusercontent.com/pat-alt/bib/main/bib.bib +fig-format: png execute: freeze: auto - echo: true eval: true + echo: true output: false + jupyter: julia-1.8 diff --git a/dev/plot_main-tutorial-regression.ipynb b/dev/plot_main-tutorial-regression.ipynb new file mode 100644 index 0000000..01d99d2 --- /dev/null +++ b/dev/plot_main-tutorial-regression.ipynb @@ -0,0 +1,1236 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Tutorial for tabular regression\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this tutorial, we compare the prediction intervals estimated by MAPIE on a\n", + "simple, one-dimensional, ground truth function\n", + "$f(x) = x \\times \\sin(x)$.\n", + "\n", + "Throughout this tutorial, we will answer the following questions:\n", + "\n", + "- How well do the MAPIE strategies capture the aleatoric uncertainty\n", + " existing in the data?\n", + "\n", + "- How do the prediction intervals estimated by the resampling strategies\n", + " evolve for new *out-of-distribution* data ?\n", + "\n", + "- How do the prediction intervals vary between regressor models ?\n", + "\n", + "Throughout this tutorial, we estimate the prediction intervals first using\n", + "a polynomial function, and then using a boosting model, and a simple neural\n", + "network.\n", + "\n", + "**For practical problems, we advise using the faster CV+ or\n", + "Jackknife+-after-Bootstrap strategies.\n", + "For conservative prediction interval estimates, you can alternatively\n", + "use the CV-minmax strategies.**\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import os\n", + "import subprocess\n", + "import warnings\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "from mapie.metrics import regression_coverage_score\n", + "from mapie.regression import MapieRegressor\n", + "from mapie.quantile_regression import MapieQuantileRegressor\n", + "from mapie.subsample import Subsample\n", + "from sklearn.linear_model import LinearRegression, QuantileRegressor\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.preprocessing import PolynomialFeatures\n", + "\n", + "os.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = \"3\"\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Estimating the aleatoric uncertainty of homoscedastic noisy data\n", + "\n", + "Let's start by defining the $x \\times \\sin(x)$ function and another\n", + "simple function that generates one-dimensional data with normal noise\n", + "uniformely in a given interval.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def x_sinx(x):\n", + " \"\"\"One-dimensional x*sin(x) function.\"\"\"\n", + " return x*np.sin(x)\n", + "\n", + "\n", + "def get_1d_data_with_constant_noise(funct, min_x, max_x, n_samples, noise, zoom=5):\n", + " \"\"\"\n", + " Generate 1D noisy data uniformely from the given function\n", + " and standard deviation for the noise.\n", + " \"\"\"\n", + " np.random.seed(59)\n", + " X_train = np.linspace(min_x, max_x, n_samples)\n", + " np.random.shuffle(X_train)\n", + " X_test = np.linspace(min_x-zoom, max_x+zoom, n_samples*5)\n", + " y_train, y_mesh, y_test = funct(X_train), funct(X_test), funct(X_test)\n", + " y_train += np.random.normal(0, noise, y_train.shape[0])\n", + " y_test += np.random.normal(0, noise, y_test.shape[0])\n", + " return (\n", + " X_train.reshape(-1, 1), y_train, X_test.reshape(-1, 1), y_test, y_mesh\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We first generate noisy one-dimensional data uniformely on an interval.\n", + "Here, the noise is considered as *homoscedastic*, since it remains constant\n", + "over $x$.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "min_x, max_x, n_samples, noise = -5, 5, 600, 0.5\n", + "X_train, y_train, X_test, y_test, y_mesh = get_1d_data_with_constant_noise(\n", + " x_sinx, min_x, max_x, n_samples, noise\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's visualize our noisy function.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.xlabel(\"x\")\n", + "plt.ylabel(\"y\")\n", + "plt.scatter(X_train, y_train, color=\"C0\")\n", + "_ = plt.plot(X_test, y_mesh, color=\"C1\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As mentioned previously, we fit our training data with a simple\n", + "polynomial function. Here, we choose a degree equal to 10 so the function\n", + "is able to perfectly fit $x \\times \\sin(x)$.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "degree_polyn = 10\n", + "polyn_model = Pipeline(\n", + " [\n", + " (\"poly\", PolynomialFeatures(degree=degree_polyn)),\n", + " (\"linear\", LinearRegression())\n", + " ]\n", + ")\n", + "polyn_model_quant = Pipeline(\n", + " [\n", + " (\"poly\", PolynomialFeatures(degree=degree_polyn)),\n", + " (\"linear\", QuantileRegressor(\n", + " solver=\"highs\",\n", + " alpha=0,\n", + " ))\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.ensemble import GradientBoostingRegressor\n", + "polyn_model = Pipeline(\n", + " [\n", + " (\"boosted_tree\", GradientBoostingRegressor())\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We then estimate the prediction intervals for all the strategies very easily\n", + "with a\n", + "`fit` and `predict` process. The prediction interval's lower and upper bounds\n", + "are then saved in a DataFrame. Here, we set an alpha value of 0.05\n", + "in order to obtain a 95% confidence for our prediction intervals.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "STRATEGIES = {\n", + " \"naive\": dict(method=\"naive\"),\n", + " \"jackknife\": dict(method=\"base\", cv=-1),\n", + " \"jackknife_plus\": dict(method=\"plus\", cv=-1),\n", + " \"jackknife_minmax\": dict(method=\"minmax\", cv=-1),\n", + " \"cv\": dict(method=\"base\", cv=10),\n", + " \"cv_plus\": dict(method=\"plus\", cv=10),\n", + " \"cv_minmax\": dict(method=\"minmax\", cv=10),\n", + " \"jackknife_plus_ab\": dict(method=\"plus\", cv=Subsample(n_resamplings=50)),\n", + " \"jackknife_minmax_ab\": dict(\n", + " method=\"minmax\", cv=Subsample(n_resamplings=50)\n", + " ),\n", + " \"conformalized_quantile_regression\": dict(\n", + " method=\"quantile\", cv=\"split\", alpha=0.05\n", + " )\n", + "}\n", + "y_pred, y_pis = {}, {}\n", + "for strategy, params in STRATEGIES.items():\n", + " if strategy == \"conformalized_quantile_regression\":\n", + " mapie = MapieQuantileRegressor(polyn_model_quant, **params)\n", + " mapie.fit(X_train, y_train, random_state=1)\n", + " y_pred[strategy], y_pis[strategy] = mapie.predict(X_test)\n", + " else:\n", + " mapie = MapieRegressor(polyn_model, **params)\n", + " mapie.fit(X_train, y_train)\n", + " y_pred[strategy], y_pis[strategy] = mapie.predict(X_test, alpha=0.05)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let’s now compare the target confidence intervals with the predicted\n", + "intervals obtained with the Jackknife+, Jackknife-minmax, CV+, CV-minmax,\n", + "Jackknife+-after-Boostrap, and conformalized quantile regression (CQR)\n", + "strategies. Note that for the Jackknife-after-Bootstrap method, we call the\n", + ":class:`~mapie.subsample.Subsample` object that allows us to train\n", + "bootstrapped models. Note also that the CQR method is called with\n", + ":class:`~mapie.quantile_regression.MapieQuantileRegressor` with a\n", + "\"split\" strategy.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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3W6mTZtGOBymoqKCH/zgB6e8dwzwySefUFlZCaD1PdBXrDong8/n01bhiqJgMBhO+WEnhMDr9WrGicFg0A2UDJCXl8d5552HqqoMGTKE73//+5pH9mQRQrBo0SJqamqAlBesvb1dM1h0dI6HlBK/368Zy/F4HIfDccr3pMViwel0dvLk6QZKFgiFQjQ0NABobahPp7R0fn6+lmqc/jD1CHydE0VKybhx48jJyQEgEoloBddOldzcXK0+j81m0zx8Ol1He3u7piM5OTlYLJbT1pF0qrFeU0nnZEkmk0yZMgWbzYYQglAopIUenCq5ubmakWwymc4tA0UIYRRCrBNCvJ3NcT/55BOee+45APbs2cPq1atP63r5+flaefL0Xp0uLDonihCCSy+9FLPZjNVq5aabbuLOO+88rWvm5uZq6ap9+/Y95VTDM4Hu0pEPPviAuXPnArB582Y2bNhwWtfLz8+npaUFr9fbqaaSjs6JYDabufzyyzUP6u23385NN910WtfsaKAMGTLkiD2kMkW3GyjAvwHbsj1oMBjUAhH37Nlz2hkO+fn5qKqKy+UiEAhoUdQ6OidCuoux3+8nNzcXi8Wi7f+eKjNmzOBb3/oWBoOBvLw8hg8f3jWT7Zl0m46kY9i2b9+uFcc7VfLz8wmHw3g8Hi24WdcRnRMlGo0Si8Xw+XyajqS9sqfKddddx/333w9AaWlpVhc63WqgCCHKgFnAX7I9djAY1Pbp0u700yHtRrNYLASDQS644ALKy8tPe5465wbr16/nV7/6FS0tLbjdbt5//32amppO65omkwmDwUBOTg5+v/+sfdB1t46kaykpitJlOmKz2fD7/UydOpXS0tKumKrOOcCKFSt47LHHaG1txW6388EHH5z2995kMmGz2bDZbLS2tp5TacaPA98Dsl5BKi0saS/K6QpLr169+Na3vkX//v2RUjJ27FgGDRrUFVPVOQfw+XyYzWba2towm8189tlnpx0zEg6HmTdvHmazmZqaGv74xz+erQGXj9ONOgKcdg2UNP379+c73/kO5eXlxGIxLrrooqyXF9c5c/H7/Xg8HiKRCEIIli9frm3zniptbW288cYb2O129u7dyxNPPJG1DNVuM1CEENcAjVLKNcc57n4hxGohxOrTXVGmkVISDAaRUmrpWKcrLCaTCbfbra2AWlpaztoVq07XkxYW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oqCgQBMVjYwghKCgoECbeU9yNB3RyCSTRUc0B+U8eeaZZzh48CCAJioaGUUIQSwW4ze/+Q1PPfUUPp8v0yZpnCdPPPEEx44dAzQd0cgsSR351a9+xTPPPEMwGMy0Se9Dc1DOk/7+fn77298SjUYzasfAwADz5s1j3rx5lJSUUF5envo9Eomc9dxt27bxpS996ZzXWL58ebrMPYnVq1efs/7Ev/3bvxEIBMbl+lOFaDSK3++nvb2dvXv38otf/CLjn0uN86Onp4dHH32UWCyWUTs0HdEIh8P4fD66u7t59913efTRR4nH45k26yQ0B+UsHDp0iB07diCl5M4776S0tJRAIICqqhmzqaCggJ07d7Jz507uu+8+HnzwwdTvRqPxrMK3aNEi/v3f//2c19i0aVM6Tb4gNGE5N0IIDAYD999/P7fffjtut5utW7dm2iyNM7Bnzx727t2LlJJPf/rT5OfnEwgEyGQfNE1HNBRFwWg08md/9mfcfPPNdHZ2snv37kybdRKag3IGpJS8/vrrvP3220gp0ev1XHvttUgpL+yD39EBTzwBDz2U+LejI+223n333dx3330sXbqUr371q7zzzjssW7aM+fPns3z5cg4dOgTAa6+9xvXXXw/AN77xDT796U+zevVq6urqThIcu92eOn716tV89KMfZcaMGdxxxx0pUX3uueeYMWMGCxcu5Etf+lJq3LEEg0Fuv/12mpqauPnmm09aQrz//vtZtGgRs2bN4h/+4R8A+Pd//3e6urpYs2YNa9asOeNxH3T0ej1WqxW73U59fT2f/OQnWbp0aabN0jgNUko2bNjAli1bADCZTFx99dWoqnphS+qajmg6kmYMBgNWqxWz2czs2bO58847aW5uzrRZJ6Fl8ZyBrq4uuru7ufbaa1GUhB9XUVFBd3c3fr8fm8127j3kjg546ilwOqG4GHy+xO833QQVFWm1t6Ojg02bNqHT6fB6vbz55pvo9Xpefvll/vZv/5bf//737zvn4MGDbNiwgZGRERobG7n//vvfl3L27rvvsm/fPsrKylixYgUbN25k0aJFfP7zn+eNN96gtraWj3/846e16Uc/+hFWq5UDBw6we/duFixYkHruW9/6Fvn5+cTjcdatW8fu3bv50pe+xPe//302bNhAYWHhGY+bO3duGl+5yYXf73/fe1RXV5chazTOxbFj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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "def plot_1d_data(\n", + " X_train,\n", + " y_train,\n", + " X_test,\n", + " y_test,\n", + " y_sigma,\n", + " y_pred,\n", + " y_pred_low,\n", + " y_pred_up,\n", + " ax=None,\n", + " title=None\n", + "):\n", + " ax.set_xlabel(\"x\")\n", + " ax.set_ylabel(\"y\")\n", + " ax.fill_between(X_test, y_pred_low, y_pred_up, alpha=0.3)\n", + " ax.scatter(X_train, y_train, color=\"red\", alpha=0.3, label=\"Training data\")\n", + " ax.plot(X_test, y_test, color=\"gray\", label=\"True confidence intervals\")\n", + " ax.plot(X_test, y_test - y_sigma, color=\"gray\", ls=\"--\")\n", + " ax.plot(X_test, y_test + y_sigma, color=\"gray\", ls=\"--\")\n", + " ax.plot(\n", + " X_test, y_pred, color=\"blue\", alpha=0.5, label=\"Prediction intervals\"\n", + " )\n", + " if title is not None:\n", + " ax.set_title(title)\n", + " ax.legend()\n", + "\n", + "\n", + "strategies = [\n", + " \"jackknife_plus\",\n", + " \"jackknife_minmax\",\n", + " \"cv_plus\",\n", + " \"cv_minmax\",\n", + " \"jackknife_plus_ab\",\n", + " \"conformalized_quantile_regression\"\n", + "]\n", + "n_figs = len(strategies)\n", + "fig, axs = plt.subplots(3, 2, figsize=(9, 13))\n", + "coords = [axs[0, 0], axs[0, 1], axs[1, 0], axs[1, 1], axs[2, 0], axs[2, 1]]\n", + "for strategy, coord in zip(strategies, coords):\n", + " plot_1d_data(\n", + " X_train.ravel(),\n", + " y_train.ravel(),\n", + " X_test.ravel(),\n", + " y_mesh.ravel(),\n", + " np.full((X_test.shape[0]), 1.96*noise).ravel(),\n", + " y_pred[strategy].ravel(),\n", + " y_pis[strategy][:, 0, 0].ravel(),\n", + " y_pis[strategy][:, 1, 0].ravel(),\n", + " ax=coord,\n", + " title=strategy\n", + " )\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "At first glance, the four strategies give similar results and the\n", + "prediction intervals are very close to the true confidence intervals.\n", + "Let’s confirm this by comparing the prediction interval widths over\n", + "$x$ between all strategies.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1, figsize=(9, 5))\n", + "ax.axhline(1.96*2*noise, ls=\"--\", color=\"k\", label=\"True width\")\n", + "for strategy in STRATEGIES:\n", + " ax.plot(\n", + " X_test,\n", + " y_pis[strategy][:, 1, 0] - y_pis[strategy][:, 0, 0],\n", + " label=strategy\n", + " )\n", + "ax.set_xlabel(\"x\")\n", + "ax.set_ylabel(\"Prediction Interval Width\")\n", + "ax.legend(fontsize=8)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As expected, the prediction intervals estimated by the Naive method\n", + "are slightly too narrow. The Jackknife, Jackknife+, CV, CV+, JaB, and J+aB\n", + "give\n", + "similar widths that are very close to the true width. On the other hand,\n", + "the width estimated by Jackknife-minmax and CV-minmax are slightly too\n", + "wide. Note that the widths given by the Naive, Jackknife, and CV strategies\n", + "are constant because there is a single model used for prediction,\n", + "perturbed models are ignored at prediction time.\n", + "\n", + "It's interesting to observe that CQR strategy offers more varying width,\n", + "often giving much higher but also lower interval width than other methods,\n", + "therefore,\n", + "with homoscedastic noise, CQR would not be the preferred method.\n", + "\n", + "Let’s now compare the *effective* coverage, namely the fraction of test\n", + "points whose true values lie within the prediction intervals, given by\n", + "the different strategies.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "pd.DataFrame([\n", + " [\n", + " regression_coverage_score(\n", + " y_test, y_pis[strategy][:, 0, 0], y_pis[strategy][:, 1, 0]\n", + " ),\n", + " (\n", + " y_pis[strategy][:, 1, 0] - y_pis[strategy][:, 0, 0]\n", + " ).mean()\n", + " ] for strategy in STRATEGIES\n", + "], index=STRATEGIES, columns=[\"Coverage\", \"Width average\"]).round(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All strategies except the Naive one give effective coverage close to the\n", + "expected 0.95 value (recall that alpha = 0.05), confirming the theoretical\n", + "garantees.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Estimating the aleatoric uncertainty of heteroscedastic noisy data\n", + "\n", + "Let's define again the $x \\times \\sin(x)$ function and another simple\n", + "function that generates one-dimensional data with normal noise uniformely\n", + "in a given interval.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def get_1d_data_with_heteroscedastic_noise(\n", + " funct, min_x, max_x, n_samples, noise\n", + "):\n", + " \"\"\"\n", + " Generate 1D noisy data uniformely from the given function\n", + " and standard deviation for the noise.\n", + " \"\"\"\n", + " np.random.seed(59)\n", + " X_train = np.linspace(min_x, max_x, n_samples)\n", + " np.random.shuffle(X_train)\n", + " X_test = np.linspace(min_x, max_x, n_samples*5)\n", + " y_train = (\n", + " funct(X_train) +\n", + " (np.random.normal(0, noise, len(X_train)) * X_train)\n", + " )\n", + " y_test = (\n", + " funct(X_test) +\n", + " (np.random.normal(0, noise, len(X_test)) * X_test)\n", + " )\n", + " y_mesh = funct(X_test)\n", + " return (\n", + " X_train.reshape(-1, 1), y_train, X_test.reshape(-1, 1), y_test, y_mesh\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We first generate noisy one-dimensional data uniformely on an interval.\n", + "Here, the noise is considered as *heteroscedastic*, since it will increase\n", + "linearly with $x$.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "min_x, max_x, n_samples, noise = 0, 5, 300, 0.5\n", + "(\n", + " X_train, y_train, X_test, y_test, y_mesh\n", + ") = get_1d_data_with_heteroscedastic_noise(\n", + " x_sinx, min_x, max_x, n_samples, noise\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's visualize our noisy function. As x increases, the data becomes more\n", + "noisy.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "plt.xlabel(\"x\")\n", + "plt.ylabel(\"y\")\n", + "plt.scatter(X_train, y_train, color=\"C0\")\n", + "plt.plot(X_test, y_mesh, color=\"C1\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As mentioned previously, we fit our training data with a simple\n", + "polynomial function. Here, we choose a degree equal to 10 so the function\n", + "is able to perfectly fit $x \\times \\sin(x)$.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "degree_polyn = 10\n", + "polyn_model = Pipeline(\n", + " [\n", + " (\"poly\", PolynomialFeatures(degree=degree_polyn)),\n", + " (\"linear\", LinearRegression())\n", + " ]\n", + ")\n", + "polyn_model_quant = Pipeline(\n", + " [\n", + " (\"poly\", PolynomialFeatures(degree=degree_polyn)),\n", + " (\"linear\", QuantileRegressor(\n", + " solver=\"highs\",\n", + " alpha=0,\n", + " ))\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We then estimate the prediction intervals for all the strategies very easily\n", + "with a\n", + "`fit` and `predict` process. The prediction interval's lower and upper bounds\n", + "are then saved in a DataFrame. Here, we set an alpha value of 0.05\n", + "in order to obtain a 95% confidence for our prediction intervals.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "STRATEGIES = {\n", + " \"naive\": dict(method=\"naive\"),\n", + " \"jackknife\": dict(method=\"base\", cv=-1),\n", + " \"jackknife_plus\": dict(method=\"plus\", cv=-1),\n", + " \"jackknife_minmax\": dict(method=\"minmax\", cv=-1),\n", + " \"cv\": dict(method=\"base\", cv=10),\n", + " \"cv_plus\": dict(method=\"plus\", cv=10),\n", + " \"cv_minmax\": dict(method=\"minmax\", cv=10),\n", + " \"jackknife_plus_ab\": dict(method=\"plus\", cv=Subsample(n_resamplings=50)),\n", + " \"conformalized_quantile_regression\": dict(\n", + " method=\"quantile\", cv=\"split\", alpha=0.05\n", + " )\n", + "}\n", + "y_pred, y_pis = {}, {}\n", + "for strategy, params in STRATEGIES.items():\n", + " if strategy == \"conformalized_quantile_regression\":\n", + " mapie = MapieQuantileRegressor(polyn_model_quant, **params)\n", + " mapie.fit(X_train, y_train, random_state=1)\n", + " y_pred[strategy], y_pis[strategy] = mapie.predict(X_test)\n", + " else:\n", + " mapie = MapieRegressor(polyn_model, **params)\n", + " mapie.fit(X_train, y_train)\n", + " y_pred[strategy], y_pis[strategy] = mapie.predict(X_test, alpha=0.05)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once again, let’s compare the target confidence intervals with prediction\n", + "intervals obtained with the Jackknife+, Jackknife-minmax, CV+, CV-minmax,\n", + "Jackknife+-after-Boostrap, and CQR strategies.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "strategies = [\n", + " \"jackknife_plus\",\n", + " \"jackknife_minmax\",\n", + " \"cv_plus\",\n", + " \"cv_minmax\",\n", + " \"jackknife_plus_ab\",\n", + " \"conformalized_quantile_regression\"\n", + "]\n", + "n_figs = len(strategies)\n", + "fig, axs = plt.subplots(3, 2, figsize=(9, 13))\n", + "coords = [axs[0, 0], axs[0, 1], axs[1, 0], axs[1, 1], axs[2, 0], axs[2, 1]]\n", + "for strategy, coord in zip(strategies, coords):\n", + " plot_1d_data(\n", + " X_train.ravel(),\n", + " y_train.ravel(),\n", + " X_test.ravel(),\n", + " y_mesh.ravel(),\n", + " (1.96*noise*X_test).ravel(),\n", + " y_pred[strategy].ravel(),\n", + " y_pis[strategy][:, 0, 0].ravel(),\n", + " y_pis[strategy][:, 1, 0].ravel(),\n", + " ax=coord,\n", + " title=strategy\n", + " )\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can observe that all of the strategies except CQR seem to have similar\n", + "constant prediction intervals.\n", + "On the other hand, the CQR strategy offers a solution that adapts the\n", + "prediction intervals to the local noise.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1, figsize=(7, 5))\n", + "ax.plot(X_test, 1.96*2*noise*X_test, ls=\"--\", color=\"k\", label=\"True width\")\n", + "for strategy in STRATEGIES:\n", + " ax.plot(\n", + " X_test,\n", + " y_pis[strategy][:, 1, 0] - y_pis[strategy][:, 0, 0],\n", + " label=strategy\n", + " )\n", + "ax.set_xlabel(\"x\")\n", + "ax.set_ylabel(\"Prediction Interval Width\")\n", + "ax.legend(fontsize=8)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One can observe that all the strategies behave in a similar way as in the\n", + "first example shown previously. One exception is the CQR method which takes\n", + "into account the heteroscedasticity of the data. In this method we observe\n", + "very low interval widths at low values of $x$.\n", + "This is the only method that\n", + "even slightly follows the true width, and therefore is the preferred method\n", + "for heteroscedastic data. Notice also that the true width is greater (lower)\n", + "than the predicted width from the other methods at $x \\gtrapprox 3$`\n", + "($x \\leq 3$). This means that while the marginal coverage correct for\n", + "these methods, the conditional coverage is likely not guaranteed as we will\n", + "observe in the next figure.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def get_heteroscedastic_coverage(y_test, y_pis, STRATEGIES, bins):\n", + " recap = {}\n", + " for i in range(len(bins)-1):\n", + " bin1, bin2 = bins[i], bins[i+1]\n", + " name = f\"[{bin1}, {bin2}]\"\n", + " recap[name] = []\n", + " for strategy in STRATEGIES:\n", + " indices = np.where((X_test >= bins[i]) * (X_test <= bins[i+1]))\n", + " y_test_trunc = np.take(y_test, indices)\n", + " y_low_ = np.take(y_pis[strategy][:, 0, 0], indices)\n", + " y_high_ = np.take(y_pis[strategy][:, 1, 0], indices)\n", + " score_coverage = regression_coverage_score(\n", + " y_test_trunc[0], y_low_[0], y_high_[0]\n", + " )\n", + " recap[name].append(score_coverage)\n", + " recap_df = pd.DataFrame(recap, index=STRATEGIES)\n", + " return recap_df\n", + "\n", + "\n", + "bins = [0, 1, 2, 3, 4, 5]\n", + "heteroscedastic_coverage = get_heteroscedastic_coverage(\n", + " y_test, y_pis, STRATEGIES, bins\n", + ")\n", + "\n", + "# fig = plt.figure()\n", + "heteroscedastic_coverage.T.plot.bar(figsize=(12, 5), alpha=0.7)\n", + "plt.axhline(0.95, ls=\"--\", color=\"k\")\n", + "plt.ylabel(\"Conditional coverage\")\n", + "plt.xlabel(\"x bins\")\n", + "plt.xticks(rotation=0)\n", + "plt.ylim(0.8, 1.0)\n", + "plt.legend(fontsize=8, loc=[0, 0])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let’s now conclude by summarizing the *effective* coverage, namely the\n", + "fraction of test\n", + "points whose true values lie within the prediction intervals, given by\n", + "the different strategies.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "pd.DataFrame([\n", + " [\n", + " regression_coverage_score(\n", + " y_test, y_pis[strategy][:, 0, 0], y_pis[strategy][:, 1, 0]\n", + " ),\n", + " (\n", + " y_pis[strategy][:, 1, 0] - y_pis[strategy][:, 0, 0]\n", + " ).mean()\n", + " ] for strategy in STRATEGIES\n", + "], index=STRATEGIES, columns=[\"Coverage\", \"Width average\"]).round(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All the strategies have the wanted coverage, however, we notice that the CQR\n", + "strategy has much lower interval width than all the other methods, therefore,\n", + "with heteroscedastic noise, CQR would be the preferred method.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Estimating the epistemic uncertainty of out-of-distribution data\n", + "\n", + "Let’s now consider one-dimensional data without noise, but normally\n", + "distributed.\n", + "The goal is to explore how the prediction intervals evolve for new data\n", + "that lie outside the distribution of the training data in order to see how\n", + "the strategies can capture the *epistemic* uncertainty.\n", + "For a comparison of the epistemic and aleatoric uncertainties, please have\n", + "a look at this source:\n", + "https://en.wikipedia.org/wiki/Uncertainty_quantification.\n", + "\n", + "Let's start by generating and showing the data.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def get_1d_data_with_normal_distrib(funct, mu, sigma, n_samples, noise):\n", + " \"\"\"\n", + " Generate noisy 1D data with normal distribution from given function\n", + " and noise standard deviation.\n", + " \"\"\"\n", + " np.random.seed(59)\n", + " X_train = np.random.normal(mu, sigma, n_samples)\n", + " X_test = np.arange(mu-4*sigma, mu+4*sigma, sigma/20.)\n", + " y_train, y_mesh, y_test = funct(X_train), funct(X_test), funct(X_test)\n", + " y_train += np.random.normal(0, noise, y_train.shape[0])\n", + " y_test += np.random.normal(0, noise, y_test.shape[0])\n", + " return (\n", + " X_train.reshape(-1, 1), y_train, X_test.reshape(-1, 1), y_test, y_mesh\n", + " )\n", + "\n", + "\n", + "mu, sigma, n_samples, noise = 0, 2, 1000, 0.\n", + "X_train, y_train, X_test, y_test, y_mesh = get_1d_data_with_normal_distrib(\n", + " x_sinx, mu, sigma, n_samples, noise\n", + ")\n", + "plt.xlabel(\"x\")\n", + "plt.ylabel(\"y\")\n", + "plt.scatter(X_train, y_train, color=\"C0\")\n", + "_ = plt.plot(X_test, y_test, color=\"C1\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As before, we estimate the prediction intervals using a polynomial\n", + "function of degree 10 and show the results for the Jackknife+ and CV+\n", + "strategies.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "polyn_model_quant = Pipeline(\n", + " [\n", + " (\"poly\", PolynomialFeatures(degree=degree_polyn)),\n", + " (\"linear\", QuantileRegressor(\n", + " solver=\"highs-ds\",\n", + " alpha=0,\n", + " ))\n", + " ]\n", + ")\n", + "STRATEGIES = {\n", + " \"naive\": dict(method=\"naive\"),\n", + " \"jackknife\": dict(method=\"base\", cv=-1),\n", + " \"jackknife_plus\": dict(method=\"plus\", cv=-1),\n", + " \"jackknife_minmax\": dict(method=\"minmax\", cv=-1),\n", + " \"cv\": dict(method=\"base\", cv=10),\n", + " \"cv_plus\": dict(method=\"plus\", cv=10),\n", + " \"cv_minmax\": dict(method=\"minmax\", cv=10),\n", + " \"jackknife_plus_ab\": dict(method=\"plus\", cv=Subsample(n_resamplings=50)),\n", + " \"jackknife_minmax_ab\": dict(\n", + " method=\"minmax\", cv=Subsample(n_resamplings=50)\n", + " ),\n", + " \"conformalized_quantile_regression\": dict(\n", + " method=\"quantile\", cv=\"split\", alpha=0.05\n", + " )\n", + "}\n", + "y_pred, y_pis = {}, {}\n", + "for strategy, params in STRATEGIES.items():\n", + " if strategy == \"conformalized_quantile_regression\":\n", + " mapie = MapieQuantileRegressor(polyn_model_quant, **params)\n", + " mapie.fit(X_train, y_train, random_state=1)\n", + " y_pred[strategy], y_pis[strategy] = mapie.predict(X_test)\n", + " else:\n", + " mapie = MapieRegressor(polyn_model, **params)\n", + " mapie.fit(X_train, y_train)\n", + " y_pred[strategy], y_pis[strategy] = mapie.predict(X_test, alpha=0.05)\n", + "\n", + "strategies = [\n", + " \"jackknife_plus\",\n", + " \"jackknife_minmax\",\n", + " \"cv_plus\",\n", + " \"cv_minmax\",\n", + " \"jackknife_plus_ab\",\n", + " \"conformalized_quantile_regression\"\n", + "]\n", + "n_figs = len(strategies)\n", + "fig, axs = plt.subplots(3, 2, figsize=(9, 13))\n", + "coords = [axs[0, 0], axs[0, 1], axs[1, 0], axs[1, 1], axs[2, 0], axs[2, 1]]\n", + "for strategy, coord in zip(strategies, coords):\n", + " plot_1d_data(\n", + " X_train.ravel(),\n", + " y_train.ravel(),\n", + " X_test.ravel(),\n", + " y_mesh.ravel(),\n", + " 1.96*noise,\n", + " y_pred[strategy].ravel(),\n", + " y_pis[strategy][:, 0, :].ravel(),\n", + " y_pis[strategy][:, 1, :].ravel(),\n", + " ax=coord,\n", + " title=strategy\n", + " )\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "At first glance, our polynomial function does not give accurate\n", + "predictions with respect to the true function when $|x| > 6$.\n", + "The prediction intervals estimated with the Jackknife+ do not seem to\n", + "increase. On the other hand, the CV and other related methods seem to capture\n", + "some uncertainty when $x > 6$.\n", + "\n", + "Let's now compare the prediction interval widths between all strategies.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1, figsize=(7, 5))\n", + "ax.set_yscale(\"log\")\n", + "for strategy in STRATEGIES:\n", + " ax.plot(\n", + " X_test,\n", + " y_pis[strategy][:, 1, 0] - y_pis[strategy][:, 0, 0],\n", + " label=strategy\n", + " )\n", + "ax.set_xlabel(\"x\")\n", + "ax.set_ylabel(\"Prediction Interval Width\")\n", + "ax.legend(fontsize=8)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The prediction interval widths start to increase exponentially\n", + "for $|x| > 4$ for the CV+, CV-minmax, Jackknife-minmax, and quantile\n", + "strategies. On the other hand, the prediction intervals estimated by\n", + "Jackknife+ remain roughly constant until $|x| \\approx 5$ before\n", + "increasing.\n", + "The CQR strategy seems to perform well, however, on the extreme values\n", + "of the data the quantile regression fails to give reliable results as it\n", + "outputs\n", + "negative value for the prediction intervals. This occurs because the quantile\n", + "regressor with quantile $1 - \\alpha/2$ gives higher values than the\n", + "quantile regressor with quantile $\\alpha/2$. Note that a warning will\n", + "be issued when this occurs.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "pd.DataFrame([\n", + " [\n", + " regression_coverage_score(\n", + " y_test, y_pis[strategy][:, 0, 0], y_pis[strategy][:, 1, 0]\n", + " ),\n", + " (\n", + " y_pis[strategy][:, 1, 0] - y_pis[strategy][:, 0, 0]\n", + " ).mean()\n", + " ] for strategy in STRATEGIES\n", + "], index=STRATEGIES, columns=[\"Coverage\", \"Width average\"]).round(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In conclusion, the Jackknife-minmax, CV+, CV-minmax, or Jackknife-minmax-ab\n", + "strategies are more\n", + "conservative than the Jackknife+ strategy, and tend to result in more\n", + "reliable coverages for *out-of-distribution* data. It is therefore\n", + "advised to use the three former strategies for predictions with new\n", + "out-of-distribution data.\n", + "Note however that there are no theoretical guarantees on the coverage level\n", + "for out-of-distribution data.\n", + "Here it's important to note that the CQR strategy should not be taken into\n", + "account for width prediction, and it is abundantly clear from the negative\n", + "width coverage that is observed in these results.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Estimating the uncertainty with different sklearn-compatible regressors\n", + "\n", + "MAPIE can be used with any kind of sklearn-compatible regressor. Here, we\n", + "illustrate this by comparing the prediction intervals estimated by the CV+\n", + "method using different models:\n", + "\n", + "* the same polynomial function as before.\n", + "\n", + "* a XGBoost model using the Scikit-learn API.\n", + "\n", + "* a simple neural network, a Multilayer Perceptron with three dense layers,\n", + " using the KerasRegressor wrapper.\n", + "\n", + "Once again, let’s use our noisy one-dimensional data obtained from a\n", + "uniform distribution.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "subprocess.run(\"pip install scikeras\", shell=True)\n", + "subprocess.run(\"pip install tensorflow\", shell=True)\n", + "subprocess.run(\"pip install xgboost\", shell=True)\n", + "\n", + "from scikeras.wrappers import KerasRegressor # noqa: E402\n", + "from tensorflow.keras import Sequential # noqa: E402\n", + "from tensorflow.keras.layers import Dense # noqa: E402\n", + "from xgboost import XGBRegressor # noqa: E402\n", + "\n", + "\n", + "min_x, max_x, n_samples, noise = -5, 5, 100, 0.5\n", + "X_train, y_train, X_test, y_test, y_mesh = get_1d_data_with_constant_noise(\n", + " x_sinx, min_x, max_x, n_samples, noise\n", + ")\n", + "\n", + "plt.xlabel(\"x\")\n", + "plt.ylabel(\"y\")\n", + "plt.plot(X_test, y_mesh, color=\"C1\")\n", + "_ = plt.scatter(X_train, y_train)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's then define the models. The boosing model considers 100 shallow\n", + "trees with a max depth of 2 while the Multilayer Perceptron has two hidden\n", + "dense layers with 20 neurons each followed by a relu activation.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def mlp():\n", + " \"\"\"\n", + " Two-layer MLP model\n", + " \"\"\"\n", + " model = Sequential([\n", + " Dense(units=20, input_shape=(1,), activation=\"relu\"),\n", + " Dense(units=20, activation=\"relu\"),\n", + " Dense(units=1)\n", + " ])\n", + " model.compile(loss=\"mean_squared_error\", optimizer=\"adam\")\n", + " return model\n", + "\n", + "\n", + "polyn_model = Pipeline(\n", + " [\n", + " (\"poly\", PolynomialFeatures(degree=degree_polyn)),\n", + " (\"linear\", LinearRegression())\n", + " ]\n", + ")\n", + "\n", + "xgb_model = XGBRegressor(\n", + " max_depth=2,\n", + " n_estimators=100,\n", + " tree_method=\"hist\",\n", + " random_state=59,\n", + " learning_rate=0.1,\n", + " verbosity=0,\n", + " nthread=-1\n", + ")\n", + "mlp_model = KerasRegressor(\n", + " build_fn=mlp,\n", + " epochs=500,\n", + " verbose=0\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's now use MAPIE to estimate the prediction intervals using the CV+\n", + "method and compare their prediction interval.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "models = [polyn_model, xgb_model, mlp_model]\n", + "model_names = [\"polyn\", \"xgb\", \"mlp\"]\n", + "for name, model in zip(model_names, models):\n", + " mapie = MapieRegressor(model, method=\"plus\", cv=10)\n", + " mapie.fit(X_train, y_train)\n", + " y_pred[name], y_pis[name] = mapie.predict(X_test, alpha=0.05)\n", + "\n", + "fig, axs = plt.subplots(1, 3, figsize=(20, 6))\n", + "for name, ax in zip(model_names, axs):\n", + " plot_1d_data(\n", + " X_train.ravel(),\n", + " y_train.ravel(),\n", + " X_test.ravel(),\n", + " y_mesh.ravel(),\n", + " 1.96*noise,\n", + " y_pred[name].ravel(),\n", + " y_pis[name][:, 0, 0].ravel(),\n", + " y_pis[name][:, 1, 0].ravel(),\n", + " ax=ax,\n", + " title=name\n", + " )\n", + "plt.show()\n", + "\n", + "\n", + "fig, ax = plt.subplots(1, 1, figsize=(7, 5))\n", + "for name in model_names:\n", + " ax.plot(X_test, y_pis[name][:, 1, 0] - y_pis[name][:, 0, 0])\n", + "ax.axhline(1.96*2*noise, ls=\"--\", color=\"k\")\n", + "ax.set_xlabel(\"x\")\n", + "ax.set_ylabel(\"Prediction Interval Width\")\n", + "ax.legend(model_names + [\"True width\"], fontsize=8)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As expected with the CV+ method, the prediction intervals are a bit\n", + "conservative since they are slightly wider than the true intervals.\n", + "However, the CV+ method on the three models gives very promising results\n", + "since the prediction intervals closely follow the true intervals with\n", + "$x$.\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.9.0 64-bit ('3.9.0')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.0" + }, + "vscode": { + "interpreter": { + "hash": "9f3749d15f85194e2be8a9af590cff47c92b4121cd966ac320a66f9707b35b3a" + } + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 04314dd..ac17c5a 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.8.1" manifest_format = "2.0" -project_hash = "ae240e8e0ebf5559f152baffe6c05f54b653e10d" +project_hash = "3d726f20636a3ccda39ed827011f908499c4d97d" [[deps.ANSIColoredPrinters]] git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" @@ -26,39 +26,50 @@ git-tree-sha1 = "52b3b436f8f73133d7bc3a6c71ee7ed6ab2ab754" uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" version = "0.4.3" +[[deps.Accessors]] +deps = ["Compat", "CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "LinearAlgebra", "MacroTools", "Requires", "Test"] +git-tree-sha1 = "eb7a1342ff77f4f9b6552605f27fd432745a53a3" +uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" +version = "0.1.22" + [[deps.Adapt]] deps = ["LinearAlgebra"] git-tree-sha1 = "195c5505521008abea5aee4f96930717958eac6f" uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" version = "3.4.0" +[[deps.ArgCheck]] +git-tree-sha1 = "a3a402a35a2f7e0b87828ccabbd5ebfbebe356b4" +uuid = "dce04be8-c92d-5529-be00-80e4d2c0e197" +version = "2.3.0" + [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.1" [[deps.ArrayInterface]] deps = ["ArrayInterfaceCore", "Compat", "IfElse", "LinearAlgebra", "Static"] -git-tree-sha1 = "d6173480145eb632d6571c148d94b9d3d773820e" +git-tree-sha1 = "6d0918cb9c0d3db7fe56bea2bc8638fc4014ac35" uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "6.0.23" +version = "6.0.24" [[deps.ArrayInterfaceCore]] deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "e6cba4aadba7e8a7574ab2ba2fcfb307b4c4b02a" +git-tree-sha1 = "c46fb7dd1d8ca1d213ba25848a5ec4e47a1a1b08" uuid = "30b0a656-2188-435a-8636-2ec0e6a096e2" -version = "0.1.23" +version = "0.1.26" [[deps.ArrayInterfaceOffsetArrays]] deps = ["ArrayInterface", "OffsetArrays", "Static"] -git-tree-sha1 = "c49f6bad95a30defff7c637731f00934c7289c50" +git-tree-sha1 = "3d1a9a01976971063b3930d1aed1d9c4af0817f8" uuid = "015c0d05-e682-4f19-8f0a-679ce4c54826" -version = "0.1.6" +version = "0.1.7" [[deps.ArrayInterfaceStaticArrays]] -deps = ["Adapt", "ArrayInterface", "ArrayInterfaceStaticArraysCore", "LinearAlgebra", "Static", "StaticArrays"] -git-tree-sha1 = "efb000a9f643f018d5154e56814e338b5746c560" +deps = ["Adapt", "ArrayInterface", "ArrayInterfaceCore", "ArrayInterfaceStaticArraysCore", "LinearAlgebra", "Static", "StaticArrays"] +git-tree-sha1 = "f12dc65aef03d0a49650b20b2fdaf184928fd886" uuid = "b0d46f97-bff5-4637-a19a-dd75974142cd" -version = "0.1.4" +version = "0.1.5" [[deps.ArrayInterfaceStaticArraysCore]] deps = ["Adapt", "ArrayInterfaceCore", "LinearAlgebra", "StaticArraysCore"] @@ -82,23 +93,34 @@ uuid = "ab4f0b2a-ad5b-11e8-123f-65d77653426b" version = "0.2.0" [[deps.BSON]] -git-tree-sha1 = "306bb5574b0c1c56d7e1207581516c557d105cad" +git-tree-sha1 = "86e9781ac28f4e80e9b98f7f96eae21891332ac2" uuid = "fbb218c0-5317-5bc6-957e-2ee96dd4b1f0" -version = "0.3.5" +version = "0.3.6" + +[[deps.BangBang]] +deps = ["Compat", "ConstructionBase", "Future", "InitialValues", "LinearAlgebra", "Requires", "Setfield", "Tables", "ZygoteRules"] +git-tree-sha1 = "7fe6d92c4f281cf4ca6f2fba0ce7b299742da7ca" +uuid = "198e06fe-97b7-11e9-32a5-e1d131e6ad66" +version = "0.3.37" [[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +[[deps.Baselet]] +git-tree-sha1 = "aebf55e6d7795e02ca500a689d326ac979aaf89e" +uuid = "9718e550-a3fa-408a-8086-8db961cd8217" +version = "0.1.1" + [[deps.BitFlags]] -git-tree-sha1 = "84259bb6172806304b9101094a7cc4bc6f56dbc6" +git-tree-sha1 = "43b1a4a8f797c1cddadf60499a8a077d4af2cd2d" uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" -version = "0.1.5" +version = "0.1.7" [[deps.BitTwiddlingConvenienceFunctions]] deps = ["Static"] -git-tree-sha1 = "eaee37f76339077f86679787a71990c4e465477f" +git-tree-sha1 = "0c5f81f47bbbcf4aea7b2959135713459170798b" uuid = "62783981-4cbd-42fc-bca8-16325de8dc4b" -version = "0.1.4" +version = "0.1.5" [[deps.Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -113,9 +135,9 @@ version = "0.4.2" [[deps.CPUSummary]] deps = ["CpuId", "IfElse", "Static"] -git-tree-sha1 = "9bdd5aceea9fa109073ace6b430a24839d79315e" +git-tree-sha1 = "a7157ab6bcda173f533db4c93fc8a27a48843757" uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" -version = "0.1.27" +version = "0.1.30" [[deps.CUDA]] deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CompilerSupportLibraries_jll", "ExprTools", "GPUArrays", "GPUCompiler", "LLVM", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "SpecialFunctions", "TimerOutputs"] @@ -147,6 +169,12 @@ git-tree-sha1 = "23fe4c6668776fedfd3747c545cd0d1a5190eb15" uuid = "af321ab8-2d2e-40a6-b165-3d674595d28e" version = "0.1.9" +[[deps.ChainRules]] +deps = ["Adapt", "ChainRulesCore", "Compat", "Distributed", "GPUArraysCore", "IrrationalConstants", "LinearAlgebra", "Random", "RealDot", "SparseArrays", "Statistics", "StructArrays"] +git-tree-sha1 = "0c8c8887763f42583e1206ee35413a43c91e2623" +uuid = "082447d4-558c-5d27-93f4-14fc19e9eca2" +version = "1.45.0" + [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra", "SparseArrays"] git-tree-sha1 = "e7ff6cadf743c098e08fca25c91103ee4303c9bb" @@ -161,15 +189,9 @@ version = "0.1.4" [[deps.CloseOpenIntervals]] deps = ["ArrayInterface", "Static"] -git-tree-sha1 = "5522c338564580adf5d58d91e43a55db0fa5fb39" +git-tree-sha1 = "d61300b9895f129f4bd684b2aff97cf319b6c493" uuid = "fb6a15b2-703c-40df-9091-08a04967cfa9" -version = "0.1.10" - -[[deps.CodeTracking]] -deps = ["InteractiveUtils", "UUIDs"] -git-tree-sha1 = "cc4bd91eba9cdbbb4df4746124c22c0832a460d6" -uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" -version = "1.1.1" +version = "0.1.11" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] @@ -178,10 +200,10 @@ uuid = "944b1d66-785c-5afd-91f1-9de20f533193" version = "0.7.0" [[deps.ColorSchemes]] -deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "Random"] -git-tree-sha1 = "1fd869cc3875b57347f7027521f561cf46d1fcd8" +deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "Random", "SnoopPrecompile"] +git-tree-sha1 = "aa3edc8f8dea6cbfa176ee12f7c2fc82f0608ed3" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.19.0" +version = "3.20.0" [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] @@ -214,25 +236,30 @@ version = "0.3.0" [[deps.Compat]] deps = ["Dates", "LinearAlgebra", "UUIDs"] -git-tree-sha1 = "3ca828fe1b75fa84b021a7860bd039eaea84d2f2" +git-tree-sha1 = "00a2cccc7f098ff3b66806862d275ca3db9e6e5a" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.3.0" +version = "4.5.0" [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" version = "0.5.2+0" +[[deps.CompositionsBase]] +git-tree-sha1 = "455419f7e328a1a2493cabc6428d79e951349769" +uuid = "a33af91c-f02d-484b-be07-31d278c5ca2b" +version = "0.1.1" + [[deps.ComputationalResources]] git-tree-sha1 = "52cb3ec90e8a8bea0e62e275ba577ad0f74821f7" uuid = "ed09eef8-17a6-5b46-8889-db040fac31e3" version = "0.3.2" [[deps.ConformalPrediction]] -deps = ["MLJ", "MLJBase", "MLJModelInterface", "Statistics", "Term"] +deps = ["CategoricalArrays", "MLJ", "MLJBase", "MLJModelInterface", "Plots", "Statistics"] path = ".." uuid = "98bfc277-1877-43dc-819b-a3e38c30242f" -version = "0.1.2" +version = "0.1.3" [[deps.ConstructionBase]] deps = ["LinearAlgebra"] @@ -240,6 +267,12 @@ git-tree-sha1 = "fb21ddd70a051d882a1686a5a550990bbe371a95" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" version = "1.4.1" +[[deps.ContextVariablesX]] +deps = ["Compat", "Logging", "UUIDs"] +git-tree-sha1 = "25cc3803f1030ab855e383129dcd3dc294e322cc" +uuid = "6add18c4-b38d-439d-96f6-d6bc489c04c5" +version = "0.1.3" + [[deps.Contour]] git-tree-sha1 = "d05d9e7b7aedff4e5b51a029dced05cfb6125781" uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" @@ -257,15 +290,15 @@ uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" version = "4.1.1" [[deps.DataAPI]] -git-tree-sha1 = "46d2680e618f8abd007bce0c3026cb0c4a8f2032" +git-tree-sha1 = "e08915633fcb3ea83bf9d6126292e5bc5c739922" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.12.0" +version = "1.13.0" [[deps.DataFrames]] deps = ["Compat", "DataAPI", "Future", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SnoopPrecompile", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] -git-tree-sha1 = "558078b0b78278683a7445c626ee78c86b9bb000" +git-tree-sha1 = "0f44494fe4271cc966ac4fea524111bef63ba86c" uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" -version = "1.4.1" +version = "1.4.3" [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] @@ -284,9 +317,14 @@ uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" [[deps.DecisionTree]] deps = ["AbstractTrees", "DelimitedFiles", "LinearAlgebra", "Random", "ScikitLearnBase", "Statistics"] -git-tree-sha1 = "fb3f7ff27befb9877bee84076dd9173185d7d86a" +git-tree-sha1 = "5ab40e9f5a554a642bc5ab1d20a198f76a1e6958" uuid = "7806a523-6efd-50cb-b5f6-3fa6f1930dbb" -version = "0.11.2" +version = "0.12.0" + +[[deps.DefineSingletons]] +git-tree-sha1 = "0fba8b706d0178b4dc7fd44a96a92382c9065c2c" +uuid = "244e2a9f-e319-4986-a169-4d1fe445cd52" +version = "0.1.2" [[deps.DelimitedFiles]] deps = ["Mmap"] @@ -306,9 +344,9 @@ version = "1.1.0" [[deps.DiffRules]] deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] -git-tree-sha1 = "8b7a4d23e22f5d44883671da70865ca98f2ebf9d" +git-tree-sha1 = "c5b6685d53f933c11404a3ae9822afe30d522494" uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" -version = "1.12.0" +version = "1.12.2" [[deps.Distances]] deps = ["LinearAlgebra", "SparseArrays", "Statistics", "StatsAPI"] @@ -322,9 +360,9 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" [[deps.Distributions]] deps = ["ChainRulesCore", "DensityInterface", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SparseArrays", "SpecialFunctions", "Statistics", "StatsBase", "StatsFuns", "Test"] -git-tree-sha1 = "04db820ebcfc1e053bd8cbb8d8bccf0ff3ead3f7" +git-tree-sha1 = "a7756d098cbabec6b3ac44f369f74915e8cfd70a" uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.76" +version = "0.25.79" [[deps.DocStringExtensions]] deps = ["LibGit2"] @@ -363,9 +401,9 @@ version = "0.3.0" [[deps.EvoTrees]] deps = ["BSON", "CUDA", "CategoricalArrays", "Distributions", "LoopVectorization", "MLJModelInterface", "NetworkLayout", "Random", "RecipesBase", "Statistics", "StatsBase", "Tables"] -git-tree-sha1 = "966e236ded10551a44b6e25ce4bbea4c12be1557" +git-tree-sha1 = "7509d36427e6fd08aa7e591d783713263fdac57e" uuid = "f6006082-12f8-11e9-0c9c-0d5d367ab1e5" -version = "0.12.4" +version = "0.14.2" [[deps.Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -407,6 +445,18 @@ git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" version = "3.3.10+0" +[[deps.FLoops]] +deps = ["BangBang", "Compat", "FLoopsBase", "InitialValues", "JuliaVariables", "MLStyle", "Serialization", "Setfield", "Transducers"] +git-tree-sha1 = "ffb97765602e3cbe59a0589d237bf07f245a8576" +uuid = "cc61a311-1640-44b5-9fba-1b764f453329" +version = "0.2.1" + +[[deps.FLoopsBase]] +deps = ["ContextVariablesX"] +git-tree-sha1 = "656f7a6859be8673bf1f35da5670246b923964f7" +uuid = "b9860ae5-e623-471e-878b-f6a53c775ea6" +version = "0.1.1" + [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] git-tree-sha1 = "7be5f99f7d15578798f338f5433b6c432ea8037b" @@ -424,9 +474,9 @@ version = "0.13.5" [[deps.FiniteDiff]] deps = ["ArrayInterfaceCore", "LinearAlgebra", "Requires", "Setfield", "SparseArrays", "StaticArrays"] -git-tree-sha1 = "5a2cff9b6b77b33b89f3d97a4d367747adce647e" +git-tree-sha1 = "04ed1f0029b6b3af88343e439b995141cb0d0b8d" uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" -version = "2.15.0" +version = "2.17.0" [[deps.FixedPointNumbers]] deps = ["Statistics"] @@ -434,6 +484,18 @@ git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" version = "0.8.4" +[[deps.Flux]] +deps = ["Adapt", "CUDA", "ChainRulesCore", "Functors", "LinearAlgebra", "MLUtils", "MacroTools", "NNlib", "NNlibCUDA", "OneHotArrays", "Optimisers", "ProgressLogging", "Random", "Reexport", "SparseArrays", "SpecialFunctions", "Statistics", "StatsBase", "Zygote"] +git-tree-sha1 = "2b85cb85f5d71f05e41089a2446ac33b8e94ebed" +uuid = "587475ba-b771-5e3f-ad9e-33799f191a9c" +version = "0.13.9" + +[[deps.FoldsThreads]] +deps = ["Accessors", "FunctionWrappers", "InitialValues", "SplittablesBase", "Transducers"] +git-tree-sha1 = "eb8e1989b9028f7e0985b4268dabe94682249025" +uuid = "9c68100b-dfe1-47cf-94c8-95104e173443" +version = "0.1.1" + [[deps.Fontconfig_jll]] deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" @@ -448,9 +510,9 @@ version = "0.4.2" [[deps.ForwardDiff]] deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "187198a4ed8ccd7b5d99c41b69c679269ea2b2d4" +git-tree-sha1 = "10fa12fe96e4d76acfa738f4df2126589a67374f" uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "0.10.32" +version = "0.10.33" [[deps.FreeType]] deps = ["CEnum", "FreeType2_jll"] @@ -470,6 +532,17 @@ git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" uuid = "559328eb-81f9-559d-9380-de523a88c83c" version = "1.0.10+0" +[[deps.FunctionWrappers]] +git-tree-sha1 = "d62485945ce5ae9c0c48f124a84998d755bae00e" +uuid = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" +version = "1.1.3" + +[[deps.Functors]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "993c2b4a9a54496b6d8e265db1244db418f37e01" +uuid = "d9f16b24-f501-4c13-a1f2-28368ffc5196" +version = "0.4.1" + [[deps.Future]] deps = ["Random"] uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" @@ -494,21 +567,21 @@ version = "0.1.2" [[deps.GPUCompiler]] deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "TimerOutputs", "UUIDs"] -git-tree-sha1 = "323949b0bbdf38c93d2ea1f7d3e68ff163c3f081" +git-tree-sha1 = "76f70a337a153c1632104af19d29023dbb6f30dd" uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" -version = "0.16.5" +version = "0.16.6" [[deps.GR]] -deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Preferences", "Printf", "Random", "Serialization", "Sockets", "Test", "UUIDs"] -git-tree-sha1 = "00a9d4abadc05b9476e937a5557fcce476b9e547" +deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Preferences", "Printf", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "UUIDs", "p7zip_jll"] +git-tree-sha1 = "051072ff2accc6e0e87b708ddee39b18aa04a0bc" uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -version = "0.69.5" +version = "0.71.1" [[deps.GR_jll]] deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Pkg", "Qt5Base_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "bc9f7725571ddb4ab2c4bc74fa397c1c5ad08943" +git-tree-sha1 = "501a4bf76fd679e7fcd678725d5072177392e756" uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" -version = "0.69.1+0" +version = "0.71.1+0" [[deps.GeoInterface]] deps = ["Extents"] @@ -518,9 +591,9 @@ version = "1.0.1" [[deps.GeometryBasics]] deps = ["EarCut_jll", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "12a584db96f1d460421d5fb8860822971cdb8455" +git-tree-sha1 = "fe9aea4ed3ec6afdfbeb5a4f39a2208909b162a6" uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -version = "0.4.4" +version = "0.4.5" [[deps.Gettext_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] @@ -559,9 +632,9 @@ version = "1.12.2+2" [[deps.HTTP]] deps = ["Base64", "CodecZlib", "Dates", "IniFile", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "a97d47758e933cd5fe5ea181d178936a9fc60427" +git-tree-sha1 = "e1acc37ed078d99a714ed8376446f92a5535ca65" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.5.1" +version = "1.5.5" [[deps.HarfBuzz_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] @@ -569,17 +642,11 @@ git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" version = "2.8.1+1" -[[deps.Highlights]] -deps = ["DocStringExtensions", "InteractiveUtils", "REPL"] -git-tree-sha1 = "0341077e8a6b9fc1c2ea5edc1e93a956d2aec0c7" -uuid = "eafb193a-b7ab-5a9e-9068-77385905fa72" -version = "0.5.2" - [[deps.HostCPUFeatures]] deps = ["BitTwiddlingConvenienceFunctions", "IfElse", "Libdl", "Static"] -git-tree-sha1 = "b7b88a4716ac33fe31d6556c02fc60017594343c" +git-tree-sha1 = "f64b890b2efa4de81520d2b0fbdc9aadb65bdf53" uuid = "3e5b6fbb-0976-4d2c-9146-d79de83f2fb0" -version = "0.1.8" +version = "0.1.13" [[deps.HypergeometricFunctions]] deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions", "Test"] @@ -593,6 +660,12 @@ git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" version = "0.2.2" +[[deps.IRTools]] +deps = ["InteractiveUtils", "MacroTools", "Test"] +git-tree-sha1 = "2e99184fca5eb6f075944b04c22edec29beb4778" +uuid = "7869d1d1-7146-5819-86e3-90919afe41df" +version = "0.4.7" + [[deps.IfElse]] git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" @@ -603,6 +676,11 @@ git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625" uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f" version = "0.5.1" +[[deps.InitialValues]] +git-tree-sha1 = "4da0f88e9a39111c2fa3add390ab15f3a44f3ca3" +uuid = "22cec73e-a1b8-11e9-2c92-598750a2cf9c" +version = "0.3.1" + [[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "d979e54b71da82f3a65b62553da4fc3d18c9004c" @@ -626,9 +704,9 @@ uuid = "3587e190-3f89-42d0-90ee-14403ec27112" version = "0.1.8" [[deps.InvertedIndices]] -git-tree-sha1 = "bee5f1ef5bf65df56bdd2e40447590b272a5471f" +git-tree-sha1 = "82aec7a3dd64f4d9584659dc0b62ef7db2ef3e19" uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" -version = "1.1.0" +version = "1.2.0" [[deps.IrrationalConstants]] git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151" @@ -681,6 +759,12 @@ git-tree-sha1 = "b53380851c6e6664204efb2e62cd24fa5c47e4ba" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" version = "2.1.2+0" +[[deps.JuliaVariables]] +deps = ["MLStyle", "NameResolution"] +git-tree-sha1 = "49fb3cb53362ddadb4415e9b73926d6b40709e70" +uuid = "b14d175d-62b4-44ba-8fb7-3064adc8c3ec" +version = "0.2.4" + [[deps.KernelDensity]] deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] git-tree-sha1 = "9816b296736292a80b9a3200eb7fbb57aaa3917a" @@ -701,9 +785,9 @@ version = "3.0.0+1" [[deps.LLVM]] deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"] -git-tree-sha1 = "e7e9184b0bf0158ac4e4aa9daf00041b5909bf1a" +git-tree-sha1 = "088dd02b2797f0233d92583562ab669de8517fd1" uuid = "929cbde3-209d-540e-8aea-75f648917ca0" -version = "4.14.0" +version = "4.14.1" [[deps.LLVMExtra_jll]] deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg", "TOML"] @@ -736,9 +820,9 @@ version = "1.8.0" [[deps.LayoutPointers]] deps = ["ArrayInterface", "ArrayInterfaceOffsetArrays", "ArrayInterfaceStaticArrays", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static"] -git-tree-sha1 = "73e2e40eb02d6ccd191a8a9f8cee20db8d5df010" +git-tree-sha1 = "7e34177793212f6d64d045ee47d2883f09fffacc" uuid = "10f19ff3-798f-405d-979b-55457f8fc047" -version = "0.1.11" +version = "0.1.12" [[deps.LazyArtifacts]] deps = ["Artifacts", "Pkg"] @@ -780,9 +864,9 @@ version = "1.8.7+0" [[deps.Libglvnd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf" +git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.3.0+3" +version = "1.6.0+0" [[deps.Libgpg_error_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -832,24 +916,24 @@ version = "3.8.0" [[deps.LogExpFunctions]] deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "94d9c52ca447e23eac0c0f074effbcd38830deb5" +git-tree-sha1 = "946607f84feb96220f480e0422d3484c49c00239" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.18" +version = "0.3.19" [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" [[deps.LoggingExtras]] deps = ["Dates", "Logging"] -git-tree-sha1 = "5d4d2d9904227b8bd66386c1138cf4d5ffa826bf" +git-tree-sha1 = "cedb76b37bc5a6c702ade66be44f831fa23c681e" uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" -version = "0.4.9" +version = "1.0.0" [[deps.LoopVectorization]] deps = ["ArrayInterface", "ArrayInterfaceCore", "ArrayInterfaceOffsetArrays", "ArrayInterfaceStaticArrays", "CPUSummary", "ChainRulesCore", "CloseOpenIntervals", "DocStringExtensions", "ForwardDiff", "HostCPUFeatures", "IfElse", "LayoutPointers", "LinearAlgebra", "OffsetArrays", "PolyesterWeave", "SIMDDualNumbers", "SIMDTypes", "SLEEFPirates", "SnoopPrecompile", "SpecialFunctions", "Static", "ThreadingUtilities", "UnPack", "VectorizationBase"] -git-tree-sha1 = "9f6030ca92d1a816e931abb657219c9fc4991a96" +git-tree-sha1 = "da5317a78e2a9f692730345cf3ff820109f406d3" uuid = "bdcacae8-1622-11e9-2a5c-532679323890" -version = "0.12.136" +version = "0.12.141" [[deps.LossFunctions]] deps = ["InteractiveUtils", "Markdown", "RecipesBase"] @@ -865,51 +949,57 @@ version = "2022.2.0+0" [[deps.MLJ]] deps = ["CategoricalArrays", "ComputationalResources", "Distributed", "Distributions", "LinearAlgebra", "MLJBase", "MLJEnsembles", "MLJIteration", "MLJModels", "MLJTuning", "OpenML", "Pkg", "ProgressMeter", "Random", "ScientificTypes", "Statistics", "StatsBase", "Tables"] -git-tree-sha1 = "025706ea81e635ac530a1d3dd365af971805bf79" +git-tree-sha1 = "9d79ef8684eb15a6fe4c3654cdb9c5de4868a81e" uuid = "add582a8-e3ab-11e8-2d5e-e98b27df1bc7" -version = "0.18.5" +version = "0.19.0" [[deps.MLJBase]] deps = ["CategoricalArrays", "CategoricalDistributions", "ComputationalResources", "Dates", "DelimitedFiles", "Distributed", "Distributions", "InteractiveUtils", "InvertedIndices", "LinearAlgebra", "LossFunctions", "MLJModelInterface", "Missings", "OrderedCollections", "Parameters", "PrettyTables", "ProgressMeter", "Random", "ScientificTypes", "Serialization", "StatisticalTraits", "Statistics", "StatsBase", "Tables"] -git-tree-sha1 = "ace5668bc6c4fd46f3e6af67ead3778804f23e5b" +git-tree-sha1 = "decaf881165c0b3c7abf1130dfe3221ee88ef99a" uuid = "a7f614a8-145f-11e9-1d2a-a57a1082229d" -version = "0.20.20" +version = "0.21.2" [[deps.MLJDecisionTreeInterface]] deps = ["DecisionTree", "MLJModelInterface", "Random", "Tables"] -git-tree-sha1 = "d0d682ef8504e1ab705f10307c587239ebb20c4d" +git-tree-sha1 = "74e8076ea6f64fcb490783f2033070b18fa3466f" uuid = "c6f25543-311c-4c74-83dc-3ea6d1015661" -version = "0.2.5" +version = "0.3.0" [[deps.MLJEnsembles]] deps = ["CategoricalArrays", "CategoricalDistributions", "ComputationalResources", "Distributed", "Distributions", "MLJBase", "MLJModelInterface", "ProgressMeter", "Random", "ScientificTypesBase", "StatsBase"] -git-tree-sha1 = "ed2f724be26d0023cade9d59b55da93f528c3f26" +git-tree-sha1 = "bb8a1056b1d8b40f2f27167fc3ef6412a6719fbf" uuid = "50ed68f4-41fd-4504-931a-ed422449fee0" -version = "0.3.1" +version = "0.3.2" + +[[deps.MLJFlux]] +deps = ["CategoricalArrays", "ColorTypes", "ComputationalResources", "Flux", "MLJModelInterface", "ProgressMeter", "Random", "Statistics", "Tables"] +git-tree-sha1 = "a47257705ebca405a25320b111345a978925bcd5" +uuid = "094fc8d1-fd35-5302-93ea-dabda2abf845" +version = "0.2.7" [[deps.MLJIteration]] deps = ["IterationControl", "MLJBase", "Random", "Serialization"] -git-tree-sha1 = "024d0bd22bf4a5b273f626e89d742a9db95285ef" +git-tree-sha1 = "be6d5c71ab499a59e82d65e00a89ceba8732fcd5" uuid = "614be32b-d00c-4edb-bd02-1eb411ab5e55" -version = "0.5.0" +version = "0.5.1" [[deps.MLJLinearModels]] deps = ["DocStringExtensions", "IterativeSolvers", "LinearAlgebra", "LinearMaps", "MLJModelInterface", "Optim", "Parameters"] -git-tree-sha1 = "bfebb824a1b9a0c6d58e417e680f5e99317534e3" +git-tree-sha1 = "7c191a2975e05387da3cd12a4c8a835a3d5186f4" uuid = "6ee0df7b-362f-4a72-a706-9e79364fb692" -version = "0.7.1" +version = "0.8.0" [[deps.MLJModelInterface]] deps = ["Random", "ScientificTypesBase", "StatisticalTraits"] -git-tree-sha1 = "0a36882e73833d60dac49b00d203f73acfd50b85" +git-tree-sha1 = "c8b7e632d6754a5e36c0d94a4b466a5ba3a30128" uuid = "e80e1ace-859a-464e-9ed9-23947d8ae3ea" -version = "1.7.0" +version = "1.8.0" [[deps.MLJModels]] deps = ["CategoricalArrays", "CategoricalDistributions", "Combinatorics", "Dates", "Distances", "Distributions", "InteractiveUtils", "LinearAlgebra", "MLJModelInterface", "Markdown", "OrderedCollections", "Parameters", "Pkg", "PrettyPrinting", "REPL", "Random", "RelocatableFolders", "ScientificTypes", "StatisticalTraits", "Statistics", "StatsBase", "Tables"] -git-tree-sha1 = "147a8e7939601f8c37204addbbe29f2bcfb876a8" +git-tree-sha1 = "08203fc87a7f992cee24e7a1b2353e594c73c41c" uuid = "d491faf4-2d78-11e9-2867-c94bc002c0b7" -version = "0.15.14" +version = "0.16.2" [[deps.MLJNaiveBayesInterface]] deps = ["LogExpFunctions", "MLJModelInterface", "NaiveBayes"] @@ -919,9 +1009,20 @@ version = "0.1.6" [[deps.MLJTuning]] deps = ["ComputationalResources", "Distributed", "Distributions", "LatinHypercubeSampling", "MLJBase", "ProgressMeter", "Random", "RecipesBase"] -git-tree-sha1 = "77209966cc028c1d7730001dc32bffe17a198f29" +git-tree-sha1 = "02688098bd77827b64ed8ad747c14f715f98cfc4" uuid = "03970b2e-30c4-11ea-3135-d1576263f10f" -version = "0.7.3" +version = "0.7.4" + +[[deps.MLStyle]] +git-tree-sha1 = "060ef7956fef2dc06b0e63b294f7dbfbcbdc7ea2" +uuid = "d8e11817-5142-5d16-987a-aa16d5891078" +version = "0.4.16" + +[[deps.MLUtils]] +deps = ["ChainRulesCore", "DataAPI", "DelimitedFiles", "FLoops", "FoldsThreads", "NNlib", "Random", "ShowCases", "SimpleTraits", "Statistics", "StatsBase", "Tables", "Transducers"] +git-tree-sha1 = "82c1104919d664ab1024663ad851701415300c5f" +uuid = "f1d291b0-491e-4a28-83b9-f70985020b54" +version = "0.3.1" [[deps.MacroTools]] deps = ["Markdown", "Random"] @@ -956,9 +1057,15 @@ uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" version = "2.28.0+0" [[deps.Measures]] -git-tree-sha1 = "e498ddeee6f9fdb4551ce855a46f54dbd900245f" +git-tree-sha1 = "c13304c81eec1ed3af7fc20e75fb6b26092a1102" uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" -version = "0.3.1" +version = "0.3.2" + +[[deps.MicroCollections]] +deps = ["BangBang", "InitialValues", "Setfield"] +git-tree-sha1 = "4d5917a26ca33c66c8e5ca3247bd163624d35493" +uuid = "128add7d-3638-4c79-886c-908ea0c25c34" +version = "0.1.3" [[deps.Missings]] deps = ["DataAPI"] @@ -973,16 +1080,23 @@ uuid = "a63ad114-7e13-5084-954f-fe012c677804" uuid = "14a3606d-f60d-562e-9121-12d972cd8159" version = "2022.2.1" -[[deps.MyterialColors]] -git-tree-sha1 = "01d8466fb449436348999d7c6ad740f8f853a579" -uuid = "1c23619d-4212-4747-83aa-717207fae70f" -version = "0.3.0" - [[deps.NLSolversBase]] deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] -git-tree-sha1 = "50310f934e55e5ca3912fb941dec199b49ca9b68" +git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" -version = "7.8.2" +version = "7.8.3" + +[[deps.NNlib]] +deps = ["Adapt", "ChainRulesCore", "LinearAlgebra", "Pkg", "Requires", "Statistics"] +git-tree-sha1 = "37596c26f107f2fd93818166ed3dab1a2e6b2f05" +uuid = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" +version = "0.8.11" + +[[deps.NNlibCUDA]] +deps = ["Adapt", "CUDA", "LinearAlgebra", "NNlib", "Random", "Statistics"] +git-tree-sha1 = "4429261364c5ea5b7308aecaa10e803ace101631" +uuid = "a00861dc-f156-4864-bf3c-e6376f28a68d" +version = "0.2.4" [[deps.NaNMath]] deps = ["OpenLibm_jll"] @@ -996,6 +1110,24 @@ git-tree-sha1 = "830c601de91378e773e7286c3a3e8964d6248657" uuid = "9bbee03b-0db5-5f46-924f-b5c9c21b8c60" version = "0.5.4" +[[deps.NameResolution]] +deps = ["PrettyPrint"] +git-tree-sha1 = "1a0fa0e9613f46c9b8c11eee38ebb4f590013c5e" +uuid = "71a1bf82-56d0-4bbc-8a3c-48b961074391" +version = "0.1.5" + +[[deps.NearestNeighborModels]] +deps = ["Distances", "FillArrays", "InteractiveUtils", "LinearAlgebra", "MLJModelInterface", "NearestNeighbors", "Statistics", "StatsBase", "Tables"] +git-tree-sha1 = "727b8f1c3f9fec6b1a805ba9bef72c73758eda02" +uuid = "636a865e-7cf4-491e-846c-de09b730eb36" +version = "0.2.1" + +[[deps.NearestNeighbors]] +deps = ["Distances", "StaticArrays"] +git-tree-sha1 = "440165bf08bc500b8fe4a7be2dc83271a00c0716" +uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce" +version = "0.4.12" + [[deps.NetworkLayout]] deps = ["GeometryBasics", "LinearAlgebra", "Random", "Requires", "SparseArrays"] git-tree-sha1 = "cac8fc7ba64b699c678094fa630f49b80618f625" @@ -1018,6 +1150,12 @@ git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f" uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" version = "1.3.5+1" +[[deps.OneHotArrays]] +deps = ["Adapt", "ChainRulesCore", "GPUArraysCore", "LinearAlgebra", "MLUtils", "NNlib"] +git-tree-sha1 = "aee0130122fa7c1f3d394231376f07869f1e097c" +uuid = "0b1bfda6-eb8a-41d2-88d8-f5af5cad476f" +version = "0.2.0" + [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" @@ -1036,15 +1174,15 @@ version = "0.3.0" [[deps.OpenSSL]] deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "OpenSSL_jll", "Sockets"] -git-tree-sha1 = "3c3c4a401d267b04942545b1e964a20279587fd7" +git-tree-sha1 = "df6830e37943c7aaa10023471ca47fb3065cc3c4" uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c" -version = "1.3.0" +version = "1.3.2" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e60321e3f2616584ff98f0a4f18d98ae6f89bbb3" +git-tree-sha1 = "f6e9dba33f9f2c44e08a020b0caf6903be540004" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "1.1.17+0" +version = "1.1.19+0" [[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -1054,9 +1192,15 @@ version = "0.5.5+0" [[deps.Optim]] deps = ["Compat", "FillArrays", "ForwardDiff", "LineSearches", "LinearAlgebra", "NLSolversBase", "NaNMath", "Parameters", "PositiveFactorizations", "Printf", "SparseArrays", "StatsBase"] -git-tree-sha1 = "b9fe76d1a39807fdcf790b991981a922de0c3050" +git-tree-sha1 = "1903afc76b7d01719d9c30d3c7d501b61db96721" uuid = "429524aa-4258-5aef-a3af-852621145aeb" -version = "1.7.3" +version = "1.7.4" + +[[deps.Optimisers]] +deps = ["ChainRulesCore", "Functors", "LinearAlgebra", "Random", "Statistics"] +git-tree-sha1 = "f1cccb9f879dd4eaa4d92b115ab793545965d763" +uuid = "3bd65402-5787-11e9-1adc-39752487f4e2" +version = "0.2.13" [[deps.Opus_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -1087,10 +1231,10 @@ uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" version = "0.12.3" [[deps.Parsers]] -deps = ["Dates"] -git-tree-sha1 = "6c01a9b494f6d2a9fc180a08b182fcb06f0958a0" +deps = ["Dates", "SnoopPrecompile"] +git-tree-sha1 = "b64719e8b4504983c7fca6cc9db3ebc8acc2a4d6" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.4.2" +version = "2.5.1" [[deps.Pipe]] git-tree-sha1 = "6842804e7867b115ca9de748a0cf6b364523c16d" @@ -1122,15 +1266,21 @@ version = "1.3.1" [[deps.Plots]] deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SnoopPrecompile", "SparseArrays", "Statistics", "StatsBase", "UUIDs", "UnicodeFun", "Unzip"] -git-tree-sha1 = "0a56829d264eb1bc910cf7c39ac008b5bcb5a0d9" +git-tree-sha1 = "6a9521b955b816aa500462951aa67f3e4467248a" uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -version = "1.35.5" +version = "1.36.6" [[deps.PolyesterWeave]] deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] -git-tree-sha1 = "b42fb2292fbbaed36f25d33a15c8cc0b4f287fcf" +git-tree-sha1 = "050ca4aa2ca31484b51b849d8180caf8e4449c49" uuid = "1d0040c9-8b98-4ee7-8388-3f51789ca0ad" -version = "0.1.10" +version = "0.1.11" + +[[deps.Polynomials]] +deps = ["LinearAlgebra", "RecipesBase"] +git-tree-sha1 = "3010a6dd6ad4c7384d2f38c58fa8172797d879c1" +uuid = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" +version = "3.2.0" [[deps.PooledArrays]] deps = ["DataAPI", "Future"] @@ -1150,16 +1300,21 @@ git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" uuid = "21216c6a-2e73-6563-6e65-726566657250" version = "1.3.0" +[[deps.PrettyPrint]] +git-tree-sha1 = "632eb4abab3449ab30c5e1afaa874f0b98b586e4" +uuid = "8162dcfd-2161-5ef2-ae6c-7681170c5f98" +version = "0.2.0" + [[deps.PrettyPrinting]] git-tree-sha1 = "4be53d093e9e37772cc89e1009e8f6ad10c4681b" uuid = "54e16d92-306c-5ea0-a30b-337be88ac337" version = "0.4.0" [[deps.PrettyTables]] -deps = ["Crayons", "Formatting", "Markdown", "Reexport", "StringManipulation", "Tables"] -git-tree-sha1 = "460d9e154365e058c4d886f6f7d6df5ffa1ea80e" +deps = ["Crayons", "Formatting", "LaTeXStrings", "Markdown", "Reexport", "StringManipulation", "Tables"] +git-tree-sha1 = "96f6db03ab535bdb901300f88335257b0018689d" uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "2.1.2" +version = "2.2.2" [[deps.Printf]] deps = ["Unicode"] @@ -1179,9 +1334,9 @@ version = "1.7.2" [[deps.Qt5Base_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] -git-tree-sha1 = "c6c0f690d0cc7caddb74cef7aa847b824a16b256" +git-tree-sha1 = "0c03844e2231e12fda4d0086fd7cbe4098ee8dc5" uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1" -version = "5.15.3+1" +version = "5.15.3+2" [[deps.QuadGK]] deps = ["DataStructures", "LinearAlgebra"] @@ -1215,17 +1370,23 @@ git-tree-sha1 = "dc84268fe0e3335a62e315a3a7cf2afa7178a734" uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439" version = "0.4.3" +[[deps.RealDot]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "9f0a1b71baaf7650f4fa8a1d168c7fb6ee41f0c9" +uuid = "c1ae055f-0cd5-4b69-90a6-9a35b1a98df9" +version = "0.1.0" + [[deps.RecipesBase]] deps = ["SnoopPrecompile"] -git-tree-sha1 = "d12e612bba40d189cead6ff857ddb67bd2e6a387" +git-tree-sha1 = "18c35ed630d7229c5584b945641a73ca83fb5213" uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.3.1" +version = "1.3.2" [[deps.RecipesPipeline]] deps = ["Dates", "NaNMath", "PlotUtils", "RecipesBase", "SnoopPrecompile"] -git-tree-sha1 = "9b1c0c8e9188950e66fc28f40bfe0f8aac311fe0" +git-tree-sha1 = "e974477be88cb5e3040009f3767611bc6357846f" uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" -version = "0.6.7" +version = "0.6.11" [[deps.Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" @@ -1273,9 +1434,9 @@ version = "0.1.0" [[deps.SLEEFPirates]] deps = ["IfElse", "Static", "VectorizationBase"] -git-tree-sha1 = "938c9ecffb28338a6b8b970bda0f3806a65e7906" +git-tree-sha1 = "c8679919df2d3c71f74451321f1efea6433536cc" uuid = "476501e8-09a2-5ece-8869-fb82de89a1fa" -version = "0.6.36" +version = "0.6.37" [[deps.ScientificTypes]] deps = ["CategoricalArrays", "ColorTypes", "Dates", "Distributions", "PrettyTables", "Reexport", "ScientificTypesBase", "StatisticalTraits", "Tables"] @@ -1313,6 +1474,11 @@ version = "1.1.1" deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" +[[deps.ShowCases]] +git-tree-sha1 = "7f534ad62ab2bd48591bdeac81994ea8c445e4a5" +uuid = "605ecd9f-84a6-4c9e-81e2-4798472b76a3" +version = "0.1.0" + [[deps.Showoff]] deps = ["Dates", "Grisu"] git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" @@ -1324,6 +1490,12 @@ git-tree-sha1 = "874e8867b33a00e784c8a7e4b60afe9e037b74e1" uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" version = "1.1.0" +[[deps.SimpleTraits]] +deps = ["InteractiveUtils", "MacroTools"] +git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" +uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" +version = "0.9.4" + [[deps.SnoopPrecompile]] git-tree-sha1 = "f604441450a3c0569830946e5b33b78c928e1a85" uuid = "66db9d55-30c0-4569-8b51-7e840670fc0c" @@ -1334,9 +1506,9 @@ uuid = "6462fe0b-24de-5631-8697-dd941f90decc" [[deps.SortingAlgorithms]] deps = ["DataStructures"] -git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508" +git-tree-sha1 = "a4ada03f999bd01b3a25dcaa30b2d929fe537e00" uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.0.1" +version = "1.1.0" [[deps.SparseArrays]] deps = ["LinearAlgebra", "Random"] @@ -1348,6 +1520,12 @@ git-tree-sha1 = "d75bda01f8c31ebb72df80a46c88b25d1c79c56d" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" version = "2.1.7" +[[deps.SplittablesBase]] +deps = ["Setfield", "Test"] +git-tree-sha1 = "e08a62abc517eb79667d0a29dc08a3b589516bb5" +uuid = "171d559e-b47b-412a-8079-5efa626c420e" +version = "0.1.15" + [[deps.StableRNGs]] deps = ["Random", "Test"] git-tree-sha1 = "3be7d49667040add7ee151fefaf1f8c04c8c8276" @@ -1356,15 +1534,15 @@ version = "1.0.0" [[deps.Static]] deps = ["IfElse"] -git-tree-sha1 = "de4f0a4f049a4c87e4948c04acff37baf1be01a6" +git-tree-sha1 = "0559586098f3cbd2e835461254ea2fcffa4a61ba" uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" -version = "0.7.7" +version = "0.8.2" [[deps.StaticArrays]] deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] -git-tree-sha1 = "f86b3a049e5d05227b10e15dbb315c5b90f14988" +git-tree-sha1 = "ffc098086f35909741f71ce21d03dadf0d2bfa76" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.5.9" +version = "1.5.11" [[deps.StaticArraysCore]] git-tree-sha1 = "6b7ba252635a5eff6a0b0664a41ee140a1c9e72a" @@ -1395,9 +1573,9 @@ version = "0.33.21" [[deps.StatsFuns]] deps = ["ChainRulesCore", "HypergeometricFunctions", "InverseFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] -git-tree-sha1 = "5783b877201a82fc0014cbf381e7e6eb130473a4" +git-tree-sha1 = "89a3bfe98f5400f4ff58bb5cd1a9e46f95d08352" uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" -version = "1.0.1" +version = "1.1.0" [[deps.StringManipulation]] git-tree-sha1 = "46da2434b41f41ac3594ee9816ce5541c6096123" @@ -1442,12 +1620,6 @@ git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" version = "0.1.1" -[[deps.Term]] -deps = ["CodeTracking", "Dates", "Highlights", "InteractiveUtils", "Logging", "Markdown", "MyterialColors", "OrderedCollections", "Parameters", "ProgressLogging", "SnoopPrecompile", "Tables", "UUIDs", "UnicodeFun"] -git-tree-sha1 = "5b5d38673d148f80e7e04569a665006d3bf91cfb" -uuid = "22787eb5-b846-44ae-b979-8e399b8463ab" -version = "1.0.4" - [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" @@ -1460,9 +1632,9 @@ version = "0.5.0" [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] -git-tree-sha1 = "9dfcb767e17b0849d6aaf85997c98a5aea292513" +git-tree-sha1 = "f2fd3f288dfc6f507b0c3a2eb3bac009251e548b" uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" -version = "0.5.21" +version = "0.5.22" [[deps.TranscodingStreams]] deps = ["Random", "Test"] @@ -1470,10 +1642,16 @@ git-tree-sha1 = "8a75929dcd3c38611db2f8d08546decb514fcadf" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" version = "0.9.9" +[[deps.Transducers]] +deps = ["Adapt", "ArgCheck", "BangBang", "Baselet", "CompositionsBase", "DefineSingletons", "Distributed", "InitialValues", "Logging", "Markdown", "MicroCollections", "Requires", "Setfield", "SplittablesBase", "Tables"] +git-tree-sha1 = "c42fa452a60f022e9e087823b47e5a5f8adc53d5" +uuid = "28d57a85-8fef-5791-bfe6-a80928e7c999" +version = "0.4.75" + [[deps.URIs]] -git-tree-sha1 = "e59ecc5a41b000fa94423a578d29290c7266fc10" +git-tree-sha1 = "ac00576f90d8a259f2c9d823e91d1de3fd44d348" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.4.0" +version = "1.4.1" [[deps.UUIDs]] deps = ["Random", "SHA"] @@ -1495,15 +1673,15 @@ version = "0.4.1" [[deps.UnicodePlots]] deps = ["ColorSchemes", "ColorTypes", "Contour", "Crayons", "Dates", "FileIO", "FreeType", "LinearAlgebra", "MarchingCubes", "NaNMath", "Printf", "Requires", "SnoopPrecompile", "SparseArrays", "StaticArrays", "StatsBase", "Unitful"] -git-tree-sha1 = "390b2e8e5535f5beb50885d1a1059f460547d3a5" +git-tree-sha1 = "e20b01d50cd162593cfd9691628c830769f68987" uuid = "b8865327-cd53-5732-bb35-84acbb429228" -version = "3.1.6" +version = "3.3.1" [[deps.Unitful]] deps = ["ConstructionBase", "Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "d57a4ed70b6f9ff1da6719f5f2713706d57e0d66" +git-tree-sha1 = "d670a70dd3cdbe1c1186f2f17c9a68a7ec24838c" uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.12.0" +version = "1.12.2" [[deps.Unzip]] git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" @@ -1512,9 +1690,9 @@ version = "0.2.0" [[deps.VectorizationBase]] deps = ["ArrayInterface", "CPUSummary", "HostCPUFeatures", "IfElse", "LayoutPointers", "Libdl", "LinearAlgebra", "SIMDTypes", "Static"] -git-tree-sha1 = "ba9d398034a2ba78059391492730889c6e45cf15" +git-tree-sha1 = "fc79d0f926592ecaeaee164f6a4ca81b51115c3b" uuid = "3d5dd08c-fd9d-11e8-17fa-ed2836048c2f" -version = "0.21.54" +version = "0.21.56" [[deps.Wayland_jll]] deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] @@ -1683,6 +1861,18 @@ git-tree-sha1 = "e45044cd873ded54b6a5bac0eb5c971392cf1927" uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" version = "1.5.2+0" +[[deps.Zygote]] +deps = ["AbstractFFTs", "ChainRules", "ChainRulesCore", "DiffRules", "Distributed", "FillArrays", "ForwardDiff", "GPUArrays", "GPUArraysCore", "IRTools", "InteractiveUtils", "LinearAlgebra", "LogExpFunctions", "MacroTools", "NaNMath", "Random", "Requires", "SparseArrays", "SpecialFunctions", "Statistics", "ZygoteRules"] +git-tree-sha1 = "a6f1287943ac05fae56fa06049d1a7846dfbc65f" +uuid = "e88e6eb3-aa80-5325-afca-941959d7151f" +version = "0.6.51" + +[[deps.ZygoteRules]] +deps = ["MacroTools"] +git-tree-sha1 = "8c1a8e4dfacb1fd631745552c8db35d0deb09ea0" +uuid = "700de1a5-db45-46bc-99cf-38207098b444" +version = "0.2.2" + [[deps.fzf_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "868e669ccb12ba16eaf50cb2957ee2ff61261c56" diff --git a/docs/Project.toml b/docs/Project.toml index ad9ad43..6291faf 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,15 +1,21 @@ [deps] +CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" ConformalPrediction = "98bfc277-1877-43dc-819b-a3e38c30242f" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DecisionTree = "7806a523-6efd-50cb-b5f6-3fa6f1930dbb" +Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" EvoTrees = "f6006082-12f8-11e9-0c9c-0d5d367ab1e5" +Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c" MLJ = "add582a8-e3ab-11e8-2d5e-e98b27df1bc7" MLJBase = "a7f614a8-145f-11e9-1d2a-a57a1082229d" MLJDecisionTreeInterface = "c6f25543-311c-4c74-83dc-3ea6d1015661" +MLJFlux = "094fc8d1-fd35-5302-93ea-dabda2abf845" MLJLinearModels = "6ee0df7b-362f-4a72-a706-9e79364fb692" MLJModelInterface = "e80e1ace-859a-464e-9ed9-23947d8ae3ea" MLJNaiveBayesInterface = "33e4bacb-b9e2-458e-9a13-5d9a90b235fa" NaiveBayes = "9bbee03b-0db5-5f46-924f-b5c9c21b8c60" +NearestNeighborModels = "636a865e-7cf4-491e-846c-de09b730eb36" PlotThemes = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" diff --git a/docs/make.jl b/docs/make.jl index 3f5b8f5..e1a6a8f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -32,8 +32,12 @@ makedocs(; ), pages=[ "🏠 Home" => "index.md", + "🤔 Tutorials" => [ + "Classification" => "classification.md", + "Regression" => "regression.md", + ], + "📖 Reference" => "reference.md", "🛠 Contribute" => "contribute.md", - "📖 Library" => "reference.md", ], ) diff --git a/docs/_metadata.yml b/docs/src/_metadata.yml similarity index 51% rename from docs/_metadata.yml rename to docs/src/_metadata.yml index f591beb..b4ac9c2 100644 --- a/docs/_metadata.yml +++ b/docs/src/_metadata.yml @@ -1,5 +1,4 @@ format: commonmark: variant: -raw_html - wrap: preserve - self-contained: true \ No newline at end of file + wrap: none \ No newline at end of file diff --git a/docs/src/classification.md b/docs/src/classification.md index 546810e..99a1994 100644 --- a/docs/src/classification.md +++ b/docs/src/classification.md @@ -1,41 +1,161 @@ +# Classification + +``` @meta +CurrentModule = ConformalPrediction +``` + +This tutorial is based in parts on this [blog post](https://www.paltmeyer.com/blog/posts/conformal-prediction/). + +### Split Conformal Classification + +We consider a simple binary classification problem. Let (*X*_(*i*),*Y*_(*i*)), *i* = 1, ..., *n* denote our feature-label pairs and let *μ* : 𝒳 ↦ 𝒴 denote the mapping from features to labels. For illustration purposes we will use the moons dataset 🌙. Using [`MLJ.jl`](https://alan-turing-institute.github.io/MLJ.jl/v0.18/) we first generate the data and split into into a training and test set: + ``` julia using MLJ -X, y = MLJ.make_blobs(1000, 2; centers=3, cluster_std=1.0) -train, test = partition(eachindex(y), 0.4, 0.4, shuffle=true) +using Random +Random.seed!(123) + +# Data: +X, y = make_moons(500; noise=0.15) +train, test = partition(eachindex(y), 0.8, shuffle=true) ``` +Here we will use a specific case of CP called *split conformal prediction* which can then be summarized as follows:[1] + +1. Partition the training into a proper training set and a separate calibration set: 𝒟_(*n*) = 𝒟^(train) ∪ 𝒟^(cali). +2. Train the machine learning model on the proper training set: *μ̂*_(*i* ∈ 𝒟^(train))(*X*_(*i*),*Y*_(*i*)). +3. Compute nonconformity scores, 𝒮, using the calibration data 𝒟^(cali) and the fitted model *μ̂*_(*i* ∈ 𝒟^(train)). +4. For a user-specified desired coverage ratio (1−*α*) compute the corresponding quantile, *q̂*, of the empirical distribution of nonconformity scores, 𝒮. +5. For the given quantile and test sample *X*_(test), form the corresponding conformal prediction set: + +*C*(*X*_(test)) = {*y* : *s*(*X*_(test),*y*) ≤ *q̂*}   (1) + +This is the default procedure used for classification and regression in [`ConformalPrediction.jl`](https://github.com/pat-alt/ConformalPrediction.jl). + +Now let’s take this to our 🌙 data. To illustrate the package functionality we will demonstrate the envisioned workflow. We first define our atomic machine learning model following standard [`MLJ.jl`](https://alan-turing-institute.github.io/MLJ.jl/v0.18/) conventions. Using [`ConformalPrediction.jl`](https://github.com/pat-alt/ConformalPrediction.jl) we then wrap our atomic model in a conformal model using the standard API call `conformal_model(model::Supervised; kwargs...)`. To train and predict from our conformal model we can then rely on the conventional [`MLJ.jl`](https://alan-turing-institute.github.io/MLJ.jl/v0.18/) procedure again. In particular, we wrap our conformal model in data (turning it into a machine) and then fit it on the training set. Finally, we use our machine to predict the label for a new test sample `Xtest`: + ``` julia -EvoTreeClassifier = @load EvoTreeClassifier pkg=EvoTrees -model = EvoTreeClassifier() +# Model: +KNNClassifier = @load KNNClassifier pkg=NearestNeighborModels +model = KNNClassifier(;K=50) + +# Training: +using ConformalPrediction +conf_model = conformal_model(model; coverage=.9) +mach = machine(conf_model, X, y) +fit!(mach, rows=train) + +# Conformal Prediction: +Xtest = selectrows(X, first(test)) +ytest = y[first(test)] +predict(mach, Xtest)[1] ``` + import NearestNeighborModels ✔ + + UnivariateFinite{Multiclass{2}} + ┌ ┐ + 0 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.94 + └ ┘ + +The final predictions are set-valued. While the softmax output remains unchanged for the `SimpleInductiveClassifier`, the size of the prediction set depends on the chosen coverage rate, (1−*α*). + +When specifying a coverage rate very close to one, the prediction set will typically include many (in some cases all) of the possible labels. Below, for example, both classes are included in the prediction set when setting the coverage rate equal to (1−*α*)=1.0. This is intuitive, since high coverage quite literally requires that the true label is covered by the prediction set with high probability. + ``` julia -using ConformalPrediction -conf_model = conformal_model(model) +conf_model = conformal_model(model; coverage=coverage) mach = machine(conf_model, X, y) fit!(mach, rows=train) + +# Conformal Prediction: +Xtest = (x1=[1],x2=[0]) +predict(mach, Xtest)[1] ``` + UnivariateFinite{Multiclass{2}} + ┌ ┐ + 0 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.5 + 1 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.5 + └ ┘ + +Conversely, for low coverage rates, prediction sets can also be empty. For a choice of (1−*α*)=0.1, for example, the prediction set for our test sample is empty. This is a bit difficult to think about intuitively and I have not yet come across a satisfactory, intuitive interpretation.[2] When the prediction set is empty, the `predict` call currently returns `missing`: + ``` julia -rows = rand(test, 10) -Xtest = selectrows(X, rows) -ytest = y[rows] -predict(mach, Xtest) -``` - - ╭───────────────────────────────────────────────────────────────────╮ - │ │ - │ (1) UnivariateFinite{Multiclass {#90CAF9}3} (1=>0.82{/#90CAF9}) │ - │ (2) UnivariateFinite{Multiclass {#90CAF9}3} (3=>0.82{/#90CAF9}) │ - │ (3) UnivariateFinite{Multiclass {#90CAF9}3} (1=>0.82{/#90CAF9}) │ - │ (4) UnivariateFinite{Multiclass {#90CAF9}3} (1=>0.82{/#90CAF9}) │ - │ (5) UnivariateFinite{Multiclass {#90CAF9}3} (1=>0.82{/#90CAF9}) │ - │ (6) UnivariateFinite{Multiclass {#90CAF9}3} (3=>0.82{/#90CAF9}) │ - │ (7) UnivariateFinite{Multiclass {#90CAF9}3} (3=>0.82{/#90CAF9}) │ - │ (8) UnivariateFinite{Multiclass {#90CAF9}3} (2=>0.82{/#90CAF9}) │ - │ (9) UnivariateFinite{Multiclass {#90CAF9}3} (1=>0.82{/#90CAF9}) │ - │ (10) UnivariateFinite{Multiclass {#90CAF9}3} (3=>0.82{/#90CAF9}) │ - │ │ - │ │ - ╰────────────────────────────────────────────────────── 10 items ───╯ +conf_model = conformal_model(model; coverage=coverage) +mach = machine(conf_model, X, y) +fit!(mach, rows=train) + +# Conformal Prediction: +predict(mach, Xtest)[1] +``` + + missing + +``` julia +cov_ = .9 +conf_model = conformal_model(model; coverage=cov_) +mach = machine(conf_model, X, y) +fit!(mach, rows=train) +Markdown.parse(""" +The following chart shows the resulting predicted probabilities for ``y=1`` (left) and set size (right) for a choice of ``(1-\\alpha)``=$cov_. +""") +``` + +The following chart shows the resulting predicted probabilities for *y* = 1 (left) and set size (right) for a choice of (1−*α*)=0.9. + +``` julia +using Plots +p_proba = plot(mach.model, mach.fitresult, X, y) +p_set_size = plot(mach.model, mach.fitresult, X, y; plot_set_size=true) +plot(p_proba, p_set_size, size=(800,250)) +``` + +![](classification_files/figure-commonmark/cell-10-output-1.svg) + +The animation below should provide some more intuition as to what exactly is happening here. It illustrates the effect of the chosen coverage rate on the predicted softmax output and the set size in the two-dimensional feature space. Contours are overlayed with the moon data points (including test data). The two samples highlighted in red, *X*₁ and *X*₂, have been manually added for illustration purposes. Let’s look at these one by one. + +Firstly, note that *X*₁ (red cross) falls into a region of the domain that is characterized by high predictive uncertainty. It sits right at the bottom-right corner of our class-zero moon 🌜 (orange), a region that is almost entirely enveloped by our class-one moon 🌛 (green). For low coverage rates the prediction set for *X*₁ is empty: on the left-hand side this is indicated by the missing contour for the softmax probability; on the right-hand side we can observe that the corresponding set size is indeed zero. For high coverage rates the prediction set includes both *y* = 0 and *y* = 1, indicative of the fact that the conformal classifier is uncertain about the true label. + +With respect to *X*₂, we observe that while also sitting on the fringe of our class-zero moon, this sample populates a region that is not fully enveloped by data points from the opposite class. In this region, the underlying atomic classifier can be expected to be more certain about its predictions, but still not highly confident. How is this reflected by our corresponding conformal prediction sets? + +``` julia +Xtest_2 = (x1=[-0.5],x2=[0.25]) +p̂_2 = pdf(predict(mach, Xtest_2)[1], 0) +``` + +Well, for low coverage rates (roughly  \< 0.9) the conformal prediction set does not include *y* = 0: the set size is zero (right panel). Only for higher coverage rates do we have *C*(*X*₂) = {0}: the coverage rate is high enough to include *y* = 0, but the corresponding softmax probability is still fairly low. For example, for (1−*α*) = 0.9 we have *p̂*(*y*=0|*X*₂) = 0.72. + +These two examples illustrate an interesting point: for regions characterized by high predictive uncertainty, conformal prediction sets are typically empty (for low coverage) or large (for high coverage). While set-valued predictions may be something to get used to, this notion is overall intuitive. + +``` julia +# Setup +coverages = range(0.75,1.0,length=5) +n = 100 +x1_range = range(extrema(X.x1)...,length=n) +x2_range = range(extrema(X.x2)...,length=n) + +anim = @animate for coverage in coverages + conf_model = conformal_model(model; coverage=coverage) + mach = machine(conf_model, X, y) + fit!(mach, rows=train) + # Probabilities: + p1 = plot(mach.model, mach.fitresult, X, y) + scatter!(p1, Xtest.x1, Xtest.x2, ms=6, c=:red, label="X₁", shape=:cross, msw=6) + scatter!(p1, Xtest_2.x1, Xtest_2.x2, ms=6, c=:red, label="X₂", shape=:diamond, msw=6) + p2 = plot(mach.model, mach.fitresult, X, y; plot_set_size=true) + scatter!(p2, Xtest.x1, Xtest.x2, ms=6, c=:red, label="X₁", shape=:cross, msw=6) + scatter!(p2, Xtest_2.x1, Xtest_2.x2, ms=6, c=:red, label="X₂", shape=:diamond, msw=6) + plot(p1, p2, plot_title="(1-α)=$(round(coverage,digits=2))", size=(800,300)) +end + +gif(anim, joinpath(www_path,"classification.gif"), fps=1) +``` + +The effect of the coverage rate on the conformal prediction set. Softmax probabilities are shown on the left. The size of the prediction set is shown on the right. + +![](www/classification.gif) + +[1] In other places split conformal prediction is sometimes referred to as *inductive* conformal prediction. + +[2] Any thoughts/comments welcome! diff --git a/docs/src/classification.qmd b/docs/src/classification.qmd index 0dc31b7..b5ab560 100644 --- a/docs/src/classification.qmd +++ b/docs/src/classification.qmd @@ -1,32 +1,187 @@ +# Classification + +```@meta +CurrentModule = ConformalPrediction +``` + ```{julia} #| echo: false using Pkg; Pkg.activate("docs") using Plots theme(:wong) +www_path = "docs/src/www" # output path for files don't get automatically saved in auto-generated path (e.g. GIFs) ``` + +This tutorial is based in parts on this [blog post](https://www.paltmeyer.com/blog/posts/conformal-prediction/). + +### Split Conformal Classification {#sec-scp} + +We consider a simple binary classification problem. Let $(X_i, Y_i), \ i=1,...,n$ denote our feature-label pairs and let $\mu: \mathcal{X} \mapsto \mathcal{Y}$ denote the mapping from features to labels. For illustration purposes we will use the moons dataset 🌙. Using [`MLJ.jl`](https://alan-turing-institute.github.io/MLJ.jl/v0.18/) we first generate the data and split into into a training and test set: + ```{julia} using MLJ -X, y = MLJ.make_blobs(1000, 2; centers=3, cluster_std=1.0) -train, test = partition(eachindex(y), 0.4, 0.4, shuffle=true) +using Random +Random.seed!(123) + +# Data: +X, y = make_moons(500; noise=0.15) +train, test = partition(eachindex(y), 0.8, shuffle=true) ``` +Here we will use a specific case of CP called *split conformal prediction* which can then be summarized as follows:^[In other places split conformal prediction is sometimes referred to as *inductive* conformal prediction.] + +1. Partition the training into a proper training set and a separate calibration set: $\mathcal{D}_n=\mathcal{D}^{\text{train}} \cup \mathcal{D}^{\text{cali}}$. +2. Train the machine learning model on the proper training set: $\hat\mu_{i \in \mathcal{D}^{\text{train}}}(X_i,Y_i)$. +3. Compute nonconformity scores, $\mathcal{S}$, using the calibration data $\mathcal{D}^{\text{cali}}$ and the fitted model $\hat\mu_{i \in \mathcal{D}^{\text{train}}}$. +4. For a user-specified desired coverage ratio $(1-\alpha)$ compute the corresponding quantile, $\hat{q}$, of the empirical distribution of nonconformity scores, $\mathcal{S}$. +5. For the given quantile and test sample $X_{\text{test}}$, form the corresponding conformal prediction set: + +$$ +C(X_{\text{test}})=\{y:s(X_{\text{test}},y) \le \hat{q}\} +$$ {#eq-set} + +This is the default procedure used for classification and regression in [`ConformalPrediction.jl`](https://github.com/pat-alt/ConformalPrediction.jl). + +Now let's take this to our 🌙 data. To illustrate the package functionality we will demonstrate the envisioned workflow. We first define our atomic machine learning model following standard [`MLJ.jl`](https://alan-turing-institute.github.io/MLJ.jl/v0.18/) conventions. Using [`ConformalPrediction.jl`](https://github.com/pat-alt/ConformalPrediction.jl) we then wrap our atomic model in a conformal model using the standard API call `conformal_model(model::Supervised; kwargs...)`. To train and predict from our conformal model we can then rely on the conventional [`MLJ.jl`](https://alan-turing-institute.github.io/MLJ.jl/v0.18/) procedure again. In particular, we wrap our conformal model in data (turning it into a machine) and then fit it on the training set. Finally, we use our machine to predict the label for a new test sample `Xtest`: + ```{julia} -EvoTreeClassifier = @load EvoTreeClassifier pkg=EvoTrees -model = EvoTreeClassifier() +#| output: true + +# Model: +KNNClassifier = @load KNNClassifier pkg=NearestNeighborModels +model = KNNClassifier(;K=50) + +# Training: +using ConformalPrediction +conf_model = conformal_model(model; coverage=.9) +mach = machine(conf_model, X, y) +fit!(mach, rows=train) + +# Conformal Prediction: +Xtest = selectrows(X, first(test)) +ytest = y[first(test)] +predict(mach, Xtest)[1] ``` +The final predictions are set-valued. While the softmax output remains unchanged for the `SimpleInductiveClassifier`, the size of the prediction set depends on the chosen coverage rate, $(1-\alpha)$. ```{julia} -using ConformalPrediction -conf_model = conformal_model(model) +#| echo: false +#| output: true + +coverage = 1.0 +using Markdown +Markdown.parse(""" +When specifying a coverage rate very close to one, the prediction set will typically include many (in some cases all) of the possible labels. Below, for example, both classes are included in the prediction set when setting the coverage rate equal to ``(1-\\alpha)``=$coverage. This is intuitive, since high coverage quite literally requires that the true label is covered by the prediction set with high probability. +""") +``` + +```{julia} +#| output: true + +conf_model = conformal_model(model; coverage=coverage) mach = machine(conf_model, X, y) fit!(mach, rows=train) + +# Conformal Prediction: +Xtest = (x1=[1],x2=[0]) +predict(mach, Xtest)[1] ``` ```{julia} +#| echo: false #| output: true -rows = rand(test, 10) -Xtest = selectrows(X, rows) -ytest = y[rows] -predict(mach, Xtest) -``` \ No newline at end of file + +coverage = .1 +using Markdown +Markdown.parse(""" +Conversely, for low coverage rates, prediction sets can also be empty. For a choice of ``(1-\\alpha)``=$coverage, for example, the prediction set for our test sample is empty. This is a bit difficult to think about intuitively and I have not yet come across a satisfactory, intuitive interpretation.^[Any thoughts/comments welcome!] When the prediction set is empty, the `predict` call currently returns `missing`: +""") +``` + +```{julia} +#| output: true + +conf_model = conformal_model(model; coverage=coverage) +mach = machine(conf_model, X, y) +fit!(mach, rows=train) + +# Conformal Prediction: +predict(mach, Xtest)[1] +``` + +```{julia} +#| output: true + +cov_ = .9 +conf_model = conformal_model(model; coverage=cov_) +mach = machine(conf_model, X, y) +fit!(mach, rows=train) +Markdown.parse(""" +The following chart shows the resulting predicted probabilities for ``y=1`` (left) and set size (right) for a choice of ``(1-\\alpha)``=$cov_. +""") +``` + +```{julia} +#| output: true + +using Plots +p_proba = plot(mach.model, mach.fitresult, X, y) +p_set_size = plot(mach.model, mach.fitresult, X, y; plot_set_size=true) +plot(p_proba, p_set_size, size=(800,250)) +``` + + +The animation below should provide some more intuition as to what exactly is happening here. It illustrates the effect of the chosen coverage rate on the predicted softmax output and the set size in the two-dimensional feature space. Contours are overlayed with the moon data points (including test data). The two samples highlighted in red, $X_1$ and $X_2$, have been manually added for illustration purposes. Let's look at these one by one. + +Firstly, note that $X_1$ (red cross) falls into a region of the domain that is characterized by high predictive uncertainty. It sits right at the bottom-right corner of our class-zero moon 🌜 (orange), a region that is almost entirely enveloped by our class-one moon 🌛 (green). For low coverage rates the prediction set for $X_1$ is empty: on the left-hand side this is indicated by the missing contour for the softmax probability; on the right-hand side we can observe that the corresponding set size is indeed zero. For high coverage rates the prediction set includes both $y=0$ and $y=1$, indicative of the fact that the conformal classifier is uncertain about the true label. + +With respect to $X_2$, we observe that while also sitting on the fringe of our class-zero moon, this sample populates a region that is not fully enveloped by data points from the opposite class. In this region, the underlying atomic classifier can be expected to be more certain about its predictions, but still not highly confident. How is this reflected by our corresponding conformal prediction sets? + +```{julia} +#| code-fold: true + +Xtest_2 = (x1=[-0.5],x2=[0.25]) +p̂_2 = pdf(predict(mach, Xtest_2)[1], 0) +``` + +```{julia} +#| echo: false +#| output: true + +Markdown.parse(""" +Well, for low coverage rates (roughly ``<0.9``) the conformal prediction set does not include ``y=0``: the set size is zero (right panel). Only for higher coverage rates do we have ``C(X_2)=\\{0\\}``: the coverage rate is high enough to include ``y=0``, but the corresponding softmax probability is still fairly low. For example, for ``(1-\\alpha)=$(cov_)`` we have ``\\hat{p}(y=0|X_2)=$(p̂_2).`` +""") +``` + +These two examples illustrate an interesting point: for regions characterized by high predictive uncertainty, conformal prediction sets are typically empty (for low coverage) or large (for high coverage). While set-valued predictions may be something to get used to, this notion is overall intuitive. + +```{julia} +#| output: true +#| label: fig-anim +#| fig-cap: "The effect of the coverage rate on the conformal prediction set. Softmax probabilities are shown on the left. The size of the prediction set is shown on the right." + +# Setup +coverages = range(0.75,1.0,length=5) +n = 100 +x1_range = range(extrema(X.x1)...,length=n) +x2_range = range(extrema(X.x2)...,length=n) + +anim = @animate for coverage in coverages + conf_model = conformal_model(model; coverage=coverage) + mach = machine(conf_model, X, y) + fit!(mach, rows=train) + # Probabilities: + p1 = plot(mach.model, mach.fitresult, X, y) + scatter!(p1, Xtest.x1, Xtest.x2, ms=6, c=:red, label="X₁", shape=:cross, msw=6) + scatter!(p1, Xtest_2.x1, Xtest_2.x2, ms=6, c=:red, label="X₂", shape=:diamond, msw=6) + p2 = plot(mach.model, mach.fitresult, X, y; plot_set_size=true) + scatter!(p2, Xtest.x1, Xtest.x2, ms=6, c=:red, label="X₁", shape=:cross, msw=6) + scatter!(p2, Xtest_2.x1, Xtest_2.x2, ms=6, c=:red, label="X₂", shape=:diamond, msw=6) + plot(p1, p2, plot_title="(1-α)=$(round(coverage,digits=2))", size=(800,300)) +end + +gif(anim, joinpath(www_path,"classification.gif"), fps=1) +``` + +![](www/classification.gif) diff --git a/docs/src/classification_files/figure-commonmark/cell-10-output-1.svg b/docs/src/classification_files/figure-commonmark/cell-10-output-1.svg new file mode 100644 index 0000000..de81765 --- /dev/null +++ b/docs/src/classification_files/figure-commonmark/cell-10-output-1.svg @@ -0,0 +1,2671 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/classification_files/figure-commonmark/cell-9-output-1.svg b/docs/src/classification_files/figure-commonmark/cell-9-output-1.svg new file mode 100644 index 0000000..8791f3f --- /dev/null +++ b/docs/src/classification_files/figure-commonmark/cell-9-output-1.svg @@ -0,0 +1,2671 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/contribute_files/figure-commonmark/mermaid-figure-1.png b/docs/src/contribute_files/figure-commonmark/mermaid-figure-1.png index dfc3a35..9abfa0a 100644 Binary files a/docs/src/contribute_files/figure-commonmark/mermaid-figure-1.png and b/docs/src/contribute_files/figure-commonmark/mermaid-figure-1.png differ diff --git a/docs/src/index.md b/docs/src/index.md index 35ef0fe..7d71acd 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -108,27 +108,22 @@ fit!(mach, rows=train) Predictions can then be computed using the generic `predict` method. The code below produces predictions for the first `n` samples. Each tuple contains the lower and upper bound for the prediction interval. ``` julia -n = 10 +n = 5 Xtest = selectrows(X, first(test,n)) ytest = y[first(test,n)] predict(mach, Xtest) ``` - ╭─────────────────────────────────────────────────────────────────╮ - │ │ - │ (1) ([0.14395897640483468], [1.5537237281612537]) │ - │ (2) ([-0.539687877793372], [0.8700768739630471]) │ - │ (3) ([-0.46442052745067525], [0.9453442243057439]) │ - │ (4) ([0.010529843675146089], [1.420294595431565]) │ - │ (5) ([0.07301045762431613], [1.4827752093807351]) │ - │ (6) ([-0.012020120998203487], [1.3977446307582158]) │ - │ (7) ([0.5297045560243977], [1.9394693077808167]) │ - │ (8) ([-0.46442052745067525], [0.9453442243057439]) │ - │ (9) ([-0.09600489213468855], [1.3137598596217306]) │ - │ (10) ([0.010529843675146089], [1.420294595431565]) │ - │ │ - │ │ - ╰──────────────────────────────────────────────────── 10 items ───╯ + ╭─────────────────────────────────────────────────────────╮ + │ │ + │ (1) (1.2801183281465092, 2.0024286641173816) │ + │ (2) (0.8012756658949756, 1.5235860018658482) │ + │ (3) (1.1850387604493555, 1.9073490964202282) │ + │ (4) (1.1185514282818692, 1.8408617642527418) │ + │ (5) (1.1651738766694149, 1.8874842126402875) │ + │ │ + │ │ + ╰───────────────────────────────────────────── 5 items ───╯ ## 🛠 Contribute diff --git a/docs/src/intro.md b/docs/src/intro.md index a0a13fa..d37b5cd 100644 --- a/docs/src/intro.md +++ b/docs/src/intro.md @@ -1,9 +1,17 @@ -`ConformalPrediction.jl` is a package for Uncertainty Quantification (UQ) through Conformal Prediction (CP) in Julia. It is designed to work with supervised models trained in [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/). Conformal Prediction is distribution-free, easy-to-understand, easy-to-use and model-agnostic. +`ConformalPrediction.jl` is a package for Uncertainty Quantification (UQ) through Conformal Prediction (CP) in Julia. It is designed to work with supervised models trained in [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) Blaom et al. (2020). Conformal Prediction is distribution-free, easy-to-understand, easy-to-use and model-agnostic. -## Installation 🚩 +# 📖 Background -You can install the first stable release from the general registry: +Conformal Prediction is a scalable frequentist approach to uncertainty quantification and coverage control. It promises to be an easy-to-understand, distribution-free and model-agnostic way to generate statistically rigorous uncertainty estimates. Interestingly, it can even be used to complement Bayesian methods. + +The animation below is lifted from a small blog post that introduces the topic and the package (\[[TDS](https://towardsdatascience.com/conformal-prediction-in-julia-351b81309e30)\], \[[Quarto](https://www.paltmeyer.com/blog/posts/conformal-prediction/#fig-anim)\]). It shows conformal prediction sets for two different samples and changing coverage rates. Standard conformal classifiers produce set-valued predictions: for ambiguous samples these sets are typically large (for high coverage) or empty (for low coverage). + +![Conformal Prediction in action: Prediction sets for two different samples and changing coverage rates. As coverage grows, so does the size of the prediction sets.](https://raw.githubusercontent.com/pat-alt/blog/main/posts/conformal-prediction/www/medium.gif) + +## 🚩 Installation + +You can install the latest stable release from the general registry: ``` julia using Pkg @@ -17,9 +25,9 @@ using Pkg Pkg.add(url="https://github.com/pat-alt/ConformalPrediction.jl") ``` -## Status 🔁 +## 🔁 Status -This package is in its very early stages of development and therefore still subject to changes to the core architecture. The following approaches have been implemented in the development version: +This package is in its early stages of development and therefore still subject to changes to the core architecture and API. The following CP approaches have been implemented in the development version: **Regression**: @@ -36,9 +44,34 @@ This package is in its very early stages of development and therefore still subj - Inductive (LABEL (Sadinle, Lei, and Wasserman 2019)) - Adaptive Inductive -I have only tested it for a few of the supervised models offered by [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/). +The package has been tested for the following supervised models offered by [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/). + +**Regression**: + +``` julia +using ConformalPrediction +keys(tested_atomic_models[:regression]) +``` -## Usage Example 🔍 + KeySet for a Dict{Symbol, Expr} with 4 entries. Keys: + :nearest_neighbor + :evo_tree + :light_gbm + :decision_tree + +**Classification**: + +``` julia +keys(tested_atomic_models[:classification]) +``` + + KeySet for a Dict{Symbol, Expr} with 4 entries. Keys: + :nearest_neighbor + :evo_tree + :light_gbm + :decision_tree + +## 🔍 Usage Example To illustrate the intended use of the package, let’s have a quick look at a simple regression problem. Using [MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/) we first generate some synthetic data and then determine indices for our training, calibration and test data: @@ -67,32 +100,29 @@ fit!(mach, rows=train) Predictions can then be computed using the generic `predict` method. The code below produces predictions for the first `n` samples. Each tuple contains the lower and upper bound for the prediction interval. ``` julia -n = 10 +n = 5 Xtest = selectrows(X, first(test,n)) ytest = y[first(test,n)] predict(mach, Xtest) ``` - ╭─────────────────────────────────────────────────────────────────╮ - │ │ - │ (1) ([-0.20063113789390163], [1.323655530145934]) │ - │ (2) ([-0.061147489871723804], [1.4631391781681118]) │ - │ (3) ([-1.4486105066363675], [0.07567616140346822]) │ - │ (4) ([-0.7160881365817455], [0.8081985314580902]) │ - │ (5) ([-1.7173644161988695], [-0.19307774815903367]) │ - │ (6) ([-1.2158809697881832], [0.3084056982516525]) │ - │ (7) ([-1.7173644161988695], [-0.19307774815903367]) │ - │ (8) ([0.26510754559144056], [1.7893942136312764]) │ - │ (9) ([-0.8716996456392521], [0.6525870224005836]) │ - │ (10) ([0.43084861624955606], [1.9551352842893919]) │ - │ │ - │ │ - ╰──────────────────────────────────────────────────── 10 items ───╯ - -## Contribute 🛠 + ╭──────────────────────────────────────────────────────────╮ + │ │ + │ (1) (-0.9864061984981062, 2.2503222170961554) │ + │ (2) (-0.7192196826151477, 2.5175087329791137) │ + │ (3) (-0.33838267507136344, 2.898345740522898) │ + │ (4) (-2.838413186252051, 0.39831522934221053) │ + │ (5) (-0.7192196826151477, 2.5175087329791137) │ + │ │ + │ │ + ╰────────────────────────────────────────────── 5 items ───╯ + +## 🛠 Contribute Contributions are welcome! Please follow the [SciML ColPrac guide](https://github.com/SciML/ColPrac). -## References 🎓 +## 🎓 References + +Blaom, Anthony D., Franz Kiraly, Thibaut Lienart, Yiannis Simillides, Diego Arenas, and Sebastian J. Vollmer. 2020. “MLJ: A Julia Package for Composable Machine Learning.” *Journal of Open Source Software* 5 (55): 2704. . Sadinle, Mauricio, Jing Lei, and Larry Wasserman. 2019. “Least Ambiguous Set-Valued Classifiers with Bounded Error Levels.” *Journal of the American Statistical Association* 114 (525): 223–34. diff --git a/docs/src/intro.qmd b/docs/src/intro.qmd index 5ff0e97..ec4c99e 100644 --- a/docs/src/intro.qmd +++ b/docs/src/intro.qmd @@ -97,7 +97,7 @@ Predictions can then be computed using the generic `predict` method. The code be ```{julia} #| output: true -n = 10 +n = 5 Xtest = selectrows(X, first(test,n)) ytest = y[first(test,n)] predict(mach, Xtest) diff --git a/docs/src/regression.md b/docs/src/regression.md new file mode 100644 index 0000000..5c7dc9a --- /dev/null +++ b/docs/src/regression.md @@ -0,0 +1,83 @@ + +# Regression + +``` @meta +CurrentModule = ConformalPrediction +``` + +This tutorial mostly replicates this [tutorial](https://mapie.readthedocs.io/en/latest/examples_regression/4-tutorials/plot_main-tutorial-regression.html#) from MAPIE. + +## Data + +We begin by generating some synthetic regression data below: + +``` julia +# Regression data: + +# Inputs: +N = 600 +xmax = 3.0 +using Distributions +d = Uniform(-xmax, xmax) +X = rand(d, N) +X = reshape(X, :, 1) + +# Outputs: +noise = 0.5 +fun(X) = X * sin(X) +ε = randn(N) .* noise +y = @.(fun(X)) + ε +using MLJ +train, test = partition(eachindex(y), 0.4, 0.4, shuffle=true) + +using Plots +scatter(X, y, label="Observed") +xrange = range(-xmax,xmax,length=N) +plot!(xrange, @.(fun(xrange)), lw=4, label="Ground truth", ls=:dash, colour=:black) +``` + +## Model + +To model this data we will use polynomial regression. There is currently no out-of-the-box support for polynomial feature transformations in `MLJ`, but it is easy enough to add a little helper function for this. Note how we define a linear pipeline `pipe` here. Since pipelines in `MLJ` are just models, we can use the generated object as an input to `conformal_model` below. + +``` julia +LinearRegressor = @load LinearRegressor pkg=MLJLinearModels +degree_polynomial = 10 +polynomial_features(X, degree::Int) = reduce(hcat, map(i -> X.^i, 1:degree)) +pipe = (X -> MLJ.table(polynomial_features(MLJ.matrix(X), degree_polynomial))) |> LinearRegressor() +``` + +Next, we conformalize our polynomial regressor using every available approach (except the Naive approach): + +``` julia +using ConformalPrediction +conformal_models = merge(values(available_models[:regression])...) +delete!(conformal_models, :naive) +# delete!(conformal_models, :jackknife) +results = Dict() +for _mod in keys(conformal_models) + conf_model = conformal_model(pipe; method=_mod, coverage=0.95) + mach = machine(conf_model, X, y) + fit!(mach, rows=train) + results[_mod] = mach +end +``` + +Finally, let us look at the resulting conformal predictions in each case. + +``` julia +using Plots +zoom = -3 +xrange = range(-xmax+zoom,xmax-zoom,length=N) +plt_list = [] + +for (_mod, mach) in results + plt = plot(mach.model, mach.fitresult, X, y, zoom=zoom, title=_mod) + plot!(plt, xrange, @.(fun(xrange)), lw=1, ls=:dash, colour=:black, label="Ground truth") + push!(plt_list, plt) +end + +plot(plt_list..., size=(1600,1000)) +``` + +![Figure 1: Conformal prediction regions.](regression_files/figure-commonmark/fig-cp-output-1.svg) diff --git a/docs/src/regression.qmd b/docs/src/regression.qmd new file mode 100644 index 0000000..384b5bd --- /dev/null +++ b/docs/src/regression.qmd @@ -0,0 +1,98 @@ +# Regression + +```@meta +CurrentModule = ConformalPrediction +``` + +```{julia} +#| echo: false +using Pkg; Pkg.activate("docs") +using Plots +theme(:wong) +``` + +This tutorial mostly replicates this [tutorial](https://mapie.readthedocs.io/en/latest/examples_regression/4-tutorials/plot_main-tutorial-regression.html#) from MAPIE. + +## Data + +We begin by generating some synthetic regression data below: + +```{julia} +#| label: fig-data +#| fig-cap: "Synthetic data." + +# Regression data: + +# Inputs: +N = 600 +xmax = 3.0 +using Distributions +d = Uniform(-xmax, xmax) +X = rand(d, N) +X = reshape(X, :, 1) + +# Outputs: +noise = 0.5 +fun(X) = X * sin(X) +ε = randn(N) .* noise +y = @.(fun(X)) + ε +using MLJ +train, test = partition(eachindex(y), 0.4, 0.4, shuffle=true) + +using Plots +scatter(X, y, label="Observed") +xrange = range(-xmax,xmax,length=N) +plot!(xrange, @.(fun(xrange)), lw=4, label="Ground truth", ls=:dash, colour=:black) +``` + +## Model + +To model this data we will use polynomial regression. There is currently no out-of-the-box support for polynomial feature transformations in `MLJ`, but it is easy enough to add a little helper function for this. Note how we define a linear pipeline `pipe` here. Since pipelines in `MLJ` are just models, we can use the generated object as an input to `conformal_model` below. + +```{julia} +LinearRegressor = @load LinearRegressor pkg=MLJLinearModels +degree_polynomial = 10 +polynomial_features(X, degree::Int) = reduce(hcat, map(i -> X.^i, 1:degree)) +pipe = (X -> MLJ.table(polynomial_features(MLJ.matrix(X), degree_polynomial))) |> LinearRegressor() +``` + +## Conformal Prediction + +Next, we conformalize our polynomial regressor using every available approach (except the Naive approach): + +```{julia} +using ConformalPrediction +conformal_models = merge(values(available_models[:regression])...) +delete!(conformal_models, :naive) +# delete!(conformal_models, :jackknife) +results = Dict() +for _mod in keys(conformal_models) + conf_model = conformal_model(pipe; method=_mod, coverage=0.95) + mach = machine(conf_model, X, y) + fit!(mach, rows=train) + results[_mod] = mach +end +``` + +Finally, let us look at the resulting conformal predictions in each case. + +```{julia} +#| output: true +#| label: fig-cp +#| fig-cap: "Conformal prediction regions." + +using Plots +zoom = 0 +xrange = range(-xmax+zoom,xmax-zoom,length=N) +plt_list = [] + +for (_mod, mach) in results + plt = plot(mach.model, mach.fitresult, X, y, zoom=zoom, title=_mod) + plot!(plt, xrange, @.(fun(xrange)), lw=1, ls=:dash, colour=:black, label="Ground truth") + push!(plt_list, plt) +end + +plot(plt_list..., size=(1600,1000)) +``` + + diff --git a/docs/src/regression_files/figure-commonmark/fig-cp-output-1.svg b/docs/src/regression_files/figure-commonmark/fig-cp-output-1.svg new file mode 100644 index 0000000..a1fba3e --- /dev/null +++ b/docs/src/regression_files/figure-commonmark/fig-cp-output-1.svg @@ -0,0 +1,4606 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/www/classification.gif b/docs/src/www/classification.gif new file mode 100644 index 0000000..60ea94a Binary files /dev/null and b/docs/src/www/classification.gif differ diff --git a/src/ConformalModels/ConformalModels.jl b/src/ConformalModels/ConformalModels.jl index dc6e072..f68ac89 100644 --- a/src/ConformalModels/ConformalModels.jl +++ b/src/ConformalModels/ConformalModels.jl @@ -5,21 +5,20 @@ import MLJModelInterface as MMI import MLJModelInterface: predict, fit, save, restore "An abstract base type for conformal models that produce interval-valued predictions. This includes most conformal regression models." -abstract type ConformalInterval <: MMI.Interval end - -"An abstract base type for conformal models that produce set-valued deterministic predictions. This includes most conformal classification models." -abstract type ConformalSet <: MMI.Supervised end # ideally we'd have MMI.Set +abstract type ConformalInterval <: MMI.Interval end "An abstract base type for conformal models that produce set-valued probabilistic predictions. This includes most conformal classification models." -abstract type ConformalProbabilisticSet <: MMI.Supervised end # ideally we'd have MMI.ProbabilisticSet +abstract type ConformalProbabilisticSet <: MMI.ProbabilisticSet end "An abstract base type for conformal models that produce probabilistic predictions. This includes some conformal classifier like Venn-ABERS." abstract type ConformalProbabilistic <: MMI.Probabilistic end -const ConformalModel = Union{ConformalInterval, ConformalSet, ConformalProbabilistic} +const ConformalModel = Union{ConformalInterval, ConformalProbabilisticSet, ConformalProbabilistic} -export ConformalInterval, ConformalSet, ConformalProbabilistic, ConformalModel +export ConformalInterval, ConformalProbabilistic, ConformalModel +include("utils.jl") +include("plotting.jl") include("conformal_models.jl") # Regression Models: diff --git a/src/ConformalModels/inductive_bayes.jl b/src/ConformalModels/inductive_bayes.jl new file mode 100644 index 0000000..7ce425c --- /dev/null +++ b/src/ConformalModels/inductive_bayes.jl @@ -0,0 +1,74 @@ +# # Simple +# "The `SimpleInductiveBayes` is the simplest approach to Inductive Conformalized Bayes." +# mutable struct SimpleInductiveBayes{Model <: Supervised} <: ConformalModel +# model::Model +# coverage::AbstractFloat +# scores::Union{Nothing,AbstractArray} +# heuristic::Function +# train_ratio::AbstractFloat +# end + +# function SimpleInductiveBayes(model::Supervised; coverage::AbstractFloat=0.95, heuristic::Function=f(y, ŷ)=-ŷ, train_ratio::AbstractFloat=0.5) +# return SimpleInductiveBayes(model, coverage, nothing, heuristic, train_ratio) +# end + +# @doc raw""" +# MMI.fit(conf_model::SimpleInductiveBayes, verbosity, X, y) + +# For the [`SimpleInductiveBayes`](@ref) nonconformity scores are computed as follows: + +# `` +# S_i^{\text{CAL}} = s(X_i, Y_i) = h(\hat\mu(X_i), Y_i), \ i \in \mathcal{D}_{\text{calibration}} +# `` + +# A typical choice for the heuristic function is ``h(\hat\mu(X_i), Y_i)=1-\hat\mu(X_i)_{Y_i}`` where ``\hat\mu(X_i)_{Y_i}`` denotes the softmax output of the true class and ``\hat\mu`` denotes the model fitted on training data ``\mathcal{D}_{\text{train}}``. The simple approach only takes the softmax probability of the true label into account. +# """ +# function MMI.fit(conf_model::SimpleInductiveBayes, verbosity, X, y) + +# # Data Splitting: +# train, calibration = partition(eachindex(y), conf_model.train_ratio) +# Xtrain = selectrows(X, train) +# ytrain = y[train] +# Xtrain, ytrain = MMI.reformat(conf_model.model, Xtrain, ytrain) +# Xcal = selectrows(X, calibration) +# ycal = y[calibration] +# Xcal, ycal = MMI.reformat(conf_model.model, Xcal, ycal) + +# # Training: +# fitresult, cache, report = MMI.fit(conf_model.model, verbosity, Xtrain, ytrain) + +# # Nonconformity Scores: +# ŷ = pdf.(MMI.predict(conf_model.model, fitresult, Xcal), ycal) # predict returns a vector of distributions +# conf_model.scores = @.(conf_model.heuristic(ycal, ŷ)) + +# return (fitresult, cache, report) +# end + +# @doc raw""" +# MMI.predict(conf_model::SimpleInductiveBayes, fitresult, Xnew) + +# For the [`SimpleInductiveBayes`](@ref) prediction sets are computed as follows, + +# `` +# \hat{C}_{n,\alpha}(X_{n+1}) = \left\{y: s(X_{n+1},y) \le \hat{q}_{n, \alpha}^{+} \{S_i^{\text{CAL}}\} \right\}, \ i \in \mathcal{D}_{\text{calibration}} +# `` + +# where ``\mathcal{D}_{\text{calibration}}`` denotes the designated calibration data. +# """ +# function MMI.predict(conf_model::SimpleInductiveBayes, fitresult, Xnew) +# p̂ = MMI.predict(conf_model.model, fitresult, MMI.reformat(conf_model.model, Xnew)...) +# v = conf_model.scores +# q̂ = Statistics.quantile(v, conf_model.coverage) +# p̂ = map(p̂) do pp +# L = p̂.decoder.classes +# probas = pdf.(pp, L) +# is_in_set = 1.0 .- probas .<= q̂ +# if !all(is_in_set .== false) +# pp = UnivariateFinite(L[is_in_set], probas[is_in_set]) +# else +# pp = missing +# end +# return pp +# end +# return p̂ +# end \ No newline at end of file diff --git a/src/ConformalModels/inductive_classification.jl b/src/ConformalModels/inductive_classification.jl index 6a1bc6a..e88774f 100644 --- a/src/ConformalModels/inductive_classification.jl +++ b/src/ConformalModels/inductive_classification.jl @@ -1,6 +1,6 @@ # Simple "The `SimpleInductiveClassifier` is the simplest approach to Inductive Conformal Classification. Contrary to the [`NaiveClassifier`](@ref) it computes nonconformity scores using a designated calibration dataset." -mutable struct SimpleInductiveClassifier{Model <: Supervised} <: ConformalSet +mutable struct SimpleInductiveClassifier{Model <: Supervised} <: ConformalProbabilisticSet model::Model coverage::AbstractFloat scores::Union{Nothing,AbstractArray} @@ -75,7 +75,7 @@ end # Adaptive "The `AdaptiveInductiveClassifier` is an improvement to the [`SimpleInductiveClassifier`](@ref) and the [`NaiveClassifier`](@ref). Contrary to the [`NaiveClassifier`](@ref) it computes nonconformity scores using a designated calibration dataset like the [`SimpleInductiveClassifier`](@ref). Contrary to the [`SimpleInductiveClassifier`](@ref) it utilizes the softmax output of all classes." -mutable struct AdaptiveInductiveClassifier{Model <: Supervised} <: ConformalSet +mutable struct AdaptiveInductiveClassifier{Model <: Supervised} <: ConformalProbabilisticSet model::Model coverage::AbstractFloat scores::Union{Nothing,AbstractArray} @@ -115,9 +115,9 @@ function MMI.fit(conf_model::AdaptiveInductiveClassifier, verbosity, X, y) L = p̂.decoder.classes ŷ = pdf(p̂, L) # compute probabilities for all classes scores = map(eachrow(ŷ),eachrow(ycal)) do ŷᵢ, ycalᵢ - ranks = sortperm(.-ŷᵢ) # rank in descending order - index_y = findall(L[ranks].==ycalᵢ)[1] # index of true y in sorted array - scoreᵢ = last(cumsum(ŷᵢ[ranks][1:index_y])) # sum up until true y is reached + ranks = sortperm(.-ŷᵢ) # rank in descending order + index_y = findall(L[ranks].==ycalᵢ)[1] # index of true y in sorted array + scoreᵢ = last(cumsum(ŷᵢ[ranks][1:index_y])) # sum up until true y is reached return scoreᵢ end conf_model.scores = scores @@ -152,5 +152,4 @@ function MMI.predict(conf_model::AdaptiveInductiveClassifier, fitresult, Xnew) return pp end return p̂ -end - +end \ No newline at end of file diff --git a/src/ConformalModels/inductive_regression.jl b/src/ConformalModels/inductive_regression.jl index 6fe62b8..ccde92e 100644 --- a/src/ConformalModels/inductive_regression.jl +++ b/src/ConformalModels/inductive_regression.jl @@ -60,6 +60,7 @@ function MMI.predict(conf_model::SimpleInductiveRegressor, fitresult, Xnew) v = conf_model.scores q̂ = Statistics.quantile(v, conf_model.coverage) ŷ = map(x -> (x .- q̂, x .+ q̂), eachrow(ŷ)) + ŷ = reformat_interval(ŷ) return ŷ end diff --git a/src/ConformalModels/plotting.jl b/src/ConformalModels/plotting.jl new file mode 100644 index 0000000..1d5eb8c --- /dev/null +++ b/src/ConformalModels/plotting.jl @@ -0,0 +1,131 @@ +using CategoricalArrays +using MLJ +using Plots + +function Plots.plot( + conf_model::ConformalModel,fitresult,X,y; + target::Union{Nothing,Real}=nothing, + colorbar=true,title=nothing,length_out=50,zoom=-1,xlims=nothing,ylims=nothing,linewidth=0.1,lw=4, + train_lab=nothing,hat_lab=nothing,plot_set_size=false, + kwargs... +) + + X = permutedims(MLJ.matrix(X)) + + is_classifier = target_scitype(conf_model.model) <: AbstractVector{<:Finite} + if !is_classifier + @assert size(X,1) == 1 "Cannot plot regression for multiple input variables." + else + @assert size(X,1) == 2 "Cannot plot classification for more than two input variables." + end + + if !is_classifier + + # REGRESSION + + # Surface range: + if isnothing(xlims) + xlims = (minimum(X),maximum(X)).+(zoom,-zoom) + else + xlims = xlims .+ (zoom,-zoom) + end + if isnothing(ylims) + ylims = (minimum(y),maximum(y)).+(zoom,-zoom) + else + ylims = ylims .+ (zoom,-zoom) + end + x_range = range(xlims[1],stop=xlims[2],length=length_out) + y_range = range(ylims[1],stop=ylims[2],length=length_out) + + title = isnothing(title) ? "" : title + + # Plot: + _lab = isnothing(train_lab) ? "Observed" : train_lab + scatter(vec(X), vec(y), label=_lab, xlim=xlims, ylim=ylims, lw=lw, title=title; kwargs...) + _x = reshape([x for x in x_range],:,1) + _x = MLJ.table(_x) + ŷ = predict(conf_model, fitresult, _x) + lb, ub = eachcol(reduce(vcat, map(y -> permutedims(collect(y)), ŷ))) + ymid = (lb .+ ub)./2 + yerror = (ub .- lb)./2 + _lab = isnothing(hat_lab) ? "Predicted" : hat_lab + plot!(x_range, ymid, label=_lab, ribbon = (yerror, yerror), lw=lw; kwargs...) + + else + + # CLASSIFICATION + + # Surface range: + if isnothing(xlims) + xlims = (minimum(X[1,:]),maximum(X[1,:])).+(zoom,-zoom) + else + xlims = xlims .+ (zoom,-zoom) + end + if isnothing(ylims) + ylims = (minimum(X[2,:]),maximum(X[2,:])).+(zoom,-zoom) + else + ylims = ylims .+ (zoom,-zoom) + end + x_range = range(xlims[1],stop=xlims[2],length=length_out) + y_range = range(ylims[1],stop=ylims[2],length=length_out) + + # Target + if !isnothing(target) + @assert target in unique(y) "Specified target does not match any of the labels." + end + if length(unique(y)) > 1 + if isnothing(target) + @info "No target label supplied, using first." + end + target = isnothing(target) ? 1 : target + _default_title = plot_set_size ? "Set size" : "p̂(y=$(target))" + else + target = isnothing(target) ? 2 : target + _default_title = plot_set_size ? "Set size" : "p̂(y=$(target-1))" + end + title = isnothing(title) ? _default_title : title + + # Predictions + Z = [] + for y=y_range, x=x_range + p̂ = predict(conf_model, fitresult, [x y])[1] + if plot_set_size + z = ismissing(p̂) ? 0 : sum(pdf.(p̂, p̂.decoder.classes) .> 0) + else + z = ismissing(p̂) ? p̂ : pdf.(p̂, 1) + end + push!(Z, z) + end + Z = reduce(hcat, Z) + Z = Z[Int(target),:] + + # Contour: + if plot_set_size + _n = length(unique(y)) + clim=(0,_n) + plt = contourf( + x_range, y_range, Z; + colorbar=colorbar, title=title, linewidth=linewidth, + xlims=xlims, + ylims=ylims, + c=cgrad(:blues, _n+1, categorical = true), + clim=clim, + kwargs... + ) + else + plt = contourf( + x_range, y_range, Z; + colorbar=colorbar, title=title, linewidth=linewidth, + xlims=xlims, + ylims=ylims, + kwargs... + ) + end + + # Samples: + y = typeof(y) <: CategoricalArrays.CategoricalArray ? y : Int.(y) + scatter!(plt, X[1,:],X[2,:],group=y; kwargs...) + + end + +end \ No newline at end of file diff --git a/src/ConformalModels/transductive_classification.jl b/src/ConformalModels/transductive_classification.jl index e982d7d..9f5b6af 100644 --- a/src/ConformalModels/transductive_classification.jl +++ b/src/ConformalModels/transductive_classification.jl @@ -1,6 +1,6 @@ # Simple "The `NaiveClassifier` is the simplest approach to Inductive Conformal Classification. Contrary to the [`NaiveClassifier`](@ref) it computes nonconformity scores using a designated trainibration dataset." -mutable struct NaiveClassifier{Model <: Supervised} <: ConformalSet +mutable struct NaiveClassifier{Model <: Supervised} <: ConformalProbabilisticSet model::Model coverage::AbstractFloat scores::Union{Nothing,AbstractArray} @@ -25,7 +25,9 @@ A typical choice for the heuristic function is ``h(\hat\mu(X_i), Y_i)=1-\hat\mu( function MMI.fit(conf_model::NaiveClassifier, verbosity, X, y) # Setup: - Xtrain, ytrain = MMI.reformat(conf_model.model, X, y) + Xtrain = selectrows(X, :) + ytrain = y[:] + Xtrain, ytrain = MMI.reformat(conf_model.model, Xtrain, ytrain) # Training: fitresult, cache, report = MMI.fit(conf_model.model, verbosity, Xtrain, ytrain) diff --git a/src/ConformalModels/transductive_regression.jl b/src/ConformalModels/transductive_regression.jl index 63ab6c6..230b923 100644 --- a/src/ConformalModels/transductive_regression.jl +++ b/src/ConformalModels/transductive_regression.jl @@ -30,7 +30,9 @@ A typical choice for the heuristic function is ``h(\hat\mu(X_i),Y_i)=|Y_i-\hat\m function MMI.fit(conf_model::NaiveRegressor, verbosity, X, y) # Setup: - Xtrain, ytrain = MMI.reformat(conf_model.model, X, y) + Xtrain = selectrows(X, :) + ytrain = y[:] + Xtrain, ytrain = MMI.reformat(conf_model.model, Xtrain, ytrain) # Training: fitresult, cache, report = MMI.fit(conf_model.model, verbosity, Xtrain, ytrain) @@ -60,6 +62,7 @@ function MMI.predict(conf_model::NaiveRegressor, fitresult, Xnew) v = conf_model.scores q̂ = Statistics.quantile(v, conf_model.coverage) ŷ = map(x -> (x .- q̂, x .+ q̂), eachrow(ŷ)) + ŷ = reformat_interval(ŷ) return ŷ end @@ -91,7 +94,9 @@ where ``\hat\mu_{-i}(X_i)`` denotes the leave-one-out prediction for ``X_i``. In function MMI.fit(conf_model::JackknifeRegressor, verbosity, X, y) # Setup: - Xtrain, ytrain = MMI.reformat(conf_model.model, X, y) + Xtrain = selectrows(X, :) + ytrain = y[:] + Xtrain, ytrain = MMI.reformat(conf_model.model, Xtrain, ytrain) # Training: fitresult, cache, report = MMI.fit(conf_model.model, verbosity, Xtrain, ytrain) @@ -131,6 +136,7 @@ function MMI.predict(conf_model::JackknifeRegressor, fitresult, Xnew) v = conf_model.scores q̂ = Statistics.quantile(v, conf_model.coverage) ŷ = map(x -> (x .- q̂, x .+ q̂), eachrow(ŷ)) + ŷ = reformat_interval(ŷ) return ŷ end @@ -209,6 +215,7 @@ function MMI.predict(conf_model::JackknifePlusRegressor, fitresult, Xnew) ub = Statistics.quantile(yᵢ .+ conf_model.scores, conf_model.coverage) return (lb, ub) end + ŷ = reformat_interval(ŷ) return ŷ end @@ -286,6 +293,7 @@ function MMI.predict(conf_model::JackknifeMinMaxRegressor, fitresult, Xnew) q̂ = Statistics.quantile(v, conf_model.coverage) # For each Xnew compute ( q̂⁻(μ̂₋ᵢ(xnew)-Rᵢᴸᴼᴼ) , q̂⁺(μ̂₋ᵢ(xnew)+Rᵢᴸᴼᴼ) ): ŷ = map(yᵢ -> (minimum(yᵢ .- q̂), maximum(yᵢ .+ q̂)), eachrow(ŷ)) + ŷ = reformat_interval(ŷ) return ŷ end @@ -378,6 +386,7 @@ function MMI.predict(conf_model::CVPlusRegressor, fitresult, Xnew) ub = Statistics.quantile(yᵢ .+ conf_model.scores, conf_model.coverage) return (lb, ub) end + ŷ = reformat_interval(ŷ) return ŷ end diff --git a/src/ConformalModels/utils.jl b/src/ConformalModels/utils.jl new file mode 100644 index 0000000..dd8366f --- /dev/null +++ b/src/ConformalModels/utils.jl @@ -0,0 +1,3 @@ +function reformat_interval(ŷ) + return map(y -> map(yᵢ -> ndims(yᵢ)==1 ? yᵢ[1] : yᵢ,y), ŷ) +end \ No newline at end of file diff --git a/test/Manifest.toml b/test/Manifest.toml index a25928c..d564dda 100644 --- a/test/Manifest.toml +++ b/test/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.8.1" manifest_format = "2.0" -project_hash = "988c4d4cb0a10e861795c29e95a79cf7c9c883cb" +project_hash = "c86d41ede7b316f1c0c615053739e4cfe0ac765b" [[deps.ANSIColoredPrinters]] git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" @@ -117,6 +117,12 @@ git-tree-sha1 = "49549e2c28ffb9cc77b3689dc10e46e6271e9452" uuid = "052768ef-5323-5732-b1bb-66c8b64840ba" version = "3.12.0" +[[deps.Cairo_jll]] +deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" +uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" +version = "1.16.1+1" + [[deps.Calculus]] deps = ["LinearAlgebra"] git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad" @@ -349,6 +355,12 @@ git-tree-sha1 = "966e236ded10551a44b6e25ce4bbea4c12be1557" uuid = "f6006082-12f8-11e9-0c9c-0d5d367ab1e5" version = "0.12.4" +[[deps.Expat_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "bad72f730e9e91c08d9427d5e8db95478a3c323d" +uuid = "2e619515-83b5-522b-bb60-26c02a35a201" +version = "2.4.8+0" + [[deps.ExprTools]] git-tree-sha1 = "56559bbef6ca5ea0c0818fa5c90320398a6fbf8d" uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" @@ -359,6 +371,18 @@ git-tree-sha1 = "5e1e4c53fa39afe63a7d356e30452249365fba99" uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" version = "0.1.1" +[[deps.FFMPEG]] +deps = ["FFMPEG_jll"] +git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8" +uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" +version = "0.4.1" + +[[deps.FFMPEG_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Pkg", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] +git-tree-sha1 = "74faea50c1d007c85837327f6775bea60b5492dd" +uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" +version = "4.4.2+2" + [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] git-tree-sha1 = "7be5f99f7d15578798f338f5433b6c432ea8037b" @@ -386,6 +410,12 @@ git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" version = "0.8.4" +[[deps.Fontconfig_jll]] +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" +uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" +version = "2.13.93+0" + [[deps.Formatting]] deps = ["Printf"] git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8" @@ -410,10 +440,22 @@ git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" version = "2.10.4+0" +[[deps.FriBidi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" +uuid = "559328eb-81f9-559d-9380-de523a88c83c" +version = "1.0.10+0" + [[deps.Future]] deps = ["Random"] uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" +[[deps.GLFW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"] +git-tree-sha1 = "d972031d28c8c8d9d7b41a536ad7bb0c2579caca" +uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" +version = "3.3.8+0" + [[deps.GPUArrays]] deps = ["Adapt", "GPUArraysCore", "LLVM", "LinearAlgebra", "Printf", "Random", "Reexport", "Serialization", "Statistics"] git-tree-sha1 = "45d7deaf05cbb44116ba785d147c518ab46352d7" @@ -432,6 +474,18 @@ git-tree-sha1 = "323949b0bbdf38c93d2ea1f7d3e68ff163c3f081" uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" version = "0.16.5" +[[deps.GR]] +deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Preferences", "Printf", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "UUIDs", "p7zip_jll"] +git-tree-sha1 = "051072ff2accc6e0e87b708ddee39b18aa04a0bc" +uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" +version = "0.71.1" + +[[deps.GR_jll]] +deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Pkg", "Qt5Base_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "501a4bf76fd679e7fcd678725d5072177392e756" +uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" +version = "0.71.1+0" + [[deps.GeoInterface]] deps = ["Extents"] git-tree-sha1 = "fb28b5dc239d0174d7297310ef7b84a11804dfab" @@ -444,12 +498,41 @@ git-tree-sha1 = "12a584db96f1d460421d5fb8860822971cdb8455" uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" version = "0.4.4" +[[deps.Gettext_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" +uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" +version = "0.21.0+0" + +[[deps.Glib_jll]] +deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "fb83fbe02fe57f2c068013aa94bcdf6760d3a7a7" +uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" +version = "2.74.0+1" + +[[deps.Graphite2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" +uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" +version = "1.3.14+0" + +[[deps.Grisu]] +git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" +uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" +version = "1.0.2" + [[deps.HTTP]] deps = ["Base64", "CodecZlib", "Dates", "IniFile", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] git-tree-sha1 = "a97d47758e933cd5fe5ea181d178936a9fc60427" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" version = "1.5.1" +[[deps.HarfBuzz_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] +git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" +uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" +version = "2.8.1+1" + [[deps.HostCPUFeatures]] deps = ["BitTwiddlingConvenienceFunctions", "IfElse", "Libdl", "Static"] git-tree-sha1 = "b7b88a4716ac33fe31d6556c02fc60017594343c" @@ -520,6 +603,12 @@ git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" uuid = "82899510-4779-5014-852e-03e436cf321d" version = "1.0.0" +[[deps.JLFzf]] +deps = ["Pipe", "REPL", "Random", "fzf_jll"] +git-tree-sha1 = "f377670cda23b6b7c1c0b3893e37451c5c1a2185" +uuid = "1019f520-868f-41f5-a6de-eb00f4b6a39c" +version = "0.1.5" + [[deps.JLLWrappers]] deps = ["Preferences"] git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" @@ -532,6 +621,24 @@ git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" version = "0.21.3" +[[deps.JpegTurbo_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b53380851c6e6664204efb2e62cd24fa5c47e4ba" +uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" +version = "2.1.2+0" + +[[deps.LAME_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c" +uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" +version = "3.100.1+0" + +[[deps.LERC_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434" +uuid = "88015f11-f218-50d7-93a8-a6af411a945d" +version = "3.0.0+1" + [[deps.LLVM]] deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"] git-tree-sha1 = "e7e9184b0bf0158ac4e4aa9daf00041b5909bf1a" @@ -544,6 +651,23 @@ git-tree-sha1 = "771bfe376249626d3ca12bcd58ba243d3f961576" uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" version = "0.0.16+0" +[[deps.LZO_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" +uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" +version = "2.10.1+0" + +[[deps.LaTeXStrings]] +git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996" +uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +version = "1.3.0" + +[[deps.Latexify]] +deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Printf", "Requires"] +git-tree-sha1 = "ab9aa169d2160129beb241cb2750ca499b4e90e9" +uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" +version = "0.15.17" + [[deps.LatinHypercubeSampling]] deps = ["Random", "StableRNGs", "StatsBase", "Test"] git-tree-sha1 = "42938ab65e9ed3c3029a8d2c58382ca75bdab243" @@ -582,6 +706,54 @@ version = "1.10.2+0" [[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +[[deps.Libffi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" +uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" +version = "3.2.2+1" + +[[deps.Libgcrypt_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] +git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" +version = "1.8.7+0" + +[[deps.Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.6.0+0" + +[[deps.Libgpg_error_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" +version = "1.42.0+0" + +[[deps.Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.16.1+1" + +[[deps.Libmount_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" +uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" +version = "2.35.0+0" + +[[deps.Libtiff_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "3eb79b0ca5764d4799c06699573fd8f533259713" +uuid = "89763e89-9b03-5906-acba-b20f662cd828" +version = "4.4.0+0" + +[[deps.Libuuid_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" +uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" +version = "2.36.0+0" + [[deps.LightGBM]] deps = ["Dates", "Libdl", "MLJModelInterface", "SparseArrays", "Statistics"] git-tree-sha1 = "658faa6a229fb5bb4aea5cc897cd99db66aafb51" @@ -723,6 +895,11 @@ deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" version = "2.28.0+0" +[[deps.Measures]] +git-tree-sha1 = "c13304c81eec1ed3af7fc20e75fb6b26092a1102" +uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" +version = "0.3.2" + [[deps.Missings]] deps = ["DataAPI"] git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f" @@ -776,6 +953,12 @@ git-tree-sha1 = "f71d8950b724e9ff6110fc948dff5a329f901d64" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" version = "1.12.8" +[[deps.Ogg_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f" +uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" +version = "1.3.5+1" + [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" @@ -816,11 +999,22 @@ git-tree-sha1 = "b9fe76d1a39807fdcf790b991981a922de0c3050" uuid = "429524aa-4258-5aef-a3af-852621145aeb" version = "1.7.3" +[[deps.Opus_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720" +uuid = "91d4177d-7536-5919-b921-800302f37372" +version = "1.3.2+0" + [[deps.OrderedCollections]] git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" version = "1.4.1" +[[deps.PCRE2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" +version = "10.40.0+0" + [[deps.PDMats]] deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] git-tree-sha1 = "cf494dca75a69712a72b80bc48f59dcf3dea63ec" @@ -839,11 +1033,40 @@ git-tree-sha1 = "6c01a9b494f6d2a9fc180a08b182fcb06f0958a0" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" version = "2.4.2" +[[deps.Pipe]] +git-tree-sha1 = "6842804e7867b115ca9de748a0cf6b364523c16d" +uuid = "b98c9c47-44ae-5843-9183-064241ee97a0" +version = "1.3.0" + +[[deps.Pixman_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29" +uuid = "30392449-352a-5448-841d-b1acce4e97dc" +version = "0.40.1+0" + [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" version = "1.8.0" +[[deps.PlotThemes]] +deps = ["PlotUtils", "Statistics"] +git-tree-sha1 = "1f03a2d339f42dca4a4da149c7e15e9b896ad899" +uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" +version = "3.1.0" + +[[deps.PlotUtils]] +deps = ["ColorSchemes", "Colors", "Dates", "Printf", "Random", "Reexport", "SnoopPrecompile", "Statistics"] +git-tree-sha1 = "21303256d239f6b484977314674aef4bb1fe4420" +uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" +version = "1.3.1" + +[[deps.Plots]] +deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SnoopPrecompile", "SparseArrays", "Statistics", "StatsBase", "UUIDs", "UnicodeFun", "Unzip"] +git-tree-sha1 = "6a9521b955b816aa500462951aa67f3e4467248a" +uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +version = "1.36.6" + [[deps.PolyesterWeave]] deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] git-tree-sha1 = "b42fb2292fbbaed36f25d33a15c8cc0b4f287fcf" @@ -895,6 +1118,12 @@ git-tree-sha1 = "53b8b07b721b77144a0fbbbc2675222ebf40a02d" uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" version = "1.94.1" +[[deps.Qt5Base_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] +git-tree-sha1 = "0c03844e2231e12fda4d0086fd7cbe4098ee8dc5" +uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1" +version = "5.15.3+2" + [[deps.QuadGK]] deps = ["DataStructures", "LinearAlgebra"] git-tree-sha1 = "97aa253e65b784fd13e83774cadc95b38011d734" @@ -927,6 +1156,12 @@ git-tree-sha1 = "d12e612bba40d189cead6ff857ddb67bd2e6a387" uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" version = "1.3.1" +[[deps.RecipesPipeline]] +deps = ["Dates", "NaNMath", "PlotUtils", "RecipesBase", "SnoopPrecompile"] +git-tree-sha1 = "e974477be88cb5e3040009f3767611bc6357846f" +uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" +version = "0.6.11" + [[deps.Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" @@ -1019,6 +1254,12 @@ version = "1.1.1" deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" +[[deps.Showoff]] +deps = ["Dates", "Grisu"] +git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" +uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" +version = "1.0.3" + [[deps.SimpleBufferStream]] git-tree-sha1 = "874e8867b33a00e784c8a7e4b60afe9e037b74e1" uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" @@ -1176,6 +1417,12 @@ version = "1.0.2" [[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +[[deps.UnicodeFun]] +deps = ["REPL"] +git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" +uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" +version = "0.4.1" + [[deps.UnicodePlots]] deps = ["ColorSchemes", "ColorTypes", "Contour", "Crayons", "Dates", "FileIO", "FreeType", "LinearAlgebra", "MarchingCubes", "NaNMath", "Printf", "Requires", "SnoopPrecompile", "SparseArrays", "StaticArrays", "StatsBase", "Unitful"] git-tree-sha1 = "390b2e8e5535f5beb50885d1a1059f460547d3a5" @@ -1188,6 +1435,11 @@ git-tree-sha1 = "d57a4ed70b6f9ff1da6719f5f2713706d57e0d66" uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" version = "1.12.0" +[[deps.Unzip]] +git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" +uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" +version = "0.2.0" + [[deps.VectorizationBase]] deps = ["ArrayInterface", "CPUSummary", "HostCPUFeatures", "IfElse", "LayoutPointers", "Libdl", "LinearAlgebra", "SIMDTypes", "Static"] git-tree-sha1 = "ba9d398034a2ba78059391492730889c6e45cf15" @@ -1199,16 +1451,208 @@ git-tree-sha1 = "58d6e80b4ee071f5efd07fda82cb9fbe17200868" uuid = "81def892-9a0e-5fdd-b105-ffc91e053289" version = "1.3.0" +[[deps.Wayland_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "3e61f0b86f90dacb0bc0e73a0c5a83f6a8636e23" +uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" +version = "1.19.0+0" + +[[deps.Wayland_protocols_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4528479aa01ee1b3b4cd0e6faef0e04cf16466da" +uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" +version = "1.25.0+0" + +[[deps.XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "58443b63fb7e465a8a7210828c91c08b92132dff" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.9.14+0" + +[[deps.XSLT_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" +uuid = "aed1982a-8fda-507f-9586-7b0439959a61" +version = "1.1.34+0" + +[[deps.Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.6.9+4" + +[[deps.Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.9+4" + +[[deps.Xorg_libXcursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd" +uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" +version = "1.2.0+4" + +[[deps.Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.3+4" + +[[deps.Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.4+4" + +[[deps.Xorg_libXfixes_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" +uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" +version = "5.0.3+4" + +[[deps.Xorg_libXi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] +git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" +uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" +version = "1.7.10+4" + +[[deps.Xorg_libXinerama_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] +git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" +uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" +version = "1.1.4+4" + +[[deps.Xorg_libXrandr_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631" +uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" +version = "1.5.2+4" + +[[deps.Xorg_libXrender_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" +uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" +version = "0.9.10+4" + +[[deps.Xorg_libpthread_stubs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb" +uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" +version = "0.1.0+3" + +[[deps.Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.13.0+3" + +[[deps.Xorg_libxkbfile_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "926af861744212db0eb001d9e40b5d16292080b2" +uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" +version = "1.1.0+4" + +[[deps.Xorg_xcb_util_image_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] +git-tree-sha1 = "0fab0a40349ba1cba2c1da699243396ff8e94b97" +uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b" +version = "0.4.0+1" + +[[deps.Xorg_xcb_util_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll"] +git-tree-sha1 = "e7fd7b2881fa2eaa72717420894d3938177862d1" +uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5" +version = "0.4.0+1" + +[[deps.Xorg_xcb_util_keysyms_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] +git-tree-sha1 = "d1151e2c45a544f32441a567d1690e701ec89b00" +uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7" +version = "0.4.0+1" + +[[deps.Xorg_xcb_util_renderutil_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] +git-tree-sha1 = "dfd7a8f38d4613b6a575253b3174dd991ca6183e" +uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e" +version = "0.3.9+1" + +[[deps.Xorg_xcb_util_wm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] +git-tree-sha1 = "e78d10aab01a4a154142c5006ed44fd9e8e31b67" +uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" +version = "0.4.1+1" + +[[deps.Xorg_xkbcomp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxkbfile_jll"] +git-tree-sha1 = "4bcbf660f6c2e714f87e960a171b119d06ee163b" +uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" +version = "1.4.2+4" + +[[deps.Xorg_xkeyboard_config_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xkbcomp_jll"] +git-tree-sha1 = "5c8424f8a67c3f2209646d4425f3d415fee5931d" +uuid = "33bec58e-1273-512f-9401-5d533626f822" +version = "2.27.0+4" + +[[deps.Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.4.0+3" + [[deps.Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" version = "1.2.12+3" +[[deps.Zstd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e45044cd873ded54b6a5bac0eb5c971392cf1927" +uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" +version = "1.5.2+0" + +[[deps.fzf_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "868e669ccb12ba16eaf50cb2957ee2ff61261c56" +uuid = "214eeab7-80f7-51ab-84ad-2988db7cef09" +version = "0.29.0+0" + +[[deps.libaom_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "3a2ea60308f0996d26f1e5354e10c24e9ef905d4" +uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" +version = "3.4.0+0" + +[[deps.libass_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47" +uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" +version = "0.15.1+0" + [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" version = "5.1.1+0" +[[deps.libfdk_aac_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55" +uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" +version = "2.0.2+0" + +[[deps.libpng_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" +uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" +version = "1.6.38+0" + +[[deps.libvorbis_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] +git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" +uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" +version = "1.3.7+1" + [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" @@ -1218,3 +1662,21 @@ version = "1.48.0+0" deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" version = "17.4.0+0" + +[[deps.x264_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2" +uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" +version = "2021.5.5+0" + +[[deps.x265_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9" +uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" +version = "3.5.0+0" + +[[deps.xkbcommon_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] +git-tree-sha1 = "9ebfc140cc56e8c2156a15ceac2f0302e327ac0a" +uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" +version = "1.4.1+0" diff --git a/test/Project.toml b/test/Project.toml index 906e83c..76c06f2 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -8,4 +8,5 @@ MLJLinearModels = "6ee0df7b-362f-4a72-a706-9e79364fb692" MLJModelInterface = "e80e1ace-859a-464e-9ed9-23947d8ae3ea" MLJScikitLearnInterface = "5ae90465-5518-4432-b9d2-8a1def2f0cab" NearestNeighborModels = "636a865e-7cf4-491e-846c-de09b730eb36" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/classification.jl b/test/classification.jl index b71498b..ca90fc4 100644 --- a/test/classification.jl +++ b/test/classification.jl @@ -1,4 +1,5 @@ using MLJ +using Plots # Data: X, y = MLJ.make_blobs(1000, 2, centers=2) @@ -34,6 +35,9 @@ conformal_models = merge(values(available_models[:classification])...) @test !isnothing(conf_model.scores) predict(mach, selectrows(X, test)) + # Plot + plot(mach.model, mach.fitresult, X, y) + end end diff --git a/test/regression.jl b/test/regression.jl index 1d6f77d..6dd4d3b 100644 --- a/test/regression.jl +++ b/test/regression.jl @@ -1,7 +1,8 @@ using MLJ +using Plots # Data: -X, y = MLJ.make_regression(1000, 2) +X, y = MLJ.make_regression(1000, 1) train, test = partition(eachindex(y), 0.8) # Atomic and conformal models: @@ -34,6 +35,9 @@ conformal_models = merge(values(available_models[:regression])...) @test !isnothing(conf_model.scores) predict(mach, selectrows(X, test)) + # Plot + plot(mach.model, mach.fitresult, X, y) + end end diff --git a/tmp.gif b/tmp.gif new file mode 100644 index 0000000..a7dfc81 Binary files /dev/null and b/tmp.gif differ