Источник: https://mlr3book.mlr-org.com/
 Пакет caret является первой реализацией инфраструктуры для построения моделей машинного обучения на R и одной из первых библиотек такого рода в целом (релиз на CRAN состоялся в 2007 году). В 2013 году по уже классическому на тот момент пакету была издана не менее классическая книга Applied Predictive Modeling, которую в комплекте с официальной документацией и сейчас можно рекомендовать в качестве вводного практического руководства по машинному обучению. Преимущества: Недостатки: Своебразной работой над ошибками стало создание семейства пакетов под общей вывеской tidymodels, основными из которых являются recipes (отвечает за создание «рецептов» предварительной обработки данных, исполняемых внутри ресемплов с обучением на обучающей выборке и применением на обучающей и валидационной), rsample (обеспечивает различные варианты разбивки данных) и относительно новый tune (реализует собственно тюнинг гиперпараметров).  Преимущества: Недостатки: Пакет mlr3 и экосистема вокруг него также представляют собой попытку исправить недостатки как более раннего пакета mlr тех же авторов, так и рассмотренных выше caret и tidymodels. mlr подробно рассматривать не будем ввиду того, что его развитие было остановлено в пользу mlr3. Преимущества: Недостатки: В основе экосистемы mlr3 лежат «нормальное» ООП, реализуемое путем использования R6-классов. R6-объекты являются изменяемыми, что позволяет работать с ними без копирования и перезаписи. Подробно изучить тему можно по официальной документации и книге Advanced R, мы же ограничимся кратким примером, позаиствованным из упомянутой книги. Новый R6-класс создается вызовом функции R6Class(): Имя объекта должно совпадать с именем класса — в данном случае это "Accumulator". У объектов есть метод new(), который позволяет создавать (или, как любят говорить настоящие программисты, инстанцировать) экземпляры класса: Функции, заданные внутри списка при определении класса, доступны как методы у экземпляров данного класса: R6-объекты передаются по ссылке: Поэтому для создания копий нужно вызывать метод clone() (указав clone(deep = TRUE) для рекурсивного копирования вложенных объектов): Это все, что нужно знать об R6 в контексте использования пакетов семейства mlr3.  Также целям устранения ненужного копирования и повышения скорости работы служит использование data.table в качестве бекенда по умолчанию (можно почитать перевод документации, недавний хабрапост Вокруг data.table и короткий обзор data.table: выжимаем максимум скорости при работе с данными в языке R). Киллер-фичей для использования в задачах машинного обучения является изменяемость таблиц data.table, позволяющая добавлять новые столбцы при помощи оператора := без перезаписи всей таблицы. Например, можно добавить столбец предсказанных значений к таблице с обучающей выборкой, не используя при этом 2х памяти относительно объема, занимаемого самой таблице. А при последовательном добавлении признаков в таблицу становится заметной еще и экономия по времени, и чем тяжелее таблица, тем экономия существеннее. Источник: https://mlr3book.mlr-org.com/ Минимальный пример решения задачи машинного обучения при помощи mlr3 выглядит следующим образом: В процессе участвуют две сущности: задача (task) и модель (learner).  Задача создается как экземпляр соответствующего класса (TaskClassif для классификации, TaskRegr для регрессии и т.д.) путем вызова метода new(). Нужно указать идентификатор задачи id, таблицу с данными backend и целевую переменную target; в случае бинарной классификации положительный класс задается параметром positive. Стандартные задачи можно получить с использованием альтернативного синтаксиса: mlr_tasks$get("iris") или tsk("iris"). Модель извлекается из списка mlr_learners при помощи метода get() и затем обучается посредством вызова метода train(), в который передается наша задача task и строки выборки, участвующие в обучении. Но удобнее создавать модели с использованием синтаксического сахара: lrn("classif.rpart", predict_type = "prob", minsplit = 50). В этом случае можно сразу задать настройки модели (predict_type = "prob") и значения гиперпараметров (minsplit = 50). После создания модели их тоже легко поменять: learner_rpart$predict_type <- "prob", learner_rpart$param_set$values$minsplit = 50.  Обученную модель используем для предсказания на новых данных при помощи метода predict_newdata(): Добавим кросс-валидацию с разбивкой на 5 фолдов: Оценим качество полученной модели. Для этого вызовем метод score() у объекта с ресемплами resample_results, передав ему список из двух метрик — accuracy "classif.acc" и classification error "classif.ce". Метрики также хранятся в списке, элементы которого извлекаются методом get(): mlr_measures$get("classif.ce"). Но мы вновь воспользуемся синтаксическим сахаром в виде функции msrs(): Осталось самое главное — произвести настройку гиперпараметров модели. Тут все немного сложнее, и вызовом одного метода дело не ограничится. Прежде всего зададим пространство для перебора значений гиперпараметров. Этим функционалом заведует отдельный пакет paradox: Мы сконструировали новый объект класса ParamSet, определив в нем диапазон проверяемых значений для числового параметра cp и целочисленного параметра minsplit; остальные гиперпараметры нашей модели rpart оставим по умолчанию.  Важным моментом является то, что объект searchspace не содержит в себе никаких реальных значений. Эти значения будут сгенерированы при вызове метода tune() объекта класса Tuner. Границы диапазонов всегда включаются в набор значений. Количество проверяемых вариантов задается числом resolution, если нужно равное количество для всех гиперпараметров, или именованным вектором param_resolutions, если нужно разное количество для разных гиперпараметров. Кроме того, фактическое число проверяемых комбинаций ограничивается бюджетом на вычисления, но об этом чуть позже. Функция generate_design_grid() позволяет получить таблицу значений гиперпараметров, по которой будет проводиться перебор: Также реализованы другие способы генерации сетки значений: generate_design_random() для случайной выборки из диапазона и generate_design_lhs() для создания дизайна эксперимента методом латинского гиперкуба. Как уже было сказано, фактическое число проверяемых комбинаций можно ограничить. Для этого существуют различные варианты Terminator-ов, реализующие ограничения по времени, количеству проверямых моделей (мы используем именно его), достижению целевого качества или выходу на плато. Для дальнейшей работы понадобится пакет mlr3tuning: Объединим все ингредиенты в один объект класса TuningInstance: Создадим тюнер — объект класса Tuner, реализующий ту или иную стратегию перебора значений гиперпараметров: Мы указали resolution = 5, что для двух гиперпараметров означает проверку 25 комбинаций. Но фактически будет проверено лишь 20 случайным образом выбранных комбинаций, поскольку мы задали terminator = term("evals", n_evals = 20). batch_size — неудачно выбранное название параметра, определяющего количество параллельно обучаемых моделей. Параллелизация в mlr3 — отдельная большая тема, выходящая за пределы данной статьи.  Заслуживает внимания тюнер tnr("design_points"): он позволяет передать созданную заранее таблицу со значениями гиперпараметров, что зачастую удобнее генерации из диапазонов (особенно если нужно перебрать значений на логарифмической шкале — без готовой таблицы придется использовать достаточно громоздкий механизм преобразования параметров, который в mlr3 тоже есть). Наконец, запустим процесс: Как видим, result не содержит ничего. Это потому, что вызов tuner$tune() приводит к изменению объекта tuning_instance: Рассмотрим подробнее, что именно происходит после вызова метода tune(): Дополнительную информацию о результатах обучения моделей можно получить из объекта tuning_instance$bmr, имеющего класс BenchmarkResult, при помощи его метода score() или функции as.data.table(tuning_instance$bmr). Что происходит на уровне отдельных ресемплов, можно понять, используя аналогичный метод для объектов ResampleResult из таблицы tuning_instance$archive(): Например, можем добавить к таблице значения метрики качества на каждом ресемпле: С основными пакетами мы уже знакомы: это mlr3, mlr3tuning и paradox. Вся экосистема представлена на заглавной картинке и в списке, а основные пакеты можно поставить при помощи мета-пакета mlr3verse: Следите за страницами по представленным ссылкам, список пакетов будет пополняться. Про пайпы (pipelines) можно было бы написать много, но много уже написали разработчики, поэтому постараемся максимально кратко изложить наиболее принципиальные для практического использования моменты. Все операции — отбор признаков, преобразования, само обучение модели — абстрагируются в виде пайпов. Для моделей есть PipeOpLearner(), для отбора признаков — PipeOpFilter(), для всех остальных преобразований — PipeOp(). Мы используем синтаксический сахар (функция po()) для всех трех случаев: Пайпы последовательно соединяются в граф при помощи оператора %>>%: У пайпов есть входы и выходы. Для графов с более сложной структурой придется явно указывать, какой выход к какому входу последующего пайпа подключать: Как сделать пайп из модели, мы уже видели (po("learner", learner = lrn("classif.rpart"))). В свою очередь, граф целиком можно сделать моделью: Получившийся объект относится к классам GraphLearner и Learner. Его можно использовать так же, как и рассмотренные выше простые Learner-ы, например: Третьего дня была реализована невиданная ранее фича, которая обсуждалась в issue How to deal with different preprocessing steps as hyperparameters: Рассмотренные в первом разделе caret и tidymodels так не умеют! Надеюсь, данный пост был полезен и зародил интерес к дальнейшему изучению и использованию фреймворка mlr3. Подробнее можно почитать в книге mlr3 book и в галерее примеров. Биостатистик