Русский
Русский
English
Статистика
Реклама

Catboost

Перевод Быстрый градиентный бустинг с CatBoost

11.11.2020 18:11:34 | Автор: admin
Привет, хабровчане! Подготовили перевод статьи для будущих учеников базового курса Machine Learning.




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


Источник


CatBoost это библиотека градиентного бустинга, созданная Яндексом. Она использует небрежные (oblivious) деревья решений, чтобы вырастить сбалансированное дерево. Одни и те же функции используются для создания левых и правых разделений (split) на каждом уровне дерева.


Источник

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

Работа с категориальными признаками


Наиболее распространенными способами обработки категориальных данных в машинном обучении является one-hot кодирование и кодирование лейблов. CatBoost позволяет использовать категориальные признаки без необходимости их предварительно обрабатывать.

При использовании CatBoost мы не должны пользоваться one-hot кодированием, поскольку это влияет на скорость обучения и на качество прогнозов. Вместо этого мы просто задаем категориальные признаки с помощью параметра cat_features.

Преимущества использования CatBoost


Есть несколько причин подумать об использовании CatBoost:

  • CatBoost позволяет проводить обучение на нескольких GPU.
  • Библиотека позволяет получить отличные результаты с параметрами по умолчанию, что сокращает время, необходимое для настройки гиперпараметров.
  • Обеспечивает повышенную точность за счет уменьшения переобучения.
  • Возможность быстрого предсказания с применением модели CatBoost;
  • Обученные модели CatBoost можно экспортировать в Core ML для вывода на устройстве (iOS).
  • Умеет под капотом обрабатывать пропущенные значения.
  • Может использоваться для регрессионных и классификационных задач.


Параметры обучения


Давайте рассмотрим общие параметры в CatBoost:

  • loss_function или objective показатель, используемый для обучения. Есть регрессионные показатели, такие как среднеквадратичная ошибка для регрессии и logloss для классификации.
  • eval_metric метрика, используемая для обнаружения переобучения.
  • Iterations максимальное количество построенных деревьев, по умолчанию 1000. Альтернативные названия num_boost_round, n_estimators и num_trees.
  • learning_rate или eta скорость обучения, которая определяет насколько быстро или медленно модель будет учиться. Значение по умолчанию обычно равно 0.03.
  • random_seed или random_state случайное зерно, используемое для обучения.
  • l2_leaf_reg или reg_lambda коэффициент при члене регуляризации L2 функции потерь. Значение по умолчанию 3.0.
  • bootstrap_type определяет метод сэмплинга весов объектов, например это может быть Байес, Бернулли, многомерная случайная величина или Пуассон.
  • depth = глубина дерева.
  • grow_policy определяет, как будет применяться жадный алгоритм поиска. Может стоять в значении SymmetricTree, Depthwise или Lossguide. По умолчанию SymmetricTree. В SymmetricTree дерево строится уровень за уровнем, пока не достигнет необходимой глубины. На каждом шаге листья с предыдущего дерева разделяются с тем же условием. При выборе параметра Depthwise дерево строится шаг за шагом, пока не достигнет необходимой глубины. Листья разделяются с использованием условия, которое приводит к лучшему уменьшению потерь. В Lossguide дерево строится по листьям до тех пор, пока не будет достигнуто заданное количество листьев. На каждом шаге разделяется нетерминальный лист с лучшим уменьшением потерь.
  • min_data_in_leaf или min_child_samples это минимальное количество обучающих сэмплов в листе. Этот параметр используется только с политиками роста Lossguide и Depthwise.
  • max_leaves или num_leaves этот параметр используется только с политикой Lossguide и определяет количество листьев в дереве.
  • ignored_features указывает на признаки, которые нужно игнорировать в процессе обучения.
  • nan_mode метод работы с пропущенными значениями. Параметры Forbidden, Min и Max. При использовании Forbidden наличие пропущенных значений вызовет ошибку. При использовании параметра Min пропущенные значения будут приняты за минимальные значения для данного признака. В Max пропущенные значения будут приняты как минимальные значения для данного признака.
  • leaf_estimation_backtracking тип бэктрекинга, использующийся при градиентном спуске. По умолчанию используется AnyImprovement. AnyImprovement уменьшает шаг спуска до того, как значение функции потерь будет меньшим, чем оно было на последней итерации. Armijo уменьшает шаг спуска до тех пор, пока не будет выполнено условие Вольфе.
  • boosting_type схема бустинга. Она может быть простой для классической схемы градиентного бустинга или упорядоченной, что обеспечит лучшее качество на небольших наборах данных.
  • score_function тип оценки, используемой для выбора следующего разбиения при построении дерева. Cosine используется по умолчанию. Другие доступные варианты L2, NewtonL2 и NewtonCosine.
  • early_stopping_rounds если стоит True, устанавливает тип детектора переобучения в Iter и останавливает обучение, когда достигается оптимальное значение.
  • classes_count количество классов для задач мультиклассификации.
  • task_type используете вы CPU или GPU. По умолчанию стоит CPU.
  • devices идентификаторы устройств GPU, которые будут использоваться для обучения.
  • cat_features массив с категориальными столбцами.
  • text_features используется для объявления текстовых столбцов в задачах классификации.


