Элегантный, прогрессивный и прагматичный

Вступление

Это сообщение в блоге - краткое размышление об элегантности принципов проектирования библиотеки Scikit-Learn. Чтобы было ясно: это не предназначено для руководства по использованию Scikit-Learn. Scikit-Learn - мощная, богатая и обширная библиотека Python для реализации машинного обучения. Библиотека предоставляет инструменты для моделирования (например, алгоритмы классификации, регрессии и кластеризации), выбора модели (например, поиск по сетке), предварительной обработки (например, извлечение признаков) и многого другого. Я утверждаю, что успех библиотеки во многом связан с ее интерфейсом и простотой использования, а также с ее мощной и глубокой функциональностью.

Как видно из приведенной ниже схемы Шпаргалка по алгоритмам Scikit-Learn, библиотека велика и чрезвычайно богата:

Но в этом блоге я использую другой подход и смотрю на другое измерение: я меньше сосредотачиваюсь на функциональности Scikit-Learn и больше на силе его дизайна, и при этом я пользуюсь возможностью поразмышлять над некоторыми из моих любимых тем программные шаблоны и дизайн. Этот пост вдохновлен классической статьей Дизайн API для программного обеспечения машинного обучения: опыт проекта scikit-learn (среди других источников - см. Ссылки ниже). Документы Scikit-Learn (через scikit-learn.org, как указано в ссылках ниже) также превосходны и отражают такой же продуманный и понятный дизайн и организацию, что и сама библиотека.

Библиотека Scikit-Learn - это напоминание о силе шаблонов проектирования в разработке программного обеспечения. Основополагающая работа по шаблонам проектирования - Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования - пропагандирует важность программирования для интерфейса, а не его реализации. Scikit-Learn скрывает огромную сложность за своими семантически простыми интерфейсами. Другими словами, библиотекой очень легко пользоваться из коробки. Конечно, важно понимать специфику алгоритмов машинного обучения, и успешные модели Scikit-Learn необходимо правильно настраивать, чтобы давать хорошие прогнозы; но даже гиперпараметры по умолчанию часто дают разумные результаты. (Действительно, для недавней модели классификации я обнаружил, что параметры по умолчанию для метода fit в DecisionTree дают лучшие результаты, чем рекомендуемые гиперпараметры после выполнения поиска по сетке!)

Кроме того, поскольку Scikit-Learn является библиотекой, а не фреймворком, его значительно проще и проще использовать. Пользователям библиотеки нужно только импортировать пакеты, необходимые для данной функциональности, которую они хотят использовать, а затем создавать экземпляры объектов из конкретных классов. Нет необходимости наследовать заданную иерархию классов или инвертировать управление.

Интерфейсы

Библиотека организована вокруг трех основных API (интерфейсов): Estimator, Predictor и Трансформатор. Важно и критически важно, что эти интерфейсы дополняют друг друга - они не представляют жестких границ между классами или точного семантического разделения, а скорее являются перекрытием. Например, классификатор DecisionTree является одновременно оценщиком и предиктором - подробнее об этом чуть позже.

Оценщики

Оценщики представляют собой основной интерфейс Scikit-Learn. Все алгоритмы обучения, контролируемые или неконтролируемые, классификации, регрессии или кластеризации, реализуют интерфейс оценщика и предоставляют метод соответствия.

Метод соответствия оценщика принимает в качестве входных данных (обучающий) вектор признаков («образцы» или «предикторы»), а также (обучающие) целевые метки (в случае контролируемых обучения), и таким образом оценщик «учится» делать прогнозы на основе невидимых данных (опять же, в случае обучения с учителем).

