воскресенье, 27 марта 2022 г.

Машинном обучение с Python в розничной торговле : отбор признаков

Применение машинного обучения начинается с общей постановки задачи. Определяем, на какой вопрос надо найти ответ и какая информация для этого нужна. Переходя к терминологии машинного обучения : определяем целевую функцию (как правило используется слово "target") и предикторы или признаки ("features"). Правильный выбор признаков является определяющим для успешного решения задачи. Их должно быть достаточно чтобы наша модель учитывала все особенности задачи, но их не должно быть слишком много, чтобы "за деревьями мы не увидели леса". 
В этой статье разберем задачу отбора признаков на следующей примере : в период с января по март происходит большая часть увольнений торгового персонала, необходимо создать модель, которая по ряду показателей сотрудника определяла в нем потенциального кандидата на увольнение. При этом задача отбора признаков в данном случае является вполне самодостаточной, так как ее решение подскажет руководству компании на что необходимо в первую очередь обращать внимание, чтобы не допустить увольнение персонала. В качестве потенциальных кандидатов на признаки были отобраны следующие показатели : пол (обозначение в данных - sex), стаж работы в полных месяцах (experience), количество отработанных смен (wd - период с января по март 2021 года), среднесменная заработная плата (salary) и индикатор увольнения (quit 0 - работает, 1-уволился) 
Загружаем и смотрим на данные : первые и последние пять строк.

df = pd.read_excel("data\shop_quit.xlsx")
pd.concat([df.head(), df.tail()])

sexexperiencewdsalaryquit
0female184.50039.00037380
1female34.50047.00030790
2female79.40042.50028870
3male4.70047.50027420
4female56.30064.00034830
116male28.10026.00026510
117female19.00063.00031770
118female18.90049.00022390
119male46.6004.00047310
120female21.50017.50021630

#Перекодируем пол
from sklearn.preprocessing import LabelEncoder

class_le = LabelEncoder ()
df.sample(5)
df.sex=class_le.fit_transform(df.sex.values)
sexexperiencewdsalaryquit
61042.30051.50033240
2079.40042.50028870
7611.60013.50016490
7501.50034.00028060
73064.70051.00025940
Посмотрим на распределение показателей среди работающих и уволившихся

plt.figure(figsize=(15, 5))
plt.subplot(131)
sns.boxplot(x="quit", y="wd", data=df)
plt.subplot(132)
sns.boxplot(x="quit", y="salary", data=df)
plt.subplot(133)
sns.boxplot(x="quit", y="experience", data=df)



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

df_group = df.groupby('quit')
df_group.agg(np.mean)

sexexperiencewdsalary
quit
00.27233.73138.0533,021.495
10.55617.23324.6942,392.000
Процент уволившихся среди мужчин в два раза выше, также в два раза выше стаж, меньше всего отличается среднесменная заработная плата. Перед переходом к непосредственно к задаче сделаем некоторые необходимые действия :

#Выделяем признаки и метки кластеров
labels_col = "quit"
features = df.drop(labels_col, axis=1)
labels = df[labels_col]
#Выделяем название признаков
features_names=features.columns
features_names


Index(['sex', 'experience', 'wd', 'salary'], dtype='object')

Теперь переходим непосредственно к нашей задаче - отбора признаков. 

Существует три типа методов отбора признаков: фильтрующие, циклические

 и вложенные. 

Фильтрующие методы отбирают наилучшие признаки, изучая их статистические 

свойства. Циклические (wrapper) методы для поиска подмножества признаков, которые 

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

признаков как часть или как продолжение процесса тренировки 

обучающегося алгоритма

Методы фильтрации

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

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

from sklearn.feature_selection import VarianceThreshold
# Создаем обработчик
vt = VarianceThreshold()
# Рассчитываем и выводим дисперсии признаков c сортировкой
vt.fit_transform(features)
feature_variances = vt.variances_
for var, name in sorted(zip(vt.variances_, features_names), key=lambda x: x[0], reverse=True):        
    print(f'{name:>10} variance = {var:.5f}')


        salary variance = 4848.00000
        experience variance = 184.20000
        wd variance = 67.50000
        sex variance = 0.21542

#Отмасштабируем признаки от нуля до единицы и заново посмотрим
 #на результат

from sklearn.preprocessing import MinMaxScaler

features_ss = MinMaxScaler().fit_transform(features)
vt.fit_transform(features_ss)
for var, name in sorted(zip(vt.variances_, features_names), key=lambda x: x[0], reverse=True):    
    print(f'{name:>10} variance = {var:.3f}')
        sex variance = 0.215
        wd variance = 0.048
    salary variance = 0.039
experience variance = 0.038