Пример с регрессией


CatBoost в своей реализации использует стандарт scikit-learn. Давайте посмотрим, как мы можем использовать его для регрессии.

Первый шаг, как всегда, импортировать регрессор и создать его экземпляр.

from catboost import CatBoostRegressorcat = CatBoostRegressor()

При обучении модели CatBoost также позволяет нам визуализировать его, установив plot=true:

cat.fit(X_train,y_train,verbose=False, plot=True)




Также мы можем выполнять кроссвалидацию и визуализировать процесс:

from catboost import Pool, cvparams = {"iterations": 100,          "depth": 2,          "loss_function": "RMSE",          "verbose": False}cv_dataset = Pool(data=X_train,                  label=y_train)scores = cv(cv_dataset,            params,            fold_count=2,             plot="True")




Аналогично вы можете выполнить grid search и визуализировать его:

grid = {'learning_rate': [0.03, 0.1],        'depth': [4, 6, 10],        'l2_leaf_reg': [1, 3, 5, 7, 9]}grid_search_result = cat.grid_search(grid, X=X_train, y=y_train, plot=True)



Также мы можем использовать CatBoost для построения дерева. Вот график первого дерева. Как вы видите из дерева, листья разделяются при одном и том же условии, например, 297, значение > 0.5.

cat.plot_tree(tree_idx=0)




CatBoost дает нам словарь со всеми параметрами модели. Мы можем вывести их, как словарь.

for key,value in cat.get_all_params().items(): print({}, {}.format(key,value))



В этой статье мы рассмотрели преимущества и ограничения CatBoost, а также ее основные параметры обучения. Затем мы реализовали простую регрессию с помощью scikit-learn. Надеюсь, вы получили достаточно информации об этой библиотеке, чтобы самостоятельно продолжить ее исследование.

Узнать подробнее о курсе.
Подробнее..

CatBoost и ML-конкурсы

15.05.2021 14:20:40 | Автор: admin

Анализ данных и базоваямодель

Вступление

Эта статья основана на данных конкурса, который компания Driven Data опубликовала для решения проблем с источниками воды в Танзании.

Информация для конкурса была получена Министерством водных ресурсов Танзании с использованием платформы с открытым исходным кодом под названием Taarifa. Танзаниясамая большая страна в Восточной Африке с населением около 60 миллионов человек. Половина населения не имеет доступа к чистой воде, а 2/3 населения страдает от плохой санитарии. В бедных домах семьям часто приходится тратить несколько часов пешком, чтобы набрать воду из водяных насосов.

Для решения проблемы с пресной воды Танзании предоставляются миллиарды долларов иностранной помощи. Однако правительство Танзании не может разрешить эту проблему по сей день. Значительная часть водяных насосов полностью вышла из строя или практически не работает, а остальные требуют капитального ремонта. Министерство водных ресурсов Танзании согласилось с Taarifa, и они запустили конкурс в надежде получить подсказки от сообщества для выполнения стоящих перед ними задач.

Данные

В данных много характеристик(фичей), связанных с водяными насосами, имеется информация относящаяся к географическим местоположениям точек с водой, организациям, которые их построили и управляют ими, а также некоторые данные о регионах, территориях местного самоуправления. Также есть информация о типах и количестве платежей.

Пункты водоснабжения разделены на исправные, нефункциональные и исправные, но нуждающиеся в ремонте. Цель конкурсапостроить модель, прогнозирующую функциональность точек водоснабжения.
Данные содержат 59400 строк и 40 столбцов. Целевая метка содержится в отдельном файле.
Показатель, используемый для этого соревнования,это classification rate, который вычисляет процент строк, в которых прогнозируемый класс совпадает с фактическим классом в тестовом наборе. Максимальное значение равно 1, а минимальное0. Цель состоит в том, чтобы максимизировать classification rate.

Анализ данных

