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

Kaggle

Перевод Дата-сайентист, который просто не может перестать выигрывать на Kaggle

24.01.2021 16:23:06 | Автор: admin
Ранее у нас в блоге уже был материал про лучших в Kaggle, а сегодня представляю вам интервью с признанным дата-сайентистом и гроссмейстером Kaggle Филиппом Сингером, который поделится своим опытом, вдохновением и и достижениями. Беседа призвана мотивировать и воодушевить других людей, которые хотят понять, что нужно, чтобы стать гроссмейстером Kaggle. Также в этом интервью мы узнаем больше об академическом прошлом Филиппа, его увлечении Kaggle и о его работе в качестве дата-сайентиста.




Филипп, в мире Kaggle более известный как Psi, стал кандидатом наук, получил диплом с отличием в области информатики в Техническом университете Граца, а также получил степень магистра в области разработки программного обеспечения и управления бизнесом. У Филиппа несколько достижений, в том числе многократные победы и высшие места на Kaggle, несколько научных наград, в том числе за лучшую работу на знаменитой Всемирной веб-конференции.

В настоящее время он занимает третье место в мире в рейтинге соревнований Kaggle. Это одновременно впечатляет и вдохновляет. Одно из самых заметных достижений Филиппа победа на Втором ежегодном турнире по большим данным NFL вместе с другим дата-сайентистом H2O.ai Дмитрием Гордеевым.

Более 2000 дата-сайентистов со всего мира соревновались на Kaggle, чтобы спрогнозировать результаты стремительной игры. Филипп Сингер и Дмитрий Гордеев получили главный приз 50 000 долларов США за их подход к задаче.


Победители конкурса Big Data Bowl 20192020 Филипп Сингер и Дмитрий Гордеев (сзади) выступают в Индианаполисе.

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


Филипп: Я получил степень доктора философии [кандидата наук] компьютерных наук в Техническом университете Граца в Австрии и был занят в исследованиях в Германии. За время своей научной карьеры я затронул множество разных тем в области науки о данных и опубликовал множество научных работ и статей на известных конференциях и в журналах. После я должен был стать профессором, это интриговало меня. Однако, хотя я люблю преподавать, я хотел углубиться в более прикладную работу, то есть хотел, чтобы моя работа имела большее влияние, чем то влияние, которое возможно в исследованиях. Это побудило меня заняться наукой о данных в качестве карьеры. Надо сказать, я до конца насладился докторской степенью и многому научился, но теперь я также рад быть в авангарде науки о данных и машинного обучения, играть по-настоящему важную роль в H2O.ai.

Как начался ваш путь на Kaggle, что поддерживало вас на пути к гроссмейстерству?



Профиль Филиппа на Kaggle

Филипп: Я зарегистрировался на Kaggle около восьми лет назад, когда начал работу в качестве доктора философии, потому что слышал о платформе и хотел её проверить. Но я сделал всего лишь образец задачи и шесть лет больше не прикасался к Kaggle. Около двух лет назад мы с Дмитрием (dott1718 на Kaggle, а теперь коллегой по работе) решили вместе попробовать посоревноваться на Kaggle, это был сайд-проект на работе. Мы ничего не ждали, но в итоге выиграли соревнование. Победа зацепила меня, с этого момента начался мой путь на Kaggle. На Kaggle мой подход всегда заключался в том, чтобы решать новые типы проблем, так я сохраняю мотивацию. Есть ещё новые интересные проблемы, которые нужно решать правильно. Кроме того, мне нравится встречаться и работать с талантливыми людьми на Kaggle, и я вижу, к чему стремится сообщество.

В последнее время вы уничтожили таблицу лидеров Kaggle, добились впечатляющих результатов, последний из которых первое место на NFL и второе в Future Impact Detection. Как вы подходите к решению таких проблем, как идёте так хорошо?


Филипп: Люди часто спрашивают меня, как выиграть соревнования Kaggle; я не думаю, что есть какой-то секретный соус, чтобы побеждать везде. Большой успех на Kaggle основывается на опыте, желании прикоснуться к чему-то новому, о чём, на первый взгляд, вы мало что знаете. Со временем я собрал особый универсальный набор инструментов, который содержит строительные блоки каждого соревнования, в котором я участвовал. Например, я понимаю, как правильно настроить кросс-валидацию, какие библиотеки задействовать в моделях, как правильно подбирать модели, отслеживать их производительность и т. п. Так что у меня освобождается больше времени, чтобы сосредоточиться на новых для меня и важных аспектах недавних соревнований. после каждого соревнования я всегда стараюсь улучшить свой рабочий процесс, чтобы повысить эффективность и способность конкурировать с соперниками.

Большой успех на Kaggle основывается на опыте, желании прикоснуться к чему-то новому, о чём, на первый взгляд, вы мало что знаете.

Как вы решаете, в каких соревнованиях участвовать?




Лучшие достижения Филиппа на Kaggle

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

Как вы обычно подходите к задаче на Kaggle? Есть ли какие-нибудь любимые ресурсы по ML (массовые открытые онлайн-курсы, блоги и т. д.), которыми вы хотите поделиться с сообществом?


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

В каких конкретных областях вы работаете как дата-сайентист в H2O.ai?



Филипп и другие гроссмейстеры Kaggle на H2O.ai

Филипп: Моя роль в H2O.ai очень многогранна. Я регулярно участвую в проектах, ориентированных на клиентов, и моя задача опираясь на опыт, поддерживать проекты в области Data Science. Кроме того, будучи гроссмейстерами Kaggle, мы всегда стараемся использовать наш опыт и знания о последних достижениях, чтобы постоянно улучшать наши продукты и разрабатывать новые передовые прототипы и решения. Это означает, например, что мы предлагаем новые функции в Driverless AI, разрабатываем приложения ИИ в Wave , демонстрируя новые методы и весь конвейер решений Data Science.

Расскажите о лучшем из того, что вы узнали на Kaggle и применяете в работе на H2O.ai?


Филипп: На Kaggle вы узнаете, как создавать надёжные модели, которые хорошо обобщаются и не слишком подвержены переобучению. На Kaggle это важно, потому что вам нужно хорошо работать с невидимыми, закрытыми данными. Это означает, что вы многое узнаете о надёжной перекрёстной проверке и о других аспектах данных, таких как сдвиги в распределении функций, или некоторые других важных аспектах. Я могу хорошо приспособить эти знания в своей работе на H2O.ai, поскольку они также являются неотъемлемой частью наших продуктов. Опираясь на наш опыт и знания, мы хотим, чтобы ML наших клиентов было надёжным.

Область Data Science стремительно развивается. Как вам удается быть в курсе всех последних событий?


Филипп: Чтобы быть в курсе последних событий, я в основном использую Kaggle; это отличный фильтр новых методов, которые либо работают с практическими и прикладными проблемами, либо не работают. Обычно надёжные методы выживают, а ненадёжные методы, которые работают лишь от случая к случаю, отфильтровываются. В то же время я слежу за известными исследователями и практиками в Твиттере и на других платформах.

Есть ли какие-то конкретные области или проблемы, в которых вы хотели бы применить свой опыт в области машинного обучения?




Филипп выступает на встрече Vienna Data Science Group 9 января 2020 г.

Филипп: Я не держу в голове ничего конкретного; обычно я стараюсь удивляться интересным проблемам, которые возникают либо на работе, либо в Kaggle. Очень важно вникать в проблемы, которые на первый взгляд не кажутся вам интересными. Можно объективно взглянуть на проблему и, вероятно, обратиться к опыту, который вы приобрели решая другие проблемы, к тем данным, что у вас есть.

Несколько советов претендентам в Data Science и Kaggle, которые только начали или хотят начать свой путь в Data Science.


Филипп: Запачкайте руки, не бойтесь потерпеть неудачу и всегда стремитесь узнавать новое.

Путь Филиппа на Kaggle был весьма примечательным. Я уверен, что его путь, преданность делу и достижения станут источником вдохновения для тех, кто уже работает или пытается сделать карьеру в Data Science.



image



Подробнее..

Введение Соревнование от финансовой группы HOME CREDIT по определеню риска дефолта заемщика

13.01.2021 10:12:44 | Автор: admin

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

Это стандартная задача классификации с учителем:

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

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

ДАННЕ

Данные предоставлены финансовой группой HomeCredit, предлагающей кредитные линии (ссуды) населению, не охваченному банковскими услугами. Всего имеется 7 различных источников данных:

  • applicationtrain / applicationtest: основные данные для обучения и тестирования с информацией о каждой кредитной заявке в HomeCredit. Каждая ссуда представлена отдельной строкой, в которой признакSKIDCURRявляется уникальным идентификатором. Данные обучающей выборки имеют меткуTARGETсо значениями:

    • 0, если ссуда была возвращена;

    • 1, если ссуда не была погашена.

  • bureau: данные о предыдущих кредитах клиента в других финансовых учреждениях. Каждый предыдущий кредит в этом файле представлен отдельной строкой, при этом для каждой записи в обучающей выборке может иметься несколько записей о предыдущих кредитах.

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

  • previousapplication: предыдущие заявки на получение ссуд в HomeCredit для клиентов, данные которых имеются в обучающей выборке. Каждая текущая ссуда в обучающей выборке может иметь несколько предыдущих ссуд, каждая из которых представлена в файле одной строкой и идентифицируется признакомSKIDPREV.

  • POSCASHBALANCE: исторические ежемесячные данные о покупках и выдаче наличных денег для клиентов, обслуживающихся в HomeCredit. Каждая строка представляет собой данные за один месяц, при этом каждая предыдущая ссуда может иметь несколько строк в данном файле.

  • creditcardbalance: ежемесячные данные о предыдущих кредитных картах, которые клиенты получили в HomeCredit. Каждая строка представляет собой информацию о балансе кредитной карты за один месяц. При этом одна кредитная карта может быть представлена несколькими строками.

  • installments_payment: история платежей по предыдущим кредитам в HomeCredit, содержит по одной строке для каждого совершенного и пропущенного платежа.

Эта диаграмма показывает, как данные связаны между собой:

Кроме того, в рамках соревнования предоставлены описания всех признаков (в файлеHomeCredit_columns_description.csv) и пример результирующего файла с предсказанными ответами.