Как видно, такой анализ не дает оснований, для удаления какого-нибудь признака,
т.к. нет показателя с низкой дисперсией. Переходим к другим, более
"продвинутым" одномерным статистическим методам фильтрации. С помощью
одномерных статистик мы определяем наличие статистически значимой взаимосвязи
между каждым признаком и зависимой переменной. Затем отбираем признаки, сильнее
всего связанные с зависимой переменной (имеющие уровень значимости, не
превышающий заданного порогового значения). В случае классификации эта
процедура известна как дисперсионный анализ (ANOVA). Ключевым свойством этих
тестов является то, что они являются одномерными, то есть они рассматривают
каждую характеристику по отдельности. Чтобы осуществить одномерный отбор
признаков в scikit-learn, вам нужно выбрать тест, обычно либо f_classif (по
умолчанию) для классификации или f_regression для регрессии. f_classif
используется для вычисления статистического показателя F дисперсионного
анализа (ANOVA) с каждым признаком и вектором целей. Оценочные значения F
проверяют, различаются ли значимо средние значения для каждой группы, когда мы
группируем этот числовой признак по вектору целей.
Все методы исключения параметров используют пороговое значение,
чтобы исключить все признаки со слишком высоким р-значением (высокое p-значение
указывает на то, что признак вряд ли связан с зависимой переменной). Методы
отличаются способами вычисления этого порогового значения, самым простым из
которых являются SelectKB, выбирающий фиксированное число k признаков, и
SelectPercentile,выбирающий фиксированный процент признаков.

Продемонстрируем эти методы на нашем наборе данных. Используем метод SelectKBest для вычисления оценок для всех признаков и используем функцию оценки по умолчанию f_classif. Задаем, что все признаки должны быть сохранены, установив k='all', чтобы можно было вывести оценки всех показателей. Если установить k равным n, то будут сохранены только лучшие n признаков.

from sklearn.feature_selection import SelectKBest
skb = SelectKBest(k='all')
skb.fit(features, labels)
for var, name in sorted(zip(skb.scores_, features_names), key=lambda x: x[0], reverse=True):
    print(f'{name:>18} score = {var:.3f}') 

            wd score = 13.869
            salary score = 6.846
               sex score = 5.910
        experience score = 3.284
В следующем примере мы меняем score_func на mutual_info_classif, что определяет
количественную зависимость между признакам и меткой. Когда две функции
независимы, эта статистика стремится к нулю, и по мере увеличения зависимости
статистика также увеличивается.
from sklearn.feature_selection import mutual_info_classif skb = SelectKBest(mutual_info_classif, k='all') skb.fit(features, labels)

for var, name in sorted(zip(skb.scores_, features_names), key=lambda x: x[0],
reverse=True):

print(f'{name:>18} score = {var:.3f}')
                wd score = 0.129
        experience score = 0.075
               sex score = 0.011
            salary score = 0.008

Циклические методы

Эти методы рассматривают выбор набора признаков как задачу поиска, где
оцениваются и сравниваются различные наборы признаков с другими наборами.
Оценки присваиваются на основе точности модели. Поскольку мы должны обучать
модель для каждой комбинации признаков, этот подход намного дороже,
чем метод фильтрации.
Одним из популярных методов-оболочек является алгоритм
рекурсивного исключения признаков. Рекурсивное устранение признаков (RFE)
работает путем рекурсивного удаления признаков и построения модели из
оставшихся. Точность модели используется для определения признаков , которые
предоставляемая библиотекой scikit-learn, находится в модуле feature_selection.
больше всего способствуют прогнозированию целевого признака. Реализация RFE,
Есть два ключевых аргумента для создания селектора RFE:
estimator : оценщик контролируемого обучения с методом подгонки, который
представляет информацию о важности функции либо через атрибут coef_, либо
через атрибут feature_importances_. n_features_to_select : int или None
(по умолчанию = None), количество признаков для выбора. Если None,
будет выбрана половина признаков.

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


from sklearn.svm import LinearSVC from sklearn.feature_selection import RFE # Создаем и обучаем классификатор svc = LinearSVC(random_state=23) rfe = RFE(estimator=svc, n_features_to_select=1) rfe.fit(features, labels) # Выводим отсортированные ранги признаков for var, name in sorted(zip(rfe.ranking_, features_names), key=lambda x: x[0]): print(f'{name:>18} rank = {var}')

               sex rank = 1
                wd rank = 2
        experience rank = 3
            salary rank = 4

Используем в RFE  модель случайного леса ,в этом случае отбор займет несколько
большее  время, в особенности если набор большой
from sklearn.ensemble import RandomForestClassifier
rfc = RandomForestClassifier(random_state=23)
# Создаем модель RFE 
rfe = RFE(estimator=rfc, n_features_to_select=1)
rfe.fit(features, labels)
# Выводим рассчитанные ранги признаков
for var, name in sorted(zip(rfe.ranking_, features_names), key=lambda x: x[0]):
    print(f'{name:>18} rank = {var}')

                wd rank = 1
            salary rank = 2
        experience rank = 3
               sex rank = 4



Встроенные методы

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

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


from sklearn.ensemble import RandomForestClassifier # Строим модель rfc = RandomForestClassifier(random_state=23) rfc.fit(features, labels) print(f'{"Label":18s}: Importance') print(26*'-') for val, name in sorted(zip(rfc.feature_importances_, features_names),key=lambda x: x[0], reverse=True): print(f'{name:>18}: {val:.2%}')


abel             : Importance
--------------------------
                wd: 43.21%
            salary: 26.90%
        experience: 23.44%
               sex: 6.45%


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


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

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