Описания полей в таблице с данными:

  • amount_tshобщий статический напор (количество воды, доступное для точки водоснабжения)

  • date_recordedдата сбора данных

  • funderкто спонсировал постройку колодца

  • gps_heightвысота на которой находится колодец

  • installerорганизация построившая колодец

  • longitudeGPS координаты (долгота)

  • latitudeGPS координаты (широта)

  • wpt_nameназвание, если оно есть

  • num_privateнет информации

  • basinгеографический водный бассейн

  • subvillageгеографическая локация

  • regionгеографическая локация

  • region_codeгеографическая локация (код)

  • district_code географическая локация (код)

  • lgaгеографическая локация

  • ward географическая локация

  • populationколичество населения около колодца

  • public_meetingда/нет

  • recorded_by кто собрал данные

  • scheme_managementкто управляет колодцем

  • scheme_nameкто управляет колодцем

  • permitтребуется ли разрешение на доступ

  • construction_yearгод постройки

  • extraction_typeтип колодца

  • extraction_type_groupгруппа типа колодца

  • extraction_type_classкласс типа колодца

  • managementкак управляется колодец

  • management_groupгруппа управления колодца

  • paymentстоимость воды

  • payment_typeтип стоимости воды

  • water_qualityкачество воды

  • quality_groupгруппа качества воды

  • quantityколичество воды

  • quantity_groupгруппа количества воды

  • sourceисточник воды

  • source_typeтип источника воды

  • source_classкласс источника воды

  • waterpoint_typeтип колодца

  • waterpoint_type_groupгруппа типа колодца

    Прежде всего, давайте посмотрим на целевую меткуклассы не имеют равномерного распределения:

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

    • уменьшать количество примеров у преобладающих классов(under-sampling)

    • дублировать строки с метками, которых мало(over-sampling)

    • генерировать синтетические выборкиэто случайная выборка атрибутов из экземпляров в классе меньшинства (SMOTE)

    • ничего не делать и использовать возможности библиотек, которые выбраны для моделирования

    Больше про тактики работы с несбалансированными датасетами можно почитать здесь.

    Посмотрим, как водяные насосы распределяются по территории страны.

    Некоторые данные содержат пустые значения.

    Видим, что выделяется фича scheme_name, у которой огромное количество пропущенных значений, в то время как по остальным их вполне умеренное количество.

    Следующая тепловая карта представляет наличие/отсутствие связи между переменными. Стоит обратить внимание на соотношение между permit, installer и funder.

    Давайте посмотрим на общую картину взаимоотношений на дендрограмме.

    В характеристиках водяных насосов есть та, которая показывает количество воды. Мы можем проверить, как количество воды связано с состоянием насосов (quantity_group).

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

    Влияет ли качество воды на состояние водяных насосов? Мы можем посмотреть на данные, сгруппированные по quality_group.

    К сожалению, этот график не очень информативен, так как количество источников с хорошей водой преобладает. Попробуем сгруппировать только источники с менее качественной водой.

    Большинство колодцев с неизвестным качеством из quality_group не работают.

    Есть еще одна интересная характеристика водных точеких тип (waterpoint_type_group).

    Анализ данных по водным точкам показывает, что в группе с other типом много неработающих насосов. Они устарели? Мы можем проверить, как влияет год постройки насоса.

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

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

    Danidaсовместная организация Танзании и Дании по финансированию скважин, и хотя у них много работающих водозаборов, процент неисправных очень высок. Похожая ситуация с RWSSP (программа сельского водоснабжения и канализации), Dhv и некоторыми другими. Следует отметить, что большинство скважин, профинансированных Германией и частными лицами, находятся в рабочем состоянии. Напротив, большое количество скважин, которые финансируются государством, не функционируют. Большинство пунктов водоснабжения, установленных центральным правительством и районным советом, также не работают.

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

    Сильно выделяются два бассейнаРувим и озеро Руква. Количество сломанных колодцев там больше всего.

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

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

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

    Часть данных была заполнена значением 0 вместо реальных данных. Мы также можем видеть, что amount_tsh выше у исправных колодцев (label = 0). Также следует обратить внимание на выбросы в функции amount_tsh. В качестве особенности можно отметить перепад высот и тот факт, что значительная часть населения проживает на высоте 500 метров над средним уровнем моря.

Подготовка данных

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

  • Фича installer содержит множество повторов с разными регистрами, орфографическими ошибками и сокращениями. Давайте сначала переведем все в нижний регистр. Затем по простым правилам уменьшаем количество ошибок и делаем группировку.

  • После очистки мы заменяем все элементы, которые встречаются менее 71 раз (0,95 квантиля), на other.

  • Повторяем по аналогии с фичей funder. Порог отсечки98.

  • Данные содержат фичи с очень похожими категориями. Выберем только по одной из них. Поскольку в датасете не так много данных, мы оставляем функцию с наименьшим набором категорий. Удаляем следующие фичи: scheme_management, quantity_group, water_quality, payment_type, extraction_type, waterpoint_type_group, region_code.

  • Заменим latitude и longitude значения у выбросов медианным значением для соответсвующего региона из region_code.

  • Аналогично повторям для пропущенных значений для subvillage и scheme_name.

  • Пропущенные значения в public_meeting и permit заменяем медианным значением.

  • Для subvillage, public_meeting, scheme_name, permit, создаем дополнительные категориальные бинарные фичи, которы будут отмечать данные с пропущенными значениями. Так как мы заменяем их на медианные, то для модели оставим информацию, что пропуски были.

  • Фичи scheme_management, quantity_group, water_quality, region_code, payment_type, extraction_type, waterpoint_type_group, date_recorded, и recorded_by можно удалить, так как там повторяются данные из других фичей, для модели они будут бесполезны.

