Полный код моего победившего решения на хакатоне Analytics Vidhya Data Science Hackathon

Хакатон Data Science Hackathon - это решающее поле битвы для начинающих исследователей данных. Цель любого хакатона - позволить своему воображению расширить границы ваших знаний и навыков в этой области.

После двух лет самообучения я решил проверить свои навыки на реальных условиях битвы с другими специалистами по данным. Поэтому я решил принять участие в онлайн-хакатонах. Я решил составить короткий список веб-сайтов и вскоре выбрал Analytics Vidhya, чтобы начать свое путешествие по хакатону.

Мой первоначальный старт был недостаточно хорош. Но мне удалось попасть в топ-50 на одном из первых четырех онлайн-хакатонов.

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

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

В августе Analytics Vidhya запустила хакатон Data Supremacy.

Ссылка на хакатон: https://datahack.analyticsvidhya.com/contest/the-data-supremacy/

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

Проблема

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

Размер набора данных:

Форма поезда: (18359, 14)
Форма теста: (15021, 13)

Цель: двоичная классификация → 0/1

Всего предоставлено функций: 13 функций [столбцы]

Понимание особенностей

Это важный шаг, который почти все пропускают.

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

После тщательного изучения всех функций я решил начать с кода.

Библиотеки

Ниже приведены библиотеки, которые я использовал на протяжении всего хакатона.

Три основные библиотеки, которые я использую почти везде.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

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

import xgboost as xgb
from xgboost.sklearn import XGBClassifier,XGBRegressor
import catboost
from catboost import CatBoostClassifier
from sklearn.preprocessing import LabelEncoder , MinMaxScaler
from sklearn.cross_validation import KFold , cross_val_score
from sklearn.metrics import accuracy_score , roc_auc_score,confusion_matrix
from sklearn.grid_search import GridSearchCV
from sklearn import metrics
from sklearn.ensemble import RandomForestRegressor,RandomForestClassifier
from sklearn.neighbors import KNeighborsRegressor,KNeighborsClassifier
from sklearn.svm import SVR
from sklearn.linear_model import LogisticRegression,LinearRegression
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.model_selection import RandomizedSearchCV

Как выглядят данные?

Пришло время прочитать обучающий и тестовый csv.

train_data = pd.read_csv('train_FG6BvLg.csv')
test_data = pd.read_csv('test_wovud0B.csv')
print("Train Shape : {}\nTest Shape : {}".format(train_data.shape,test_data.shape))

train_data.head()

Пришлось разделить снимок на две картинки, так как на одной картинке его не будет видно.

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

Предварительная обработка

Около 75% моего общего времени было посвящено предварительной обработке данных.

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

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

Во-первых, пора убрать текстовую часть из столбца город.

city_21 → 21

train_city = train_data.city.str.split("_")
test_city = test_data.city.str.split("_")

Он будет содержать список с двумя значениями (первое - «город», а второе - присвоенный номер).

i=0
for x in train_city:
    train_data.loc[i,'city'] = x[1]
    i=i+1

Вы можете увидеть сделанные изменения. Я сделал то же самое для test_data.

Затем я решил проверить, есть ли в данных отсутствующие значения.

train_data.isnull().sum()

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

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

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

target = train_data.pop('target')
enrollee_id = test_data['enrollee_id']
combined_data = train_data.append(test_data)

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

combined_data.city= combined_data.city.astype('int')

Я заменил столбец пол на целую категорию:

combined_data.gender.replace({'Male':2,'Female':0,'Other':1},inplace=True)

Я сделал то же самое для еще двух столбцов:

combined_data.enrolled_university.replace({'no_enrollment':1,'Part time course':2,'Full time course':3},inplace=True)
combined_data.major_discipline.replace({'No Major':0,'Other':1,'Arts':2,'Humanities':3,'Business Degree':4,'STEM':5},inplace=True)

Столбцы опыт и размер компании содержали строки типа «‹ 10 »,« ›500» и «500–1000». Это не позволило сделать столбец целым.

combined_data.experience.replace({'>20':21,'<1':0},inplace=True)

Чтобы проиллюстрировать, как выглядел размер company_size:

combined_data.company_size.value_counts()

combined_data.company_size.replace({'<10':0,'10/49':1,'50-99':2,'100-500':3,'500-999':4,'1000-4999':5,'5000-9999':6,'10000+':7},inplace=True)

Столбец company_type имел категориальные значения:

combined_data.company_type.value_counts()

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

combined_data.company_type.replace({'Other':0,'Early Stage Startup':1,'Funded Startup':2,'NGO':3,'Public Sector':4,'Pvt Ltd':5},inplace=True)

