Проект за прогнозиране на оттеглянето на клиенти с PySpark

Преглед

Проектът е част от Capstone Project на Udacity Data Scientist Nanodegree. Един от най-често срещаните бизнес проблеми, прогнозирането на оттеглянето на клиенти е важна част от ролята на Data Scientist в бизнес и продуктова среда. Този проект попада в областта на музикалната индустрия и една от най-важните части на днешния свят на стрийминг е поддържането на клиентите привързани към бизнеса чрез предотвратяване на отлив.

Подобно на Spotify, този проект използва подобни данни за хипотетичен Sparkify — услуга за стрийминг на музика, с различна информация относно всяка сесия на работа на приложението от няколко потребители за определен период от време.

Данните за проекта Capstone са предоставени от Udacity. Размерът на данните е 248 MB, което е част от оригиналните данни, които са 12 GB. Данните съдържат информация относно всеки запис на сесия, направен от потребител в Sparkify, и също така съдържат основната променлива, която представлява интерес - дали потребителят е анулирал абонамента си или не.

По този начин, в името на този проект, с намерението да го мащабираме до целия набор от данни от 12 GB, това е проект за прогнозиране на оттеглянето на клиенти, използващ python API на Apache Spark, pyspark.

Проблемът

Като се имат предвид данните, този проект е проблем с прогнозиране на оттеглянето на клиенти за Sparkify. Потребителите предават поточно музиката, която харесват, и са или на безплатна версия (където се поставят реклами между песните), или на премиум версия, без реклами, но с фиксирана месечна такса. Това, наред с други аспекти на всяка сесия от потребител, колко вероятно е той да се оттегли.

Моделът използва обозначена целева функция, наречена Churn,, която приема стойност 1, ако клиентът се е отказал. Двоичният характер на проблема се разглежда от алгоритъм за класификация, използващ pyspark.ml — библиотеката за машинно обучение на Pyspark.

Проектът включваше (в общи линии) следните стъпки:

  • Зареждане и почистване на данните
  • Проучвателен анализ на данни — съдържащ визуализации на данни и изследвания, използващи статистика.
  • Инженеринг на функциите — въз основа на EDA, следващата стъпка е инженерингови характеристики за прогнозиране на целевата променлива — Churn
  • Моделиране — Това е разделът, който използва различни алгоритми за класификация и ги сравнява въз основа на показател за оценка F1-Score (повече за избрания показател по-долу). Тази стъпка също така включва настройка на модела, при която избраните модели се вземат за търсене в мрежа, за да се оптимизира изборът на модел.
  • Размисли и потенциални подобрения — какво може да се направи по-добро и какви са ограниченията?

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

Данни

Данните са предоставени от Udacity и са дълги приблизително 544 000 реда и са с размер 248 MB. Използвах Watson Studio на IBM Cloud за качване на данните и работа върху Jupyter Notebook с активирани Python 3.7 и Spark 3.0.

Обща описателна статистика за данните:

Данните не съдържат NaN стойности. Данните user_id в DataFrame обаче съдържат празни стойности.

По принцип, вместо NaN, DataFrame съдържа стойности с празни интервали за потребители, които се почистват.

Графиката по-долу показва типичната продължителност на всяка сесия в секунди.

Други демографски данни като пол и местоположение също се визуализират, за да получите по-добра представа за операциите на Sparkify.

Потребителите идват в мнозинството от 4 региона в САЩ.

Както виждаме на графиката по-горе, повечето потребители идват от Калифорния, Тексас, клъстер Ню Йорк/Ню Джърси/Пенсилвания, Флорида, Илинойс и т.н. — основно там, където има най-висока гъстота на населението и по-големи градове.

Променливата auth показва броя на влезлите потребители и броя на анулираните. Влезлите основно представляват общия брой уникални потребители. Отменено е броят потребители, които са анулирали. Въпреки това сега това е начина, по който се измерва отливът, както е разгледано по-долу.

Друго важно нещо, което трябва да се отбележи, е нивото, на което потребителите са в данните.

Безплатните потребители са 370, а потребителите, използващи платената версия, са 321. Общите уникални потребители са 448, така че броят на променените потребители е (370 + 321) — 448 = 243 потребители, които са променили нивото си.

Функциите gender, auth, length, level, location, page, song, ts, userAgent се изследват по-нататък, като се разбиват по техните категорични разделения и се анализира поведението на отлив въз основа на тези категорични разделения. Повече информация за EDA е предоставена в следващия раздел.

Проучвателен анализ на данни

Това е може би най-дългата, както и най-интересната част от всеки проект — частта за почистване и проучване на данни.

Целевата променлива Churn се взема, както е предложено от Udacity, от събитието, наречено Cancellation Confirmation от променливата page. Маркирането на това събитие и създаването на фиктивна променлива, приемаща 1, ако потребителят е отменил или не, е начинът, по който се извлича целевата променлива.

flag_event = udf(lambda x: 1 if x == 'Cancellation Confirmation' else 0, IntegerType())
df_new_churn = df_new.withColumn('Churned', flag_event('page'))
df_new_churn.dropDuplicates(['userId']).select('Churn').groupby('Churn').count().collect()

