Исследование данных

Мои первые соревнования kaggle! Испытанию подвергаются все приобретенные навыки. Я создам модель для прогнозирования состояния водяных насосов. Если интересно, данные предоставлены DrivenData и моей записной книжкой jupter, расположенной здесь.

Для начала быстрая визуализация данных по региону и статусу.

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

Наша цель - обучить модель, которая может точно отражать наши прогнозы о 54,3% функциональных, 38,41% нефункциональных и 7,29% функциональных, нуждающихся в ремонте, как видно из приведенного выше графика. Для начала сделаю базовую модель.

Базовый уровень - Модель классификатора случайного леса

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

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

# Import all necessary libraries
# Open read and Save csv files to a dataframe
X_train = pd.read_csv(X_train)
X_test = pd.read_csv(X_test)
y_train = pd.read_csv(y_train)
# Drop 'id' feature for y_train since we're predicting 'status_group
y_train = y_train.drop(columns='id')
# Split to train and validation set
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, random_state=42, test_size=.2)

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

# Encode categorical features
encoder = ce.OrdinalEncoder()
# Fit & Transform
X_train = encoder.fit_transform(X_train)

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

model = RandomForestClassifier(n_jobs=-1, random_state=42)
# Fit training data to model
model.fit(X_train, y_train)
# Encode X_val categorical values
X_val = encoder.transform(X_val)
# Predict X_val
y_pred = model.predict(X_val)
# Print Accuracy Score
print('Validation Set Accuracy Score:', accuracy_score(y_val, y_pred))

Оценка точности составляет 79,65%, это действительно хорошее начало для базовой модели, но мы можем добиться большего!

Исходный уровень - важность перестановки

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

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

Исходный уровень - установка и повторное испытание после снятия

Удалите все функции с важностью перестановки (вес) ≤0, затем повторно установите модель в новый обучающий набор и проверьте ее точность.

# Remove unwanted features, Permutation Importance(weight)<= 0
# Fit training data to model
model.fit(X_train, y_train)
# Predict X_val
y_pred = model.predict(X_val)
# Print Accuracy Score
print('Retest Validation Set Accuracy Score:', accuracy_score(y_val, y_pred))

Наша новая и улучшенная оценка точности составляет 79,81%, что на 0,16% больше. Небольшое улучшение и хорошая базовая модель. Далее я буду реализовывать очистку данных, масштабирование данных и разработку функций.

Лучшая модель - Feature Engineer

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

##### TIME FEATURES #####
# Feature
date_recorded = [2011-1-1, 2013-6-15, 2014-12-30...]
# Feature engineer
week = [1, 21, 52...] # Used pandas datetime to calculate
month = [1 , 6, 12...]
year = [2011, 2013, 2014...]
##### BINARY FEATURE #####
# Feature
construction_year = [1999, 2000, 0, ...]
# Feature engineer
is_construct = [True, True, False, ...] # 1 for yes and 0 for no
##### AGE FEATURE #####
age = year_recorded - construction_year
##### RATE FEATURES #####
#Feature
amount_tsh = [300, 400, 5000...]
population = [150, 0, 1000...]
# Feature engineer
water_per_person = [2, 0, 5...] # amount_tsh / population

Лучшая модель - заполнить NaN (нулевые значения)

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

# Fill NaN - First need to concat X_train and X_test to get the mean then fill it
 
X_train['public_meeting'] = X_train['public_meeting'].fillna(lambda x: random.choice(X_train[X_train['public_meeting'] != np.nan])['public_meeting'])
X_test['public_meeting'] = X_test['public_meeting'].fillna(lambda x: random.choice(X_test[X_test['public_meeting'] != np.nan])['public_meeting'])
--------------------------------------------------------------
X_train['permit'] = X_train['permit'].fillna(lambda x: random.choice(X_train[X_train['permit'] != np.nan])['permit'])
X_test['permit'] = X_test['permit'].fillna(lambda x: random.choice(X_test[X_test['permit'] != np.nan])['permit'])