В рамках данной статьи я буду использовать только основные данные для обучения и тестирования (application_train / application_test), который должны быть более понятными. Это позволит установить базовый уровень, который можно постепенно улучшать. В подобных соревнованиях лучше постепенно осмысливать проблему, чем сразу полностью погрузиться в нее и запутаться! Однако если вы хотите иметь хоть какую-то надежду на серьезный результат, в дальнейшем нужно будет использовать все данные.

МЕТРИКА: ROC AUC

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

Метрика ROC AUC может показаться пугающей, но она относительно проста, если разобраться с двумя отдельными концепциями.

Кривая ошибок (ROC)отображает соотношение между долей объектов от общего количества носителей признака, верно классифицированных как несущие признак, и долей объектов от общего количества объектов, не несущих признака, ошибочно классифицированных как несущие признак:

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

Площадь под кривой (AUC)уже содержит объяснение в своем названии. Это просто область под кривой ROC (интеграл кривой). Этот показатель находится в диапазоне от 0 до 1, при этом лучшая модель получает более высокий балл. Модель, которая просто угадывает результат случайным образом, будет иметь ROC AUC = 0,5.

Когда мы оцениваем классификатор в соответствии с метрикой ROCAUC, мы генерируем не точные прогнозы 0 или 1, а скорее вероятность от 0 до 1. Это может сбивать с толку, потому что обычно мы предпочитаем думать с точки зрения точности, однако, когда мы сталкиваемся с проблемой несбалансированных классов (далее мы увидим, что это так) точность не лучшая метрика. Например, если бы я хотел построить модель, которая могла бы обнаруживать террористов с точностью 99,9999%, я бы просто сделал модель, предсказывающую, что каждый человек не является террористом. Ясно, что это будет неэффективно (полнота будет равна нулю), поэтому мы будем использовать более сложные показатели, такие как ROCAUC или оценка F1, чтобы более точно отразить эффективность классификатора. Модель с высоким значением ROCAUC также будет иметь высокую точность, но кроме этого ROCAUC лучше отражает и другие характеристики модели.

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

ИМПОРТ

Я буду использовать типичный стек библиотек для работы с данными:numpyиpandasдля манипулирования данными,sklearnpreprocessingдля работы с категориальными переменными,matplotlibиC11CC12CC13Cдля построения графиков и диаграмм. Также импортирую дополнительные модули для упрощения работы.

import osimport numpy as npimport pandas as pdpd.set_option('display.max_columns', None)from sklearn.preprocessing import LabelEncoderimport matplotlib.pyplot as pltimport seaborn as sns# Подавление предупрежденийimport warningswarnings.filterwarnings('ignore')

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

# Список доступных файловprint(os.listdir("../input/"))

POSCASHbalance.csv, bureaubalance.csv, applicationtrain.csv, previousapplication.csv, installmentspayments.csv, creditcardbalance.csv, samplesubmission.csv, applicationtest.csv, bureau.csv]

# Тренировочные данныеapp_train = pd.read_csv('../input/application_train.csv')print('Training data shape: ', app_train.shape)app_train.head()

Training data shape: (307511, 122)

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

# Тестовые данныеapp_test = pd.read_csv('../input/application_test.csv')print('Testing data shape: ', app_test.shape)app_test.head()

Testing data shape: (48744, 121)

Тестовых данных значительно меньше, и в них отсутствует столбец TARGET.

ИССЛЕДОВАТЕЛЬСКИЙ АНАЛИЗ ДАННХ (EXPLORATORY DATA ANALYSIS EDA)

Исследовательский анализ данных (EDA) это открытый процесс, в ходе которого необходимо рассчитывать статистики и строить графики, чтобы найти тенденции, аномалии, закономерности или взаимосвязи в данных. Цель EDA узнать, что могут нам сказать наши данные. Обычно он начинается с общего обзора, а затем сужается к конкретным областям, когда мы находим интересные области данных. Результаты могут быть интересны сами по себе, или их можно использовать для принятия решения о выборе модели, например, помогая нам решить, какие признаки использовать.

ИЗУЧИМ РАСПРЕДЕЛЕНИЕ ЦЕЛЕВОЙ МЕТКИ

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

app_train['TARGET'].value_counts()
app_train['TARGET'].astype(int).plot.hist();

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

ПРОВЕРИМ ПРОПУЩЕННЕ ЗНАЧЕНИЯ

Теперь мы можем посмотреть количество и процент пропущенных значений в каждом столбце.

# Функция для расчета пропущенных значений в столбцах датафреймаdef missing_values_table(df):        # Общее количество пропущенных значений        mis_val = df.isnull().sum()        # Доля пропущенных значений        mis_val_percent = 100 * df.isnull().sum() / len(df)        # Таблица с результатом расчета        mis_val_table = pd.concat([mis_val, mis_val_percent], axis=1)        # Переименовываем столбцы        mis_val_table_ren_columns = mis_val_table.rename(        columns = {0 : 'Missing Values', 1 : '% of Total Values'})        # Сортируем по столбцу с долей пропущенных значений в порядке убывания        mis_val_table_ren_columns = mis_val_table_ren_columns[            mis_val_table_ren_columns.iloc[:,1] != 0].sort_values(        '% of Total Values', ascending=False).round(1)        # Вывод сводной информации        print("Your selected dataframe has " + str(df.shape[1]) + " columns.\n"                  "There are " + str(mis_val_table_ren_columns.shape[0]) +              " columns that have missing values.")        return mis_val_table_ren_columns# Статистика пропущенных значенийmissing_values = missing_values_table(app_train)missing_values.head(10)

Your selected dataframe has 122 columns.

There are 67 columns that have missing values.

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

ТИП ДАННХ ПРИЗНАКОВ

Настало время посмотреть на количество признаков каждого типа данных.int64иfloat64 числовые переменные (которые могут быть дискретными или непрерывными). Признаки с типом данныхobjectсодержат строки и являютсякатегориальными признаками.

# Количество признаков каждого типа данныхapp_train.dtypes.value_counts()

Теперь узнаем количество уникальных записей в каждом из столбцов типаobject(категориальных).

# Количество уникальных значений в каждом из категориальных признаковapp_train.select_dtypes('object').apply(pd.Series.nunique, axis = 0)

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

КОДИРОВАНИЕ КАТЕГОРИАЛЬНХ ПЕРЕМЕННХ

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

Кодирование метки (Label encoding): назначаем каждой уникальной категории в категориальной переменной целое число. Новые столбцы не создаются. Пример показан ниже:

Однопроходное кодирование (One-hot encoding): создаем новый столбец для каждой уникальной категории в категориальной переменной. Каждое наблюдение получает 1 в столбце для соответствующей категории и 0 во всех других новых столбцах.

Проблема с кодированием меток состоит в том, что оно дает категориям произвольный порядок. Значение, присвоенное каждой из категорий, является случайным и не отражает каких-либо неотъемлемых аспектов категории. В приведенном выше примере программист получает метку 4, а специалист по данным 1, но если бы мы повторили тот же процесс снова, метки могли бы быть перевернутыми или совершенно другими. Фактическое присвоение целых чисел произвольно. Следовательно, когда мы выполняем кодирование меток, модель может использовать относительное значение функции (например, программист = 4 и специалист по данным = 1) для присвоения весов, а это совсем не то, чего мы хотим. Если у нас есть только два уникальных значения для категориальной переменной (например, мужчина / женщина), тогда кодирование метки подойдет, но для более чем двух уникальных категорий безопасным вариантом является однопроходное кодирование.

Об относительных достоинствах этих подходов ведутся споры, и некоторые модели могут без проблем работать с категориальными переменными, закодированными метками. Один из участников рассматриваемого мной соревнования Kaggle-masterWill Koehrsen, считает, что для категориальных переменных с множеством классов наиболее безопасным подходом является однопроходное кодирование, поскольку оно не навязывает произвольные значения категориям. И в этом с ним согласны многие специалисты в области машинного обучения и анализа данных. Единственным недостатком этого метода является то, что количество признаков (измерений данных) может увеличиваться из-за категориальных переменных со многими категориями. Чтобы справиться с этим, можно выполнить однопроходное кодирование с последующим применением методаPCAили другимиметодами уменьшения размерности, чтобы уменьшить количество измерений (при этом, все еще пытаясь сохранить полезную информацию).

В своем примере я буду использовать LabelEncoding для любых категориальных переменных только с 2 категориями и One-HotEncoding для любых категориальных переменных с более чем 2 категориями. Возможно, потребуется изменить этот подход по мере того, как мы углубимся в проект, но пока можно посмотреть, к чему это приведет. Я также не буду использовать какие-либо методы уменьшение размерности.

LABEL ENCODING И ONE-HOT ENCODING

Реализуем описанные выше методы: для любой категориальной переменной (dtype == object) с двумя уникальными категориями я буду использовать кодирование меток, а для любой категориальной переменной с более чем двумя уникальными категориями однопроходное кодирование.

Для кодирования меток воспользуюсь методом LabelEncoder из библиотеки Scikit-Learn, а для однопроходного кодирования функцией pandas get_dummies(df).

# Создаем объект label encoderle = LabelEncoder()le_count = 0# Проходим по всем столбцамfor col in app_train:    if app_train[col].dtype == 'object':        # Если признак имеет 2 или менее уникальных значения        if len(list(app_train[col].unique())) <= 2:            # Обучаем LabelEncoder на тренировочных данных            le.fit(app_train[col])            # Трансформируем обучающий и тестовый датафреймы            app_train[col] = le.transform(app_train[col])            app_test[col] = le.transform(app_test[col])                        # Подсчитываем, сколько признаков обработано методом LabelEncoder            le_count += 1print('%d columns were label encoded.' % le_count)

3 columns were label encoded.

# Применяем one-hot encoding к категориальным признакамapp_train = pd.get_dummies(app_train)app_test = pd.get_dummies(app_test)print('Training Features shape: ', app_train.shape)print('Testing Features shape: ', app_test.shape)

raining Features shape: (307511, 243)

Testing Features shape: (48744, 239).

ВРАВНИВАНИЕ ДАННХ ОБУЧЕНИЯ И ТЕСТИРОВАНИЯ

В данных для обучения и тестирования должны быть одни и те же признаки (столбцы). Однопроходное кодирование создало больше столбцов в обучающих данных, потому что в них имелись категориальные переменные с категориями, не представленными в данных тестирования. В связи с этим необходимо выровнять фреймы данных. Сначала извлекаем целевой столбец из данных обучения (потому что его нет в данных тестирования, но нужно сохранить эту информацию). При выполнении выравнивание необходимо убедиться, что установлен параметр axis = 1, чтобы выровнять фреймы данных на основе столбцов, а не строк!