udf е дефинирана от потребителя функция, импортирана от pyspark.sql.functions.

  • Поведение на отлив средно във времето

  • Час от деня — кога потребителите, които се оттеглят, са най-активни за един ден?

  • Дни в месеца — кога през месеца потребителите са били най-активни?

  • Кой браузър и платформи се използват най-често от потребител?

  • Нарастваща активност по най-посещавани страници.

Графиката вдясно по-горе показва, че най-посещаваната страница е страницата NextSong, както се очаква в услуга за стрийминг на музика. Забележете как потребителите, които не са се отказали, не са посетили страницата за потвърждение на анулиране. Това е доказателство, че използването на това събитие за маркиране на фиктивната целева променлива Churn улавя действителното събитие на разбиване.

Инженеринг на характеристиките

Това е разделът, в който въз основа на проучвателния анализ на данни характеристиките се избират или проектират и след това се избират, за да бъдат предадени в готова за окончателно обучение Spark DataFrame, с целеви (етикет) вектор и плътен вектор на характеристиките. Плътният вектор е форматът, използван от ML библиотеката на Spark за обучение на данните за предсказване на вектора на етикета.

ML библиотеката на Spark има клас за инженеринг на функции, VectorAssembler, за преобразуване на колоните с характеристики в плътен вектор.

vector = VectorAssembler(inputCols=num_features, outputCol='numerical_features')
# This code uses the VectorAssembler to convert the multiple 
# numerical columns into a Dense Vector

Категориалните променливи

Избраните категориални променливи са кодирани с етикет. За разлика от създаването на фиктивни променливи за броя на категориите във всяка функция, кодирането на етикети кодира различните категории като цели числа, индексирани от 0 до n брой категории в една и съща променлива.

  • Пол — Етикет, кодиращ пол, което прави 0 и 1 за две уникални стойности, идентифицирайки потребителя по подходящ начин.
  • Ниво — По същия начин, за две различни нива, променливите са кодирани с етикети.
  • браузър — използваните 4 различни браузъра са кодирани от 0 до 3.
  • платформа — различните платформи, използвани от потребителите за преглед на Sparkify, са кодирани с етикети.

Числените променливи

  • Дължина — Вземат се средната дължина, стандартното отклонение на средната дължина, минималните и максималните стойности на дължината за всеки потребител.
  • Страница — Въз основа на активността на потребителите в различни страници се вземат най-посещаваните страници, обобщени по брой.
  • Общ брой песни — Общият брой песни, слушани от всеки потребител, се приема като друга числена променлива.
  • Уникални песни — Общият брой уникални песни от всеки потребител. Тази променлива показва разнообразието от песни, слушани от потребителя.
  • Уникални изпълнители — Уникален брой изпълнители, слушани от всеки потребител.
  • Процент на операциите преди 15-то число на месеца — Забелязвайки как потребителите, които са се отказали, са извършили много операции през първата половина на месеца.
  • Процент на операциите след 12:00 ч. на ден — подобно на променливата по-горе, както отпадащите, така и оставащите потребители имат висок скок в активността в по-късната половина на деня.
  • Целевата променлива ще бъде `Churn`.

Моделиране

За проблема с класификацията са избрани 4 алгоритъма — RandomForestClassifier , GBTClassifier , LogisticRegression и LinearSVM . Метриката, избрана за оценка на моделите, е F1-Score. F1-Score взема предвид фалшивите положителни и фалшивите отрицателни резултати в своето изчисление, за разлика от оценката за точност, която взема само истинските положителни и истинските отрицателни резултати. Поради дисбалансираното естество на целевата променлива, F1-Score дава по-безпристрастен резултат.

F1-Резултат

Резултатът F1 е хармонично средно изчисление на резултата за прецизност и резултата за припомняне. Резултатът за прецизност изчислява дела на правилно идентифицирания отлив от всички предвидени отлив.

Резултатът за припомняне е съотношението на правилно идентифицираните случаи на напускане от всички действителнислучаи на напускане. F1-Score е комбинация от двете. Когато има дисбаланс в целевите класове, честотата на фалшивите положителни и истински отрицателни резултати е по-висока и по този начин F1-Score показва по-добра мярка за точност при прогнозиране на отлив.

Данните се разделят на набор за обучение и набор за валидиране. Наборът за валидиране е с размер от 15% от общите данни.

Хиперпараметрична настройка

Градиентно подсиленият класификатор на дърветата отне известно време за обучение и като се има предвид неговия относително по-нисък F1-резултат, реших да не се опитвам да настройвам хиперпараметрите. Но за случаите на логистична регресия, линейна SVM и класификатор на произволни гори се извършва търсене в мрежа с параметри с кръстосано валидиране и се оценява с F1-Score.

1. Логистична регресия

