Паттерны проектирования в библиотеках и коде
Паттерн «фабрика»
Паттерн «фабрика» отделяет объекты, такие, как обучающие данные, от того, как они создаются. Создание этих объектов иногда может быть сложным (например, распределенные загрузчики данных), но использование данного паттерна помогает пользователям, упрощая создание объектов и обеспечивая соблюдение ограничений, которые помогают предотвращать ошибки.
Базовая фабрика может быть определена через интерфейс или абстрактный класс. Затем, чтобы создать новую фабрику, мы можем создать ее подкласс и предоставить детали собственной реализации.
PyTorch Dataset — хороший пример. Чтобы создать наши собственные наборы данных, мы должны создать подкласс Dataset и переопределить методы __len__и __getitem__
. Первый возвращает размер набора данных, а второй поддерживает индексирование для получения i-го примера. Вот пример создания пользовательского набора данных.
Gensim textcorpus — еще один пример, упрощающий чтение текстовых файлов для последующего языкового моделирования. Пользователям необходимо переопределить get_texts()
– метод для чтения и обработки одной строки (или документа) и возврата ее в виде последовательности слов.
Последний пример — Hugging Face Dataset, не требующий подкласса. Он предоставляет пользователям простой способ загрузки данных в Apache Arrow, который обеспечивает быстрый поиск с низкими требованиями к памяти. Он также включает методы потоковой передачи, чередования, перемешивания и т. д.
Паттерн «адаптер»
Паттерн «адаптер» повышает совместимость между интерфейсами, такими форматы данных как CSV, Parquet, JSON и т.д. Это позволяет объектам (например, сохраненным данным) с несовместимыми интерфейсами взаимодействовать. В конкретных конвейерах обработки данных адаптеры часто используются для чтения данных, хранящихся в различных форматах, в стандартный объект данных, такой как Dataframe.
Например, у Pandas есть почти 20 адаптеров для чтения большинства типов файловых хранилищ в pandas Dataframe.
Точно так же Spark имеет адаптеры для чтения из различных форматов данных, таких как Parquet, JSON, CSV, Hive и текстовые файлы.
Другим примером является Apache Arrow, который предоставляет стандартный формат столбцов для нескольких платформ данных, таких как Pandas, Spark, Parquet, Cassandra и других. (Набор данных Hugging Face использует Arrow для своей локальной системы кэширования.)
Паттерн «декоратор»
Паттерн «декоратор» позволяет пользователям легко добавлять функциональные возможности в свой существующий код. Объекты можно «декорировать» (т. е. добавлять функциональные возможности) во время выполнения без необходимости обновления структуры или поведения других объектов того же класса.
В Python методы декорирования легко реализуются с помощью @синтаксиса. Использование @decorator
эквивалентно вызову method()
— method = decorator(method)
.
Удобным примером встроенного оператора является оператор functool lru_cache(). Он сохраняет самые последние вызовы x в словаре, который сопоставляет входные аргументы с возвращаемыми результатами. Вот пример использования кэша для эффективного вычисления чисел Фибоначчи.
Другой пример — декораторы Pytest для определения фикстур. Затем на эти фикстуры можно ссылаться в последующих тестах. Думайте о фикстурах как о методах, которые генерируют данные для тестирования ожидаемого поведения. Эти фикстуры вызываются перед запуском любых тестов и совместно используются тестами. Вот несколько примеров фикстур для загрузки демонстрационных данных и обученных моделей.
Простой декоратор, который я часто использую — это таймер, который измеряет, сколько времени занимает вызов метода, и возвращает результаты. Это полезно при создании прототипов, которые вызывают разные модели — с разной задержкой — для проверки времени, затрачиваемого на каждый вызов.
Паттерн «стратегия»
Паттерн «стратегия» позволяет пользователям изменять предполагаемое поведение объекта или алгоритм. Пользователи могут создавать новые объекты для каждой стратегии (или алгоритма), и в зависимости от используемого объекта стратегии поведение контекста может меняться во время выполнения. Это отделяет алгоритмы от клиентов, добавляя гибкости и возможности повторного использования кода.
Большинство библиотек машинного обучения имеют встроенные алгоритмы и настройки. Тем не менее, они также предоставляют пользователям возможность добавлять свои собственные алгоритмы, если они соответствуют интерфейсу стратегии.
Например, XGBoost предоставляет различные методы построения деревьев (например, точное, приблизительное, гистологическое, gpu_hist) и несколько целевых функций, таких как квадрат ошибки, логистическое и попарное ранжирование. Тем не менее, мы также можем предоставить пользовательскую целевую функцию, если захотим, например, приведенный ниже пример ошибки квадратного журнала.
Точно так же конвейер Hugging Face позволяет легко использовать различные стратегии (также известные как языковые модели) для логического вывода. Pipelines поддерживает такие задачи, как анализ настроений, перевод, ответы на вопросы и многое другое.
Паттерн «итератор»
Паттерн «итератор» обеспечивает способ просмотра объектов в коллекции объектов. Это отделяет алгоритм обхода от контейнера данных, и пользователи могут задать свой собственный алгоритм. Например, если у нас есть данные в дереве, мы можем захотеть переключаться между обходом в ширину и обходом в глубину.
Примером может служить DataLoader из PyTorch, который позволяет пользователям определять размер пакета, перетасовку, число работников и даже предоставлять пользовательские функции сортировки.
Паттерн «конвейер»
Паттерн «конвейер» («pipeline») позволяет пользователям связывать последовательность преобразований. Преобразования — это этапы обработки данных, такие как очистка данных, разработка функций, уменьшение размерности и т. д. В конец конвейера можно добавить блок оценки (также известный как модель машинного обучения). Таким образом, конвейер принимает данные в качестве входных данных, преобразует их и в конце обучает модель.
ИМХО, в конвейерах ML все, от подготовки данных до разработки функций и гиперпараметров модели, является параметром для настройки. Возможность связывать и варьировать преобразования и блоки оценки обеспечивает удобную настройку параметров конвейера. Как изменятся показатели модели, если мы будем использовать непрерывные переменные или добавим триграммы к нашим текстовым функциям?
При использовании конвейера мы должны знать, что ввод и вывод каждого шага преобразования должны иметь стандартизированный формат, такой как кадр данных Pandas. Кроме того, каждое преобразование должно иметь необходимые методы, такие как .fit()
и .transform()
.
Pipeline из Sklearn позволяет пользователям предоставлять список преобразований и окончательную оценку. Мы также можем использовать TransformerMixin для создания пользовательских преобразований.
MLlib из Spark также использует концепцию конвейеров, построенных на основе фреймов данных, преобразователей и блоков оценки.
Паттерны проектирования в системах
Думаю, паттерны проектирования применяются не только в коде и библиотеках, но и в системах. Вот два паттерна, обычно наблюдаемые в системах машинного обучения.
Паттерн «Заместитель»
Паттерн «заместитель» («proxy») позволяет нам заменить рабочую базу данных или службу. Затем прокси-сервер может выполнять задачи от имени конечной точки.
Одним из примеров является использование кеша прокси-сервера для обслуживания поисковых запросов. Поисковые запросы следуют принципу Парето, когда на долю запросов приходится важная часть запросов. Таким образом, путем кэширования результатов самых популярных запросов (например, предложение запроса, классификация запроса, извлечение и ранжирование) мы можем уменьшить объем вычислений, необходимых для логического вывода в реальном времени.
На изображении ниже при исполнении запроса наш сервис сначала проверяет, доступен ли результат в кэше. Если это так, он возвращает результат из кеша; если нет, запрос передается для логического вывода в реальном времени.
Другой пример — использование обратного прокси для обслуживания моделей. Один из способов масштабирования — обслуживание через несколько машин (т. е. горизонтальное масштабирование). Тем не менее, мы по-прежнему хотим предоставить единый адрес для доступа. Обратный прокси может принимать входящие запросы через один адрес и распределять их по нескольким серверам, чтобы распределить нагрузку и обеспечить низкую задержку.
В чем разница между балансировщиком нагрузки и обратным прокси-сервером?
«Когда мы говорим о балансировщике нагрузки, мы имеем в виду очень конкретную вещь – сервер, который балансирует входящие запросы между двумя или более веб-серверами для распределения нагрузки.
Однако обратный прокси-сервер, как правило, имеет ряд функций:
- Балансировка нагрузки: как обсуждалось выше.
- Кэширование: он может кэшировать содержимое веб-сервера (серверов), расположенного за ним, и тем самым снижать нагрузку на веб-сервер (серверы) и возвращать статическое содержимое обратно запрашивающему без необходимости получать данные с веб-сервера (серверов).
- Безопасность: он может защитить веб-сервер(ы), предотвращая прямой доступ из интернета; он может сделать это простыми средствами, просто скрыв веб-сервер(ы), или он может использовать активные компоненты, которые фактически просматривают входящие запросы в поисках вредоносного кода.
Ускорение SSL: при использовании SSL, он может служить точкой завершения для этих SSL-сессий, чтобы снять нагрузку по шифрованию с веб-сервера(ов)». (источник)
Паттерн «посредник»
Паттерн «посредник» предоставляет посредника, который ограничивает прямую связь между службами, заставляя службы сотрудничать — косвенно — через посредника. Это способствует слабой связи, не позволяя службам явно ссылаться друг на друга и реализовывать интерфейс других служб.
Если в наших существующих системах есть посредники, мы должны ожидать предоставления услуг машинного обучения через них, а не напрямую для нижестоящих приложений (например, сайтов электронной коммерции, торговых заказов). Посредники обычно имеют стандартные требования (например, схемы запросов) и ограничения (например, задержку, ограничение скорости), которых необходимо придерживаться.
Например, у нас может быть несколько виджетов рекомендаций для отображения на домашней странице Netflix. Вместо того, чтобы позволять виджетам решать, как они будут показаны пользователям, мы можем потребовать, чтобы каждый виджет проходил через посредника, который ранжирует виджеты. Посредник также может выполнять проверки (например, исключать виджеты с менее чем x элементами) или обновлять виджеты (например, выполнять дедупликацию элементов в нескольких виджетах).
Хочу подтянуть знания по математике, но не знаю, с чего начать. Что делать?
Если базовые концепции языка программирования можно достаточно быстро освоить самостоятельно, то с математикой могут возникнуть сложности. Чтобы помочь освоить математический инструментарий, «Библиотека программиста» совместно с преподавателями ВМК МГУ разработала курс по математике для Data Science, на котором вы:
- подготовитесь к сдаче вступительных экзаменов в Школу анализа данных Яндекса;
- углубитесь в математический анализ, линейную алгебру, комбинаторику, теорию вероятностей и математическую статистику;
- узнаете роль чисел, формул и функций в разработке алгоритмов машинного обучения.
- освоите специальную терминологию и сможете читать статьи по Data Science без постоянных обращений к поисковику.
Курс подойдет как начинающим специалистам, так и действующим программистам и аналитикам, которые хотят повысить свой уровень или перейти в новую область.
Комментарии