Недавно я провел небольшое исследование Elasticsearch, чтобы узнать, подходит ли он моим потребностям на работе. Больше всего меня волнует поддержка Python. Если вы занимаетесь наукой о данных, то в Python для Elasticsearch есть три библиотеки, которые вы, возможно, захотите изучить: elasticsearch-py
, elasticsearch-dsl-py
и eland
.
elasticsearch-py
предоставляет вам API низкого уровня, с помощью которых вы можете делать все, что вам нужно, с Elasticsearch. elasticsearch-dsl-py
- это клиентская библиотека более высокого уровня, которая более питонична и находится поверх elasticsearch-py
. Чтобы увидеть разницу, вот код прямо из их github:
elasticsearch-py против elasticsearch-dsl-py
Ниже приведен поисковый запрос с использованием elasticsearch-py
:
from elasticsearch import Elasticsearch client = Elasticsearch() response = client.search( index="my-index", body={ "query": { "bool": { "must": [{"match": {"title": "python"}}], "must_not": [{"match": {"description": "beta"}}], "filter": [{"term": {"category": "search"}}] } }, "aggs" : { "per_tag": { "terms": {"field": "tags"}, "aggs": { "max_lines": {"max": {"field": "lines"}} } } } } ) for hit in response['hits']['hits']: print(hit['_score'], hit['_source']['title']) for tag in response['aggregations']['per_tag']['buckets']: print(tag['key'], tag['max_lines']['value'])
Для сравнения, вот код, использующий elasticsearch-dsl-py
:
from elasticsearch import Elasticsearch from elasticsearch_dsl import Search client = Elasticsearch() s = Search(using=client, index="my-index") \ .filter("term", category="search") \ .query("match", title="python") \ .exclude("match", description="beta") s.aggs.bucket('per_tag', 'terms', field='tags') \ .metric('max_lines', 'max', field='lines') response = s.execute() for hit in response: print(hit.meta.score, hit.title) for tag in response.aggregations.per_tag.buckets: print(tag.key, tag.max_lines.value)
Думаю, совершенно очевидно, что делать, если вам просто нужно выполнить несколько простых запросов. :)
Машинное обучение с Eland… не так много
eland
- это точно не полноценная библиотека машинного обучения. Насколько мне известно, пока он делает следующее:
- Позволяет управлять данными в Elasticsearch, как если бы вы работали с фреймом данных pandas, поэтому вы можете выполнять предварительную обработку данных в Elasticsearch вместо того, чтобы делать это в своей оперативной памяти.
- Развертывание моделей scikit-learn в Elasticsearch и выполнение там логических выводов.
Вот и все. Если вы пока не слишком разочарованы, читайте дальше. :)
Я использовал примеры игрушек на их веб-сайте, чтобы протестировать некоторые функции самостоятельно с некоторыми изменениями.
Просто чтобы вы знали, прежде чем я начал пробовать Elasticsearch, я надеялся обнаружить с его помощью некоторые аномалии, поэтому я начал с некоторых сгенерированных данных только для этой цели.
Создание набора данных игрушек
from datetime import datetime import eland as ed from eland.conftest import * from eland.ml import MLModel from elasticsearch import Elasticsearch from elasticsearch_dsl import Search, Q import matplotlib import matplotlib.pyplot as plt import numpy as np import pandas as pd from sklearn.datasets import make_moons from sklearn.ensemble import IsolationForest from sklearn.model_selection import train_test_split from sklearn.tree import DecisionTreeClassifier from tqdm import tqdm sample_size = 100000 outlier_fraction = 0.01 outlier_set_size = int(sample_size * outlier_fraction) inlier_set_size = sample_size - outlier_set_size random_gen = np.random.RandomState(1113) X_raw, y_raw = make_moons(n_samples=sample_size, shuffle=True, noise=outlier_fraction, random_state=None) X_normed = 4 * (X_raw - np.array([0.5, 0.25]))X_noise = random_gen.uniform(low=-6, high=6, size=(outlier_set_size, 2)) X = np.concatenate([X_normed, X_noise]) df = pd.DataFrame(X) df.columns = ['feature_1', 'feature_2']
Давайте визуализируем данные:
plt.figure(figsize=(16, 9)) plt.scatter(X[:, 0], X[:, 1])
Модель обучения
Я выбрал простой Isolation Forest, чтобы посмотреть, что произойдет.
anomaly_algo = IsolationForest(n_estimators=100, contamination=outlier_fraction, random_state=1113) y_iso = anomaly_algo.fit(X).predict(X)
И вот результат
plt.figure(figsize=(16, 9)) plt.scatter(X[:, 0], X[:, 1], c=y_iso)
На этом этапе я очень надеялся увидеть, как логический вывод будет работать в Elasticsearch, но, к сожалению, все прошло не так хорошо ...
Подключение и развертывание модели в Elasticsearch
Мне не удалось заставить развертывание работать на моем локальном компьютере. Если вы попытаетесь сделать это, вы получите сообщение об ошибке 403 о том, что ваша лицензия недостаточно эффективна для того, что вам нужно. Я предполагаю, что это либо проблема с настройками, либо я не установил X-Pack. В любом случае я решил, что это слишком сложно, поэтому я пошел на получение 14-дневной пробной учетной записи Elastic Cloud и просто сделал это там.
Вот минимальный код, чтобы заставить его работать (… или не работать).
es = Elasticsearch(cloud_id='<your_cloud_id>', http_auth=("<your_username>", "<your_password>")) es_model = MLModel.import_model(es_client=es, model=anomaly_algo, model_id='model', feature_names=df.columns )
Если вы запустите это, вы получите такую ошибку:
NotImplementedError: Importing ML models of type <class 'sklearn.ensemble._iforest.IsolationForest'>, not currently implemented
Очень плохо. Оказалось, что Elasticsearch поддерживает только пару моделей контролируемого обучения на основе дерева. Я перечислю их здесь, чтобы сэкономить вам время:
- DecisionTreeClassifier
- DecisionTreeRegressor
- RandomForestRegressor
- RandomForestClassifier
- XGBClassifier
- XGBRegressor
- LGBMRegressor
- LGBMClassifier
Итак, после некоторого поиска в Google я узнал об этой печальной истине и решил хотя бы опробовать один алгоритм в качестве доказательства концепции.
Контролируемое обучение с помощью Elasticsearch
Поскольку наш набор данных не помечен. Мы можем использовать Лес изоляции, который мы создали для этого чуть выше. Сначала мы назначаем псевдометки каждой строке и меняем значения выбросов на 0 вместо -1.
df['label'] = y_iso df.loc[df['label'] != 1, 'label'] = 0 df.head()
А затем разделение данных
X_train, X_test, y_train, y_test = train_test_split(df.loc[:, ['feature_1', 'feature_2']], df['label'], test_size=0.33, shuffle=True, random_state=42)
На этом этапе мы можем построить простую модель дерева решений:
sl_model = DecisionTreeClassifier() sl_model.fit(X_train, y_train)
Теперь наступает ключевая часть. С помощью всего одной строчки кода мы можем легко развернуть нашу модель в Elasticsearch:
es_model = MLModel.import_model(es_client=es, model=sl_model, model_id='model001', feature_names=list(df.columns) )
И давайте попробуем делать выводы с помощью Elasticsearch
y_pred = es_model.predict(X_test[:500].values) plt.figure(figsize=(16, 9)) plt.scatter(x=X_test.iloc[:500, 0], y=X_test.iloc[:500, 1], c=y_pred[:500])
Ключевые выводы
- Модели Scikit-learn можно развернуть в Elasticsearch с помощью однострочника.
- На данный момент поддерживаются только древовидные модели.
- Модели обучения без учителя не поддерживаются.
Чего нельзя делать с Elasticsearch
- Фрейм данных pandas можно преобразовать в фрейм данных eland с помощью
ed.pandas_to_eland
. Но фреймворк eland можно использовать только для обработки данных. Вы не можете обучать свои модели с помощью scikit-learn или каких-либо API-интерфейсов Elasticsearch, поэтому после обработки данных с помощью eland вам нужно будет преобразовать фрейм данных обратно в pandas с помощьюto_pandas
. - Вы не можете обучать модели на Elasticsearch
- Вы не можете делать выводы для данных, хранящихся в Elasticsearch.
- Существует ограничение на количество данных, которые вы можете выводить за раз. Все, что слишком велико, приведет к тайм-ауту.