Настроените тук параметри са maxIter и regParam.

  • maxIter е максималният брой пъти за изпълнение на алгоритъма за оптимизиране на функцията за загуба.
  • regParam е параметърът за регулиране. По подразбиране elasticNetParam е зададено на 0.0, което означава, че положителен `regParam` и 0 elasticNetParam е регресия на гребен. Тези допълнителни санкции се добавят за контрол на аспекти като мултиколинеарност.
  • Диапазонът на maxIter в решетката за настройка е [50, 100], а за regParam е [0.0, 0.01, 0.001].

Кръстосаният валидатор с търсене в мрежата даде F1-резултат от 80,77%, което е леко подобрение спрямо оригиналния модел. Налагането на допълнителни наказания даде по-добър резултат. Въпреки това има някои недостатъци на логистичната регресия, с които трябва да внимаваме, което е обяснено в дълбочина в бележника на Python на този проект, в Github.

2. Линеен SVM

Настроените тук параметри са същите като логистичната регресия.

Производителността е същата, като добавянето на опция за допълнителна регулация не подобрява допълнително модела.

  • Може би с други хиперпараметри можем да получим по-добър f1-резултат и резултати за точност.
  • Въпреки това, с повече данни и балансиран набор от данни, моето лично мнение е, че Linear SVM ще работи по-добре.
  • Диапазонът на maxIter в решетката за настройка е [50, 100, 150], а regParam е [0.0, 0.01].

3. Класификатор на случайни гори

Настроените тук параметри са maxDepth и numTrees.

  • maxDepth е дълбочината на всяко дърво на решенията в ансамбъла. Теоретично, колкото по-дълбоки са дърветата, толкова повече информация се събира, за да се направи класификацията, но това също може да доведе до пренастройване. Ще видим защо това няма значение.
  • numTrees е общо взето общият брой на дърветата на решенията, които трябва да се изпълнят в ансамбъла и след това да бъдат осреднени. По подразбиране стойността е 20 и колкото по-малки са дърветата, толкова по-голям е размерът на извадката, необходим за изпълнение на всяко дърво на решенията.
  • Стойностите на параметъра maxDepth, зададени в мрежата за настройка, са [5, 10], а диапазонът numTrees е [10, 20].

Настройката на хиперпараметъра за Random Forest Classifier не подобри допълнително модела в сравнение с първоначално стартирания модел.

  • Въпреки това, 88,5% F1-Score не е лош резултат и по този начин, по отношение на мащабиране на алгоритъма към по-големия набор от данни от 12 GB, бих препоръчал Random Forest ensemble Classifier, с някои мисли в главата. Тези мисли са споменати в раздела Размисли и подобрения по-долу.

Резюме на резултатите в моделите с търсене в мрежата

Заключителни бележки

Отражения

  • Случайният класификатор на горите дава най-високия F1-резултат от приблизително 88,5% и по този начин, говорейки за мащабируемост на този анализ къмпълния набор от данни 12 GB, Random Forest Classifier трябва да се има предвид. F1-резултатът е по-разумна метрика за разглеждане, тъй като точността не отчита изцяло дисбалансираното естество на целевата променлива (Churn).
  • Другите модели, като алгоритмите за линейна SVC и логистична регресия, също се представят добре, но не толкова добре, колкото Random Forest.
  • Един от най-важните аспекти на този проект, който липсва, е възможно стратифицирано разделянена наборите за обучение и валидиране. Стратификацията все още води до небалансиран набор от данни, който след това ще бъде разделен на две въз основа на дадените пропорции, но е по-добър от произволно разделяне.

Подобрения

  • Размерът на набора от данни и извършеният анализ са ограничени от броя на отделните потребители в набора от данни. Следователно, тъй като вече е малък, небалансираният характер на набора от данни може да доведе до надценени показатели, като например висок F1-резултат. Ако целевата променлива беше балансирана, показателите със сигурност щяха да бъдат по-ниски, но може или не могат да бъдат достатъчно ниски, за да направят този анализ невалиден.
  • Едно възможно решение е недостатъчно вземане на проби. По-малката извадка от класа на мнозинството (Churn = 0), за да бъде равен на класа на малцинството (Churn = 1), ще доведе до по-безпристрастни оценки на показателите, като F1-резултат и точност. Има обаче недостатъци. Недостатъчното вземане на проби намалява много размера на набора от данни и може да няма достатъчно данни за обучение с помощта на предложените модели. Въпреки че може да се вземе предвид, все още е риск, когато вземете толкова малък набор от данни. Прилагането на недостатъчна извадка към набора от данни от 12 GB може или не може да има тези последици, но със сигурност ще бъде безпристрастно.
  • Друго решение е прилагането наподобна на SMOTE техника за свръхизвадкаот класа на малцинството до размера на класа от мнозинството. Това има подобни последици като недостатъчната извадка, но в друга посока. Свръхсемплирането по същество прави синтетични копия на съществуващи точки от данни и по този начин може да доведе до пренастройване.

Много благодаря на Udacity за предоставянето на прекрасно учебно изживяване, каквото със сигурност беше този проект Capstone! :)

Посетете този проект в Github тук! и ме ръкопляскате, ако ви харесва! =)

Препратки

Kaggle

StackOverflow

Документация на Spark ML

Udacity Data Scientist Nanodegree