Site icon AppTractor

Как предсказать LTV клиента с помощью машинного обучения и Python

Введение

У вас много клиентов, но вы не знаете, что с ними делать?

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

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

В этом руководстве, используя набор данных Kaggle, мы сделаем следующее:

Блокнот, который я использовал для урока, можно найти здесь.

Шаг 1: Сбор данных

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

import pandas as pd
# Load transaction data from CSV
df = pd.read_csv(data_path) # path to your data

# Convert Date column to date-time object
df.Date = pd.to_datetime(df.Date)df.head(10)

Вывод:

Шаг 2: Разработка фичи

Давность, частота, деньги (RFM)

RFM (Recency Frequency Monetary) — это метод осмысленной количественной оценки клиентов, который может служить хорошей отправной точкой, когда дело доходит до выполнения любой аналитики данных о транзакциях, специфичных для клиентов.

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

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

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

# Data before cut off
observed = df[df[date_col] < cut_off

# Data after cut off
future = df[
(df[date_col] > cut_off) &
(df[date_col] < cut_off + pd.Timedelta(label_period_days, unit='D'))]

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

# Copy transactions
cut_off = df.Date.max()
recency = df[df.Date < cut_off].copy()

# Group customers by latest transaction
recency = recency.groupby(customer_id_column)[date_column].max()

recency = (max_date - recency).dt.days).reset_index().rename(
columns={date_column:'recency'})

# Copy transactions
cut_off = df.Date.max()
frequency = df[df.Date < cut_off].copy()

# Set date column as index
frequency.set_index(date_column, inplace=True)
frequency.index = pd.DatetimeIndex(frequency.index)

# Group transactions by customer key and by distinct period
# and count transactions in each period
frequency = frequency.groupby([customer_id_column, pd.Grouper(freq="M", level=date_column)]).count()

# (Optional) Only count the number of distinct periods a transaction 
# occurred. Else, we will be calculating total transactions in each 
# period instead.
frequency[value_column] = 1 # Store all distinct transactions

# Sum transactions
frequency = frequency.groupby(customer_id_column).sum().reset_index().rename(
columns={value_column : 'frequency'})

# Copy transactions
cut_off = df.Date.max()
value = df[df.Date < cut_off].copy()

# Set date column as index
value.set_index(date_column, inplace=True)
value.index = pd.DatetimeIndex(value.index)

