Это последний проект для Data Science Nano Degree, в котором я стремлюсь ответить на вопрос: учитывая набор данных, содержащий активность пользователей в Sparkify, службе потоковой передачи музыки, можно ли предсказать, какие пользователи уйдут?

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

Весь код можно найти здесь.

Исследовательский анализ данных с точки зрения оттока

После прочтения mini_sparkify_event_data.json отток был определен как те пользователи, которые посетили веб-сайт: "Подтверждение отмены"

# create churn label
churn_event_func = udf(lambda x: True if x == 'Cancellation Confirmation' else False)
# window over which to apply the event
windowval = Window.partitionBy('userId')
# label user who've ever churned
df = df \
    .withColumn('churn_event', churn_event_func('page'))\
    .withColumn('churn', max('churn_event').over(windowval))

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

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

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

Функции

После этого исходный набор данных был очищен и преобразован в полезные функции:

1. Lifetime: время в сервисе перед отменой
2. total_songs: общее количество прослушанных песен
3. num_thumb_up: количество положительных отзывов в сервисе
4. num_thumb_down: количество положительных отзывов в сервисе
5. add_to_playlist: общее количество песен, добавленных в плейлисты
6. add_friend: количество добавленных друзей
7. avg_songs_played: среднее количество песен, прослушанных за сеанс
8 пол: пол

Подробно об этих возможностях можно узнать в репозитории.

В результате получается фрейм данных с едва ли 192 полными строками/уникальными учетными записями.

Конвейер машинного обучения

Был представлен конвейер для выполнения масштабирования признаков (с использованием стандартного масштабатора) и подгонки модели с использованием CV со всеми моделями, использующими их значения по умолчанию. На этом этапе метрика, используемая для определения соответствия модели, была F1 из-за дисбаланса в выходных классах (переработанные и непереработанные). Хотя на тестовом наборе сообщалось о точности и F1.

def model_tuning(model_name, model, data_train, data_test, paramGrid = ParamGridBuilder().build(), numFolds = 3):
    # set evaluator
    f1_evaluator = MulticlassClassificationEvaluator(metricName='f1')
    
    # pipeline will take care of scaling according to the training folds
    pipeline  = Pipeline(stages = [scaler, model])
    
    #cross val
    crossval = CrossValidator(estimator=pipeline,
                              evaluator=f1_evaluator, 
                              estimatorParamMaps=paramGrid,
                              numFolds = numFolds)
    # fitting the model
    start   = time()
    cvModel = crossval.fit(data_train)
    end     = time()
    print('The training process took {:.3f} seconds'.format(end - start))
    # only the best model is used for printing its parameters
    best_mod = cvModel.bestModel
    param_dict = best_mod.stages[-1].extractParamMap()
sane_dict = {}
    for k, v in param_dict.items():
        sane_dict[k.name] = v
    print(model_name + ' best parameters')
    print(sane_dict)
    
    # best_model is saved
    model_filename = model_name.replace(" ", "")+'_model'
    print ('Saving the model as:', model_filename)
    try:
        best_mod.write().overwrite().save(model_filename)
    except Exception as e: 
        print("Model Saving failed")
        print(e)        
    
    # printing the results on the test data
    results = cvModel.transform(data_test)
    evaluator = MulticlassClassificationEvaluator(predictionCol="prediction")
    print(model_name+' Metrics:')
    print('Accuracy: {:.3f}'.format(evaluator.evaluate(results, {evaluator.metricName: "accuracy"})))
    print('F-1 Score:{:.3f}'.format(evaluator.evaluate(results, {evaluator.metricName: "f1"})))  
    return best_mod

Различные модели, протестированные с настройками по умолчанию, следующие:

1. Логистическая регрессия
2. Деревья с градиентным усилением
3. Случайный лес
4. Классификатор дерева решений

Результаты

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

С учетом обработанных признаков было обнаружено, что лучшей моделью для дальнейшего тестирования был классификатор дерева решений с точностью: 0,769 и оценкой F-1: 0,734 на тестовом наборе. Использование F-1 помогло избежать ловушки использования точности для количественной оценки несбалансированного набора.

Дальнейшая настройка приводит к переоснащению обучающего набора, что приводит к Точности: 0,718 и F-1 Score: 0,675 на тестовом наборе. Скорее всего из-за малого размера данных.

С точки зрения важности функции, 3 наиболее релевантные по порядку: «время жизни», «добавить_друга» и «всего_песен». Хотя это может измениться, если используется полный набор данных.

Перспективы и улучшения:

Есть несколько аспектов этого анализа, которые могут быть расширены и могут кардинально изменить результат.

1. Полный набор данных: использованная выборка составляет менее 1% от общего набора данных (12 ГБ), поэтому приведенные здесь выводы имеют ограниченную достоверность
2. numFolds: относится к 1. в этом случае количество сгибов было установлен на 3, но с большими наборами данных это может быть увеличено до 10
3. Особенности: можно использовать больше категориальных признаков
4. Обнаружение выбросов: не было предпринято никаких усилий для обнаружения выбросов, которые были бы важны для исследование в полном наборе данных
5. Обработка дисбаланса: для управления этой точкой могут быть полезны такие методы, как избыточная/недостаточная выборка или различные рационы обучения/тестирования
6. Вменение отсутствующих данных: строки/счета с отсутствующими значения были удалены, но такие значения можно было ввести в более позднем исследовании.