Scikit-Learn невероятен. Он позволяет своим пользователям использовать практически любую модель машинного обучения, о которой вы только можете подумать, а также многие из них, о которых вы, возможно, даже не слышали! Всего в двух строчках кода!

Однако в нем нет всего. Например, нигде нет порядковой регрессии. А его возможности глубокого обучения… отсутствуют. Но кого это волнует? Вы можете найти это в другом месте, верно?

Правда. Но! Scikit-Learn - это не просто моделирование. Есть также несколько действительно отличных инструментов, которые помогут вам упростить процесс моделирования, например GridSearchCV и Pipeline. Эти инструменты бесценны, но они работают только с моделями Scikit-Learn. Оказывается, если Scikit-Learn и наши повелители Google не передадут его нам напрямую, мы сможем создать наши собственные модели, совместимые с Scikit-Learn! И это проще, чем вы думаете!

В этом посте я собираюсь создать то, чего явно не хватает в Scikit-Learn: возможность использовать кластеризацию k -средств для переноса обучения в Pipeline. То есть ввод результатов кластеризации в модель обучения с учителем, чтобы найти оптимальное значение k.

Предупреждение: впереди ООП!

Этот пост будет немного техническим. В частности, я предполагаю, что у вас есть практические знания в области объектно-ориентированного программирования (ООП). То есть вы знаете, как и зачем использовать ключевое слово class Python.

Шаблон Scikit-Learn

Одна из лучших особенностей Scikit-Learn - его невероятная согласованность. Установка одного типа модели номинально аналогична установке любого другого типа модели. То есть моделировать в Scikit-Learn так же просто, как:

model = MyModel(parameters)
model.fit(X, y)

Вот и все! Теперь вы можете проанализировать свою модель, возможно, с помощью методов модели .predict() и .score(). Фактически, каждый оценщик Scikit-Learn гарантированно имеет 5 методов:

  • .fit()
  • .predict()
  • .score()
  • .set_params()
  • .get_params()

Создайте свой собственный

Чтобы построить нашу собственную модель, нам нужно только создать класс, который имеет перечисленные выше 5 методов, и реализовать их «обычным способом». Похоже, это большая работа. К счастью, Scikit-Learn делает за нас тяжелую работу. Чтобы построить нашу собственную модель, мы можем наследовать от базового класса, встроенного в Scikit-Learn.

В сторону: наследование

В ООП, если мы указываем, что один класс наследует от другого, «подкласс» получает все методы «суперкласса».

Синтаксис наследования выглядит так:

class Car(Vehicle):
    # stuff...

Автомобиль - это вид транспортного средства. Класс Car будет включать все методы класса Vehicle, а также многое другое, что мы можем определить в определении класса Car.

Что дает нам Scikit-Learn?

Чтобы соответствовать Scikit-Learn, наша модель должна наследовать от некоторого миксина. mixin - это просто класс, который никогда не предназначался для работы сам по себе, вместо этого он просто содержит множество методов, которые вы можете добавить к своему текущему классу посредством наследования. Scikit-Learn дает нам по одной модели для каждого общего типа: RegressorMixin, ClassifierMixin, ClusterMixin, TransformerMixin и некоторых других, о которых нам не нужно беспокоиться.

Все, что мы хотим построить сами, мы делаем, просто отменяя то, что мы унаследовали!

Это было утомительно, и примеров, которые можно было бы показать, нет. Сначала простой пример. Далее пример мотивации с помощью кластеризации.

Пример 1: нулевая модель

Нулевая модель, иногда называемая «базовой» моделью, - это модель, в которой у вас нет информации, кроме случайных предположений. Например, нулевая модель для задачи регрессии будет просто брать среднее y ваших обучающих данных и использовать его в качестве каждого прогноза. Для классификации нужно просто брать класс большинства для каждого прогноза. Например, если вам нужно было предсказать, выиграет ли кто-нибудь в лотерею, нулевая модель будет диктовать, что вы всегда проигрываете, поскольку это наиболее вероятный результат.

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

import numpy as np
from sklearn.base import RegressorMixin
class NullRegressor(RegressorMixin):
    def fit(self, X=None, y=None):
        # The prediction will always just be the mean of y
        self.y_bar_ = np.mean(y)
    def predict(self, X=None):
        # Give back the mean of y, in the same
        # length as the number of X observations
        return np.ones(X.shape[0]) * self.y_bar_