train_labels = app_train['TARGET']# Выравниваем тренировочный и тестовый датафреймы, сохраняем только признаки, имеющиеся в обеих таблицахapp_train, app_test = app_train.align(app_test, join = 'inner', axis = 1)# Возвращаем метку с ответами обратноapp_train['TARGET'] = train_labelsprint('Training Features shape: ', app_train.shape)print('Testing Features shape: ', app_test.shape)

Training Features shape: (307511, 240)

Testing Features shape: (48744, 239)

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

ВОЗВРАЩАЕМСЯ К ИССЛЕДОВАТЕЛЬСКОМУ АНАЛИЗУ ДАННХ АНОМАЛИИ

Одна из проблем, на которую вы всегда должны обращать внимание при выполнении EDA, это аномалии в данных. Они могут возникнуть из-за неправильно набранных чисел, ошибок в измерительном оборудовании или они могут являться достоверными, но экстремальными измерениями. Одним из способов поиска аномалий является просмотр статистики столбца с помощью методаdescribe. Числа в столбце DAYS_BIRTH отрицательны, поскольку они записаны относительно текущей заявки на получение кредита. Чтобы увидеть эту статистику в годах, мы можем умножить столбец на -1 и разделить на количество дней в году:

(app_train['DAYS_BIRTH'] / -365).describe()

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

app_train['DAYS_EMPLOYED'].describe()

Это выглядит неправильно максимальное значение (кроме того, что оно положительное) около 1000 лет!

app_train['DAYS_EMPLOYED'].plot.hist(title = 'Days Employment Histogram');plt.xlabel('Days Employment');

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

anom = app_train[app_train['DAYS_EMPLOYED'] == 365243]non_anom = app_train[app_train['DAYS_EMPLOYED'] != 365243]print('The non-anomalies default on %0.2f%% of loans' % (100 * non_anom['TARGET'].mean()))print('The anomalies default on %0.2f%% of loans' % (100 * anom['TARGET'].mean()))print('There are %d anomalous days of employment' % len(anom))

The non-anomalies default on 8.66% of loans

The anomalies default on 5.40% of loans

There are 55374 anomalous days of employment

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

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

# Создаем признак, показывающий наличие аномального значенияapp_train['DAYS_EMPLOYED_ANOM'] = app_train["DAYS_EMPLOYED"] == 365243# Заполняем аномальные значения значением nanapp_train['DAYS_EMPLOYED'].replace({365243: np.nan}, inplace = True)app_train['DAYS_EMPLOYED'].plot.hist(title = 'Days Employment Histogram');plt.xlabel('Days Employment');

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

Чрезвычайно важное замечание: все, что мы делаем с данными обучения, необходимо делать и с данными тестирования. Обязательно создайте новый столбец и заполните аномальные значенияnp.nanв данных тестирования.

app_test['DAYS_EMPLOYED_ANOM'] = app_test["DAYS_EMPLOYED"] == 365243app_test["DAYS_EMPLOYED"].replace({365243: np.nan}, inplace = True)print('There are %d anomalies in the test data out of %d entries' % (app_test["DAYS_EMPLOYED_ANOM"].sum(), len(app_test)))

There are 9274 anomalies in the test data out of 48744 entries

КОРРЕЛЯЦИИ

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

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

  • .000.19 очень слабый

  • .20-.39 слабый

  • .400.59 умеренный

  • 0,600,79 сильный

  • 0,801,0 очень сильный

# Найдем корреляцию признаков с целевой меткой и отсортируем результатcorrelations = app_train.corr()['TARGET'].sort_values()# Отобразим результат вычисленийprint('Most Positive Correlations:\n', correlations.tail(15))print('\nMost Negative Correlations:\n', correlations.head(15))

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

app_train['DAYS_BIRTH'] = abs(app_train['DAYS_BIRTH'])app_train['DAYS_BIRTH'].corr(app_train['TARGET'])

-0.07823930830982694

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

Стоит подробнее проанализировать эту переменную. Во-первых, можно построить гистограмму возраста. Чтобы сделать график более понятным, проведем осьxчерез годы.

plt.style.use('fivethirtyeight')# Построим распределение по возрасту в годахplt.hist(app_train['DAYS_BIRTH'] / 365, edgecolor = 'k', bins = 25)plt.title('Age of Client'); plt.xlabel('Age (years)'); plt.ylabel('Count');

Само по себе возрастное распределение не говорит нам ничего, кроме того, что в нем отсутствуют явные выбросы, поскольку все возрасты выглядят разумно. Чтобы визуализировать влияние возраста на цель, мы построим графикоценки плотности ядра(KDE), раскрашенный значением цели.График оценки плотности ядра показывает распределение одной переменнойи может рассматриваться как сглаженная гистограмма (она создается путем вычисления ядра, обычно гауссовского, в каждой точке данных, а затем усреднения всех отдельных ядер для получения единой сглаженной кривой). Для этого графика мы будем использовать seaborn kdeplot.

plt.figure(figsize = (10, 8))sns.kdeplot(app_train.loc[app_train['TARGET'] == 0, 'DAYS_BIRTH'] / 365, label = 'target == 0')sns.kdeplot(app_train.loc[app_train['TARGET'] == 1, 'DAYS_BIRTH'] / 365, label = 'target == 1')plt.xlabel('Age (years)'); plt.ylabel('Density'); plt.title('Distribution of Ages');

Кривая target == 1 смещена в сторону младшего конца диапазона. Хотя это несущественная корреляция (коэффициент корреляции -0,07), эта переменная, вероятно, будет полезна в модели машинного обучения, поскольку она действительно влияет на цель. Давайте посмотрим на эту взаимосвязь с другой стороны: средняя непогашенная задолженность по возрастным группам.

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

age_data = app_train[['TARGET', 'DAYS_BIRTH']]age_data['YEARS_BIRTH'] = age_data['DAYS_BIRTH'] / 365age_data['YEARS_BINNED'] = pd.cut(age_data['YEARS_BIRTH'], bins = np.linspace(20, 70, num = 11))age_data.head(10)
# Группируем по ячейкам и считаем среднее значениеage_groups  = age_data.groupby('YEARS_BINNED').mean()age_groups
plt.figure(figsize = (8, 8))plt.bar(age_groups.index.astype(str), 100 * age_groups['TARGET'])plt.xticks(rotation = 75); plt.xlabel('Age Group (years)'); plt.ylabel('Failure to Repay (%)')plt.title('Failure to Repay by Age Group');

Прослеживается четкая тенденция: молодые заемщики чаще не возвращают кредит. Уровень невозврата составляет более 10% для трех младших возрастных групп и ниже 5% для самой старшей возрастной группы.

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

ВНЕШНИЕ ИСТОЧНИКИ

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

Давайте посмотрим на эти переменные.

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

ext_data = app_train[['TARGET', 'EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH']]ext_data_corrs = ext_data.corr()ext_data_corrs
plt.figure(figsize = (8, 6))# Тепловая карта корреляцийsns.heatmap(ext_data_corrs, cmap = plt.cm.RdYlBu_r, vmin = -0.25, annot = True, vmax = 0.6)plt.title('Correlation Heatmap');

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

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

plt.figure(figsize = (10, 12))for i, source in enumerate(['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3']):    plt.subplot(3, 1, i + 1)    sns.kdeplot(app_train.loc[app_train['TARGET'] == 0, source], label = 'target == 0')    sns.kdeplot(app_train.loc[app_train['TARGET'] == 1, source], label = 'target == 1')    plt.title('Distribution of %s by Target Value' % source)    plt.xlabel('%s' % source); plt.ylabel('Density');plt.tight_layout(h_pad = 2.5)

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

ПАРНЙ ГРАФИК

В качестве дополнительного исследовательского сюжета мы можем построить парный график признаков EXTSOURCE и признака DAYSBIRTH. График пар отличный инструмент для исследования, потому что он позволяет видеть отношения между несколькими парами признаков, а также распределения отдельных признаков. Здесь мы используем библиотеку визуализации seaborn и функцию PairGrid, чтобы создать график пар с диаграммами рассеяния в верхнем треугольнике, гистограммами на диагонали и графиками плотности ядра 2D и коэффициентами корреляции в нижнем треугольнике.

plot_data = ext_data.drop(columns = ['DAYS_BIRTH']).copy()plot_data['YEARS_BIRTH'] = age_data['YEARS_BIRTH']plot_data = plot_data.dropna().loc[:100000, :]# Функция для расчета коэффициента корреляции между двумя признакамиdef corr_func(x, y, **kwargs):    r = np.corrcoef(x, y)[0][1]    ax = plt.gca()    ax.annotate("r = {:.2f}".format(r),                xy=(.2, .8), xycoords=ax.transAxes,                size = 20)# Создаем объект парного графикаgrid = sns.PairGrid(data = plot_data, size = 3, diag_sharey=False,                    hue = 'TARGET',                     vars = [x for x in list(plot_data.columns) if x != 'TARGET'])grid.map_upper(plt.scatter, alpha = 0.2)grid.map_diag(sns.kdeplot)grid.map_lower(sns.kdeplot, cmap = plt.cm.OrRd_r);plt.suptitle('Ext Source and Age Features Pairs Plot', size = 32, y = 1.05);

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

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

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

Подробнее..

Соревнование KAGGLE по определению риска дефолта заемщика. Разработка признаков

14.01.2021 08:18:31 | Автор: admin

Введение: Соревнование от финансовой группы HOME CREDIT по определению риска дефолта заемщика

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

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

В рамках этой статьи я рассмотрю два простых метода построения признаков:

  • Полиномиальные признаки;

  • Признаки, основанные на понимании предметной области.

ПОЛИНОМИАЛЬНЕ ПРИЗНАКИ