Модель

Данные содержат большое количество категориальных признаков. Наиболее подходящим для получения базовой модели, на мой взгляд, является CatBoost. Это высокопроизводительная библиотека для градиентного бустинга с открытым исходным кодом.

Не будем подбирать оптимальные параметры, пусть это будет домашним заданием. Напишем функцию для инициализации и обучения модели.

def fit_model(train_pool, test_pool, **kwargs):    model = CatBoostClassifier(        max_ctr_complexity=5,        task_type='CPU',        iterations=10000,        eval_metric='AUC',        od_type='Iter',        od_wait=500,        **kwargs    )return model.fit(        train_pool,        eval_set=test_pool,        verbose=1000,        plot=False,        use_best_model=True)

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

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

def classification_rate(y, y_pred):    return np.sum(y==y_pred)/len(y)

Поскольку данных мало, разбивать полную выборку на обучающую и проверочную частине лучший вариант. В этом случае лучше использовать методику OOF (Out-of-Fold). Мы не будем использовать сторонние библиотеки; давайте попробуем написать простую функцию. Обратите внимание, что разбиение набора данных на фолды необходимо стратифицировать.

def get_oof(n_folds, x_train, y, x_test, cat_features, seeds):    ntrain = x_train.shape[0]    ntest = x_test.shape[0]              oof_train = np.zeros((len(seeds), ntrain, 3))    oof_test = np.zeros((ntest, 3))    oof_test_skf = np.empty((len(seeds), n_folds, ntest, 3))    test_pool = Pool(data=x_test, cat_features=cat_features)     models = {}    for iseed, seed in enumerate(seeds):        kf = StratifiedKFold(            n_splits=n_folds,            shuffle=True,            random_state=seed)                  for i, (train_index, test_index) in enumerate(kf.split(x_train, y)):            print(f'\nSeed {seed}, Fold {i}')            x_tr = x_train.iloc[train_index, :]            y_tr = y[train_index]            x_te = x_train.iloc[test_index, :]            y_te = y[test_index]            train_pool = Pool(data=x_tr, label=y_tr, cat_features=cat_features)            valid_pool = Pool(data=x_te, label=y_te, cat_features=cat_features)model = fit_model(                train_pool, valid_pool,                loss_function='MultiClass',                random_seed=seed            )            oof_train[iseed, test_index, :] = model.predict_proba(x_te)            oof_test_skf[iseed, i, :, :] = model.predict_proba(x_test)            models[(seed, i)] = modeloof_test[:, :] = oof_test_skf.mean(axis=1).mean(axis=0)    oof_train = oof_train.mean(axis=0)    return oof_train, oof_test, models

Чтобы уменьшить зависимость от случайности разбиения, мы зададим несколько разных начальных значений для расчета предсказанийпараметр seeds.

Кривая обучения одного изфолдовКривая обучения одного изфолдов

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

Посмотрев на важность фичей в модели, мы можем убедиться в отсутствии явной утечки информации (лика).

После усреднения прогнозов:

balanced accuracy: 0.6703822994494413classification rate: 0.8198316498316498

Попробуем загрузить результаты на сайт с конкурсом и посмотрим на результат.

Учитывая, что на момент написания этой статьи результат топ-5 был только на 0,005 лучше, мы можем сказать, что базовая модель получилась хорошей.

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

balanced accuracy: 0.6549535670689709classification rate: 0.8108249158249158

Результат заметно хуже.

Итоги

В статье:

  • познакомились с данными и поискали идеи для модели;

  • очистили и подготовили данные для модели;

  • приняли решение использовать CatBoost, так как основная масса фичей категориальные;

  • написали функцию для OOF-предсказания;

  • получили отличный результат для базовой модели.

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

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

Код из статьи можно посмотреть здесь.

Подробнее..

Датасет о мобильных приложениях

25.05.2021 12:05:50 | Автор: admin

Вступление

Моя основная работа связана с мобильной рекламой, и время от времени мне приходится работать с данными о мобильных приложениях. Я решил сделать некоторые данные общедоступными для тех, кто хочет попрактиковаться в построении моделей или получить представление о данных, которые можно собрать из открытых источников. Я считаю, что открытые наборы данных всегда полезны сообществу. Сбор данных часто бывает сложной и унылой работой, и не у всех есть возможность сделать это. В этой статье я представлю датасет и, используя его, построю одну модель.

Данные

Датасет опубликован на сайте Kaggle.

DOI: 10.34740/KAGGLE/DSV/2107675.