Я также сделал несколько замен в оставшихся столбцах.

combined_data.last_new_job.replace({'never':0,'>4':5,'nan':1},inplace=True)
combined_data.relevent_experience.replace({'No relevent experience':0,'Has relevent experience':1},inplace=True)

А как насчет пропущенных значений?

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

combined_data.major_discipline.fillna(0,inplace=True)
combined_data.company_type.fillna(0,inplace=True)
combined_data.company_size.fillna(0,inplace=True)
combined_data.enrolled_university.fillna(0,inplace=True)
combined_data['enrolled_university.1'].fillna(0,inplace=True)

Поскольку данные имели разный диапазон значений. Я решил стандартизировать данные, прежде чем перейти к нашей модели.

Я использовал MinMaxScaler, который позволяет мне настраивать минимальное и максимальное значение диапазона.

values  =  combined_data.values
scalar = MinMaxScaler(feature_range=(0,5))
x_scaled = scalar.fit_transform(values)
combined_data = pd.DataFrame(x_scaled,columns=combined_data.columns)

Чтобы перейти к нашей модели, нам нужно удалить столбец ID.

combined_data.drop('enrollee_id',axis=1,inplace=True)

Модель

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

Я начал с RandomForest на CatBoost.

clf = CatBoostClassifier(iterations=200,depth=4,eval_metric='AUC',l2_leaf_reg=9,learning_rate=0.1)

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

def model_fit(alg, dtrain, target,dtest):
    xgb_param = alg.get_xgb_params()
    xgtrain = xgb.DMatrix(dtrain.values, label=target)
    cvresult = xgb.cv(xgb_param, xgtrain, num_boost_round=alg.get_params()['n_estimators'], nfold=5,
        early_stopping_rounds=50)
    
    #alg.fit(dtrain,target,use_best_model=True,eval_set=(x_val,y_val))
    alg.fit(dtrain,target)
    print("Model Report")
    print("Accuracy is {}".format(alg.score(x_val,y_val)))
    
    feat_imp = pd.Series(alg.feature_importances_,dtrain.columns).sort_values(ascending=False)
    feat_imp.plot(kind='bar', title='Feature Importances')
    plt.ylabel('Feature Importance Score')
    
    y_pred = alg.predict_proba(dtest)[:,1]
    return y_pred

Приведенный выше фрагмент кода включает подгонку модели и построение графика важности функции в одной функции.

Наконец идет моя модель и ее параметр:

clf = XGBClassifier(
 learning_rate =0.3,
 n_estimators=100,
 max_depth=3,
 min_child_weight=1000,
 gamma=0.7,
 subsample=0.45,
 colsample_bytree=0.4,
 objective= 'binary:logistic',
 nthread=1,
 scale_pos_weight=1,
 seed=27,
reg_alpha =0.7,
 random_state=200)

Как вы получили эти параметры?

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

param_test2 = {
 'n_estimators':[150,170,200],
    'learning_rate':[0.03,0.05],
    'max_depth':[4,10,15,25],
    'min_child_weight':[1,2,5,10],
    'subsample':[0.5,0.7,1],
    'colsample_bylevel':[0.45,0.7,1],
    'colsample_bytree':[0.45,0.7,1],
    'gamma':[0,0.1,0.5,1]
}
gsearch2 = GridSearchCV(estimator = XGBClassifier(), 
 param_grid = param_test2, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch2.fit(x_train,y_train)
gsearch2.grid_scores_, gsearch2.best_params_, gsearch2.best_score_

GridSearchCV распечатывает лучшие параметры из перестановки переданных гиперпараметров. (Для выставления очков установлено значение «roc_auc»)

После получения соответствующего гиперпараметра пришло время подогнать модель.

y_pred = model_fit(clf,x_train,y_train,test_data)    

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

Подождите, мы должны оценить нашу модель

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

k_fold = KFold(len(train_data), n_folds=8, shuffle=True, random_state=0)
print(np.mean(cross_val_score(clf, train_data, target, cv=k_fold, n_jobs=1))) 

KFold предоставит индексы обучения / тестирования для разделения данных на наборы для обучения и тестирования. Он разделит набор данных на k последовательных сверток. Поскольку точность модели и kfold очень похожи, модель работает правильно.

Подчинение

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

submission = pd.DataFrame({'enrollee_id': enrollee_id , 'target': y_pred})
submission.to_csv('submission.csv',index=False)

Результат

Вся тяжелая работа окупилась. Я занял 3-е место на хакатоне среди 1000 человек по всему миру. Ниже приведен снимок таблицы лидеров.

Мое имя пользователя: jsiddhesh96

Код можно найти в моем репозитории GitHub: