partial_fit в scikit-learn: обучение порциями без сброса модели

Новые размеченные примеры поступают каждый день. Полностью переобучать модель после каждой порции неудобно, а вызов fit на очередном файле обычно начнёт обучение заново. Нужен алгоритм с отдельной операцией продолжения.

Проверить поддержку инкрементального обучения

Метод partial_fit есть у ограниченного набора estimator. У SGDClassifier он выполняет одну эпоху по переданной порции и сохраняет состояние для следующего вызова. Размер порций, порядок примеров и число проходов становятся частью процедуры обучения. Заменять fit на partial_fit у произвольной модели нельзя: у неё может вообще не быть такого метода.

При первом вызове классификатору нужен полный набор возможных классов. Первая порция может содержать только класс 0, но модель должна заранее знать о будущем классе 1. Параметр classes задаёт пространство ответов. В следующих вызовах его можно не повторять, если состав классов остаётся прежним.

Отделить состояние модели от порции

Объект создаётся один раз перед циклом. Каждая итерация получает новую порцию X и y и обновляет тот же estimator. Если перенести конструктор внутрь цикла, модель будет забывать прошлое. Если вызвать fit между partial_fit, часть обучающего состояния, включая счётчик шагов, будет сброшена.

В примере признаки уже находятся в сопоставимом небольшом диапазоне. Для SGD это существенно: разные масштабы влияют на обновления весов. Если добавляете преобразования, их способ обновления тоже должен быть определён для потока. Обычный Pipeline не превращает все шаги автоматически в инкрементальные.

from sklearn.linear_model import SGDClassifier

batches = [
    ([[-2.0], [-1.0]], [0, 0]),
    ([[1.0], [2.0]], [1, 1]),
    ([[-1.5], [1.5]], [0, 1]),
]
model = SGDClassifier(loss="log_loss", random_state=7,
                      learning_rate="constant", eta0=0.1,
                      shuffle=False)
steps = []
for index, (X, y) in enumerate(batches):
    if index == 0:
        model.partial_fit(X, y, classes=[0, 1])
    else:
        model.partial_fit(X, y)
    steps.append(model.t_)
assert model.classes_.tolist() == [0, 1]
assert steps == [3.0, 5.0, 7.0]
prediction = model.predict([[-2.0], [2.0]])
assert prediction.tolist() == [0, 1]
print("steps:", steps, "predictions:", prediction.tolist())

Что сохранять между запусками

Сохраните обученный estimator целиком и положение в потоке. Коэффициентов недостаточно для точного продолжения: алгоритм использует дополнительное состояние. Каждая порция должна быть учтена ровно столько раз, сколько предполагает процедура обучения. Повтор после сбоя может незаметно обучить модель на тех же строках второй раз.

Счётчик t_ в примере подтверждает, что обновления продолжались. Он не измеряет полезность модели на будущих данных. Для выбора режима обучения потребуется отдельная отложенная проверка, а для эксплуатации — понятная граница публикации новой версии. Обучающий объект лучше не менять одновременно с использованием его для прогнозов.

Закрепить базовую операцию

В открытой практике вы создадите estimator, вызовете fit и получите прогноз через predict. Это основа, с которой удобно сравнить отдельную операцию продолжения partial_fit. Откройте урок «Первый fit и predict на своих данных». Затем вернитесь к сценарию статьи и проверьте его на своём небольшом наборе. Посмотрите программу курса первой ML-модели, чтобы пройти путь от обучения к использованию результата.

Продолжить на практике