Обзор проекта

В основе этого проекта лежит анализ и поиск предложений, которые успешно привлекают существующих клиентов компании и привлекают новых.

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

Чтобы создать модель, которая может классифицировать успех специального предложения, я работал в три этапа:

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

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

Постановка задачи

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

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

Окончательный результат модели покажет, будет ли предложение эффективным или нет.

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



Некоторая борьба с данными

Starbucks предоставляет нам три набора данных в формате json: портфолио, профиль и расшифровка. В то время как первый предлагает нам перечень активных предложений, второй содержит демографические данные о членстве, а третий из них - о том, как клиенты взаимодействуют с доступными предложениями. Давайте кратко проанализируем эти наборы данных.

Набор данных портфолио

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

Набор данных профиля

Первое, что бросается в глаза в наборе данных, - это то, что есть участники, которым 118 лет.

Люди стареют, но вряд ли есть 2,175 клиентов, которым исполнилось 118 лет. Мы также заметили, что в затронутых строках также есть пустые столбцы пола и дохода, что составляет 12,97% от набора данных, поэтому их лучше отбросить.

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

Набор данных стенограммы

Главное, что мы видим здесь, это то, что столбец значений имеет разное содержимое, которое зависит от столбца событий.

Если событие относится к одному из трех возможных статусов предложения (просмотрено, получено или выполнено), столбец значение будет содержать идентификатор предложения, если событие относится к транзакции, в столбце значения будет указана сумма.

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

Это результат этого до разделения набора данных:

Проверить данные

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

Продолжая демографические данные, мы можем видеть на рисунке 2, что мужчин-членов больше, чем женщин, во всех возрастных диапазонах, кроме 80-летних.

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

С другой стороны, на рисунке 4 можно увидеть, что годом, когда количество участников увеличилось больше всего, как женщин, так и мужчин, был 2017 год. Это интересно. Возможно, в том году маркетинговая кампания изменилась, потому что раньше участниц всегда было больше.

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

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

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

В описании суммы непрерывной переменной четко указано, что максимальная сумма составляет 1062,28 $. Это говорит о том, что это выбросы. Мы выберем порог 0,995 и заменим все приведенные выше данные на среднее значение. В приведенном ниже коде показано, как это сделать.

Объединение всего в одном

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

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

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

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

Наконец, сохраняется основной набор данных с именем Combined_data. Мы загрузим это позже в следующую и последнюю записную книжку, где мы построим и выполним наши три модели.

Метрики

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

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



В качестве примера в нашем проекте:

  • предложение выполнено = 1
  • не предлагать succsseful = 0

Как вы можете видеть на изображении выше, существует четыре типа результатов в зависимости от их классификации: TP, TN, FP и FN. В коде этого проекта, расположенном на github (ссылка в конце статьи), вы можете увидеть каждую из них для каждой из используемых моделей. Эти результаты позволяют получить следующие показатели:

  • Точность
  • Точность
  • Отзывать
  • F1-счет

Давайте взглянем на них вкратце:

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

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

Точность сообщает о качестве модели машинного обучения в задачах классификации. В нашем проекте эта метрика ответит на вопрос:

В: Какой процент предложений будет считаться успешным?

Напомнить сообщает нам о количестве, которое модель машинного обучения способна определить. В нашем проекте эта метрика ответит на вопрос:

В: Какой процент предложений, признанных успешными, мы можем определить?

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

Производительность сборки и модели

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

  • Логистическая регрессия
  • Повышение градиента
  • Случайный лес

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

ROC-AUC варьируется от 0 до 1. Модель, прогнозы которой ошибочны на 100%, имеет AUC 0,0; тот, чьи прогнозы верны на 100%, имеет AUC 1,0.

Чтобы провести тест с каждой моделью, я разработал динамическую функцию, которая получает необходимые параметры. И мы будем использовать RandomizedSearchCV с 12 итерациями , чтобы оптимизировать время обучения.

В отличие от GridSearchCV, не все значения параметров проверяются, а скорее фиксированное количество настроек параметров выбирается из указанных распределений. Число пробных настроек параметров задается n_iter.

Логистическая регрессия

Результаты этого алгоритма менее хороши по сравнению с двумя другими.

# construct a params dict to tune the model
grid_params = {
    'penalty': ['l1', 'l2'],
    'C': [0.001, 0.01, 0.1, 1, 10, 100, 1000]}
# instantiate a logistic regression classifer object
log_reg = LogisticRegression(random_state=42, solver='liblinear')

Повышение градиента

С помощью gardient boost мы немного улучшаемся и получаем лучшие показатели, чем с логистической регрессией.