Для 293392 приложений (наиболее популярных) собраны токены описаний и сами данные приложений, включая оригинальное описание. В наборе данных нет имен приложений; их идентифицируют уникальные идентификаторы. Перед токенизацией большинство описаний были переведены на английский язык.

В датасете 4 файла:

  • bundles_desc.csvсодержит только описания;

  • bundles_desc_tokens.csvсодержит токены и жанры;

  • bundles_prop.csv, bundles_summary.csvсодержат рпзличные характеристики приложений и даты релиза/обновления.

EDA

Прежде всего, давайте посмотрим, как данные распределяются по операционным системам.

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

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

histnorm ='probability' # type of normalization

Следующий график показывает, что большинство приложений обновляются регулярно.

Основные данные были собраны за короткий период времени в январе 2021 года.

Добавим новую фичу - количество месяцев между датой выпуска и последним обновлением.

df['bundle_update_period'] = \    (pd.to_datetime(        df['bundle_updated_at'], utc=True).dt.tz_convert(None).dt.to_period('M').astype('int') -      df['bundle_released_at'].dt.to_period('M').astype('int'))у

Интересно посмотреть, как распределены жанры приложений. Принимая во внимание дисбаланс ОС, я нормализую данные для гистограммы.

Мы видим, что жанры полностью не пересекаются. Особенно это заметно в играх. Для анализа такая ситуация крайне неприятна. Что мы можем с этим поделать? Самое очевидное - уменьшить количество жанров для Android и привести их к тому же виду, что и для iOS путем сведения всех игровых жанров к одному Games. Но я полагаю, что это не лучший вариант, так как будет потеря информации. Попробуем решить обратную задачу. Для этого нужно построить модель, которая будет предсказывать жанры приложений по их описанию.

Модель

Я создал несколько дополнительных фичей, используя длину описания и количество токенов.

def get_lengths(df, columns=['tokens', 'description']):    lengths_df = pd.DataFrame()    for i, c in enumerate(columns):        lengths_df[f"{c}_len"] = df[c].apply(len)        if i > 0:            lengths_df[f"{c}_div"] = \                lengths_df.iloc[:, i-1] / lengths_df.iloc[:, i]            lengths_df[f"{c}_diff"] = \                lengths_df.iloc[:, i-1] - lengths_df.iloc[:, i]    return lengths_dfdf = pd.concat([df, get_lengths(df)], axis=1, sort=False, copy=False)

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

Для обучения используются данные Android-приложений.

android_df = df[df['store_os']=='android']ios_df = df[df['store_os']=='ios']

Окончательный список фичей модели выглядит следующим образом:

columns = [    'genre', 'tokens', 'bundle_update_period', 'tokens_len',    'description_len', 'description_div', 'description_diff',    'description', 'rating', 'reviews', 'score',    'released_at_month']

Я разделил данные на две части - train и validation. Обратите внимание, что разделение должно быть стратифицировано.

train_df, test_df = train_test_split(    android_df[columns], train_size=0.7, random_state=0, stratify=android_df['genre'])y_train, X_train = train_df['genre'], train_df.drop(['genre'], axis=1)y_test, X_test = test_df['genre'], test_df.drop(['genre'], axis=1)

В качестве библиотеки для модели я выбрал CatBoost. CatBoost - это высокопроизводительная библиотека для градиентного бустинга на деревьях решений с открытым исходным кодом. Основным преимуществом является то, что CatBoost может использовать категориальные и текстовые фичи без дополнительной предварительной обработки. Текстовые фичи для классификации поддерживаются начиная с версии 0.19.1

В Нетрадиционный анализ тональности текста: BERT vsCatBoost я привожу пример того, как CatBoost работает с текстом и сравниваю его с BERT.

!pip install -U catboost

При работе с CatBoost рекомендую использовать Pool. Это удобная оболочка, объединяющая метки и другие метаданные, такие как категориальные и текстовые фичи. Бонусом идет снижение затрат памяти, так как не происходит дополнительная конвертация внутри библиотеки.

train_pool = Pool(    data=X_train,     label=y_train,    text_features=['tokens', 'description'])test_pool = Pool(    data=X_test,     label=y_test,     text_features=['tokens', 'description'])

Напишем функцию для инициализации и обучения модели. Я не подбирал оптимальные параметры; пусть это будет еще одним домашним заданием.

def fit_model(train_pool, test_pool, **kwargs):    model = CatBoostClassifier(        random_seed=0,        task_type='GPU',        iterations=10000,        learning_rate=0.1,        eval_metric='Accuracy',        od_type='Iter',        od_wait=500,        **kwargs    )return model.fit(        train_pool,        eval_set=test_pool,        verbose=1000,        plot=True,        use_best_model=True    )

Текстовые фичи используются для создания новых числовых фичей. Но для этого необходимо объяснить CatBoost, что именно мы хотим от него получить.