# Get mean or total sales amount for each customer
value = value.groupby(customer_id_column[value_column].mean().reset_index()
.rename(columns={value_column : 'value'})

cut_off = df.Date.max()
age = df[df.Date < cut_off].copy()

# Get date of first transaction
first_purchase = age.groupby(customer_id_column)[date_column].min().reset_index()

# Get number of days between cut off and first transaction
first_purchase['age'] = (cut_off - first_purchase[date_column]).dt.days

Мы можем обернуть все эти функции в одну:

def customer_rfm(data, cut_off, date_column, customer_id_column, value_column, freq='M'):

cut_off = pd.to_datetime(cut_off)

# Compute Recency
recency = customer_recency(data, cut_off, date_column, customer_id_column)

# Compute Frequency
frequency = customer_frequency(data, cut_off, date_column, customer_id_column, value_column, freq=freq)

# Compute average value
value = customer_value(data, cut_off, date_column, customer_id_column, value_column)

# Compute age
age = customer_age(data, cut_off, date_column, customer_id_column)

# Merge all columns
return recency.merge(frequency, on=customer_id_column).merge(
on=customer_id_column).merge(age, on=customer_id_column)

В идеале это может дать информацию об удержании клиентов в течение определенного периода времени. Это может выглядеть примерно так:

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

labels = future.groupby(id_col)[value_col].sum()

В некоторых случаях, выполняя это один раз для всего набора данных и подбирая модель для прогнозирования меток, можно получить приемлемую точность. Однако, если присмотреться, то можно спросить: а вдруг в наблюдаемый период произошло что-то интересное? И это правильный вопрос. Простое выполнение этого один раз для набора данных игнорирует всю сезонность в данных и рассматривает только один конкретный период для меток. Тут мы введем то, что я называю рекурсивным RFM.

Рекурсивный RFM

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

Допустим, данные начинаются слева в начале года. Мы выберем частоту (например, один месяц) и пройдемся по набору данных, вычислив наши функции на основе наблюдаемых (o) и сгенерировав наши метки для будущего (f). Идея состоит в том, чтобы рекурсивно вычислить эти функции, чтобы модель могла узнать, как поведение клиентов меняется с течением времени.

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

Для каждой даты отсечки (co):

Объединим все наборы данных в цикле.

Это показано в коде ниже:

def recursive_rfm(data, date_col, id_col, value_col, freq='M', start_length=30, label_period_days=30):

# Resultant list of datasets
dset_list = []

# Get start and end dates of dataset
start_date = data[date_col].min() + pd.Timedelta(start_length, unit="D")
end_date = data[date_col].max() - pd.Timedelta(label_period_days, unit="D")

# Get dates at desired interval
dates = pd.date_range(
start=start_date, end=end_date, freq=freq
data[date_col] = pd.to_datetime(data[date_col]
)

for cut_off in dates:
  # split by observed / future
  observed = data[data[date_col] < cut_off
  future = data[
                 (data[date_col] > cut_off) &
                 (data[date_col] < cut_off + pd.Timedelta(
                  label_period_days,  unit='D'))
               ]

  # Get relevant columns
  rfm_columns = [date_col, id_col, value_col]
  _observed = observed[rfm_columns]

  # Compute features from observed
  rfm_features = customer_rfm(
       _observed, cut_off, date_col, id_col, value_col
  )

  # Compute labels from future
  labels = future.groupby(id_col)[value_col].sum()

  # Outer join features with labels to ensure customers
  # not in observed are still recorded with a label of 0
  dset = rfm_features.merge(
       labels, on=id_col, how='outer'
  ).fillna(0)
  dset_list.append(dset)

# Concatenate all datasets
full_dataset = pd.concat(dset_list, axis=0)
res = full_dataset[full_dataset.recency != 0].dropna(axis=1, how='any')
return res

rec_df = recursive_rfm(data_for_rfm, 'Date', 'Customer_ID', 'Sales_Amount')

Теперь, когда мы сгенерировали наш набор данных, мы готовы к моделированию и прогнозированию! Все, что нам нужно сделать сейчас, это перетасовать и выполнить разделение обучения/тестирования на наших результирующих данных. Мы будем использовать 80% для обучения и 20% для тестирования.

from sklearn.model_selection import train_test_split
rec_df = rec_df.sample(frac=1) # Shuffle

# Set X and y
X = rec_df[['recency', 'frequency', 'value', 'age']]
y = rec_df[['Sales_Amount']].values.reshape(-1)

# Set test ratio and perform train / test split
test_size = 0.2
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=42, shuffle=True)

Шаг 3: Модель

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

В этом примере мы попробуем Random Forest Regressor, так как он очень прост в своей реализации, и поэтому его очень легко сразу использовать.

from sklearn.ensemble import RandomForestRegressor

# Initialize and fit model on train dataset
rf = RandomForestRegressor().fit(X_train, y_train)

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

from sklearn.metrics import mean_squared_error

# Create Dataframe and populate with predictions and actuals
# Train set
predictions = pd.DataFrame()
predictions['true'] = y_train
predictions['preds'] = rf.predict(X_train)

# Test set
predictions_test = pd.DataFrame()
predictions_test['true'] = y_test
predictions_test['preds'] = rf.predict(X_test)

# Compute error
train_rmse = mean_squared_error(predictions.true, predictions.preds)**0.5

test_rmse = mean_squared_error(predictions_test.true, predictions_test.preds)**0.5

print(f"Train RMSE: {train_rmse}, Test RMSE: {test_rmse}")

Вывод:

Train RMSE: 10.608368028113563, Test RMSE: 28.366171873961612

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

С тестовым среднеквадратичным отклонением ~ 28,4 наши прогнозы отклоняются примерно на 28.40 доллара на еще невидимых данных. Кроме того, RMSE нашего тренировочного набора данных значительно ниже, чем RMSE нашего теста, что указывает на переобучение. Другими словами, модель слишком зависит от обучающих данных, чтобы делать свои прогнозы, и затрудняется предсказать данные, которые она раньше не видела. Для человека это аналогично «закрытости», ему нужно научиться находить связи, которые можно обобщить на невидимые данные. Анализируя наши результаты, мы видим, что есть возможности для улучшения.

Прогнозируемые и фактические расходы

Именно на этом этапе мы можем выполнить некоторую настройку гиперпараметров или попробовать разные модели, чтобы увидеть, что лучше всего подходит для наших данных. Существуют такие инструменты, как H2O AutoML, которые позволяют вам попробовать 10 или даже 100 моделей для одной задачи.

Вывод

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

Источник

Exit mobile version