# Minimum number of samples required to split a node
min_split_samples = [2, 5, 8, 11]
# Minimum number of samples required at each leaf node
min_samples_leaf = [1, 2, 4, 6, 8]
# Create the random grid
gb_random_grid = {'loss': ['deviance', 'exponential'],
                   'learning_rate': [0.1, 0.01, 0.001],
                   'n_estimators': [10, 30, 50, 100, 150, 200, 250, 300, 350],
                   'min_samples_leaf': min_samples_leaf,
                   'min_samples_split': min_split_samples}
# instantiate the classifier object
gb_clf = GradientBoostingClassifier(random_state=42)

Случайный лес

Наконец, случайный лес дает наилучшие результаты.

# Number of trees in random forest
n_estimators = [10, 50, 100, 150, 200, 250, 300, 350]
# Number of features to consider at every split
max_features = ['auto', 'sqrt']
# Maximum number of levels in tree
max_depth = [int(x) for x in np.arange(3, 13)]
max_depth.append(None)
# Minimum number of samples required to split a node
min_split_samples = [2, 5, 8]
# Minimum number of samples required at each leaf node
min_samples_leaf = [1, 2, 4]
# Create the random grid
random_grid_params = {'n_estimators': n_estimators,
               'max_features': max_features,
               'max_depth': max_depth,
               'min_samples_split': min_split_samples,
               'min_samples_leaf': min_samples_leaf}
# instantiate a random forest classifier
rf_clf = RandomForestClassifier(random_state=42)

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

relative_importance = rf_random.best_estimator_.feature_importances_

Сравнение ROC-AUC

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

# Calculate AUC-ROC measure
    fpr, tpr, thresholds = roc_curve(y_test, predicted)
    roc_auc = auc(fpr, tpr)

Оценка и проверка модели

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

# Number of trees in random forest
n_estimators = [10, 50, 100, 150, 200, 250, 300, 350, 500]
# Number of features to consider at every split
max_features = ['auto', 'sqrt']
# Maximum number of levels in tree
max_depth = [int(x) for x in np.arange(3, 13)]
max_depth.append(None)
# Minimum number of samples required to split a node
min_split_samples = [2, 5, 8]
# Minimum number of samples required at each leaf node
min_samples_leaf = [1, 2, 4]
  • n_estimators: количество деревьев в лесу. (В Scikit-learn этот параметр называется n_estimators)
  • max_features: максимальное количество функций, учитываемых для разделения узла.

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

  • max_depth: максимальное количество уровней в каждом дереве решений.
  • min_split_samples: минимальное количество точек данных, помещенных в узел перед разделением узла.
  • min_samples_leaf: минимальное количество точек данных, разрешенное в листовом узле.

После обучения модели это лучшие параметры:

>> rf_random.best_params_
{'n_estimators': 500,
 'min_samples_split': 5,
 'min_samples_leaf': 1,
 'max_features': 'auto',
 'max_depth': 12}

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

С другой стороны, увеличение глубины (на max_depth) отдельных деревьев увеличивает возможное количество учитываемых комбинаций характеристик и значений. .

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



И это лучшая оценка, некоторые из наиболее интересных параметров выделены жирным шрифтом:

>> rf_random.best_estimator_
RandomForestClassifier(bootstrap=True, ccp_alpha=0.0, class_weight=None,
                       criterion='gini', max_depth=12, max_features='auto',
                       max_leaf_nodes=None, max_samples=None,
                       min_impurity_decrease=0.0, min_impurity_split=None,
                       min_samples_leaf=1, min_samples_split=5,
                       min_weight_fraction_leaf=0.0, n_estimators=500,
                       n_jobs=None, oob_score=False, random_state=42, verbose=0,
                       warm_start=False)

Здесь есть несколько интересных параметров, на которые стоит присмотреться. Тип критерия может быть индексом gini или энтропии в деревьях решений классификации.

Индекс Джини имеет значения в интервале [0, 0,5], тогда как интервал энтропии равен [0, 1]. В вычислительном отношении энтропия более сложна, поскольку в ней используются логарифмы, и, следовательно, расчет индекса Джини будет быстрее.

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



Оценка

Окончательная оценка модели представлена ​​матрицей неточностей, описанной выше. Это, наконец, представление производительности нашей модели.

Обоснование

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

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

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

Улучшения

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

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



На уровне кода этот метод подходит для проекта с использованием метода VotingClasifier ensemble.

from sklearn.ensemble import VotingClassifier
#create a dictionary of our models
estimators=[('lr', log_reg_random), ('gb', gboost_random), 
            ('rf', rf_random)]
#create our voting classifier, inputting our models
ensemble = VotingClassifier(estimators, voting='hard')
#fit model to training data
ensemble.fit(X_train, y_train)
#test our model on the test data
ensemble.score(X_test, y_test)

Выводы

Проект открыт для проверки производительности с другими типами алгоритмов классификации, такими как, например, SVM, K-NN, XGBoost или LightGBM. Кроме того, для улучшения моделей было бы полезно получить больше демографической информации о клиентах.

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

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

Смотрите полный код в моем репозитории github.