Одним из простых методов построения признаков является созданиеполиномиальных признаков. В этом методе создаются признаки, которые являются степенями существующих признаков, а также определенными взаимодействиями между существующими признаками. Например, мы можем создать переменные EXTSOURCE1 ^ 2 и EXTSOURCE2 ^ 2, а также такие переменные, как EXTSOURCE1 * EXTSOURCE2, EXTSOURCE1 * EXTSOURCE2 ^ 2, EXTSOURCE1 ^ 2 x EXTSOURCE2 ^ 2 и так далее. Эти признаки, представляющие собой комбинацию нескольких отдельных переменных, называются условиями взаимодействия, поскольку они фиксируют взаимодействия между переменными. Другими словами, хотя две переменные сами по себе могут не иметь сильного влияния на цель, объединение их вместе в одну взаимодействующую переменную может показать связь с целью. Термины взаимодействия обычно используются в статистических моделях, чтобы уловить влияние нескольких переменных, но в машинном обучении используются не очень часто. Тем не менее, мы можем попробовать несколько вариантов, чтобы увидеть, могут ли они помочь нашей модели предсказать, вернет ли клиент ссуду или нет.

Следующим программным кодом я создам полиномиальные признаки, используя переменные EXTSOURCE и переменную DAYSBIRTH.В Scikit-Learn есть полезный классPolynomialFeatures, который создает полиномы и условия взаимодействия до определенной степени. Достаточно использовать степень 3, чтобы увидеть результат (при создании полиномиальных признаков, нежелательно использовать слишком высокую степень, потому что количество признаков экспоненциально масштабируется со степенью, а также можно столкнуться спроблемами переобучения).

poly_features = app_train[['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH', 'TARGET']]poly_features_test = app_test[['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH']]from sklearn.preprocessing import Imputerimputer = Imputer(strategy = 'median')poly_target = poly_features['TARGET']poly_features = poly_features.drop(columns = ['TARGET'])poly_features = imputer.fit_transform(poly_features)poly_features_test = imputer.transform(poly_features_test)from sklearn.preprocessing import PolynomialFeatures# Создаем объект PolynomialFeatures, указав степень взаимодействия, равную 3poly_transformer = PolynomialFeatures(degree = 3)poly_transformer.fit(poly_features)poly_features = poly_transformer.transform(poly_features)poly_features_test = poly_transformer.transform(poly_features_test)print('Polynomial Features shape: ', poly_features.shape)

Polynomial Features shape: (307511, 35)

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

poly_transformer.get_feature_names(input_features = ['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH'])[:15]

Теперь у нас имеется 35 признаков новых: индивидуальные признаки; признаки, повышенные до степени 3; взаимодействующие признаки. Можно посмотреть, коррелируют ли какие-либо из этих новых признаков с целевой меткой.

poly_features = pd.DataFrame(poly_features,                              columns = poly_transformer.get_feature_names(['EXT_SOURCE_1', 'EXT_SOURCE_2',                                                                            'EXT_SOURCE_3', 'DAYS_BIRTH']))poly_features['TARGET'] = poly_target# Найдем корреляцию с целевой меткойpoly_corrs = poly_features.corr()['TARGET'].sort_values()print(poly_corrs.head(10))print(poly_corrs.tail(5))

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

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

poly_features_test = pd.DataFrame(poly_features_test,                                   columns = poly_transformer.get_feature_names(['EXT_SOURCE_1', 'EXT_SOURCE_2',                                                                                 'EXT_SOURCE_3', 'DAYS_BIRTH']))poly_features['SK_ID_CURR'] = app_train['SK_ID_CURR']app_train_poly = app_train.merge(poly_features, on = 'SK_ID_CURR', how = 'left')poly_features_test['SK_ID_CURR'] = app_test['SK_ID_CURR']app_test_poly = app_test.merge(poly_features_test, on = 'SK_ID_CURR', how = 'left')app_train_poly, app_test_poly = app_train_poly.align(app_test_poly, join = 'inner', axis = 1)print('Training data with polynomial features shape: ', app_train_poly.shape)print('Testing data with polynomial features shape:  ', app_test_poly.shape)

Training data with polynomial features shape: (307511, 275)

Testing data with polynomial features shape: (48744, 275)

ПРИЗНАКИ, ОСНОВАННЕ НА ПОНИМАНИИ ПРЕДМЕТНОЙ ОБЛАСТИ

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

  • CREDITINCOMEPERCENT: процент суммы кредита относительно дохода клиента;

  • ANNUITYINCOMEPERCENT: процент аннуитетного платежа кредита относительно дохода клиента;

  • CREDITTERM: продолжительность платежа в месяцах;

  • DAYSEMPLOYED_PERCENT: процент отработанных дней по отношению к возрасту клиента.

app_train_domain = app_train.copy()app_test_domain = app_test.copy()app_train_domain['CREDIT_INCOME_PERCENT'] = app_train_domain['AMT_CREDIT'] / app_train_domain['AMT_INCOME_TOTAL']app_train_domain['ANNUITY_INCOME_PERCENT'] = app_train_domain['AMT_ANNUITY'] / app_train_domain['AMT_INCOME_TOTAL']app_train_domain['CREDIT_TERM'] = app_train_domain['AMT_ANNUITY'] / app_train_domain['AMT_CREDIT']app_train_domain['DAYS_EMPLOYED_PERCENT'] = app_train_domain['DAYS_EMPLOYED'] / app_train_domain['DAYS_BIRTH']app_test_domain['CREDIT_INCOME_PERCENT'] = app_test_domain['AMT_CREDIT'] / app_test_domain['AMT_INCOME_TOTAL']app_test_domain['ANNUITY_INCOME_PERCENT'] = app_test_domain['AMT_ANNUITY'] / app_test_domain['AMT_INCOME_TOTAL']app_test_domain['CREDIT_TERM'] = app_test_domain['AMT_ANNUITY'] / app_test_domain['AMT_CREDIT']app_test_domain['DAYS_EMPLOYED_PERCENT'] = app_test_domain['DAYS_EMPLOYED'] / app_test_domain['DAYS_BIRTH']

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

plt.figure(figsize = (12, 20))for i, feature in enumerate(['CREDIT_INCOME_PERCENT', 'ANNUITY_INCOME_PERCENT', 'CREDIT_TERM', 'DAYS_EMPLOYED_PERCENT']):    plt.subplot(4, 1, i + 1)    sns.kdeplot(app_train_domain.loc[app_train_domain['TARGET'] == 0, feature], label = 'target == 0')    sns.kdeplot(app_train_domain.loc[app_train_domain['TARGET'] == 1, feature], label = 'target == 1')    plt.title('Distribution of %s by Target Value' % feature)    plt.xlabel('%s' % feature); plt.ylabel('Density');plt.tight_layout(h_pad = 2.5)

Сложно сказать заранее, будут ли полезны эти новые признаки. Единственный способ узнать наверняка попробовать их.

BASELINE

Для наивного решения можно было бы указать одно и то же значение для всех примеров из тестового набора. Нас просят предсказать вероятность невыплаты ссуды, поэтому можно предположить вероятность 0,5 для всех наблюдений на тестовой выборке. Это даст нам площадь под кривой ошибок (AUC ROC), равную 0,5. Но лучше воспользоваться для нашего бейзлайна чуть более сложной моделью: логистической регрессией.

РЕАЛИЗАЦИЯ ЛОГИСТИЧЕСКОЙ РЕГРЕССИИ

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

from sklearn.preprocessing import MinMaxScaler, Imputerif 'TARGET' in app_train:    train = app_train.drop(columns = ['TARGET'])else:    train = app_train.copy()features = list(train.columns)test = app_test.copy()# Заполняем пропущенные значения медианным значением признакаimputer = Imputer(strategy = 'median')# Масштабируем признаки в диапазон 0-1scaler = MinMaxScaler(feature_range = (0, 1))imputer.fit(train)train = imputer.transform(train)test = imputer.transform(app_test)scaler.fit(train)train = scaler.transform(train)test = scaler.transform(test)print('Training data shape: ', train.shape)print('Testing data shape: ', test.shape)

Training data shape: (307511, 240)

Testing data shape: (48744, 240)

Для первой модели я использую LogisticRegressionиз библиотеки Scikit-Learn. Единственное изменение, которое потребуется в настройки модели по умолчанию, уменьшупараметр регуляризацииC, контролирующий степень переобучения (более низкое значение должно уменьшить переобучение). Это даст чуть лучшие результаты, чем LogisticRegression с параметрами по умолчанию, но все равно установит низкую планку для любых будущих моделей.

Сначала создаем модель, затем обучаем ее с помощью метода.fit, а затем делаем прогнозы на основе тестовых данных с помощью метода.predict_proba(нам нужны вероятности, а не целые числа 0 или 1).

from sklearn.linear_model import LogisticRegression# Создаем модель и тренируем ееlog_reg = LogisticRegression(C = 0.0001)log_reg.fit(train, train_labels)

Теперь, когда модель обучена, можно использовать ее для прогнозирования. В задаче требуется спрогнозировать вероятность невыплаты ссуды, поэтому использую метод моделиpredict.proba. Он возвращает массив m*2, где m количество наблюдений. Первый столбец это вероятность того, что цель будет равна 0, а второй вероятность того, что цель будет равна 1 (поэтому для каждой строки два столбца в сумме должны быть равными 1). Нам нужна вероятность того, что ссуда не будет погашена, поэтому выберу второй столбец.

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

log_reg_pred = log_reg.predict_proba(test)[:, 1]

Прогнозы должны быть в формате, показанном в файле samplesubmission.csv, где есть только два столбца: SKID_CURR и TARGET. Создадим фрейм данных в этом формате из набора тестов и прогнозов.

submit = app_test[['SK_ID_CURR']]submit['TARGET'] = log_reg_predsubmit.head()

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

submit.to_csv('log_reg_baseline.csv', index = False)

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

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

При отправке результата на оценку базовый показатель данной модели должен быть приблизительно равен 0,671.

УЛУЧШЕННАЯ МОДЕЛЬ: СЛУЧАЙНЙ ЛЕС

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

from sklearn.ensemble import RandomForestClassifierrandom_forest = RandomForestClassifier(n_estimators = 100, random_state = 50, verbose = 1, n_jobs = -1)random_forest.fit(train, train_labels)# Определяем важность признаковfeature_importance_values = random_forest.feature_importances_feature_importances = pd.DataFrame({'feature': features, 'importance': feature_importance_values})# Формируем предсказания для тестовых данныхpredictions = random_forest.predict_proba(test)[:, 1]

Оценка этой модели чуть выше базового уровня и составляет примерно 0,678 балла.

ДЕЛАЕМ ПРОГНОЗ, ИСПОЛЬЗУЯ СПЕЦИАЛЬНЕ ПРИЗНАКИ

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

