diff --git a/.github/workflows/runtests.yml b/.github/workflows/runtests.yml
index 51fd19b..9ee01b1 100644
--- a/.github/workflows/runtests.yml
+++ b/.github/workflows/runtests.yml
@@ -14,6 +14,8 @@ permissions:
jobs:
test_packages:
+ # exclude pull requests spawned by the generate_notebooks workflow:
+ if: ${{ !startsWith(github.head_ref, 'auto-update-notebooks') }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
diff --git a/slides/README.md b/slides/README.md
new file mode 100644
index 0000000..546aa06
--- /dev/null
+++ b/slides/README.md
@@ -0,0 +1,5 @@
+# Source files for generating the "Using MLJ" slides
+
+This folder contains Quarto source only. Rendered PDF slides can be found in the relevant
+notebook source, e.g.,
+[/docs/src/notebooks/UsingMLJ/01_basics/slides.pdf](/docs/src/notebooks/UsingMLJ/01_basics/slides.pdf]).
diff --git a/slides/src/figures/Pumas_AI_Primary_Black_1098x398.png b/slides/src/figures/Pumas_AI_Primary_Black_1098x398.png
new file mode 100755
index 0000000..1f81414
Binary files /dev/null and b/slides/src/figures/Pumas_AI_Primary_Black_1098x398.png differ
diff --git a/slides/src/img/QRcodeLearnMLJ.svg b/slides/src/img/QRcodeLearnMLJ.svg
new file mode 100644
index 0000000..bad7e03
--- /dev/null
+++ b/slides/src/img/QRcodeLearnMLJ.svg
@@ -0,0 +1,579 @@
+
+
+
\ No newline at end of file
diff --git a/slides/src/img/machine_construct.drawio b/slides/src/img/machine_construct.drawio
new file mode 100644
index 0000000..1bb5b14
--- /dev/null
+++ b/slides/src/img/machine_construct.drawio
@@ -0,0 +1 @@
+7Zddb5swFIZ/DZeVwA4fuVxpu0lrNWnRNO3SwQasGR9knBL262eC+RJU66Qkk6JJucDvOT74PK+DwcFxcfyoSJm/AGXCQS49OvjBQcjzEHLan0ubTol6IVOc2qRR2PFfzIquVQ+csmqWqAGE5uVcTEBKluiZRpSCep6WgpjftSQZWwi7hIil+p1Tnfd9Bdsx8InxLNd9f2EXKEifbDupckKhnkj40cGxAtDdVXGMmWjh9Vy6eU9vRIeFKSb1eyYEu6YEiKof6ef9V135zTf55c5WeSXiYBu2i9VNT0DBQVLWFvEcfF/nXLNdSZI2WhvPjZbrQthwpRX8HEhho6RciBgEqFM1TH0W0c2QOYlEaI+DwETskpjS7Phmr95A0Gw9BgXTqjEpdkLPvJkP69HBcGu1fGLesOuI3TXZUHkEay4s27/gjK7NOU1TlCRrnGmwD/wzcfbnnD13CXpgOgUdXoozvk3OaPtn0F5wTdCb2wS9eQdotPbouBhofwG6IEnOJVvwNl3rNag9Kglm0pyrlYjgmTTDxFBiRr9vGXJzCH6wgYJT2t5m1cXRZ7ctD1LbYxwjO34iBRct0edDwikxy45BVnCqeMq3TbhncDCYG7hZ+aOEK/75l/IvXPrXvSTdhHvnN8zz/rVj0cIxSjT5b9hwGIXXc8wMxzfkU2zynYEffwM=
\ No newline at end of file
diff --git a/slides/src/img/machine_construct.svg b/slides/src/img/machine_construct.svg
new file mode 100644
index 0000000..1fbed0e
--- /dev/null
+++ b/slides/src/img/machine_construct.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/slides/src/img/machine_fit.drawio b/slides/src/img/machine_fit.drawio
new file mode 100644
index 0000000..4698d9a
--- /dev/null
+++ b/slides/src/img/machine_fit.drawio
@@ -0,0 +1 @@
+7Zldb9sgFIZ/yy5y2coY20ku2/Rj0jatWjRtvZqowTEaNhEmTbxfP4jxJ26aao0zRblKeMHY53mxOUeM4CzZ3Au0jL9wTNjIdfBmBG9GrguAE6gfreSFMtEtLSwExWZQLczpH2JEx6griknWGig5Z5Iu22LI05SEsqUhIfi6PSzirH3XJVoQS5iHiNnqD4plXMYVTOuOj4QuYlnGNy46ElQONpFkMcJ83ZDg7QjOBOey+JdsZoRpeCWX4rq7F3qrBxMklftcEMzzJeeT7DH69PRNZn7+Pf16YWZ5RmxlAjYPK/OSgOCrFBM9CRjB63VMJZkvUah718pzpcUyYaY7k4L/rkhBpUSUsRlnXGxng9gnE+xVIxs9E/cJBoHqsQMrn5IISTYNyQR6T3hCpMjVENNbMs/bzXXt4HhqtLhhXrXqkFk1i2rmGqz6Y9i+gbM7NOcoitww7OOMg6fAfyfOfpszcGzQFdMm6PGhOMPT5OxOXwcNgiFBe6cJ2tsDtNv36TgYaN8CnaAwpimxeKsQZR/UElXK1UVtrkZCjC5S1QwVOKL0aw2Mqk3wynQkFGN9m14Xa58dPT1PpdnGoWvadyihTBP9vAopRuqxZzzN+HbG7XgThPMODgZtA72eF2Xc459/KP/Gtn9FknQS7r2/YQAc27GJ5RhGEp0Nqzaj8f/m2HTozWiYLBa0OcOytDheGltOfHLbfnfTsEkPm8eCwQuzgRLZyeukh01kwYmWZt4epIfNZIFdnJ1T2TdU19DreVcG3WeBXfWdk9ldlnng6JbZ9eM5m23tSN2C8fiWBZZlEZWS4F8qUJRkZ++qt62btx3fO7t4tOwiKb7SJxOaO0NZRsO2eXttMw3Qip/If2oXLn0AS+FRCRfOpeP4pXKzMU4VrbzZeiCCKgJ6DWxFO4l50a6Mr0RIdjEpxkkkFkTuGGeqOIJbJzK2+Q1z/R3eCsKQpM+k9bB9hps7PHCqInuxyg26a6aI21zlNs5dOhN1vi+B05mn4GLNs11+VdT/sCLt4lh9TT6cPyJVogw7h0WlYQf4iKhmfeBXOFwfm8Lbvw==
\ No newline at end of file
diff --git a/slides/src/img/machine_fit.svg b/slides/src/img/machine_fit.svg
new file mode 100644
index 0000000..904e198
--- /dev/null
+++ b/slides/src/img/machine_fit.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/slides/src/img/machine_predict.drawio b/slides/src/img/machine_predict.drawio
new file mode 100644
index 0000000..b39425b
--- /dev/null
+++ b/slides/src/img/machine_predict.drawio
@@ -0,0 +1 @@
+7VrLcpswFP2WLrxMBhAPe5k4iTuTdpo27TReZRQkG00BeYQcm3x9hZF4iTjOwzjjYWV0JC7cc6Sre5EHYBytJwwugu8U4XBgGWg9ABcDyzJNwxU/GZLmyDBrZcCcESQHlcAtecISNCS6JAgntYGc0pCTRR30aRxjn9cwyBhd1YfNaFh/6gLOsQbc+jDU0b8E8UD55Y7Kjq+YzAOu/PPyjgiqwdKTJICIrioQuByAMaOU51fReozDjDzFS37f1TO9xYsxHPNdbnBv0wWlw2Q6u374xRMn/RP/OJFWHmG4lA7Ll+WpYoDRZYxwZsQcgPNVQDi+XUA/610JzQUW8CiU3Qln9F/BFBDIjIThmIaUbawB5OAhsouRlZ6h9QBcV/Tojqm3xIzjdQWSjk4wjTBnqRgiexXnab25KhX0RhILKuIVsw7KWTMvLJfEigvJ7St4trrmeTabWb7fxjNyH1zng3h26jybhk50wWmVaG9fPIPj5NkavUy06XZJtH2cRNs7EG21hY69Ee1oREfQD0iMNb6Fi7yNVEVVTMVNdV4lBEMyj0XTF8RhgZ9nhBGxCZ7JjogglD2mVcVSZyMzT2Mut3FgyfYVjEiYMfpt6RMExWuPaZzQjcXNeOmE8QEKunUB7ZaF4rXo5+xLP0/XL0+SjkK9jxfMNA+t2FBTDEEOe8GKzcj7bIqNut6MuslizTrPQJUWh0tjleGj2/abm4bOdLd5rNl5YdZRIjt8meluE1nzSEszewemu81kTb0461PZV1TXwG5ZK53us6Ze9fXJ7DbJbPPgkun1Y5/N1nakZsF4eMlcTbIZ4Ryje+EojJJeu2K1NfO2w2unF4+aXDhGZ9nJRMZ7CJOE+HXxdtpmKkQL/lh6l6lw6phAAVMBnBinhuEo5GItlcpbabV1gxkRDGRzYAPqScyzciV0yXy8jZN8HIdsjvmWcbKKw6h2IqOLXxHX2aItwyHk5BHXXrZNcPmEG0qEZ89WuW5zzuR+y7usyrlLw1AjvrhGw07Oi2ZnM/0Kr98xI/XiWESTL30QKRJl0DgsUoIdLIio46r3BJGWKuS1YWVN+J20ll1P8wgjW2UwyRoqlrw9SHyuxT9qfHpxXO9ti980GgcKNrA6Xf6W/iFhwTAiPu8jgBJp2Cy03A7TiGvP+z1Ko8liPHm6NiY/3enyruVYPsar+z5x36aavce4LZrlHzXypVn+3QVc/gc=
\ No newline at end of file
diff --git a/slides/src/img/machine_predict.svg b/slides/src/img/machine_predict.svg
new file mode 100644
index 0000000..eae18ef
--- /dev/null
+++ b/slides/src/img/machine_predict.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/slides/src/img/machine_save.svg b/slides/src/img/machine_save.svg
new file mode 100644
index 0000000..be7aac3
--- /dev/null
+++ b/slides/src/img/machine_save.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/slides/src/img/nested_resampling.png b/slides/src/img/nested_resampling.png
new file mode 100644
index 0000000..0a33396
Binary files /dev/null and b/slides/src/img/nested_resampling.png differ
diff --git a/slides/src/img/overfitting0.png b/slides/src/img/overfitting0.png
new file mode 100755
index 0000000..2a022e5
Binary files /dev/null and b/slides/src/img/overfitting0.png differ
diff --git a/slides/src/img/overfitting1.png b/slides/src/img/overfitting1.png
new file mode 100755
index 0000000..fa6fdd5
Binary files /dev/null and b/slides/src/img/overfitting1.png differ
diff --git a/slides/src/img/overfitting2.png b/slides/src/img/overfitting2.png
new file mode 100755
index 0000000..139dbd8
Binary files /dev/null and b/slides/src/img/overfitting2.png differ
diff --git a/slides/src/img/overfitting3.png b/slides/src/img/overfitting3.png
new file mode 100755
index 0000000..0ee4435
Binary files /dev/null and b/slides/src/img/overfitting3.png differ
diff --git a/slides/src/img/overfitting4.png b/slides/src/img/overfitting4.png
new file mode 100755
index 0000000..bf7ed4f
Binary files /dev/null and b/slides/src/img/overfitting4.png differ
diff --git a/slides/src/img/overfitting5.png b/slides/src/img/overfitting5.png
new file mode 100755
index 0000000..3482927
Binary files /dev/null and b/slides/src/img/overfitting5.png differ
diff --git a/slides/src/img/overfitting6.png b/slides/src/img/overfitting6.png
new file mode 100755
index 0000000..459c904
Binary files /dev/null and b/slides/src/img/overfitting6.png differ
diff --git a/slides/src/img/overfitting7.png b/slides/src/img/overfitting7.png
new file mode 100755
index 0000000..347ce42
Binary files /dev/null and b/slides/src/img/overfitting7.png differ
diff --git a/slides/src/img/pipeline.drawio b/slides/src/img/pipeline.drawio
new file mode 100644
index 0000000..e2e8a23
--- /dev/null
+++ b/slides/src/img/pipeline.drawio
@@ -0,0 +1 @@
+7Vjfb5swEP5r8tgqwUDI45r+mLROkxpNXZ8mBxvwZjhmTBP2188kR4DiVJVG2qnaU3LfHeb8fb47JxOyTLc3iubJZ2BcTpwp207I5cRxZu7cMx81UiEyXfh7JFaCIdYCK/GbN4GIloLxoheoAaQWeR8MIct4qHsYVQo2/bAIZP+tOY35AFiFVA7Re8F0gujMX7SOj1zECb46cOZ7R0qbYNxJkVAGmw5EriZkqQD0/lu6XXJZs9fwsn/u+oj3kJjimX7JA/6qygGC4iH6tL7ThVd9zb6c4SqPVJa4YUxWVw0DCsqM8XqR2YRcbBKh+SqnYe3dGNENluhUorvQCn4emCIGiYSUS5CgdqsR5vGAuYfIjidw1sT3jWe4sSZLrjTfdiDc6A2HlGtVmRD0EuQcT10jwaajIHERTDrqHSIpHpv4sHTLrPmC5NqJ5nca3A0t7+6d74tfJZ0TsT3zBrxyZg4amqB0AjFkVF616EXL/NRYbcwtQI58/+BaV1g1tNTQV8MQqKpv+PzOeKiNc68xL7dd52WF1lDFffZ1ykdPH0IFlCrkzxw5PGGaqpjrZ+KIXWHFJdXisZ/H38j1XJKvVxdRFDlhaKsL5q99b6S6WPTrYjYbFgZxLHXRtPHReSbvk2dv9pRoct5w2OXas3Dtz89PxbY3YDsXOZci4wPWzUa1jdqGsAwy/oRdhKgUcWbM0NDHDX5R0ybMRP2AjlQwtutvNi37PS+CTGN3Iw7a1zQVsub1tgwFoybtJWQF7FbcxeMmpiPo6PdldC1zZG6R0BthilgFnA8ENJyfJeYKYZpzFprrl3onSo4v3sw5FFa3BAOLfu6pbgGLt7gFnHSaB8Npbt158JbTPBiUTaFpxqhihrP/FXN8bDkvnFnuCBPLemyGDe9fuR8MWLVwf/we9rQ3vfr94HiR9tg2v8lNk6jrNFa8KOB9Fcu4Gp6wWIzZ/lGw83X+byFXfwA=
\ No newline at end of file
diff --git a/slides/src/img/pipeline.svg b/slides/src/img/pipeline.svg
new file mode 100644
index 0000000..627abff
--- /dev/null
+++ b/slides/src/img/pipeline.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/slides/src/img/scitypes_small.png b/slides/src/img/scitypes_small.png
new file mode 100644
index 0000000..8e04fad
Binary files /dev/null and b/slides/src/img/scitypes_small.png differ
diff --git a/slides/src/img/target_transformer.drawio b/slides/src/img/target_transformer.drawio
new file mode 100644
index 0000000..9bebfc9
--- /dev/null
+++ b/slides/src/img/target_transformer.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/slides/src/img/target_transformer.svg b/slides/src/img/target_transformer.svg
new file mode 100644
index 0000000..f41f799
--- /dev/null
+++ b/slides/src/img/target_transformer.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/slides/src/mlj1_basics.qmd b/slides/src/mlj1_basics.qmd
new file mode 100644
index 0000000..013edac
--- /dev/null
+++ b/slides/src/mlj1_basics.qmd
@@ -0,0 +1,234 @@
+---
+title: "Using MLJ. Lesson 1: Basics"
+author: "Anthony Blaom"
+format:
+ revealjs:
+ title: |
+ Using MLJ
+
+ Lesson 1: Basics
+ theme: [default, theme.scss]
+ margin-left: '3rem'
+ aspectratio: 32
+ navigation: horizontal
+ toc: false
+ section-titles: true
+ slide-level: 2
+ self-contained-math: true
+ auto-stretch: false
+ embed-resources: true
+ logo: figures/Pumas_AI_Primary_Black_1098x398.png
+ css: styles.css
+ slide-number: true
+ template-partials:
+ - title-slide.html
+
+---
+
+## Introducing the "Using MLJ" lessons
+
+**MLJ** is a Machine Learning toolbox written for Julia.
+
+This series of video lessons focuses on how to **use** MLJ.
+
+The lessons are not a replacement for a formal machine learning course, but can be used in
+conjunction with such a course.
+
+The lessons have been developed by **PumasAI**, and are used in the *AI for Drug
+Development Course* offered by **PumasAI** and its partner **SOPHAS**.
+
+## Introducing the "Using MLJ" lessons
+
+### Series prerequisites
+
+We assume you already know some Julia. At the very least:
+
+- You are confident interacting with Julia using the **REPL**.
+
+- You know how to install new Julia packages and how to manage Julia **package
+ environments**.
+
+There are lots of online resources for getting up to speed with Julia.
+
+Any machine learning practice depends on a solid grounding in Statistics and Linear
+Algebra.
+
+
+## Lesson 1 Goals
+
+ We want to:
+
+1. Get to know the basic MLJ objects: **models**, **machines**
+2. Understand the basic **fit**/**predict** workflow
+3. Know how to estimate model **performance**:
+ - by hand
+ - with advanced tools (`evaluate`)
+4. Understand **scientific types** and their role in data preparation (`scitype`, `schema`)
+
+
+## Lesson 1 Prerequisites
+
+1. Familiarity with the concept of **supervised learning**
+2. Understand the distinction between **hyper-parameters** and **learned parameters** in
+ learning.
+3. Previous exposure to the idea of **cross-validation**
+
+## Getting more help
+
+The **Resources** page linked below contains:
+
+- Slides for this presentation
+- Julia code for the demos
+- Links to general MLJ learning resources
+
+{fig-align="center" width="25%"}
+
+ https://github.com/JuliaAI/MLJ.jl/tree/dev/examples/using_mlj
+
+
+## Supervised learning
+
+You are given **hourly** temperature readings in a room:
+
+{fig-align="center" width="100%"}
+
+
+## Supervised learning {.pause}
+
+You are given **hourly** temperature readings in a room:
+
+{fig-align="center" width="100%"}
+
+. . .
+
+**Your mission:** Predict the temperature at any time.
+
+
+## Supervised learning
+
+You try linear regression:
+
+{fig-align="center" width="100%"}
+
+
+## Supervised learning
+
+You try fitting a cubic:
+
+{fig-align="center" width="100%"}
+
+
+## Supervised learning {.pause}
+
+How about a 12th order polynomial?
+
+{fig-align="center" width="100%"}
+
+Fits all the data perfectly!
+
+
+## Performance evaluation
+
+Lucky for me, I held back readings on the **half-hour** to test your skills.
+
+{fig-align="center" width="100%"}
+
+
+## Performance evaluation
+
+Luck for me, I held back readings on the **half-hour** to test your skills.
+
+{fig-align="center" width="100%"}
+
+Adding the lengths of the red lines, we get a **performance estimate** (mean absolute
+error).
+
+
+## What's a model? {.pause}
+
+In MLJ, a **model** is the specification of a learning algorithm and its hyper-parameters
+(specified before learning begins).
+
+**Examples:**
+
+- Polynomial (by least squares) of degree 5.
+- A decision tree classifier with maximum depth 3.
+- A ridge regressor with L2 regularization of 0.1.
+
+. . .
+
+A **model** does *not* include learned parameters (**fitted_params** in MLJ docs).
+
+*Warning:* Outside of MLJ, a "model" might include learned parameters.
+
+
+## Machines
+
+A machine starts life as a marriage of a **model** and **data**:
+
+{fig-align="center" width="54%"}
+
+
+## Life cycle of a machine {.pause}
+
+{fig-align="center" width="54%"}
+
+. . .
+
+*Aside:* In `fit` we can specify `rows=...` to say which `data` observations to train
+on. So `data` could be *all* available training data. Or, you might exclude a holdout test
+set.
+
+## Life cycle of a machine
+
+{fig-align="center" width="73%"}
+
+
+## Life cycle of a machine
+
+{fig-align="center" width="73%"}
+
+## Why machines?
+
+The machine syntax anticipates an advanced MLJ feature called **learning networks**, in
+which complex MLJ workflows are "exported" as new standalone models.
+
+Learning networks are outside of the scope of this lesson.
+
+## Live coding demonstration
+
+We now demonstrate a **regression** task in which we:
+
+- Split data into `X` and `y`
+- Split observation indices into `test` and `train`
+- Choose a regression model
+- Estimate the performance by hand
+- Use a shortcut
+- Estimate using cross-validation
+
+
+## Scientific types
+
+**Machine types**: indicate how data is represented on the machine: `Float64`, `String`,
+etc.
+
+**Scitypes:** indicate how data will be *interpreted*:
+
+{fig-align="center" width="80%"}
+
+MLJ provides:
+
+- `scitype(object)`: to see how MLJ models will interpret `object`, as it is
+ currently encoded (use `schema` instead for tables)
+
+- `coerce`: to change the machine encoding to match the desired interpretation (scitype)
+
+
+## More live coding
+
+We now demonstrate a **classification** task in which we:
+
+- Coerce data scitypes to ensure correct interpretation by MLJ
+- Horizontally and vertically split data as before
+- Demonstrate **one-hot encoding** to get `Continuous` input scitypes
+- Estimate performance by hand and with `evaluate`.
diff --git a/slides/src/mlj2_model_composition.qmd b/slides/src/mlj2_model_composition.qmd
new file mode 100644
index 0000000..47cd8ac
--- /dev/null
+++ b/slides/src/mlj2_model_composition.qmd
@@ -0,0 +1,261 @@
+---
+title: "Using MLJ. Lesson 2: Model Composition"
+author: "Anthony Blaom"
+from: markdown+emoji
+format:
+ revealjs:
+ title: |
+ Using MLJ
+
+ Lesson 2: Model Composition
+ theme: [default, theme.scss]
+ margin-left: '3rem'
+ aspectratio: 32
+ navigation: horizontal
+ toc: false
+ section-titles: true
+ slide-level: 2
+ self-contained-math: true
+ auto-stretch: false
+ embed-resources: true
+ logo: figures/Pumas_AI_Primary_Black_1098x398.png
+ css: styles.css
+ slide-number: true
+ template-partials:
+ - title-slide.html
+---
+
+## Goals
+
+MLJ's design enables flexible **model composition**. Here we learn:
+
+1. What a **composite model** is
+2. How to construct model **pipelines**
+2. How composite models help us avoid **data leakage**
+3. About the **model wrapper**, `TransformedTargetModel`
+4. About other model wrappers
+5. Other kinds of model composition
+
+## Prerequisites
+
+
+1. **Lesson 1: Basics** (supervised learning,
+ machines, models, evaluation)
+2. Prior exposure to common ML pre-processing operations, such as one-hot encoding and
+ standardization
+3. Familiarity with cross-validation and the concept of **data leakage**
+
+## Getting more help
+
+The **Resources** page linked below contains:
+
+- Slides for this presentation
+- Julia code for the demos
+- Links to general MLJ learning resources
+
+{fig-align="center" width="25%"}
+
+ https://github.com/JuliaAI/MLJ.jl/tree/dev/examples/using_mlj
+
+
+## What is a composite model?
+
+A **composite model** is a model that has other models as hyper-parameters.
+
+. . .
+
+```julia
+julia> forest = EnsembleModel(DecisionTreeClassifier())
+ProbabilisticEnsembleModel(
+ model = DecisionTreeClassifier(
+ max_depth = -1,
+ min_samples_leaf = 1,
+ min_samples_split = 2,
+ min_purity_increase = 0.0,
+ n_subfeatures = 0,
+ post_prune = false,
+ merge_purity_threshold = 1.0,
+ display_depth = 5,
+ feature_importance = :impurity,
+ rng = Random.TaskLocalRNG()),
+ atomic_weights = Float64[],
+ bagging_fraction = 0.8,
+ rng = Random.TaskLocalRNG(),
+ n = 100,
+ acceleration = CPU1{Nothing}(nothing),
+ out_of_bag_measure = Any[])
+```
+
+. . .
+
+`forest.model.max_depth` is a **nested hyper-parameter**.
+
+## Kinds of composite models in MLJ
+
+The simplest kinds of model composition in MLJ:
+
+- **pipelines**
+- **model wrappers**
+
+Not discussed here:
+
+- A **model stack** (`Stack`) wraps multiple supervised learners
+
+- **Learning networks** are maximally flexible. Used internally to implement all the
+ above
+
+## Model pipelines
+
+{fig-align="center" width="100%"}
+
+**Main point**: `pipeline` is new, standalone, supervised model, behaving like any
+other.
+
+For example, you can use `evaluate` to estimate the performance of `pipeline`.
+
+. . .
+
+### Syntax
+
+```julia
+pipeline = OneHotEncoder() |> Standardization() |> RidgeRegressor()
+```
+
+which is syntactic sugar for
+
+```julia
+pipeline = Pipeline(OneHotEncoder(), Standardizer(), RidgeRegressor())
+```
+which also allows for passing some keyword options.
+
+## Data leakage
+
+Why does the following workflow, combining standardization and ridge regression, have
+**data leakage**?
+
+**Step 1.** Standardize all the input data:
+
+```julia
+mach = machine(Standardizer(), X) |> fit!
+Xstand = transform(mach, X)
+```
+
+**Step 2.** Evaluate the performance of a ridge regressor:
+
+```julia
+evaluate(RidgeRegressor(), Xstand, y, resampling=CV(nfolds=2), measure=rms)
+```
+
+. . .
+
+
+**Answer:** Let `fold1` and `fold2` be the CV folds. When training the ridge regressor on
+`fold1`, `evaluate` is using data standardized using parameters learned from **all** the
+data, which includes `fold2`. So `fold2` is a "tainted" dataset, not appropriate for
+getting an unbiased estimate of the model's performance, which what
+`evaluate` does to get the first CV score.
+
+
+## Data Leakage
+
+In the following workflow, training on each CV fold includes learning appropriate
+standardization parameters, but using only data from that fold. So data leakage is
+avoided:
+
+```julia
+pipeline = Standardizer() |> RidgeRegressor()
+evaluate(pipeline, X, y, resampling=CV(nfolds=2), measure=rms)
+```
+
+. . .
+
+**Model wrappers**, discussed next, similarly help us to avoid many other sources of data
+leakage.
+
+## A model wrapper for target transformations
+
+Some supervised models perform poorly unless the **target** data is standardized.
+
+Sample task:
+
+1. Learn standardization parameters for target $y$.
+2. Apply standardization to $y$ to get $z$
+2. Train a ridge regressor using some input features $X$ and target $z$
+3. Predict on some new data $X_{\mathrm{new}}$ to obtain $\hat z$
+4. *Inverse* transform $\hat z$ to obtain $\hat y$.
+
+Notice Steps 2 and 5 both make use of the same learned standardization parameters.
+
+
+## Target transformations
+
+In code:
+
+```julia
+pstandardizer = Standardizer()
+regressor = RidgeRegressor()
+
+mach1 = machine(standardizer, y) |> fit!
+z = transform(mach1, y)
+
+mach2 = machine(regressor, X, z) |> fit!
+ẑ = predict(mach2, X)
+ŷ = inverse_transform(mach1, ẑ)
+```
+
+The fitted machine `mach1` gets used twice, in lines 10 and 14.
+
+A simple pipeline cannot replicate this workflow.
+
+## Target transformations
+
+{fig-align="center" width="65%"}
+
+## Target transformations
+
+Model wrappers to the rescue:
+
+`model = RidgeRegressor()`
+
+`wrapped_model = TransformedTargetModel(model, transformer=Standardizer())`
+
+. . .
+
+The `wrapped_model` behaves like `model`, but with target standardization automatically
+enforced internally, protecting against data leakage.
+
+
+## Live coding
+
+We now demonstrate a supervised learning task making use of both a **pipeline** and the
+`TransformedTargetModel` **wrapper** to mitigate data leakage.
+
+## Other model wrappers in MLJ
+
+- `TunedModel(model)`: for tuning hyperparameters of `model` - see Lesson 3!
+
+. . .
+
+- `BalancedModel(model)`: to use `model` in conjunction with oversampling/undersampling
+ algorithms that correct for **class imbalance**
+
+. . .
+
+- `EnsembleModel(model)`: to create a **bagged** ensemble of `model` clones (e.g, random
+ forest)
+
+. . .
+
+
+- `IteratedModel(model)`: to wrap an iterative `model` in various iteration controls or
+ callbacks, such as **early stopping** criteria and live inspection of training losses
+
+. . .
+
+- `BinaryThresholdPredictor(model)`: for converting a probabilistic predictor into a
+ deterministic one, given a threshold probability for the "positive" outcome.
+
+. . .
+
+- `RecursiveFeatureElimination(model)`: for selecting features based on rankings of a
+ supervised `model` that reports feature importances (wrapped model is a transformer)
diff --git a/slides/src/mlj3_model_tuning.qmd b/slides/src/mlj3_model_tuning.qmd
new file mode 100644
index 0000000..573add3
--- /dev/null
+++ b/slides/src/mlj3_model_tuning.qmd
@@ -0,0 +1,169 @@
+---
+title: "Using MLJ. Lesson 3: Model Tuning"
+author: "Anthony Blaom"
+format:
+ revealjs:
+ title: |
+ Using MLJ
+
+ Lesson 3: Model Tuning
+ theme: [default, theme.scss]
+ margin-left: '3rem'
+ aspectratio: 32
+ navigation: horizontal
+ toc: false
+ section-titles: true
+ slide-level: 2
+ self-contained-math: true
+ auto-stretch: false
+ embed-resources: true
+ logo: figures/Pumas_AI_Primary_Black_1098x398.png
+ css: styles.css
+ slide-number: true
+ template-partials:
+ - title-slide.html
+
+---
+
+## Goals
+
+Model **tuning** (or **optimization**) refers to the process of choosing the "best"
+hyper-parameters of a model. The same principles apply to choosing between *different*
+models (algorithms).
+
+Here we learn:
+
+1. How to generate a **learning curve**, a visual tool for tuning a **single** model
+ hyperparameter.
+2. How to conceptualize model tuning as a **model wrapper**.
+3. How to use the `TunedModel` wrapper in practice, to tune **multiple** parameters
+ while guarding against data leakage.
+4. How to use `TunedModel` to choose from a **list** of models, of possibly varying type.
+4. Why tuning-as-wrapper can lead to **nested resampling**.
+
+## Prerequisites
+
+1. **Lesson 1: Basics** (supervised learning, machines, models, evaluation)
+2. **Lesson 2: Model Composition** (pipelines and model wrappers)
+3. Previous exposure to random forest models helpful.
+
+## Getting more help
+
+The **resources page** below contains:
+
+- Slides for this presentation
+- Julia code for the demos
+- Links to general MLJ learning resources
+
+{fig-align="center" width="25%"}
+
+ https://github.com/JuliaAI/MLJ.jl/tree/dev/examples/using_mlj
+
+
+## Live coding
+
+We will now demonstrate the use of MLJ **learning curves** to examine the effect of a single
+hyper-parameter.
+
+## Tuning as model wrapper
+
+We want to automate the
+process of optimizing one or more hyper-parameters.
+
+## Tuning as model wrapper
+
+In MLJ we wrap a supervised `model` in a "tuning strategy" like this:
+
+`tuned_model = `
+
+ `TunedModel(model; ranges=..., resampling=CV(), measure=..., ...)`
+
+
+. . .
+
+
+### Fitting
+
+Fitting `tuned_model` to some data does this:
+
+1. Construct a sequence of `model` mutations `model1`, `model2`, `model3`, ... , of `model`
+ with hyper-parameters varying over the specified `range`.
+
+2. Calls `evaluate` on each model mutant, using the specified `resampling` strategy and
+ `measure` to get **out-of-sample** estimates of performance
+
+3. Identifies the model with the best performance
+
+4. **Retrains** the best model **using all supplied data**.
+
+
+## Tuning as model wrapper
+
+### What does this mean for new predictions?
+
+```julia
+mach = machine(tuned_model, X, y) |> fit!
+ŷ₁ = predict(mach, Xnew)
+```
+
+```julia
+best_model = report(mach).best_model
+mach = machine(best_model, X, y) |> fit!
+ŷ₂ = predict(mach, Xnew)
+```
+
+Then `ŷ₁ == ŷ₂`.
+
+## Tuning as model wrapper
+
+You can think of `tuned_model` as a "self-tuning" counterpart of `model`.
+
+More precisely, in `tuned_model` the hyper-parameters specified by `range` have been
+transformed from **hyper-parameters** to **learned parameters**.
+
+::: {.callout-caution title="Warning"}
+
+Adding to the learned parameters **adds complexity** and whence the risk of over-fitting.
+
+:::
+
+## Live coding
+
+We now demonstrate the use of the `TunedModel` wrapper.
+
+## Nested resampling
+
+In our live coding we:
+
+**1.** Wrapped a model called `pipe` in a tuning strategy:
+
+`tuned_pipe = TunedModel(pipe, resampling=CV(nfolds=4), ...)`
+
+Here the `CV` is called **inner resampling **.
+
+**2.** Evaluated the wrapped model:
+
+`evaluate(tuned_pipe, X, y; resampling=CV(nfolds=3)`
+
+Here the `CV` is called **outer resampling **.
+
+
+## Nested resampling
+
+{fig-align="center" width="60%"}
+
+Image source: The `mlr` project.
+
+
+## Final observations
+
+- It's important to remember that a `TunedModel` is *not* a model + pre-optimized
+ hyperparameters. Hyperparameters are not optimized until training the model. In
+ particular, the optimized hyperparameters generally assume *different* values on each
+ training fold in CV estimation of the model's performance.
+
+- While the wrapper approach is the safest way to avoid common data leakage pitfalls in
+ model tuning, nested resampling can make it computationally expensive, especially when
+ used in conjunction with other model wrapping algorithms, such as `IteratedModel` or
+ `Stack`. In those cases, one may decide to "freeze" the hyper-parameters by extracting
+ `best_model` from `report(mach)`.
diff --git a/slides/src/styles.css b/slides/src/styles.css
new file mode 100755
index 0000000..02ccde5
--- /dev/null
+++ b/slides/src/styles.css
@@ -0,0 +1,75 @@
+/* ---------------------------------------------
+ Slide Logo Positioning
+--------------------------------------------- */
+.reveal .slide-logo {
+ top: 0;
+ right: 12px;
+ max-height: 1.5em;
+ position: absolute;
+}
+
+/* ---------------------------------------------
+ Base Font Settings
+--------------------------------------------- */
+.reveal div {
+ font-size: 20pt;
+ line-height: 1.6;
+}
+
+/* ---------------------------------------------
+ Headings
+--------------------------------------------- */
+.reveal h1,
+.reveal h2,
+.reveal h3,
+.reveal h4,
+.reveal h5,
+.reveal h6 {
+ font-weight: normal;
+ margin: 0.5rem 0 1rem 0.5rem;
+}
+
+/* h1: Big centered title */
+.reveal h1 {
+ font-size: 2.5rem;
+ margin-bottom: 3rem;
+ text-align: center;
+}
+
+/* h2: Section heading */
+.reveal h2 {
+ font-size: 2.5rem;
+ margin-bottom: 3rem;
+ padding: 10px 0;
+}
+
+/* h3: Sub-section heading */
+.reveal h3 {
+ font-size: 1.75rem;
+ padding: 10px 0;
+}
+
+/* h4: Emphasized inline heading */
+.reveal h4 {
+ font-size: 1.25rem;
+ font-weight: bold;
+}
+
+/* ---------------------------------------------
+ Title Page Metadata
+--------------------------------------------- */
+.quarto-title-author-name,
+.date {
+ font-size: 1.5rem;
+}
+
+/* ---------------------------------------------
+ Slide Number Positioning
+--------------------------------------------- */
+.slide-number,
+.reveal.has-logo .slide-number {
+ bottom: 14px;
+ left: 50px;
+ top: unset;
+ color: #777;
+}
diff --git a/slides/src/theme.scss b/slides/src/theme.scss
new file mode 100755
index 0000000..a98496b
--- /dev/null
+++ b/slides/src/theme.scss
@@ -0,0 +1,27 @@
+/*-- scss:defaults --*/
+
+// fonts
+$font-family-sans-serif: "Computer Modern" !default;
+// $font-family-serif: 'Computer Modern' !default;
+
+// colors
+$body-bg: #ffffff !default;
+$body-color: #000 !default;
+$link-color: #51483d !default;
+$selection-bg: #26351c !default;
+
+// headings
+$presentation-heading-font: 'Computer Modern' !default;
+$presentation-heading-color: #3333b3 !default;
+
+/*-- scss:rules --*/
+
+.reveal a {
+ line-height: 1.3em;
+}
+
+.reveal .progress {
+ background: rgba(0, 0, 0, 0.2);
+ color: #3333b3;
+ height: 1%;
+}
diff --git a/slides/src/title-slide.html b/slides/src/title-slide.html
new file mode 100755
index 0000000..8f2058f
--- /dev/null
+++ b/slides/src/title-slide.html
@@ -0,0 +1,43 @@
+
+