Я являюсь сотрудником Эластичный на момент написания статьи
Вывод машинного обучения – это просто математика. У вас есть некоторые параметры, прокачивайте их через некоторые функции, и бум, вы получаете результат. Хотя на первый взгляд это просто, все инструменты могут усложниться. Могу ли я написать сценарий вывода простой модели в 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”