poly_features_names = list(app_train_poly.columns)imputer = Imputer(strategy = 'median')poly_features = imputer.fit_transform(app_train_poly)poly_features_test = imputer.transform(app_test_poly)scaler = MinMaxScaler(feature_range = (0, 1))poly_features = scaler.fit_transform(poly_features)poly_features_test = scaler.transform(poly_features_test)random_forest_poly = RandomForestClassifier(n_estimators = 100, random_state = 50, verbose = 1, n_jobs = -1)random_forest_poly.fit(poly_features, train_labels)# Формируем предсказания на тестовых данныхpredictions = random_forest_poly.predict_proba(poly_features_test)[:, 1]
app_train_domain = app_train_domain.drop(columns = 'TARGET')domain_features_names = list(app_train_domain.columns)imputer = Imputer(strategy = 'median')domain_features = imputer.fit_transform(app_train_domain)domain_features_test = imputer.transform(app_test_domain)scaler = MinMaxScaler(feature_range = (0, 1))domain_features = scaler.fit_transform(domain_features)domain_features_test = scaler.transform(domain_features_test)random_forest_domain = RandomForestClassifier(n_estimators = 100, random_state = 50, verbose = 1, n_jobs = -1)random_forest_domain.fit(domain_features, train_labels)feature_importance_values_domain = random_forest_domain.feature_importances_feature_importances_domain = pd.DataFrame({'feature': domain_features_names, 'importance': feature_importance_values_domain})predictions = random_forest_domain.predict_proba(domain_features_test)[:, 1]

Оценка для этой модели составляет 0,679 балла, что указывает на низкую значимость разработанных признаков в этом случае.

ИНТЕРПРЕТАЦИЯ МОДЕЛИ: ВАЖНОСТЬ ПРИЗНАКОВ

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

def plot_feature_importances(df):    df = df.sort_values('importance', ascending = False).reset_index()    df['importance_normalized'] = df['importance'] / df['importance'].sum()    plt.figure(figsize = (10, 6))    ax = plt.subplot()    ax.barh(list(reversed(list(df.index[:15]))),             df['importance_normalized'].head(15),             align = 'center', edgecolor = 'k')    ax.set_yticks(list(reversed(list(df.index[:15]))))    ax.set_yticklabels(df['feature'].head(15))    plt.xlabel('Normalized Importance'); plt.title('Feature Importances')    plt.show()    return df# Отображаем важность признаков без учета специальных признаковfeature_importances_sorted = plot_feature_importances(feature_importances)

Как и ожидалось, наиболее важные признаки связаны с EXTSOURCE и DAYSBIRTH. Можно заметить, что существует лишь несколько признаков, имеющих существенное значение для модели. Это говорит о том, что можно отказаться от многих признаков без снижения качества (и даже увидеть его увеличение). Это не самый изощренный метод интерпретации модели или уменьшения размерности, но он позволяет начать понимать, какие факторы модель принимает во внимание при прогнозировании.

feature_importances_domain_sorted = plot_feature_importances(feature_importances_domain)

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

ВВОД

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

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

В целом, необходимо следовать общей схемепроекта машинного обучения:

  1. Разберитесь в проблеме и данных

  2. Очистите и отформатируйте данные (в основном это сделали для нас организаторы конкурса)

  3. Проведите исследовательский анализ данных

  4. Создайте базовую модель

  5. Улучшить модель

  6. Интерпретируйте модель

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

Я надеюсь, что моя статья помогла вам принять участие в этом (или каком-либо другом) соревновании по машинному обучению, и что теперь вы готовы самостоятельно и с помощью сообщества начать дальнейшую работу над серьезными проблемами!

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

Подробнее..

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-предсказания;

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

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

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

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

Подробнее..

Почему меня разочаровали результаты Kaggle ARC Challenge

23.06.2020 14:06:23 | Автор: admin
Кто-то с ужасом, а кто-то с нетерпением ждет ИИ как в произведениях фантастов. С личностью, эмоциями, энциклопедическими знаниями и главное с интеллектом, то есть способностями к логическим выводам, оперированию абстрактными понятиями, выделению закономерностей в окружающем мире и превращению их в правила. Как мы знаем, именно такой ИИ теоретики называют сильным или ещё AGI. Пока это далеко не мейнстримное направление в машинном обучении, но руководители многих больших компаний уже считают, что сложность их бизнеса превысила когнитивные способности менеджеров и без настоящего ИИ двигаться вперёд станет невозможно. Идут дискуссии, что же это такое, каким он должен быть, как сделать тест чтобы уж точно понять, что перед нами AGI, а не очередной blackbox, который лучше человека решает локальную задачу например, распознавание лица на фотографии.

Три недели назад на каггле прошло первое в истории платформы соревнование по сильному ИИ Abstraction and Reasoning Challenge. Чтобы проверить способность моделей к обобщению и решению абстрактных задач, все участники суммарно решили только чуть менее половины задач. Решение-победитель справляется приблизительно с 20% из них и то девятичасовым перебором вручную захардкоженных правил (ограничение в девять часов установили организаторы).

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

Вызов


В ноябре 2019 года создатель Keras Франсуа Шолле написал статью Об оценке интеллекта. На хабре краткий пересказ уже выложила Rybolos. Ключевой практический элемент статьи датасет для проверки способности алгоритмов к абстрактному мышлению в человеческом смысле. Просто поглядеть на него можно здесь.

Примеры задач из датасета; наверху вход, внизу ответ





Для человека эти задачи легко решаемы и напоминают блок из теста на IQ они сводятся к набору трансформаций над картинками от 30x30 до 1x1: продолжить узор, восстановить удаленный кусок, обрезать, закрасить замкнутые области, найти лишний объект и т.д.

Соревнование


Не заставило себя долго ждать и само соревнование на Kaggle на основе этого датасета, призы в котором были не самые большие в зависимости от скора $5K-8K за первое место. Для сравнения в проходившем параллельно соревновании DFDC победивший Селим Сефербеков получил полмиллиона долларов.

Тем не менее, соревнование привлекло несколько грандмастеров Kaggle: rohanrao (H20), kazanova (H20, кстати третье место в глобальном рейтинге Kaggle), boliu0 (NVIDIA), titericz (NVIDIA), tarunpaparaju, много очень сильных ребят из ODS, в том числе Влада Голубева и Илью Ларченко, которые взяли третье место. Всего до LeaderBoard дошли 914 команд.

Участникам предлагалось обучить модель на 400 задачах, в каждой из которых есть train (три-пять картинок), ответ и тест (одна-две картинки и соответственно один-два ответа). Этот датасет вручную разметил Davide Bonin на комбинации из 192 элементарных трансформаций.

Такой же по объему датасет (400 задач) предлагался для валидации (eval), впрочем, на нем можно было и обучаться особенно с учетом того, что задачи на нем отличались от обучающего. То есть сочетание трансформаций на тесте могли не встречаться на трейне, например вместо операции crop crop + resize. В лидерборде было 100 задач, при этом на каждое задание можно было выдавать три варианта ответа, достаточно чтобы хотя бы один был верным.

Интересные идеи


CNN c TensorFlow Lattice


Не попали в лидерборд.TensorFlow Lattice позволяет накладывать достаточно жесткие ограничения на фичи при обучении сеток: монотонность, взаимную важность, выпуклость, взаимное доверие, унимодальность. Это существенно увеличивает способности сеток к экстраполяции и сглаживает разделяющие поверхности. Для привлечения внимания картинка про сравнение разделяющих поверхностей разных моделей на одной задаче из блога TensorFlow Core.



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



Вариационные автоэнкодеры


Заняли 131 место из 914 в лидерборде. Похожая идея наложить ограничения, но не на пространство признаков как в TF Lattice, а на пространство скрытых переменных, то есть использовать вариационные автоэнкодеры. О них на хабре есть отличная статья.

Это решение было доработано и дошло до LeaderBoard.На картинке в первой строке входное изображение, во второй правильный ответ, в третьей результат работы модели.



Генетический алгоритм



Занял 631 место из 914 в лидерборде. В этом кейсе в качестве генов реализовано одиннадцать трансформаций DSL генетического алгоритма. В ходе селекции по стратегии элитизма отбираются наиболее сильные гены:


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

Авторы рассматривают задачу как многокритериальную оптимизацию насколько близкой получилась ширина и высота выходных картинок, совпадение цветов и положение клеток.
Несмотря на то, что такой подход что-то выбил на LB (631/914), реализация генетического алгоритма для большего числа элементарных трансформаций будет работать существенно дольше. Это, скорее всего, и стало препятствием к развитию этого решения.

Графовый подход


Занял 165 место из 914 на лидерборде. Одна из наиболее человекообразных идей
выделить на исходных изображениях объекты и далее работать с их трансформациями. Для выделения объектов применялся алгоритм k-clique-communities algorithm графовой библиотеки networkx, и справился он на отлично:







К сожалению, ноутбук с трансформациями автор не оставил, есть только выделение объектов, однако автор вошел в топ-19 на лидерборде.

Языковая модель


Заняла 592 место из 914 на лидерборде. На начало 2019 года BERT state-of-the-art языковая модель. За последние месяцы было множество её усовершенствований: RoBERTa, DistilBERT, ALBERT и другие. Здесь идея решения основывается на двух фактах:
  • Способности BERT работать с последовательностями.
  • Механизме attention, который можно научить вычленять связи даже между достаточно удаленными элементами последовательности в противовес идее о влиянии на элемент только нескольких соседних.

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





А вот результат работы обученной модели (справа):

Жаль, что на других задачах результаты не были такими хорошими.

Работающие решения


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

Например, задание про изменение цветов при сохранении размера изображения: ноутбук Zoltan, занявшего в итоге шестое место, вошел в решение Влада Голубева и Ильи Ларченко, которые заняли третье место. Решение по сути представляет объединение нескольких, в том числе публичных. Так, идеи Ильи описаны в его репозитории, он декомпозировал задачи на абстракции (цвета, блоки, маски), в терминах которых для которых реализовал трансформации, решающие 32 задания. К этому добавляются решения Влада как с похожим подходом на правилах и трансформациях, так и модель xgboost.

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


Обучающие примеры; слева входная картинка, справа правильный ответ.



А вот результат работы решения:

Достаточно похожим выглядит решение , взявшее второе место:
  1. Определить на входе тип задания (из шести по классификации автора) и параметры входных изображений.
  2. Иногда упростить задание например, поменять один цвет или повернуть объекты.
  3. Перебирать в цикле различные трансформации (реализовано 51) и их комбинации чтобы выбрать три максимально близкие к ответу картинки.
  4. Выполнить преобразования, обратные тем, что были на шаге 2, к трем кандидатам.
  5. Иногда имеет смысл сделать аугментацию например, из исходных примеров сделать задачи монохромными или только с одной формой.

Чемпион и 10 000 строк кода


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



Что в итоге?


На фоне вала статей и выступлений на конференциях A* об успехах в задачах создания сложных моделей по нескольким примерам результаты соревнования оказались совершенно неожиданными и обескураживающими.

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

Так как уже в процессе соревнований участники делятся идеями и наработками, участие в челлендже казалось отличным способом узнать, какие подходы сейчас на переднем крае в области создания сложных моделей с минимальной обучающей выборкой или без таковой: zero-shot learning (ZSL), one-shot learning, few-shot learning, prototype learning и domain shift. Конечно, перечисленные проблемы подразумевают изменение доменной области, а не самой задачи классификация остается классификацией. Но это самое проработанное направление в части обобщения моделей.

Сами мы решили попробовать GAN-архитектуру поверх feature extractor, которая у нас так и не зашла. Однако, примерно за три-четыре недели до конца соревнования стало понятно, что выигрывают решения на правилах. Обсудив внутри, мы пришли к выводу что, потратив кучу времени на написание, мы вряд ли повысим свои компетенции в современных методах, поэтому направили энергию на другие задачи.

В тысячный раз подтвердился тезис о прикладном характере машинного обучения это набор инструментов, который надо уметь применять четко понимая задачу. В то же время бессмысленно применять даже самые крутые инструменты к непонятной задаче. Можно провести аналогию со станком, ЧПУ и 3D-принтером: несмотря на то, что область применения на каждом этапе растет, пока даже близко нет волшебной палочки, способной создать абсолютно любой предмет.

Чтобы обучить машину, мало придумать ей абстрактный язык (DSL или набор трансформаций), придумать функцию потерь и показать пару примеров. Челлендж прошёл, но осталось разочарование: то, что маркетологи до сих пор продают как AI, на поверку оказывается набором инженерных хитростей. Флёр волшебства окончательно растаял.
Подробнее..

Анализ результатов работы архитектуры YoloV3 на медицинских снимках

31.05.2021 12:16:05 | Автор: admin

Данная статья представляет собой обзор на оригинальную статью на Medium (эксперименты проводятся с изменениями некоторых условий).

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

http://personeltest.ru/aways/github.com/ultralytics/yolov3https://github.com/ultralytics/yolov3

https://github.com/ultralytics/yolov3

Можно заметить, что прямоугольники подписаны какими-то словами и числами. На картинке это person и tie. Рядом с этими словами написаны числа (у человека слева это person с 0.59, tie - 0.62). Эти слова образуют виды объектов (например, машина, человек, кот, мяч и т.д.), которые нужно распознать, а числа, записанные рядом с этими словами, есть вероятность того, что данный объект принадлежит этому классу. (Опять же у человека справа, стоит "person 0.59". Это значит, что в выделенном прямоугольнике есть объект класса person - человек - с вероятностью 0.59). И да, число - вероятность объекта в данном прямоугольника, принимает значения от 0 до 1.

Задача

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

Пневмония является одной из распространенных болезней, которое представляет собой воспалительное заболевание легких. По официальным данным, именно она является одной из опасных инфекционных заболеваний за последние 20 лет. В 2019-м эти болезни оказались четвертой причиной смертности в мире (от них скончались 2,6 млн человек). Обычно пневмония проявляется в виде областей повышенной непрозрачности на снимках рентгенограммы. Однако диагностика рентгенограмм затруднена из-за ряда причин, связанных с состоянием легких. И иногда даже опытному специалисту бывает сложно поставить диагноз.

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

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

Решать эту задачу мы будем с использованием нейронной сети.

Модель

В качестве такой сети возьмем архитектуру YOLOv3. Почему именно она? Да, просто захотели =) Более подробно про эту архитектуру можно почитать на официальном сайте и Хабре.

YOLOv3 представляет собой нейронную сеть, основанную на архитектуре YOLO (You Only Look Once). Она примечательна тем, что CNN (Convolutional Neural Network) применяется один раз ко всему изображению сразу (отсюда и название). YOLOv3 состоит из 106-ти свёрточных слоев. Стоит отметить, что у YOLOv3 есть несколько слоев (их 3), которые предназначены для детекции объектов разного размера. На картинке ниже представлена архитектура YOLOv3:

http://personeltest.ru/aways/www.researchgate.net/figure/The-framework-of-YOLOv3-neural-network-for-ship-detection_fig2_335228064https://www.researchgate.net/figure/The-framework-of-YOLOv3-neural-network-for-ship-detection_fig2_335228064

https://www.researchgate.net/figure/The-framework-of-YOLOv3-neural-network-for-ship-detection_fig2_335228064

При использовании YOLO изображение делится на сетку с ячейками размером 13 х 13. Для чего нужны эти ячейки? Дело в том, что каждая такая ячейка прогнозирует количество bounding box'ов (или ограничивающих прямоугольников) и вероятность того, что в данной области находится некоторый объект. Эта вероятность (точнее, число) называется confidence value (доверительное значение). И получается, что если в некоторой области объекта нет, то его доверительное значение маленькое (точнее, этого мы хотим достичь). Ниже представлена схема работы YOLOv3.

http://personeltest.ru/aways/medium.com/nerd-for-tech/a-real-time-object-detection-model-using-yolov3-algorithm-for-non-gpu-computers-8941a20b445https://medium.com/nerd-for-tech/a-real-time-object-detection-model-using-yolov3-algorithm-for-non-gpu-computers-8941a20b445

https://medium.com/nerd-for-tech/a-real-time-object-detection-model-using-yolov3-algorithm-for-non-gpu-computers-8941a20b445

Также примечательно, что YOLO использует, так называемые anchor boxes (якорные рамки). Подробнее о них написано в статье на Medium. Это достаточно сложная для понимания(лично для автора этой статьи) концепция. Нам важно лишь то, что anchor boxes (якорные рамки) используются для прогнозирования bounding box'ов и рассчитаны они с помощью датасета COCO с использованием кластеризации k-средних.

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

Данные

С задачей определились, с моделью определились. Что еще надо? Правильно, данные. Данные берутся из платформы Kaggle, в которой проводились соревнования по детекции пневмонии. Вот данные.

Изучим эти данные более подробно. Нам понадобятся изображения из файлов stage_2_train_images.zip и stage_2_test_images.zip. Данные, которые давались на соревновании, представляют собой набор снимков рентгенограммы грудной клетки. В датасете (а именно так называются набор данных) содержатся 26684 рентгеновских снимков разных пациентов. Данные снимки представляют собой изображения в формате DICOM в разрешении 1024 х 1024. Пример изображения представлен ниже.

Class

Target

Patients

Lung Opacity

1

9555

No Lung Opacity / Not Normal

0

11821

Normal

0

8851

Так как изображения находятся в формате DICOM. То мы преобразуем эти изображения в формат JPG с помощью следующей функции.

import pydicom as dicomimport osfrom tqdm import tqdmimport numpy as npimport cv2import pandas as pdперевод dicom в jpgdef dicom_to_jpg(source_folder,destination_folder,labels):    images_path = os.listdir(source_folder)    image_dirs_label = {'image_dir':[],'Target':[]}    for n, image in tqdm(enumerate(images_path)):        ds = dicom.dcmread(os.path.join(source_folder, image))        pixel_array_numpy = ds.pixel_array        image = image.replace('.dcm', '.jpg')        cv2.imwrite(os.path.join(destination_folder, image), pixel_array_numpy)        image_dirs_label['image_dir'].append(os.path.join(destination_folder, image))        image_dirs_label['Target'].append(train_labels[train_labels.patientId== image.split('.')[0]].Target.values[0])    print('{} dicom files converted to jpg!'.format(len(images_path)))    return pd.DataFrame(image_dirs_label)

Выделяются 3 класса, которые представляют для интерес: Normal 0, No Lung Opacity / Not Normal 0, Lung Opacity 1. Классы Class, целевые признаки Target и количество изображений Patients, соответствующего класса, представлены в таблице выше. И картинка ниже показывает изображения каждого класса.

Для нас особый интерес представляют классы, сигнализирующие пневмонию (positive или на картинке выше Lung Opacity). И соотношение этого класса к классу изображений здоровых пациентов (negative) равно примерно 1:4 (ниже есть диаграмма, иллюстрирующая данное соотгношение).

Дисбаланс классовДисбаланс классов

Дисбаланс классов

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

import albumentations as Aimport pandas as pdimport cv2import ostransformertransform = A.Compose([        A.RandomRotate90(),        A.Flip(),        A.Transpose(),        A.OneOf([            A.IAAAdditiveGaussianNoise(),            A.GaussNoise(),        ], p=0.2),        A.OneOf([            A.MotionBlur(p=.2),            A.MedianBlur(blur_limit=3, p=0.1),            A.Blur(blur_limit=3, p=0.1),        ], p=0.2),        A.ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.2, rotate_limit=45, p=0.2),        A.OneOf([            A.OpticalDistortion(p=0.3),            A.GridDistortion(p=.1),            A.IAAPiecewiseAffine(p=0.3),        ], p=0.2),        A.OneOf([            A.CLAHE(clip_limit=2),            A.IAASharpen(),            A.IAAEmboss(),            A.RandomBrightnessContrast(),        ], p=0.3),        A.HueSaturationValue(p=0.3),    ])

Реализация модели

Данный раздел будет обновляться в будущем(ибо есть технические "подводные камни", о которых не рассказано, но о которых стоит рассказать)