Достаточно просто! Теперь мы можем делать как обычно ...

model = NullRegressor()
model.fit(X, y)
model.predict(X)

Важная часть состоит в том, что наш новый NullRegressor теперь совместим со всеми встроенными инструментами Scikit-Learn, такими как cross_val_score и GridSearchCV.

Пример 2: «Настройка» кластера с помощью поиска по сетке

Этот пример был получен из любопытства, когда коллега спросил меня, могу ли я «настроить» модель k -means, используя GridSearchCV и Pipeline. Первоначально я сказал нет, поскольку вам нужно будет использовать кластеризатор в качестве преобразователя для передачи в вашу контролируемую модель, что не позволяет Scikit-Learn. Но почему это нас останавливает? Мы только что узнали, как взломать Scikit-Learn, чтобы делать все, что мы хотим! По сути, я хочу создать следующий конвейер:

Pipeline([
    ("sc", StandardScaler()),
    ("km", KMeansSomehow()),
    ("lr", LogisticRegression()
])

Где KMeansSomehow() - кластер, используемый в качестве преобразователя Scikit-Learn. То есть он добавляет метки кластера, закодированные одним нажатием, в матрицу данных X, чтобы затем передать их в нашу модель. Чтобы это работало, мы начнем с определения класса, который наследуется от TransformerMixin. Затем мы дадим ему соответствующие методы .fit(), .transform() и .fit_transform().

Но для начала инициализация:

from sklearn.base import TransformerMixin
from sklearn.cluster import KMeans
class KMeansTransformer(TransformerMixin):
    def __init__(self, *args, **args):
        self.model = KMeans(*args, **args)

Цель self.model - содержать базовую модель кластера. Но что такое *args и **kwargs, спросите вы? Это ярлык для ленивых программистов. По сути, они захватывают все остальные аргументы, которые вы передаете в __init__(), и передают их прямо KMeans(). По сути, я говорю: «Все, что я передаю в KMeansTransformer, будет также передано в KMeans, и мне слишком лень выяснять, какими могут быть эти аргументы в будущем».

Далее нам нужно указать соответствующие методы подгонки:

from self.preprocessing import OneHotEncoder
class KMeansTransformer(TransformerMixin):
    def __init__(self, *args, **args):
        self.model = KMeans(*args, **args)
    def fit(self, X):
        self.X = X
        self.model.fit(X)
    def transform(self, X):
        # Need to reshape into a column vector in order to use
        # the onehot encoder.
        cl = self.model.predict(X).reshape(-1, 1)
        
        self.oh = OneHotEncoder(
            categories="auto", 
            sparse=False,
            drop="first"
        )
        cl_matrix = self.oh.fit_transform(cl)      
 
        return np.hstack([self.X, cl_matrix])
    def fit_transform(self, X, y=None):
        self.fit(X)
        return self.transform(X)

Так и должно быть! Теперь мы можем использовать этот KmeansTransformer как встроенный преобразователь Scikit-Learn. В довершение всего, попробуйте этот пример:

from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_blobs
X, y = make_blobs(
    n_samples=100,
    n_features=2,
    centers=3
)
pipe = Pipeline([
    ("sc", StandardScaler()),
    ("km", KMeansTransformer()),
    ("lr", LogisticRegression(penalty="none", solver="lbfgs"))
])
pipe.fit(X, y)
pipe.score(X, y)
# ==> 1.0

В менее искусственных примерах вы также можете использовать GridSearchCV, чтобы найти оптимальное количество кластеров для передачи в вашу логистическую регрессию (или любую другую модель, которая у вас есть).

Заключение

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

Этот пост в блоге педантичен? Конечно. Вам это когда-нибудь понадобится? Нет, возможно, вам это никогда не понадобится, но дело не в этом. Этот метод используется для создания чего-либо. Требуется строитель, чтобы понять необходимость создания более креативного решения. Плюс, как однажды сказал один мудрый человек:

Вы никогда не меньше хотите знать больше.

Надеюсь, вы оцените добавление этого инструмента в свой набор инструментов. Овладейте им, и вы можете просто достичь настоящего подъема в области машинного обучения.