Лучшая модель - заполните NaN средним / медианным / режимом

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

# Replace 0 with the mean
X_train['age'] = X_train['age'].replace(0, round(pd.concat([X_train,X_test])['age'].mean()))
X_test['age'] = X_test['age'].replace(0, round(pd.concat([X_train,X_test])['age'].mean()))
--------------------------------------------------
X_train['gps_height'] = X_train['gps_height'].replace(0, round(pd.concat([X_train,X_test])['gps_height'].mean()))
X_test['gps_height'] = X_test['gps_height'].replace(0, round(pd.concat([X_train,X_test])['gps_height'].mean()))

Объедините X_train и X_test, чтобы получить среднее значение всего набора данных.

Лучшая модель - падение столбцов

Затем я отказался от функций, основанных на двух принципах: 1) Избыточность 2) Важность перестановки ≤ 0.

Резервирование

  • Quantity_group и количество имеют одинаковые значения - Drop Quantity_group
  • Construction_year и age (функция спроектирована) аналогичны. Год постройки падения

Важность перестановки ≤ 0

  • Recorded_by, id, num_private, amount_tsh, wpt_name и subvillage имели веса меньше 0.
# Features to dropped
drop_cols = ['quantity_group','construction_year','recorded_by','id','num_private','amount_tsh', 'wpt_name','subvillage','management_group']
# Drop from train and test data set
X_train = X_train.drop(columns=drop_cols)
X_test = X_test.drop(columns=drop_cols)
# Drop 'id' use 'status_group' for prediction
y_train = y_train.drop(columns='id')

Лучшая модель - масштаб

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

continuous_col = ['population','gps_height','age','week','month']
scaled = MinMaxScaler()
X_train[continuous_col] = scaled.fit_transform(X_train[continuous_col])

Лучшая модель - перекрестная проверка рандомизированного поиска

Прошел 9 итераций по разделению обучающих данных на 5 сегментов. Из 5 ковшей 1-й ковш был набором для мини-тестов, а остальные 4 ковша - для тестового мини-поезда и так далее. На каждой итерации мои param_distributions проверялись, чтобы найти лучший.

После многих попыток настройки гиперпараметров. Моими лучшими параметрами были n_estimator 325 и максимальная глубина 20:

param_distributions = {
    'n_estimators': [300,325,350],
    'max_depth': [15,20,25]
}
# Define the model and input inside RSCV
model = RandomForestClassifier(n_jobs=-1, random_state=42)
# RSCV
search = RandomizedSearchCV(
    estimator=model,
    param_distributions=param_distributions,
    scoring='accuracy',
    n_iter=9,
    n_jobs=-1,
    cv=5,
    verbose=10,
    return_train_score=True,
    random_state=42
)
# Fit to training data
search.fit(X_train, y_train)
# Accuracy score
print('Training Accuracy Score:', search.best_score_)

Наилучшая оценка точности обучающих данных составляет 81,01%.

# Fit X_val
X_val = encoder.transform(X_val)
# Partial scale x_val
X_val[continuous_col] = scaled.fit_transform(X_val[continuous_col])
# Test Validation
best = search.best_estimator_
y_pred = best.predict(X_val)
print('Validation Set Accuracy Score:', accuracy_score(y_val, y_pred))

Набор для проверки вернул результат 81,35%. Хорошо, что не подошло ни занижено, ни переборщило.

Лучшая модель - перепроверьте важность перестановки

Последняя проверка для проверки признака, важность перестановки (вес) ≤ 0 и ее нет.

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

Сравнение - фактическое прогнозирование по сравнению с прогнозом

Похоже, наша модель неплохо справилась. Разница в предсказании функционального предсказания составляет около 7%, нефункционального - около 4%, а функционального восстановления потребности - около 3,5%.

Что я узнал!

Добавляйте, удаляйте, настраивайте, промывайте и повторяйте.

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