Теперь датасет мы "расширили". Преобразованные изображения и исходные файлы в форматах JPG и DICOM будем анализировать с использованием архитектуры YOLOv3 с основой (backbone'ом) DarkNet. Подробнее про DarkNet можно почитать здесь. Затем основа архитектуры YOLOv3 (в данном случае Darknet) заменяется на обученную классификационную модель CheXNet. CheXNet представляет собой 121-слойную свёрточую нейронную сеть, которая определяет области легких, сигнализирующих о пневмонии. Рекомендуется прочитать эту научную работу про CheXNet. Эта модель обучена на классификацию 14 классов, поэтому так как мы решаем задачу бинарной классификации, то последние слои CheXNet необходимо установить на классификацию 2-х классов (negative пневмонии нет и positive пневмония есть). И реализовать в коде данную модель можно с помощью библиотеки TensorFlow, в которой есть готовая заготовка DenseNet121. Реализация этой модели представлено ниже.

# Для CheXNet устанавлиются веса classifier_weights.hdf5, которые можно скачать отсюдаhttps://drive.google.com/file/d/1Bd50DpRWorGMDuEZ3-VHgndpJZwUGTAr/viewfrom absl import flagsfrom absl.flags import FLAGSimport numpy as npimport tensorflow as tffrom tensorflow.keras import Modelfrom tensorflow.keras.applications import DenseNet121from tensorflow.keras.layers import (    Add,    Concatenate,    Conv2D,    Input,    Lambda,    LeakyReLU,    MaxPool2D,    UpSampling2D,    ZeroPadding2D,    BatchNormalization,    Dense)def base_model(chexnet_weights=None,size=None):    dense_net_121 = DenseNet121(input_shape = [size,size,3], include_top = False,pooling = 'avg')    base_model_output = Dense(units = 14, activation = 'relu')(dense_net_121.output)    base_model = Model(inputs = dense_net_121.input,outputs = base_model_output)    output_layer = Dense(1, activation = 'sigmoid')(base_model.layers[-2].output)    model = Model(inputs = base_model.inputs, outputs = output_layer)    if chexnet_weights:        model.load_weights(chexnet_weights)    final_base_model = Model(inputs = model.inputs, outputs = model.layers[-3].output)    return final_base_modeldef ChexNet(name=None, chexnet_weights='PATH_TO_WEIGTHS/classifier_weights.hdf5',size=None):    chexnet = base_model(chexnet_weights = chexnet_weights, size = size)    back_bone = Model(inputs = chexnet.inputs, outputs=(chexnet.get_layer('pool3_conv').output,                                                           chexnet.get_layer('pool4_conv').output,                                                           chexnet.output),name=name)    return back_bone

Теперь посмотрим на количество параметров каждой модели:

Model

Total params

Trainable params

Non-trainable params

DarkNet

61576342

61523734

52608

CheXNet

27993206

27892662

100544

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

Обучение

Полученная архитектура нейронной сети YOLOv3 с основой CheXNet обучается на преобразованных данных(над которыми был совершен процесс аугментации).

Стоит отметить то, что мы сначала обучаем (1 эпоху) на всех классах изображений (positive и negative), а затем на изображениях, в которых есть пневмония (класса positive). Это делается потому что в YOLOv3 изображение 416 х 416 делится на сетку 13 х 13 (416 / 32 = 13). И прогноз делается для каждой ячейки сетки 13 х 13. И если количество anchor box'ов равно 3, тогда каждая такая ячейка сетки 13 х 13 связана с 3-мя anchor box'ами. То есть размерность будет 13 х 13 х 3 = 507 (всего будет столько предсказаний). Получается, что для одного изображения мы делаем 507 предсказаний. И даже если изображение относится к классу positive (пневмония есть) и в нем есть 2 области непрозрачности (помутнения), то будет 2 положительных предсказания и 507-2=505 отрицательных предсказаний. Как видно, число отрицательных предсказаний намного больше. Поэтому если мы снова добавим отрицательные изображения, это сделает нашу модель "предвзятой" по отношению к отрицательному классу.

Для начала, мы делаем ImageDataGenerator для обучения модели. Это связано с тем, что набор данных достаточно большой (и он не поместится в оперативную память), а данный инструмент позволяет нам облегчить чтение изображений во время обучения модели.

# true_augmented_labels - это DataFrame, который содержит информацию о всех изображениях (и о изначальных, и аугментированных(преобразованных)datagen=ImageDataGenerator(        rescale = 1. / 255.,        validation_split = 0.20)train_generator = datagen.flow_from_dataframe(dataframe = true_augmented_labels,x_col = "image_dir",y_col = "Target",subset = "training",batch_size = 4,seed = 42,shuffle = True,class_mode = "binary",target_size = (416, 416))valid_generator = datagen.flow_from_dataframe(dataframe = true_augmented_labels,x_col = "image_dir",y_col = "Target",subset = "validation",batch_size = 4,seed = 42,shuffle = True,class_mode = "binary",target_size = (416, 416))

Затем мы обучаем нашу модель на всех классах изображений (и positive, и negative), заранее замораживая последние слои модели.

# веса brucechou1983_CheXNet_Keras_0.3.0_weights.h5 и classifier_weights.hdf5можно скачать отсюда https://www.kaggle.com/theewok/chexnet-keras-weights/version/1и отсюда https://github.com/junaidnasirkhan/Replacing-YoloV3-Backbone-with-ChexNet-for-Pneumonia-Detectiondense_net_121 = DenseNet121(input_shape = [416,416] + [3], include_top = False, pooling = 'avg')base_model_output = Dense(units = 14, activation = 'relu')(dense_net_121.output)base_model = Model(inputs = dense_net_121.input, outputs = base_model_output)загрузка "тренированных" весовbase_model.load_weights('brucechou1983_CheXNet_Keras_0.3.0_weights.h5')заморозка последних слоев моделиfor layer in base_model.layers[:10]:    layer.trainable = Falseустанавлием последние слои модели на бинарную классификациюoutput_layer = Dense(1, activation = 'sigmoid')(base_model.layers[-2].output)model = Model(inputs = base_model.inputs, outputs = output_layer)model.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy', f1_m]) checkpoint = ModelCheckpoint(filepath = 'classifier_weights.hdf5', monitor = 'val_accuracy',  verbose = 0, save_best_only = True, save_weights_only = True, mode = 'auto')log_dir = "classifier_logs/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")tensorboard = TensorBoard(log_dir = log_dir, histogram_freq = 1, write_graph = True, write_grads = True)callback_list = [checkpoint, tensorboard]обучаем модельmodel.fit(train_generator,  validation_data = valid_generator,  epochs = 1, # в оригинальной статье стоит 3  steps_per_epoch = len(train_generator),  callbacks = callback_list)

Затем нам надо написать функцию обучения на positive изображениях (причина описана выше). Она представлена ниже

# Для обучения модели были созданы файлы rsna_train_pos.tfrecord и rsna_val_pos.tfrecordКлассы изображений записываются в формате .names (в нашем случае)это классы "opacity" и "no_opacity"model = train(dataset = 'PATH_TO_TFRECORD/rsna_train_pos.tfrecord',          val_dataset = 'PATH_TO_TFRECORD/rsna_val_pos.tfrecord',          backbone = 'chexnet',          classes = 'PATH_TO_CLASSES/RSNA_VOC.names',           size = 416,          epochs = 30,          batch_size = 16,          learning_rate = 1e-4,          num_classes = 1)

После обучения веса модели бинарной классификации сохраняются в виде файла формата hdf5.

Результаты обучения

Ниже представлен результат обучения данной архитектуры (YOLOv3 с классификационной моделью CheXNet).

С параметрами learning_rate = 1e-4, epoch = 20

Посмотрим на loss'ы

Аналогично для learning_rate = 1e-4, epochs = 30

Посмотрим на loss'ы

Выводы

  • Над исходными данными был совершен процесс аугментации с целью увеличения количества данных.

  • Анализ количество обучаемых параметров моделей CheXNet и DarkNet показал, что таких параметров меньше у модели CheXNet, что делает ее обучение быстрым по сравнению с обучением модели DarkNet.

  • Архитектура с классификационной моделью CheXNet была обучена 1 эпоху на изображениях всех классов, а затем 20 эпох и 30 эпох на изображениях, содержащих признаки пневмонии.

  • Эксперименты показали, что с увеличением числа epoch, растет и точность предсказаний модели.

Перспективы

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

  • обучения с изменением параметров (увеличения количества эпох, значения learning_rate)

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

  • модель CheXNet можно заменить другую классификационную модель

Ссылки

Подробнее..

Перевод Полезные приемы и лучшие практики от Kaggle

27.05.2021 18:04:31 | Автор: admin

В преддверии старта курса "Machine Learning. Professional" делимся традиционным переводом полезного материала.


Из этой статьи вы узнаете то, что можно узнать, только потратив множество часов на изучение и практику.

Об этом проекте

Kaggle прекрасное место. Это золотая жила для дата-сайентистов и инженеров машинного обучения. Не так много платформ, на которых вы можете найти высококачественные, эффективные, воспроизводимые, отобранные экспертами, потрясающие примеры коды в одном месте.

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

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

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

А в остальном дело за малым, мое ОКР практически вынуждает меня выкладывать все имеющиеся у меня знания в области науки о данных. Таким образом я представляю вам первый выпуск моего еженедельника Полезные приемы и лучшие практики от Kaggle. На протяжении всей серии я буду писать обо всем, что может быть полезно во время типичного рабочего процесса в области data science, включая фрагменты кода распространенных библиотек, передовые практики, которым следуют ведущие эксперты в области с Kaggle, и т. д. - все, что я узнал за прошедшую неделю. Наслаждайтесь!

1. Отображение только нижней части корреляционной матрицы

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

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

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

houses = pd.read_csv('data/melb_data.csv')# Calculate pairwise-correlationmatrix = houses.corr()# Create a maskmask = np.triu(np.ones_like(matrix, dtype=bool))# Create a custom diverging palettecmap = sns.diverging_palette(250, 15, s=75, l=40,                             n=9, center="light", as_cmap=True)plt.figure(figsize=(16, 12))sns.heatmap(matrix, mask=mask, center=0, annot=True,             fmt='.2f', square=True, cmap=cmap)plt.show();

Полученный в результате график намного легче интерпретировать, и он не так отвлекает избыточными данными. Сначала мы строим корреляционную матрицу, используя метод DataFrame .corr. Затем мы используем функцию np.ones_like с dtype, установленным в bool, чтобы создать матрицу значений True с той же формой, что и наш DataFrame:

>>> np.ones_like(matrix, dtype=bool)[:5]array([[ True, True, True, True, True, True, True, True, True, True, True, True, True], [ True, True, True, True, True, True, True, True, True, True, True, True, True], [ True, True, True, True, True, True, True, True, True, True, True, True, True], [ True, True, True, True, True, True, True, True, True, True, True, True, True], [ True, True, True, True, True, True, True, True, True, True, True, True, True]])

Затем мы передаем его в функцию Numpy .triu, которая возвращает двумерную логическую маску, которая содержит значения False для нижнего треугольника матрицы. Затем мы можем передать его функции Seaborn heatmap для построения подмножества матрицы в соответствии с этой маской:

sns.heatmap(matrix, mask=mask, center=0, annot=True,               fmt='.2f', square=True, cmap=cmap)

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

2. Добавление отсутствующих значений в value_counts

Небольшой удобный трюк с value_counts заключается в том, что вы можете увидеть долю пропущенных значений в любом столбце, установив dropna в False:

>>> houses.CouncilArea.value_counts(dropna=False, normalize=True).head()NaN           0.100810Moreland      0.085641Boroondara    0.085420Moonee Valley 0.073417Darebin       0.068778Name: CouncilArea, dtype: float64

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

>>> missing_props = houses.isna().sum() / len(houses)>>> missing_props[missing_props > 0].sort_values(ascending=False                                                 BuildingArea 0.474963YearBuilt    0.395803CouncilArea  0.100810Car          0.004566dtype: float64

Сначала найдите пропорции, разделив количество отсутствующих значений на длину DataFrame. Затем вы можете отфильтровать столбцы с 0%, т.е. выбирать только столбцы с пропущенными значениями.

3. Использование Pandas DataFrame Styler

Многие из нас никогда не осознают огромный неиспользованный потенциалpandas. Недооцененной и часто упускаемой из виду особенностью pandas является ее способность стилизовать свои DataFrameы. Используя атрибут .style для DataFrameов pandas, вы можете применять к ним условные конструкции и стили. В качестве первого примера давайте посмотрим, как можно изменить цвет фона в зависимости от значения каждой ячейки:

>>> diamonds = sns.load_dataset('diamonds')>>> pd.crosstab(diamonds.cut, diamonds.clarity).\                style.background_gradient(cmap='rocket_r')

Это практически тепловая карта без использования функции Seaborn heatmap. Здесь мы подсчитываем каждую комбинацию огранки и чистоты алмаза с помощью pd.crosstab. Используя .style.background_gradient с цветовой палитрой, вы можете легко определить, какие комбинации встречаются чаще всего. Только из приведенного выше DataFrame мы можем видеть, что большинство алмазов имеют идеальную огранку, а самая распространенная комбинация - с типом чистоты VS2.

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

>>> pd.crosstab(diamonds.cut, diamonds.clarity,          aggfunc=np.mean, values=diamonds.price).\          style.background_gradient(cmap='flare')

На этот раз мы агрегируем цены на бриллианты для каждой комбинации огранки и чистоты. Из стилизованного DataFrame мы видим, что самые дорогие бриллианты имеют чистоту VS2 или премиальную огранку. Но было бы лучше, если бы мы могли отображать агрегированные цены, округляя их. Мы также можем изменить это с помощью .style:

>>> agg_prices = pd.crosstab(diamonds.cut, diamonds.clarity,                         aggfunc=np.mean, values=diamonds.price).\                         style.background_gradient(cmap='flare')>>> agg_prices.format('{:.2f}')

Изменив в методе .format строку формата {:.2f} мы указываем точность в 2 числа после запятой.

С .style предел - ваше воображение. Имея базовые познания в CSS, вы можете создавать собственные функции стилизации под свои нужды. Ознакомьтесь с официальным руководством pandas для получения дополнительной информации.

4. Настройка глобальных конфигураций графиков с помощью Matplotlib

При выполнении EDA (Exploratory Data Analysis) вы обнаружите, что сохраняете некоторые настройки Matplotlib одинаковыми для всех ваших графиков. Например, вы можете захотеть применить настраиваемую палитру для всех графиков, использовать более крупные шрифты для меток, изменить расположение легенды, использовать фиксированные размеры фигур и т. д.

Указание каждого настраиваемого изменения графиков может быть довольно скучной, повторяющейся и длительной задачей. К счастью, вы можете использовать rcParams из Matplotlib для установки глобальных конфигураций для ваших графиков:

from matplotlib import rcParams

rcParams - это просто старый словарь Python, содержащий настройки по умолчанию для Matplotlib:

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

# Remove top and right spinesrcParams['axes.spines.top'] = FalsercParams['axes.spines.right'] = False# Set fixed figure sizercParams['figure.figsize'] = [12, 9]# Set dots per inch to 300, very high quality imagesrcParams['figure.dpi'] = 300# Enable autolayoutrcParams['figure.autolayout'] = True# Set global fontsizercParams['font.style'] = 16# Fontsize of ticklabelsrcParams['xtick.labelsize'] = 10rcParams['ytick.labelsize'] = 10

Вы можете избежать большого количества повторяющейся работы, установив все сразу после импорта Matplotlib. Просмотреть все другие доступные настройки можно, вызвав rcParams.keys().

5. Настройка глобальных конфигураций Pandas.

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

  • get_option() / set_option() - получить/установить значение одного параметра.

  • reset_option() - сбросить один или несколько параметров до значений по умолчанию.

  • description_option() - вывести описание одного или нескольких параметров.

  • option_context() - выполнить блок кода с набором параметров, которые после выполнения возвращаются к предыдущим настройкам.

Все параметры имеют имена без учета регистра и находятся с помощью регулярного выражения из под капота. Вы можете использовать pd.get_option, чтобы узнать, какое поведение используется по умолчанию, и изменить его по своему вкусу с помощью set_option:

>>> pd.get_option(display.max_columns)20

Например, указанный выше параметр управляет количеством столбцов, которые должны отображаться, когда в DataFrame очень много столбцов. Сегодня большинство наборов данных содержит более 20 переменных, и всякий раз, когда вы вызываете .head или другие функции отображения, pandas ставит раздражающее многоточие, чтобы обрезать результат:

>>> houses.head()

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

>>> pd.set_option(display.max_columns, None)

Выше я полностью убираю ограничение:

>>> houses.head()

Вы можете вернуться к настройке по умолчанию с помощью:

pd.reset_option(display.max_columns)

Как и в столбцах, вы можете настроить количество отображаемых строк по умолчанию. Если вы установите для display.max_rows значение 5, вам не придется все время вызывать .head():

>>> pd.set_option(display.max_rows, 5)>>> houses

В настоящее время plotly становится очень популярным, поэтому было бы неплохо установить его как график по умолчанию для pandas. Поступая таким образом, вы будете получать интерактивные графические диаграммы всякий раз, когда вы вызываете .plot для DataFrameов pandas:

pd.set_option(plotting.backend, plotly)

Обратите внимание, что для этого вам необходимо установить plotly.

Если вы не хотите испортить поведение по умолчанию или просто хотите временно изменить определенные настройки, вы можете использовать pd.option_context в качестве диспетчера контекста. Временное изменение поведения будет применено только к блоку кода, который следует за оператором. Например, если есть большие числа, у pandas есть раздражающая привычка преобразовывать их в стандартную нотацию. Вы можете временно избежать этого, используя:

>>> df = pd.DataFrame(np.random.randn(5, 5))>>> pd.reset_option('display.max_rows')>>> with pd.option_context('float_format', '{:f}'.format):        df.describe()

Вы можете увидеть список доступных параметров в официальном руководстве пользователя pandas.


Узнать подробнее о курсе "Machine Learning. Professional"

Подробнее..

Перевод Магия Ensemble Learning

17.10.2020 14:04:36 | Автор: admin

Привет, Хабр!Приглашаем Data Engineer'ов и специалистов по Machine Learning на бесплатный Demo-урокВывод ML моделей в промышленную среду на примере онлайн-рекомендаций. А также мы публикуем статью Luca Monno Head of Financial Analytics at CDP SpA.


Одним из наиболее полезных и простых методов машинного обучения является Ensemble Learning. Ensemble Learning это метод, лежащий в основе XGBoost, Бэггинга, Случайного Леса и многих других алгоритмов.

На Towards Data Science есть много классных статей, но я выбрал две истории (первая и вторая), которые мне больше всего понравились. Так зачем же писать еще одну статью про EL? Потому что я хочу показать вам, как это работает на простом примере, который дал мне понять, что здесь нет никакого волшебства.

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

У меня было две разных модели (два слабых обучающих алгоритма) с показателями out-of-sample R равными 0,90 и 0,93 соответственно. Перед тем, как посмотреть на результат, я думал, что получу R где-то между двумя изначальными значениями. Другими словами, я считал, что EL можно использовать, чтобы модель работала не так плохо, как самая худшая, но и не настолько хорошо, как могла бы работать лучшая модель.

К моему величайшему удивлению, результаты простого усреднения предсказаний дали R в 0,95.

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

Что такое Ensemble Learning

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

С помощью регрессии можно усреднить показатели имеющихся моделей.

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

Почему EL работает лучше

Основная причина, по которой EL работает лучше, заключается в том, что у каждого предсказания есть ошибка (знаем мы это из теории вероятности), объединение двух предсказаний может помочь уменьшить ошибку, и, соответственно, улучшить показатели производительности (RMSE, R и т.д.).

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

Если смотреть на показатель R, то у первого и второго обучающего алгоритма он будет равен -0.01, 0.22, соответственно, тогда как у ансамбля он будет равен 0.73.

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

Когда EL работает лучше

Давайте рассмотрим два обучающих алгоритма, работающих с одинаковыми данными.

Здесь видно, что объединение двух моделей не сильно улучшило производительность. Изначально для двух обучающих алгоритмов показатели R был равны -0,37 и 0,22, соответственно, а для ансамбля получилось -0,04. То есть модель EL получила среднее значение показателей.

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

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

Однородные и разнородные модели

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

Сравнение с диверсификацией портфеля

EL работает по аналогии с диверсификацией в теории портфеля, но тем лучше для нас.

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

Цитируя Уоррена Баффета:

Диверсификация это защита от невежества, для того, кто не знает, что он делает, она [диверсификация] имеет очень мало смысла.

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

Подведем итоги

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

Если у вас есть две или более модели, которые хорошо работают, не выбирайте между ними: используйте их все (но с осторожностью)!


Интересно развиваться в данном направлении? Запишитесь на бесплатный Demo-урокВывод ML моделей в промышленную среду на примере онлайн-рекомендаций и участвуйте в онлайн-встрече с Андреем Кузнецовым Machine Learning Engineer в Mail.ru Group.

Подробнее..

Категории

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

  • Имя: Макс
    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