1. Обучим модель, сохраним её и проверим совпадение прогнозов.

import joblib

import numpy as np

 

from sklearn.datasets import make_classification

from sklearn.model_selection import train_test_split

from sklearn.pipeline import make_pipeline

from sklearn.preprocessing import StandardScaler

from sklearn.linear_model import LogisticRegression

from sklearn.metrics import accuracy_score

 

X, y = make_classification(

    n_samples=400,

    n_features=5,

    n_informative=3,

    n_redundant=0,

    random_state=42

)

 

X_train, X_test, y_train, y_test = train_test_split(

    X, y,

    test_size=0.2,

    stratify=y,

    random_state=42

)

 

model = make_pipeline(

    StandardScaler(),

    LogisticRegression(max_iter=1000)

)

 

model.fit(X_train, y_train)

 

before = model.predict(X_test)

 

artifact = {

    "model": model,

    "version": "1.0",

    "feature_names": [

        "feature_1",

        "feature_2",

        "feature_3",

        "feature_4",

        "feature_5"

    ],

    "accuracy": accuracy_score(y_test, before)

}

 

joblib.dump(artifact, "saved_model.joblib")

 

loaded = joblib.load("saved_model.joblib")

 

after = loaded["model"].predict(X_test)

 

print("Версия:", loaded["version"])

print("Точность:", round(loaded["accuracy"], 3))

print("Прогнозы совпадают:", np.array_equal(before, after))

Результат:

Прогнозы совпадают: True.

Сохранение всего Pipeline обеспечивает повторное применение тех же преобразований, которые использовались при обучении.

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

2. Реализуем проверку схемы JSON-запроса.

import numpy as np

 

feature_names = [

    "age",

    "income",

    "experience"

]

 

def validate_request(request):

    if not isinstance(request, dict):

        raise ValueError("Ожидается JSON-объект")

 

    if set(request.keys()) != set(feature_names):

        raise ValueError("Неверная схема признаков")

 

    values = []

 

    for name in feature_names:

        value = request[name]

 

        if (

            isinstance(value, bool)

            or not isinstance(value, (int, float))

            or not np.isfinite(value)

        ):

            raise ValueError(

                f"Некорректное значение признака: {name}"

            )

 

        values.append(float(value))

 

    return np.array([values])

 

valid_request = {

    "age": 30,

    "income": 250000,

    "experience": 5

}

 

invalid_request = {

    "age": 30,

    "income": 250000

}

 

print("Корректный запрос:")

print(validate_request(valid_request))

 

print("Некорректный запрос:")

 

try:

    print(validate_request(invalid_request))

except ValueError as error:

    print("Ошибка:", error)

Результат:

Корректный запрос преобразуется в массив с тремя признаками.

Некорректный запрос отклоняется из-за отсутствующего признака experience.

Проверка схемы необходима, поскольку модель ожидает определённое количество, порядок и типы признаков. Изменение контракта может привести к ошибкам сервиса или неправильным прогнозам.

3. Реализуем учебный мониторинг входных данных и качества модели.

import numpy as np

from sklearn.metrics import accuracy_score

 

rng = np.random.default_rng(42)

 

train_feature = rng.normal(

    loc=50,

    scale=10,

    size=1000

)

 

current_feature = rng.normal(

    loc=60,

    scale=10,

    size=1000

)

 

current_feature[::20] = np.nan

 

train_mean = np.mean(train_feature)

current_mean = np.nanmean(current_feature)

 

difference = abs(current_mean - train_mean)

 

missing_rate = np.mean(

    np.isnan(current_feature)

)

 

y_true = np.array([1, 0, 1, 1, 0, 0, 1, 0])

y_pred = np.array([1, 0, 0, 1, 0, 0, 1, 1])

 

accuracy = accuracy_score(y_true, y_pred)

 

print("Среднее train:", round(train_mean, 2))

print("Среднее current:", round(current_mean, 2))

print("Изменение:", round(difference, 2))

print("Доля пропусков:", round(missing_rate, 3))

print("Точность:", round(accuracy, 3))

 

if difference > 5:

    print("Требуется анализ изменения распределения")

Результат:

Доля пропусков: 0.05, или 5%.

Точность: 0.75.

Изменение среднего значения превышает учебный порог 5, поэтому программа выводит предупреждение.

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

Обнаружение data drift само по себе не доказывает ухудшения качества модели и не означает, что переобучение необходимо выполнять немедленно.