Ключевой принцип проектирования заключается в том, что создание оценщика (где, например, вы указываете гиперпараметры модели) не связано с процессом обучения (когда вы подбираете модель с обучающими данными - вашими векторами признаков, например, X_train). ; а также ваши обучающие метки / целевые переменные, например, Y_train). То есть, когда вы конструируете оценщик (например, классификатор DecisionTree, как отмечалось ранее), вы передаете гиперпараметры, но не передаете обучающие данные. ; данные обучения передаются с помощью метода fit. Как отмечалось в статье Дизайн API…, это разделение похоже на идею частичного приложения функции, где определенные аргументы привязаны - или заморожены - к одной функции, а затем к этой функции (с ее замороженными аргументами) передается другой функции - разновидности функции высшего порядка или функциональной композиции. Действительно, этот шаблон как бы поддерживается стандартной библиотекой Python через functools.partial.

Это разделение также могло быть достигнуто с помощью более навязчивой механики классов, то есть путем введения другой иерархии классов. Тем не менее, создатели Scikit-Learn мудро выбрали более прагматичный путь, даже если на первый взгляд кажется, что класс Estimator объединяет две семантические границы.

С точки зрения машинного обучения Оценщик - это

Объект, который управляет оценкой и декодированием модели. Модель оценивается как детерминированная функция:

- параметры, предоставляемые при построении объекта или с помощью set_params;

- глобальное случайное состояние numpy.random, если для параметра random_state оценщика установлено значение None;

- и любые данные или свойства выборки, переданные последнему вызову fit, fit_transform или fit_predict, или данные, переданные аналогичным образом в последовательности вызовов partial_fit ». (глоссарий Scikit-Learn)

Предсказатели

Один из сбивающих с толку аспектов выбора интерфейса Scikit-Learn - разделение предсказателей и оценщиков; Предикторы расширяют интерфейс оценщика, и для того, чтобы данная модель «работала», она должна реализовывать (и предоставлять) метод прогноз. В самом деле, глоссарий Scikit-Learn обозначает предиктор как «средство оценки, поддерживающее прогнозирование и / или соответствие_предсказу». Тем не менее, Scikit-Learn семантически разделяет эти два понятия через API. Хотя это не руководство по использованию библиотеки, и хотя это может констатировать очевидное, метод прогноз, который модель реализует как часть интерфейса Predictor, делает работа по прогнозированию результатов - с учетом особенностей теста; и после обучения модели предсказатель возвращает предсказанные метки для заданного входного вектора признаков. Предикторы могут также предоставлять вероятности, а также оценки прогнозов, что является частью другого принципа проектирования (обсуждается вкратце).

Диаграмма классов ниже обозначает конкретную иерархию классов для общего классификатора - DecisionTree - где мы видим, что класс ведет себя как оценщик (на самом деле это оценщик, через наследование), а также Predictor - в том смысле, что он реализует метод predic. Кроме того, предоставляемый им метод score (который возвращает среднюю точность заданных тестовых данных и меток) является производным от класса «mixin» - ClassifierMixin. Так называемые «смешанные» классы - это стандартный способ разделения и упаковки определенных функций (обычно предлагаемых с помощью метода одного класса) для использования посредством простого наследования, избегая при этом «ужасного ромбовидного узора», который представляет опасность при множественном наследовании.

В то время как механика классов чудесным образом объединяет и уравновешивает сложность проектирования и реализации, интерфейс кода прекрасно и удивительно прост в использовании, что позволяет вам создавать экземпляры, подгонять и использовать модель в трех строках кода:

Трансформеры

Неудивительно, что Трансформеры изменяют данные. В статье «Дизайн API…» об этом лучше всего говорится:

Алгоритмы предварительной обработки, выбора признаков, извлечения признаков и уменьшения размерности предоставляются в виде преобразователей в библиотеке.

Как и модели, трансформаторы также должны быть установлены, и после этого можно вызвать их метод transform. В соответствии с элегантной и последовательной простотой библиотеки метод fit всегда возвращает оценку, которую он вызывал. on, что позволяет связать методы подгонки и преобразования. Для удобства преобразователи также поддерживают метод fit_transform, поэтому пользователи могут выполнять оба действия за один шаг.

На приведенной ниже диаграмме классов показано, как преобразователь StandardScaler также является оценщиком, а также смешивается с классом TransformerMixin. Однако код для использования этого преобразователя - еще раз - удивительно прост.