CatBoostClassifier имеет несколько параметров:

  • tokenizersиспользуемые для предварительной обработки фичей текстового типа перед созданием словаря;

  • dictionariesиспользуется для предварительной обработки фичей текстового типа;

  • feature_calcersиспользуется для расчета новых фичей;

  • text_processingобщий JSON-конфиг для токенизаторов, словарей и вычислителей, который определяет, как текстовые фичи преобразуются в фичи с плавающей точкой.

Четвертый параметр заменяет первые три и, на мой взгляд, самый удобный, так как в одном указывается, как работать с текстом.

tpo = {    'tokenizers': [        {            'tokenizer_id': 'Sense',            'separator_type': 'BySense',        }    ],    'dictionaries': [        {            'dictionary_id': 'Word',            'token_level_type': 'Word',            'occurrence_lower_bound': '10'        },        {            'dictionary_id': 'Bigram',            'token_level_type': 'Word',            'gram_order': '2',            'occurrence_lower_bound': '10'        },        {            'dictionary_id': 'Trigram',            'token_level_type': 'Word',            'gram_order': '3',            'occurrence_lower_bound': '10'        },    ],    'feature_processing': {        '0': [            {                'tokenizers_names': ['Sense'],                'dictionaries_names': ['Word'],                'feature_calcers': ['BoW']            },            {                'tokenizers_names': ['Sense'],                'dictionaries_names': ['Bigram', 'Trigram'],                'feature_calcers': ['BoW']            },        ],        '1': [            {                'tokenizers_names': ['Sense'],                'dictionaries_names': ['Word'],                'feature_calcers': ['BoW', 'BM25']            },            {                'tokenizers_names': ['Sense'],                'dictionaries_names': ['Bigram', 'Trigram'],                'feature_calcers': ['BoW']            },        ]    }}

Запустим обучение:

model_catboost = fit_model(    train_pool, test_pool,    text_processing = tpo)
AccuracyAccuracyLossLoss
bestTest = 0.6454657601

Только две фичи имеют большое влияние на модель. Скорее всего, качество можно повысить за счет использования summary, но, поскольку этих данных нет в приложениях iOS, быстро применить не удастся. Можно использовать модель, которая может получить короткий абзац текста из описания. Я оставлю это задание в качестве домашнего задания читателям.

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

Чтобы получить такой вектор, нам нужно усложнить процесс, используя предсказания OOF (Out-of-Fold). Не будем использовать сторонние библиотеки; попробуем написать простую функцию.

def get_oof(n_folds, x_train, y, x_test, text_features, seeds):        ntrain = x_train.shape[0]    ntest = x_test.shape[0]              oof_train = np.zeros((len(seeds), ntrain, 48))    oof_test = np.zeros((ntest, 48))    oof_test_skf = np.empty((len(seeds), n_folds, ntest, 48))    test_pool = Pool(data=x_test, text_features=text_features)     models = {}    for iseed, seed in enumerate(seeds):        kf = StratifiedKFold(            n_splits=n_folds,            shuffle=True,            random_state=seed)                  for i, (tr_i, t_i) in enumerate(kf.split(x_train, y)):            print(f'\nSeed {seed}, Fold {i}')            x_tr = x_train.iloc[tr_i, :]            y_tr = y[tr_i]            x_te = x_train.iloc[t_i, :]            y_te = y[t_i]            train_pool = Pool(                data=x_tr, label=y_tr, text_features=text_features)            valid_pool = Pool(                data=x_te, label=y_te, text_features=text_features)            model = fit_model(                train_pool, valid_pool,                random_seed=seed,                text_processing = tpo            )            x_te_pool = Pool(                data=x_te, text_features=text_features)            oof_train[iseed, t_i, :] = \                model.predict_proba(x_te_pool)            oof_test_skf[iseed, i, :, :] = \                model.predict_proba(test_pool)            models[(seed, i)] = model    oof_test[:, :] = oof_test_skf.mean(axis=1).mean(axis=0)    oof_train = oof_train.mean(axis=0)    return oof_train, oof_test, models

Обучение трудозатратно, но в результате получили:

  • oof_trainOOF-предсказания для Android приложений

  • oof_testOOF-предсказания для iOS приложений

  • modelsall OOF-модели для каждого фолда и сида

from sklearn.metrics import accuracy_scoreaccuracy_score(    android_df['genre'].values,    np.take(models[(0,0)].classes_, oof_train.argmax(axis=1)))

За счет фолдов и усреднения по нескольким случайным разбиениям качество немного улучшилось.

OOF accuracy: 0.6560790777135628

Я созданную фичу android_genre_vec, копируем значения из oof_train для приложений Android и oof_test для приложений iOS.

idx = df[df['store_os']=='ios'].indexdf.loc[df['store_os']=='ios', 'android_genre_vec'] = \    pd.Series(list(oof_test), index=idx)idx = df[df['store_os']=='android'].indexdf.loc[df['store_os']=='android', 'android_genre_vec'] = \    pd.Series(list(oof_train), index=idx)

