Рубрики
Без рубрики

Вывод модели ML в безболезненном

Исследование вывода модели в Elasticsearch безболезненно. С тегами elasticsearch, java, машинное обучение.

Я являюсь сотрудником Эластичный на момент написания статьи

Вывод машинного обучения – это просто математика. У вас есть некоторые параметры, прокачивайте их через некоторые функции, и бум, вы получаете результат. Хотя на первый взгляд это просто, все инструменты могут усложниться. Могу ли я написать сценарий вывода простой модели в Elasticsearch?

Что такое [Эластичный поиск |Безболезненный]

Elasticsearch – это распределенное, спокойное и открытое хранилище данных. Базовым магазином является Lucene с кучей вкусностей, построенных сверху.

Безболезненный – это безопасный, простой и гибкий язык сценариев, предназначенный для Elasticsearch. Вы можете использовать пользовательские сценарии в поиск время , в много разные агрегации , и даже во время глотания . Он безумно мощный и гибкий. Но с большой властью приходит большая ответственность.

Вывод машинного обучения в безболезненном

Безболезненный 100,5 (даже не 101)

Безболезненный можно использовать несколькими способами:

  • встроенный: где весь сценарий включен в вызов API
  • сохранено: Сценарий хранится в состоянии кластера Elasticsearch.

Безболезненные сценарии могут ссылаться на поля в данном контексте (поля документа, поля _source). У них также есть доступ к объекту params . Это может быть предусмотрено для повторного использования скрипта с различными входными параметрами.

Простые модели

Линейная регрессия , будучи интуитивно понятной и простой, является очень хорошим местом для начала экспериментов.

Тривиально безболезненно реализовать одномерную линейную регрессию.

# Storing a simple linear regression function script
PUT _scripts/linear_regression_inference
{
  "script": {
    "lang": "painless",
    "source": """
    // This assumes the parameter definitions will be given when used
    // This also assumes a single target.
    double total = params.intercept;
    for (int i = 0; i < params.coefs.length; ++i) {
      total += params.coefs.get(i) * doc[params['x'+i]].value;
    }
    return total;
    """
  }
}

Он обрушил простую модель в scikit-learn , на диабет набор данных. Здесь используются результирующие параметры модели в сценарии для возврата поля сценария .

GET diabetes_test/_search
{
  "script_fields": {
    "regression_score": {
      "script": {
        "id": "linear_regression_inference",
        # Here are the model parameters. The linear regression coefficients and intercept. 
        "params": {
          # coef_ attribute from sklearn 
          "coefs": [-35.55683674, -243.1692265, 562.75404632, 305.47203008, -662.78772128, 324.27527477, 24.78193291, 170.33056502, 731.67810787, 43.02846824],
          # intercept_ attribute from sklearn
          "intercept": 152.53813351954059,
          "x0": "age",
          "x1": "sex",
          "x2": "bmi",
          "x3": "bp",
          "x4": "s1",
          "x5": "s2",
          "x6": "s3",
          "x7": "s4",
          "x8": "s5",
          "x9": "s6"
        }
      }
    }
  }
}

Написание пользовательского кода вывода для каждого типа модели может утомить. Более сложные модели потребуют постоянно растущей библиотеки функций. Существует множество библиотек выводов, с которыми можно поэкспериментировать. Зачем изобретать велосипед заново? Можно ли заставить человека работать с Безболезненностью?

m2cgen для безболезненного

m2cgen – это библиотека python, которая преобразует обученные модели в статический код. Хотя поддерживаются только определенные модели, сгенерированный код отлично работает. Безболезненно поддерживает подмножество Java, а в m2cgen в качестве потенциального вывода используется Java. Возможно создание безболезненных сценариев из обученных моделей!

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

import xgboost as xgb
from sklearn import datasets
from sklearn.metrics import mean_squared_error
import m2cgen as m2c


diabetes = datasets.load_diabetes() # load data
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(diabetes.data, diabetes.target, test_size=0.2, random_state=0)
print(diabetes.feature_names)
model = xgb.XGBRegressor(max_depth=6, learning_rate=0.3, n_estimators=50)
model.fit(X_train,y_train)
java_model = m2c.export_to_java(model)
java_model = java_model.replace("input", "params")
for idx, val in enumerate(diabetes.feature_names):
    java_model = java_model.replace("[" + str(idx) + "]", "[\"" + val + "\"]")
print(java_model)

Вот результат (усеченный)

double var0;
        if ((params["s5"]) >= (0.0216574483)) {
            if ((params["bmi"]) >= (0.0131946635)) {
                var0 = 72.2889786;
            } else {
            ...
            return ((((((((((((((((((((((((((((((((((((((((((((((((((0.5) + (var0)) + (var1)) + (var2)) + (var3)) + (var4)) + (var5)) + (var6)) + (var7)) + (var8)) + (var9)) + (var10)) + (var11)) + (var12)) + (var13)) + (var14)) + (var15)) + (var16)) + (var17)) + (var18)) + (var19)) + (var20)) + (var21)) + (var22)) + (var23)) + (var24)) + (var25)) + (var26)) + (var27)) + (var28)) + (var29)) + (var30)) + (var31)) + (var32)) + (var33)) + (var34)) + (var35)) + (var36)) + (var37)) + (var38)) + (var39)) + (var40)) + (var41)) + (var42)) + (var43)) + (var44)) + (var45)) + (var46)) + (var47)) + (var48)) + (var49);

Сгенерированный скрипт огромен. Почти 7000 строк. Любой скажет вам, что это уже слишком.

Но работает ли это?

Тьфу:

{
  "error" : {
    "root_cause" : [
      {
        "type" : "illegal_argument_exception",
        "reason" : "exceeded max allowed stored script size in bytes [65535] with size [307597] for script [diabetes_xgboost_model]"
      }
    ],
    "type" : "illegal_argument_exception",
    "reason" : "exceeded max allowed stored script size in bytes [65535] with size [307597] for script [diabetes_xgboost_model]"
  },
  "status" : 400
}

Ограничения на размер скрипта разумны. Сохраненные сценарии помещаются в объект состояния кластера. Чем больше хранимых сценариев (и чем больше сценариев), тем больше общая производительность кластера начнет снижаться и может привести к другим проблемам.

Но что, если бы я не заботился о своем здоровье в кластере? Мне нужна моя модель и я хочу этого сейчас!

PUT _cluster/settings
{
  "transient": {
    "script.max_size_in_bytes": 10000000
  }
}

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

Время ставить сценарий:

PUT _scripts/diabetes_xgboost_model
{
  "script": {
    "lang": "painless",
    "source": """
    ...very large source...
    """
    }
}

Теперь я могу использовать свой сохраненный скрипт!

"regression": {
    "bucket_script": {
        "buckets_path": {
            "age": "age",
            "sex": "sex",
            "bmi": "bmi",
            "bp": "bp",
            "s1": "s1",
            "s2": "s2",
            "s3": "s3",
            "s4": "s4",
            "s5": "s5",
            "s6": "s6"
        },
        "script": {
          "id": "diabetes_xgboost_model"
        }
   }
}

В этом примере он используется в качестве bucket_script агрегации.

Следует ли вам делать это на производстве? Нет.

Было ли это забавным исследованием? Для меня, да! m2cgen – это такое замечательное открытие, и его сочетание с гибкостью безболезненного стоило изучить.

Вот суть, содержащая код python + полностью сгенерированный скрипт

Оригинал: “https://dev.to/benwtrent/ml-model-inference-in-painless-25n6”