Прозрачность

Все гиперпараметры, используемые для построения оценщиков и преобразователей, а также результаты подгонки и прогнозирования видны пользователям интерфейса как общедоступные атрибуты. На первый взгляд может показаться, что это противоречит принципам инкапсуляции и сокрытия информации, которые долгое время были отличительной чертой объектно-ориентированного проектирования, где состояние объекта обычно становится доступным только через посредничество методов класса. Но этот выбор дизайна значительно упрощает библиотеку (предотвращая распространение методов доступа): поскольку интерфейс уже согласован (и одновременно понятен и убедителен), добавление бесплатных методов для получения ключевых атрибутов модели или результатов прогнозирования только снизит ее эффективность.

Основные структуры данных

Разработчики библиотеки также приняли прямое и конкретное решение основывать основные представления данных, необходимые для машинного обучения, на многомерных массивах Numpy, а не вводить специальный набор классов для инкапсуляции данных, представляющих функции и метки / цели. Как и в случае со многими другими вариантами, это как бы снижает барьер для входа - пользователю не нужно изучать новую иерархию классов для представления данных машинного обучения - а также обеспечивает производительность с точки зрения как времени, так и пространства, учитывая, что Numpy оптимизирован для производительности с использованием C.

Составление оценщиков - трубопроводы

Единый и единообразный интерфейс для всех основных семантических компонентов - оценщиков, предикторов и преобразователей - предоставляет библиотеке дополнительные возможности и гибкость, позволяя пользователям составлять новые или расширенные функциональные возможности посредством объединения в цепочку оценщиков. . Помня, что преобразователи являются своего рода оценщиками (вспомните приведенную выше диаграмму классов для StandardScaler, полученную из BaseEstimator), Scikit-Learn предоставляет класс Pipeline, который позволяет пользователю объединять в цепочку несколько преобразователей; поскольку все преобразователи используют один и тот же интерфейс, конвейер может подгонять данные пользователя ко всем преобразователям (путем итерации по коллекции преобразователей, с помощью которых он был построен), а затем, если fit_transform , применяя преобразования и возвращая преобразованные данные. Приведенный ниже фрагмент кода демонстрирует, насколько легко заполнить отсутствующие значения средним значением аналогичных функций (с помощью класса SimpleImputer), а затем масштабировать данные (с помощью класса StandardScaler).

Расширяемость за счет утиного ввода

Последний принцип дизайна, о котором я хочу упомянуть, - это использование библиотекой так называемой утиной печати, позволяющей расширять библиотеку. Утиная типизация означает, что если класс поддерживает определенный метод - т. Е. Если он выглядит как утка, то его можно использовать взаимозаменяемо с другими объектами того же интерфейса, т. Е. равно утка. Это позволяет избежать наследования, которое теоретически делает код менее связанным, хрупким и сложным. То есть, если пользователь хочет расширить библиотеку, например, написав собственный преобразователь, ему не обязательно наследовать от классов Scikit-Learn. Таким образом, создатели библиотеки привносят в Scikit-Learn гибкость и прагматизм Pythonic.

Заключение

Специалистам по данным, будь то студенты или профессионалы, повезло, что у них есть такая глубоко богатая и хорошо продуманная библиотека, как Scikit-Learn, которая позволяет нам решать сложные задачи машинного обучения с помощью красивого и хорошо продуманного кода.

использованная литература

Ларс Буйтинк, Жиль Лупп, Матье Блондель, Фабиан Педрегоса, Андреас Мюллер, Оливье Гризель, Влад Никулае, Питер Преттенхофер, Александр Грамфор, Жак Гроблер, Роберт Лейтон, Джейк Вандерплас, Арно Жоли, Брайан Холт, Гаэль Варокво для проектирования машин, API-дизайн обучающее программное обеспечение: опыт проекта scikit-learn », https://arxiv.org/abs/1309.0238 , 2013 г.

Аурелиен Жерон, Практическое машинное обучение с помощью Scikit-Learn и TensorFlow, O’Reilly Media, 2013 г.