Дополнительно был добавлен android_genre, в котором находится жанр с максимальной вероятностью.

df.loc[df['store_os']=='ios', 'android_genre'] = \    np.take(models[(0,0)].classes_, oof_test.argmax(axis=1))df.loc[df['store_os']=='android', 'android_genre'] = \    np.take(models[(0,0)].classes_, oof_train.argmax(axis=1))

После всех манипуляций, можно наконец-то посмотреть и сравнить распределение приложений по жанрам.

Итоги

В статье:

  • представлен новый бесплатный датасет;

  • сделан небольшой EDA;

  • созданы несколько новых фичей;

  • создана модель для предсказания жанров приложений по описаниям.

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

Код из статьи можно посмотреть здесь.

Подробнее..

Несколько мыслей про ранжирование

06.06.2021 08:10:54 | Автор: admin
Случайный лес

1. Вступление


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


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


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


2. Первый взгляд


Скажите, пожалуйста, а сколько всего может быть способов перестановки результатов ранжирования? Для массивов из двух элементов интуиция легко поможет найти правильный ответ: [Первый, Второй] и [Второй, Первый]. А для массива из 30 элементов? Применим формулу комбинаторики: n! (факториал мощности множества). Для множества из 30 элементов количество возможных перестановок будет равно 265252859812191058636308480000000. Даже для рейтинга ТОП-10 есть 3628800 вариантов перестановки.


Представим, что порядок элементов задаётся случайно. Какая вероятность появления нужного элемента на первом месте? Пусть всего 10 элементов. Нас интересует один конкретный элемент. Порядок всех остальных уже не имеет значения. По сути, это аналогично случайному выбору из мешка синего шарика, где 9 красных и 1 синий шарик. Тогда вероятность составляет 1/10. Другими словами, при многократном повторении эксперимента примерно в 10% случаев мы увидим этот элемент на первом месте.


Кстати, а с какой вероятностью один и тот же элемент окажется на первом месте два раза подряд? Запуски алгоритмов мы считаем независимыми событиями, так как один запуск никак не влияет на другой. Следовательно, это произведение вероятностей независимых событий: 1/10 * 1/10. Это касается и всех последующих попыток, но тут красивее возводить вероятность в соответствующую степень.


Я дважды упоминал про случайную сортировку. А вдруг там другой алгоритм ранжирования? Можно ли вычислить формулу ранжирования, если код закрыт? Оказывается, что при определённых условиях это возможно. Нам потребуется многократно проводить эксперимент: передавать аргументы функции (факторы ранжирования) и фиксировать её ответ. Предлагаю посмотреть на пример с линейной регрессией.


3. Открываем чёрный ящик


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


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


Попробуем преобразовать большой массив в специальную структуру данных, которая содержит описательную статистику (среднеквадратическое отклонение, максимум, минимум, среднее, медиана, нижний и верхний квартиль). Но не будем торопиться с таким решением. Есть даже известный набор данных (квартет Энскомба), который хорошо иллюстрирует эту проблему. Очень советую про него прочитать.


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


Это гистограмма распределения

В итоге мы видим распределение случайной величины. Регулируя число интервалов можно добиться адекватного представления. Теперь нам нужен способ увидеть взаимосвязь этой случайной величины с другой. Отобразим наблюдения в виде точек на плоскости, где по оси X будет score, а по Y наш единственный предиктор (признак). И вот перед вами появляется график:


Взаимосвязь

На нём показано, что обе переменных сильно коррелируют между собой, следовательно, делаем предположение о линейной зависимости (score=a+bx). Не все точки выстроились на одну линию, что говорит нам о небольшом влиянии неизвестного фактора. Это я специально сделал, для красоты.


Подобрать коэффициенты в нашем случае труда не составит. Так, например, класс LinearRegression (из scikit-learn) после обучения (fit) позволяет получить смещение (intercept) и массив коэффициентов (coef). Подставляем значения в формулу и проверяем результат с помощью метрик mean_absolute_error и median_absolute_error. Собственно, это и есть решение нашей задачи. В случае множества признаков суть не меняется: под капотом всё устроено аналогичным образом (смещение + скалярное произведение вектора признаков и соответствующих коэффициентов).


Предупреждение о подводных камнях: обязательно внимательно изучайте данные, а при необходимости выполняйте подготовку, включая масштабирование и преобразование категориальных признаков (one-hot). Кроме этого, увеличение количества признаков сильно затрудняет визуальный анализ даже специальные алгоритмы уменьшения размерности (PCA, Isomap, TSNE) не всегда помогут отобразить информацию в удобном для человека виде.


4. Простая формула ранжирования


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


