Страницы

Поиск по вопросам

среда, 26 февраля 2020 г.

Как подобрать признаки, которые влияют на целевую переменную больше других?

#python #анализ_данных #feature_engineering


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

Как вариант решения, кажется интересным следующее:


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


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

Как правильно построить подобный алгоритм, чтобы учесть взаимное влияние признаков
на целевую переменную?
    


Ответы

Ответ 1



Вообще-то говоря, вопрос отбора признаков - не такая уж простая задача, как кажется. Кстати, в англоязычной литературе эта задача называется "feature selection". Ваше решение, как одно из наиболее простых и интуитивных - имеет право на жизнь ("красиво" она описана вот тут - http://www.machinelearning.ru/wiki/images/2/21/PZAD2017_09_featureselection.pdf). Если же вы хотите более глубоко или математически корректно отбирать признаки, то в первую очередь надо понять - а зачем? Вариантов - несколько. Например - упрощение или вернее сокращение времени на обучение, снижение риска переобучения, повышение точности (т.к. наличие неинформативных признаков ее снижает) и пр. Далее надо понимать, сколько у вас признаков изначально - десяток (например, в мед.диагностике) или десяток тысяч(в классификации изображений. В анализе текстов - там свой, особый случай. Естественно, надо понимать, у вас задача классификации или кластеризации, и.т.д. Для каждого случая свои методы, подходы и единого варианта решения - тем более, в отрыве от семантики задачи - не существует. Обзор современных методов отбора признаков можно найти вот тут: https://www.sciencedirect.com/user/login?returnURL=/getaccess/pii/S0045790613003066 или тут: http://www.mathnet.ru/links/3a35d35d65879c35692c5cfa56af64af/ia429.pdf Вот тут, описание проблем с неплохим "roadmap" для исследователя: https://machinelearningmastery.com/an-introduction-to-feature-selection/ У Воронцова есть отдельная лекция по теме: https://www.youtube.com/watch?v=n4qKbFd25Sk Тут тоже вроде неплохо изложен материал: https://ru.coursera.org/lecture/unsupervised-learning/odnomiernyi-otbor-priznakov-t0xdz Неплохой обзор и описанием инструментов - тут: https://www.sciencedirect.com/user/login?returnURL=/getaccess/pii/S0045790613003066 Ну, и я оставил за скобками Метод главных компонент, который также может использоваться говоря математическим языком - для понижения размерности признакового пространства, а говоря человеческим языком - специального сокращения количества признаков.

Ответ 2



Ваш подход кажется мне вполне рабочим если исключить мультиколлинеарность признаков и если в качестве метрики вы планируете обучать и проверять модель для каждого набора столбцов. Как указано в некоторых ссылках из ответа @passant - "удачные" наборы признаков могут отличаться для разных алгоритмов классификации. Поэтому многие методы из sklearn.feature_selection ожидают estimator (в вашем случае объект соответствующего классификатора) в качестве входного параматра. В этом может помочь sklearn.feature_selection.SelectFromModel, который в качестве estimator принимает любой алгоритм у которого после обучения модели присутствует атрибут feature_importances_ или coef_.

Ответ 3



Попробовал несколько способов подбора параметров. Как оказалось, мой «наивный» подход показал лучший результат на моих данных, потому что: Методы основанные на деревьях задают важность признаков, но не оптимальный набор. Методы рекурсивного исключения принимают на вход «алгоритм», который будет использован для построения модели. В случае категориальных данных и логистической регрессии мы передаем очень много созданных «dummy» признаков. Я же искал метод, который найдет набор признаков, которые дадут лучший результат. Как оказалось, этот набор отличается от первых наиболее «важных» N признаков. Ниже пример кода некоторых способов, которые я попробовал: Простой перебор prepare_data_to_process — преобразует кат. данные, использует RobustScaler для вещественных и т.д. На выходе pd.DataFrame и имена используемых признаков, разделенные по типу. def test_feature_set(data_categorical, data_numerical, y): X, numeric_cols, categorical_cols = prepare_data_to_process( data_categorical, data_numerical, data_categorical.columns, data_numerical.columns ) if X.shape[0] == 0 or X.shape[1] == 0: return [-1.] return cross_val_score(LogisticRegression(), X, y) def leave_only_best(data_categorical, data_numerical, cat_columns, num_columns, labels): baseline = test_feature_set( data_categorical[cat_columns], data_numerical[num_columns], labels ) current_feature_num = len(cat_columns) + len(num_columns) print "\r\n\r\nBaseline: ", np.mean(baseline), "feature num:", current_feature_num num_result_set = list() cat_result_set = list() for index in range(len(num_columns)-1): columns = num_columns[:index] + num_columns[index+1:] scores = test_feature_set( data_categorical, data_numerical[columns], labels ) num_result_set.append(( num_columns[index], scores )) print "Without %s:" % num_columns[index], np.mean(scores) for index in range(len(cat_columns)-1): columns = cat_columns[:index] + cat_columns[index+1:] scores = test_feature_set( data_categorical[columns], data_numerical, labels ) cat_result_set.append(( cat_columns[index], scores )) print "Without %s:" % cat_columns[index], np.mean(scores) new_num_columns = list() new_cat_columns = list() for column, scores in num_result_set: if np.mean(baseline) >= np.mean(scores): new_num_columns.append(column) for column, scores in cat_result_set: if np.mean(baseline) >= np.mean(scores): new_cat_columns.append(column) new_feature_num = len(new_num_columns) + len(new_cat_columns) new_baseline = test_feature_set( data_categorical[new_cat_columns], data_numerical[new_num_columns], labels ) if np.mean(new_baseline) < np.mean(baseline): return num_columns, cat_columns if new_feature_num < current_feature_num: return leave_only_best( data_categorical, data_numerical, new_cat_columns, new_num_columns, labels ) return new_num_columns, new_cat_columns def find_optimal_feature_set(scoring): data_categorical, data_numerical, y = load_and_split() return leave_only_best( data_categorical, data_numerical, data_categorical.columns.tolist(), data_numerical.columns.tolist(), y ) num_columns, cat_columns = find_optimal_feature_set(scoring) Поиск признаков, которые вносят наибольший вклад с помощью ExtraTreesClassifier. Код заимствован из спавки scikit-learn def most_important_features(): data_categorical, data_numerical, y = load_and_split() encoder = LabelEncoder() data_categorical = data_categorical.apply(lambda x: encoder.fit_transform(x)).astype('str') data_categorical = fill_missed_categorical(data_categorical).astype('category') X = data_numerical.join(data_categorical) forest = ExtraTreesClassifier(n_estimators=250, random_state=0) forest.fit(X, y) importances = forest.feature_importances_ std = np.std([tree.feature_importances_ for tree in forest.estimators_], axis=0) indices = np.argsort(importances)[::-1] print("Feature ranking:") for f in range(X.shape[1]): print("%d. feature %s (%f)" % (f + 1, X.columns[indices[f]], importances[indices[f]])) plt.figure(figsize=(16,9)) plt.title("Feature importances") plt.bar(range(X.shape[1]), importances[indices], color="r", yerr=std[indices], align="center") plt.xticks(range(X.shape[1]), [f for f in range(1, X.shape[1]+1)]) plt.xlim([-1, X.shape[1]]) plt.show() return indices, importances, X.columns indices, importances, columns = most_important_features() Должно получиться что–то вроде такого: Ваше изображения будут прономерованные имена признаков с численным «вкладом». По графику можно выбрать «диапазоны» важности, после чего в цикле добавлять признаки и смотреть как меняется время работы алгоритма и итоговый результат. RFECV — рекурсивное исключение признаков Позволяет отсортировать признаки по их важности (группами!) def test_rfecv(): data_categorical, data_numerical, y = load_and_split() X, dummy_train, scaler, numeric_cols, categorical_cols = prepare_data_to_process( data_categorical, data_numerical, [], data_numerical.columns ) print "result data:", X.shape selector = RFECV(LogisticRegression(), cv=get_cv(), scoring=scoring) selected = selector.fit(X, y) return selector, selected, X.columns selector, selected, select_columns = test_rfecv() for item in zip(select_columns, selected.ranking_): print item

Комментариев нет:

Отправить комментарий