Часть 2: Разработка признаков, моделирование и анализ после моделирования
#DataAnalytics #DataVisualization #Python #ML #MachineLearning
Разработка функций
Выбор функций: ручнойвыбор
- Удалить функции уникального идентификатора = user_id, order_id, product_id
- Удалите функции, основанные на времени: как мы упоминали в предыдущей главе = created_at, Ship_at, Delivered_At, Prep_Time, Delivery_Time, Total_Time
- Удалить функцию с высокой кардинальностью: есть две функции с высокой кардинальностью. City (всего 1289 уникальных значений) и product_name (всего 4487). К счастью, у нас есть состояние, доступное в базе данных, поэтому мы можем использовать его вместо этого и уменьшить кардинальность до 49. Кроме того, для product_name нет значительных различий в количестве покупок каждого продукта (максимум 4 покупки на продукт, только 2 продукта). ). Вместо этого я решил представить продукты, используя функцию product_category = city, product_name.
- Удалить географическое местоположение: потому что мы уже конвертировали его в расстояние = d_lat, d_long, u_lat, u_long
Для статистического отбора я буду использовать два метода: встроенный и фильтрующий. Я расскажу об этом после процесса выбора модели.
статус, возраст, пол, штат, traffic_source, product_retail_price, product_category, dc_name, s2e_dist, end_dc, e2c_dist
Это список функций для моделирования (ранняя стадия)
Обработка выбросов, масштабирование признаков и преобразование признаков
Местоположение пользователя (штат)
Как упоминалось в предыдущей главе, я решил убрать Аляску и Гавайи.
Стоимость товара в каталоге
Судя по визуализации, есть заметные выбросы. Эти выбросы появляются в нескольких категориях продуктов и статусах. Учитывая степень их присутствия, их удаление вряд ли приведет к значительной потере информации. Кроме того, распределение переменной цены листинга сильно смещено вправо, поэтому я применю логарифмическое преобразование.
- Удалить выброс : удалить диапазон выше 99,5 процентилей (удалить верхние 25)
- Преобразование функции: преобразование журнала
расстояние e2c
Функция e2c_dist также демонстрирует распределение с перекосом вправо. Применение логарифмического преобразования оказалось неэффективным. Вместо этого я использовал преобразование Бокса-Кокса.
- Преобразование функции : преобразование Бокса-Кокса
Кодирование функций
- Двоичное значение [статус, пол]:Порядковый номер. Для «status» я намерен вручную закодировать его, поэтому целевой переменной «Returned» присваивается значение 1 для упрощения интерпретации. Что касается «пола», в будущем я могу изучить другие методы, учитывая участие ЛГБТК+. Пока F=0, M=1.
- Низкая кардинальность [traffic_source, dc_name, end_dc] : горячее кодирование.
- Высокая кардинальность [состояние, product_category] . На текущем этапе я буду использовать Oгорячее кодирование, поскольку оно позволяет нам интерпретировать важность функции на основе отдельной категории внутри функции. Наша главная цель — выявить проблемы, которые приводят к возврату продукции, а не прогнозировать, какие заказы могут быть возвращены. Если проклятие размерности станет проблемой, я могу рассмотреть возможность использования целевого кодирования в качестве альтернативы.
Моделирование
Подготовка данных
- Обработка дисбаланса классов: наше целевое значение не сильно отличается, поэтому я буду решать эту проблему, используя метод передискретизации. Размер данных увеличился с 4923 до 7026 экземпляров.
- Разделение тестового обучения: обучение 80% и тестирование 20%
- Модель: логистическая регрессия, случайный лес, XGBoost и CatBoost.
- Метрика оценки: Отзыв. Основное внимание уделяется решению и сокращению существующей проблемы, которая может привести к высокой стоимости кампании. Мы можем снизить стоимость позже, после какой-то кампании, изменив метрику на более сбалансированную.
- CV = 5
- случайное семя = 42
Выбор модели
Num_feature = ['age','product_retail_price','s2e_dist','e2c_dist'] Bi_feature = ['gender'] Low_card_feature = ['traffic_source','dc_name','end_dc'] Hi_card_feature = ['state','product_category']
Я пишу конвейер, чтобы предотвратить утечку данных и обеспечить согласованность. Только CatBoost, я позволю модели напрямую обрабатывать переменные высокой мощности без кодирования.
Чтобы найти лучший результат (отзыв) для каждой модели, я использую GridSearchCV с широким диапазоном гиперпараметров. Такой подход обеспечит равные возможности для каждой модели.
В результате XGBoost является самой эффективной моделью. Поскольку модель уже демонстрирует хороший диапазон отзыва на уровне 0,8459%, нет необходимости в статистическом выборе признаков. Основное внимание будет уделено тонкой настройке гиперпараметров для дальнейшей оптимизации производительности.
Основная проблема с моделью - переоснащение, в первую очередь в результате передискретизации. Чтобы решить эту проблему, я решил применить регуляризацию L1 и некоторые гиперпараметры, которые уменьшают переоснащение.
Первоначальные результаты показывают, что модель точной настройки (FT) лучше работает с обучающим набором данных по сравнению с моделью FT+L1. Однако применительно к тестовому набору. Модель FT показывает более низкий балл. Следовательно, я решил продолжить тонкую настройку модели FT+L1, которая дает многообещающие результаты. Эта модель обеспечивает баланс за счет использования гиперпараметров, которые предлагают меньшую сложность модели при достижении более высоких показателей по сравнению с моделью без L1.
#FT+L1plus model # Param parametersXGBoostFTL1p = { 'XGBoostFT__n_estimators': [130,140,145,150,155,160], 'XGBoostFT__learning_rate': [0.05, 0.1], 'XGBoostFT__max_depth': [9,10,11,12,13,14], 'XGBoostFT__subsample': [0.85,0.9] } model_xgboostFTL1p = XGBClassifier(alpha=1,random_state=seed) # Pipeline pipelineXGBoostFTL1p = Pipeline(steps=[ ('preprocess', preprocessor), ('XGBoostFT', model_xgboostFTL1p) ]) # Grid model_XGBoostFTL1p = GridSearchCV( pipelineXGBoostFTL1p, parametersXGBoostFTL1p, cv=5, scoring='recall', verbose=False ) # Result model_XGBoostFTL1p.fit(X_train, y_train) print("Best parameters: ", model_XGBoostFTL1p.best_params_) print("Best score: ", model_XGBoostFTL1p.best_score_)
Постмоделирующий анализ
Я предпочитаю использовать значения SHAP для интерпретации важности функций и рекомендаций. На мой взгляд, SHAP более надежен по сравнению с прямым извлечением важности функций из модели. Этот подход помогает смягчить предубеждения, связанные с высокой кардинальностью и коррелированными функциями, а также с нелинейными отношениями и т. д.
Один недостаток, с которым я столкнулся при работе с Shapley Values, — это сложность интеграции с пайплайном. Извлечение информации часто требует обхода измерений (массивов), что может занять много времени, особенно в случае многоклассовой проблемы. К счастью, в данном случае мы имеем дело с бинарной классификацией.
Характеристики расстояния (E2C и S2E), по-видимому, оказывают значительное влияние на эту модель.
Первые четыре числовых признака (непрерывный и дискретный) могут быть сложными для интерпретации из-за их сложных взаимосвязей. В то время как функции бинарного класса (из горячих) обычно легче интерпретировать.
- e2c_distи возраст: на крайних границах диапазона функции есть заметное наблюдение, что чем больше расстояние, тем выше вероятность возврата заказа. Об этом можно судить по наблюдаемым положительным и отрицательным влияниям.
- product_retail_price. Более низкие цены сильнее влияют на вероятность возврата.
- s2e_dist: невозможно интерпретировать с помощью визуализации.
- пол: очевидно, что женщины с большей вероятностью вернут товар. [Женщина=0, Мужчина=1]
Что касается остальных бинарных функций, я сгруппирую их на основе их основного атрибута и интерпретирую 10 основных функций.
Спасибо, что прочитали мою статью. В качестве благодарности я хотел бы поделиться полезным трюком, который я разработал для эффективной интерпретации большого количества классов. (Давайте оставим это между нами. Я не хочу, чтобы мой профессор ударил меня по голове 🤣)
X_train_preprocessed_df — это преобразованная версия X_train с использованием препроцессора внутри конвейера. Он хранится в виде DataFrame.
Feature_Name — это список названий функций (столбцов).
#Create Dataframe from shapvalue with X_train_preprocessed_df columns DF_shap_values = pd.DataFrame(shap_values.values, columns=X_train_preprocessed_df.columns) DF_shap_values.index = X_train_preprocessed_df.index def calculate_recommendation(row): feature_recommendations = [] #Calculate feature_value and threshold for feature in Feature_Name: shap_value = row[feature] feature_value = X_train_preprocessed_df.loc[row.name, feature] feature_threshold = Feature_thresholds[feature] shap_threshold = 0 #return recommendation if feature_value > feature_threshold: if shap_value > shap_threshold: recommendation = "Decrease" else: recommendation = "Increase" else: if shap_value > shap_threshold: recommendation = "Increase" else: recommendation = "Decrease" feature_recommendations.append(recommendation) return feature_recommendations Feature_thresholds = X_train_preprocessed_df.mean().to_dict() DF_recom = DF_shap_values.apply(calculate_recommendation, axis=1) # Convert into a DataFrame DF_recommendations_temp = pd.DataFrame(DF_recom.tolist(), columns=DF_shap_values.columns) # Calculate the mode of each column feature_modes = DF_recommendations_temp.mode().iloc[0] # Map the modes to the corresponding features in DF_recommendation DF_recommendation['Recommendation'] = DF_recommendation['Feature Name'].map(feature_modes)
Целью этого кода является применение рекомендаций «Уменьшение» и «Увеличение» на основе значений формы и порога формы. Порог формы рассчитывается как среднее значений формы для каждой функции.
Логика рекомендаций будет варьироваться в зависимости от цели анализа. В этом случае, когда цель состоит в том, чтобы уменьшить количество возвращенных заказов, любая функция с более высоким значением формы, превышающим пороговое значение, будет рассматриваться для рекомендации «Уменьшить».
Текущая реализация предоставляет бинарные рекомендации на основе расчета mode. При желании рекомендации можно расширить, включив в них дополнительные категории, например «Слегка отрицательные» или «Сильно положительные», изменив расчет соответствующим образом.
Давай продолжим.
Похоже, что мой расчет (рекомендация) совпадает с визуализацией (вручную), которую я описал ранее. 😅
Я приведу вам пример о Рекомендации.
- Источник трафика Поиск (бинарный признак, где 1 – наличие, 0 – отсутствие), рекомендация Увеличить подразумевает, что пользователи, пришедшие из источника поиска, более высокая вероятность невозврата товара.
- Источник трафика Органический, Уменьшениепредполагает, что пользователи, пришедшие из органического источника, с большей вероятностью вернут продукт.
Далее я представлю сводную таблицу, сгруппированную по признаку происхождения.
Удивительно, но рекомендации модели по категориям товаров отличаются от нашего EDA. №1 — Шорты (10-е место в рейтинге самых возвратных заказов по категории товаров). Кроме того, Нижнее белье, которое занимает второе место, не появляется в 10 самых популярных возвращенных заказах.
Реализация. Мы можем предложить нашей команде по маркетингу в первую очередь отдать предпочтение шортам, нижнему белью, а также футболкам и футболкам.
Бывший. улучшить понимание пользователя и улучшить четкость измерения размера или предоставить больше образцов изображений и четких рекомендаций по измерению размера и т. д.
Результат end_dc согласуется с нашим EDA, подчеркивая необходимость обратить внимание на Лос-Анджелес. Это говорит о том, что Лос-Анджелес является важной областью, требующей особого внимания и целенаправленных стратегий. Также он находится в Калифорнии (штат).
Между тем, Лос-Анджелес в качестве dc_name (начальный dc) рекомендуется увеличить, это означает, что хранящийся там продукт соответствует end_dc.
Объединив информацию из трех таблиц. Я полагаю, что Лос-Анджелес нуждается в дополнительных распределительных центрах или любых других средствах для помощи в доставке своей продукции.
КОНЕЦ
Спасибо за чтение.
Я надеюсь, что у вас есть радостный и обогащающий опыт.