Или придумать формулу только для определённого типа задач? Уверен, что вы сталкивались с полнотекстовом поиском в реляционных базах данных (PostgreSQL, MySQL, SQLite) или в специальных системах (Elasticsearch, Sphinx, Solr). Например, в Elasticsearch формула ранжирования видна в режиме explain, а явно задать её в запросе можно через script_score. Не исключено, что вы сами пробовали написать BM25 (TF-IDF-подобный алгоритм).


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


Мы уже говорили про линейную регрессию. Действительно, это вполне универсальный алгоритм. Несмотря на всю простоту его применяют в серьёзных проектах. Благодаря своей скорости работы это замечательное решение в тех задачах, где критически важна производительность. Линейные алгоритмы могут быть частью иерархических формул ранжирования ненужные объекты отбрасываются простыми моделями, чтобы не расходовать драгоценные ресурсы действительно сложных алгоритмов.


Ярким примером фильтра выступает логистическая регрессия. Это быстрая и лёгкая в реализации модель классификации. Под капотом это обычная линейная регрессия, только результат её работы передают в сигмоиду. Далее выполняется проверка если результат больше 0.5, то класс 1, иначе класс 0. По сути, это формула гиперплоскости, разделяющей классы. На вид это простые алгоритмы, но их применяют даже в сложных задачах классификации текстов. Разумеется, тексты предварительно подготавливаются и преобразуются в вектор признаков (примеры алгоритмов: CountVectorizer, TfidfVectorizer, Word2Vec).


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


5. Более сложное ранжирование


Сложными задачами ранжирования будем называть такие, которые нельзя решить написанием хорошего запроса на SQL или доработкой компонента для подсчёта рейтинга. Другими словами, нет конкретной формулы или алгоритма. Самый банальный пример: как бы разработчик не старался, но простая функция не сможет классифицировать фотографии наравне с ResNet50 из Keras. Даже для создания хорошего алгоритма учёта поведенческих факторов может потребоваться использовать K-means для кластеризации контента и метрик поведения пользователей.


По этой причине разработчику пригодятся знания алгоритмов машинного обучения. В современных проектах это нужно всё чаще. Обратите внимание, что мы сейчас не говорим про тонкости настройки или проверки алгоритмов машинного обучения. Это явно выходит за пределы моей заметки. Речь о том, что алгоритмы машинного обучения сами могут создать формулу ранжирования, при условии, что вы располагаете подходящим размеченном набором данных. Теперь задумаемся на тем, как задействовать сильную модель в своём проекте.


Идея состоит в том, чтобы получить возможность быстро добавлять готовые модели в любой проект. Без подключения дополнительных зависимостей. И проекты могут быть на разных языках программирования. Или это вообще специальные устройства. Когда мы говорили про линейные модели, то вы могли заметить, что такие алгоритмы очень легко добавить в любой проект. Достаточно просто скопировать массив с коэффициентами, который будет использован в соответствующем методе. А вот с комитетами решающих деревьев не всё так просто. В качестве конкретных реализаций будут выступать Catboost и Random forest.


Хочу вам предложить одно из решений, которое мне давилось делать для переноса модели на специальные устройства. Идея следующая: получим из обученной и проверенной модели её деревья, а в циклах (по шаблонам) сформируем исходный код. Собственно, код генерируется по шаблонам (получается простыня условий) и добавляется в проект в виде отдельного файла на нужном языке программирования.


Так, например, в случае Random forest мы получим много относительно глубоких деревьев. Каждое дерево будет получать на вход набор данных (отдельные деревья могут работать только со своими признаками) и возвращать тот результат, который предскажет его лист (терминальный узел). В итоге будет массив предсказаний, где каждый элемент является ответом дерева. В задачах регрессии следует вычислить среднее значение, а для классификации тот класс, за который проголосовало большинство деревьев (либо распределение голосов, которое можно рассматривать как вероятность принадлежности к классу).


Повторюсь, этот подход использовался для специальных устройств. Однако, в ряде случаев так можно делать для любых проектов не будет лишних зависимостей и производительность может быть выше, а так же есть возможность самому более тонко настраивать работу деревьев. Если с Random forest такое упражнение доводилось делать для реальных проектов, то с Catboost я просто побаловался в субботу на своём домашнем компьютере.


У ранее упомянутого Catboost есть возможность сохранения модели в формате JSON. В массиве oblivious_trees будут решающие деревья (обычно пни зависит от гиперпараметров), которые содержат индексы признаков (split_index) и границу разделения (border). Схема экспорта модели аналогичная, только специфика работы деревьев отличается, а именно, они друг за другом последовательно снижают энтропию.


6. Выводы


Лучше использовать принцип KISS, однако, если задача требует сложного решения, то можно вспомнить про предложенные подходы. При этом, всегда следует осознавать смысл (финальную цель) и учитывать соотношение затрат, рисков и ожидаемой пользы.


7. Послесловие


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

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru