Изследвайте своите големи данни с помощта на Spark, подход за начинаещи.

Ако сте работили с модели на машинно обучение преди, познавате усещането, когато имате работа с огромни набори от данни. В един момент всички осъзнаваме тази бариера и избираме да работим с по-малка част от данни, за да започнем с анализ на данни и модели за обучение.

Въведение

Големите данни са много неясни, но всички можем да се съгласим, че всеки път, когато почувстваме ограничение на изчислителната мощност на нашите лаптопи или машини, поради огромния размер на набор от данни, виждаме това като проблем с големите данни. И нека си признаем, библиотеките за анализ на данни на Python като „pandas“ имат свои собствени ограничения за манипулиране на огромни масиви от данни поради ограниченията на локалната памет и проблеми с производителността.

Бизнес разбирателство

Sparkify е измислена услуга за стрийминг на музика като Spotify или Pandora и аз ще използвам Sparkify Churn Prediction като изявление на проблем, за да обясня подробно тази статия. Ще използвам PySpark за този проект.

Забележка: Наборът от данни на Sparkify беше предоставен от програмата Udacity Data Scientist Nanodegree и тази статия е част от моя проект Capstone.

Разбиране на данните

Започнах с проучвателен анализ на данни на моята локална машина с малък набор от данни на Sparkify, предоставен от Udacity.

# loading dataset
df = spark.read.json("mini_sparkify_event_data.json")
# columns in dataset
df.printSchema()
root
 |-- artist: string (nullable = true)
 |-- auth: string (nullable = true)
 |-- firstName: string (nullable = true)
 |-- gender: string (nullable = true)
 |-- itemInSession: long (nullable = true)
 |-- lastName: string (nullable = true)
 |-- length: double (nullable = true)
 |-- level: string (nullable = true)
 |-- location: string (nullable = true)
 |-- method: string (nullable = true)
 |-- page: string (nullable = true)
 |-- registration: long (nullable = true)
 |-- sessionId: long (nullable = true)
 |-- song: string (nullable = true)
 |-- status: long (nullable = true)
 |-- ts: long (nullable = true)
 |-- userAgent: string (nullable = true)
 |-- userId: string (nullable = true)
# Number of Rows
df.count()
286500
# showing types of events in dataset or pages a user might visit while using the Sparkify app.
df.select("page").dropDuplicates().show()
+--------------------+
|                page|
+--------------------+
|              Cancel|
|    Submit Downgrade|
|         Thumbs Down|
|                Home|
|           Downgrade|
|         Roll Advert|
|              Logout|
|       Save Settings|
|Cancellation Conf...|
|               About|
|            Settings|
|     Add to Playlist|
|          Add Friend|
|            NextSong|
|           Thumbs Up|
|                Help|
|             Upgrade|
|               Error|
|      Submit Upgrade|
+--------------------+

Подготовка на данни

По време на подготовката на данните се справих с предизвикателства, свързани с данните, като липсващи стойности, почистване на данни, вписване на категориални променливи и идентифициране на целева променлива, за да предскажа отхвърлени клиенти.

За да предвидим точно изоставените клиенти, се нуждаем от тяхната информация за userId, следователно почистваме данните, като премахваме всички редове, където е userId или нулев, или празен.

Добавяне на целева променлива като Churn. Ако потребител е посетил страницата „Потвърждение за анулиране“, тогава колоната Churn има стойност 1 else 0 .

Визуализации на данни

Нека разберем по-задълбочено клиентите на Sparkify, използвайки визуализации.

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

Събитията по-долу представляват много важен набор от функции за прогнозиране на отпаднали клиенти.

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

Време е да изберете важни функции за обучение на нашите модели за машинно обучение.

['gender', 'level', 'page', 'userId', 'Churn']

Колоната страница допълнително ще бъде разделена на своите събития.

df.groupBy(["userId", "Churn", "gender", "level"]).count().show()
+------+-----+------+-----+-----+
|userId|Churn|gender|level|count|
+------+-----+------+-----+-----+
|200008|    0|     1|    1| 1988|
|    93|    0|     0|    0|  566|
|100013|    1|     1|    0|  303|
|    91|    0|     0|    1| 2862|
|     4|    0|     0|    0|  141|
|300021|    0|     1|    1| 4650|
|100019|    1|     0|    0|   89|
|100021|    1|     0|    0|  319|
|   140|    0|     1|    1| 5662|
|    73|    1|     1|    0|   16|
|    59|    0|     0|    1|  693|
|200025|    0|     0|    0|  597|
|    41|    0|     1|    1| 2220|
|    37|    0|     0|    0|   79|
|    54|    1|     1|    1| 2859|
|    69|    0|     1|    0|   39|
|200024|    1|     0|    0|  342|
|300005|    0|     1|    0|   64|
|    27|    0|     0|    0|  291|
|   101|    1|     0|    0|   76|
+------+-----+------+-----+-----+
only showing top 20 rows
Event_list = [(row['page']) for row in df.select('page').dropDuplicates().collect()]
for event in Event_list:
    df_temp = df.filter(df.page==event).groupBy(df.userId).count()
    df_temp = df_temp.withColumnRenamed('count', event)
    df_sparkify = df_sparkify.join(df_temp, 'userId', how='left')

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

df_sparkify.printSchema()
root
 |-- userId: integer (nullable = true)
 |-- Churn: integer (nullable = true)
 |-- gender: integer (nullable = true)
 |-- level: integer (nullable = true)
 |-- count: long (nullable = false)
 |-- Cancel: long (nullable = true)
 |-- Submit Downgrade: long (nullable = true)
 |-- Thumbs Down: long (nullable = true)
 |-- Home: long (nullable = true)
 |-- Downgrade: long (nullable = true)
 |-- Roll Advert: long (nullable = true)
 |-- Logout: long (nullable = true)
 |-- Save Settings: long (nullable = true)
 |-- Cancellation Confirmation: long (nullable = true)
 |-- About: long (nullable = true)
 |-- Settings: long (nullable = true)
 |-- Add to Playlist: long (nullable = true)
 |-- Add Friend: long (nullable = true)
 |-- NextSong: long (nullable = true)
 |-- Thumbs Up: long (nullable = true)
 |-- Help: long (nullable = true)
 |-- Upgrade: long (nullable = true)
 |-- Error: long (nullable = true)
 |-- Submit Upgrade: long (nullable = true)

Забележка: Dataframe може да изглежда различно от пълния набор от данни, работещ на AWS Spark.

След промяна на стойностите на низовете на всички колони в цифрова форма рамката с данни е готова за моделиране.

Забележка: Премахване на колоните „отказ“ и „Потвърждение на анулирането“, за да се предотврати изтичане на данни по време на моделиране.

Моделиране

Библиотеката AWS Spark MLlib поддържа много контролирани и неконтролирани алгоритми за машинно обучение. Използвах класификатора на дървото на решенията, класификатора на произволни гори и класификатора за усилване на градиента, за да обуча три модела и да сравня резултатите, преди да избера печелившия модел.

Тестово обучение

# spliting in 70% training and 30% testing set
train_data,test_data = final_data.randomSplit([0.7,0.3])

Класификатори

# Using three classifiers before finalizing best performer
dtc=DecisionTreeClassifier(labelCol='Churn',featuresCol='features')
rfc=RandomForestClassifier(labelCol='Churn',featuresCol='features', numTrees=300)
gbt=GBTClassifier(labelCol='Churn',featuresCol='features')
# fit the models
dtc_model = dtc.fit(train_data)
rfc_model = rfc.fit(train_data)
gbt_model = gbt.fit(train_data)
# transform the models
dtc_predictions = dtc_model.transform(test_data)
rfc_predictions = rfc_model.transform(test_data)
gbt_predictions = gbt_model.transform(test_data)

Метрика за оценка

В pySpark не можем да отпечатаме матрицата на объркването, вместо това трябва да използваме MulticlassClassificationEvaluator, за да оценим точността и f1_score. Можем също да използваме BinaryClassificationEvaluator, за да изчислим Площ под ROC кривата и Площ под Precision-Recall.

# calculating accuracy
acc_evaluator = MulticlassClassificationEvaluator(labelCol="Churn",predictionCol="prediction",metricName="accuracy")
# calculating f1 score
f1_evaluator = MulticlassClassificationEvaluator(labelCol="Churn",predictionCol="prediction", metricName="f1")
areaUnderROC = BinaryClassificationEvaluator(labelCol='Churn',metricName='areaUnderROC')
areaUnderPR = BinaryClassificationEvaluator(labelCol='Churn',metricName='areaUnderPR')
dtc_acc = acc_evaluator.evaluate(dtc_predictions)
rfc_acc = acc_evaluator.evaluate(rfc_predictions)
gbt_acc = acc_evaluator.evaluate(gbt_predictions)
dtc_f1 = f1_evaluator.evaluate(dtc_predictions)
rfc_f1 = f1_evaluator.evaluate(rfc_predictions)
gbt_f1 = f1_evaluator.evaluate(gbt_predictions)
dtc_auc = areaUnderROC.evaluate(dtc_predictions)
rfc_auc = areaUnderROC.evaluate(rfc_predictions)
gbt_auc = areaUnderROC.evaluate(gbt_predictions)
dtc_aupr = areaUnderPR.evaluate(dtc_predictions)
rfc_aupr = areaUnderPR.evaluate(rfc_predictions)
gbt_aupr = areaUnderPR.evaluate(gbt_predictions)

Резултати

Прогнозирането и предотвратяването на оттеглянето на клиенти означава голям допълнителен потенциален източник на ползи за всяка компания. Нашата цел от самото начало беше да идентифицираме тези клиенти на Sparkify.

След сравняване на всички модели по различни показатели за оценка, Подобряването на градиента се представя по-добре от другите два. Можете да видите, че f1-резултатът за алгоритъма за усилване на градиента е 0,90, докато f1-резултатът за произволна гора е 0,79.

Вярвам в модел с по-висок резултат за припомняне за клас на положителен отлив. От гледна точка на Lehman, изборът на модел, който може да предвиди по-малко фалшиви отрицателни резултати (случаи, когато клас е 1, но прогнозиран като 0), вместо фалшиви положителни резултати (случаи, когато клас е 0, но прогнозиран като 1), за успешното идентифициране на клиентите, които отпадат.

Ще избера Gradient Boosting Classifier като окончателен модел за сега.

Разположете

Ще пусна този код на AWS Spark Cluster като моя бъдеща работа. Но в момента не внедрявам този модел никъде.

Бъдещи работи

  1. В бъдеще мога да се опитам да отделя целия набор от данни в месечни или седмични данни и да предскажа за следващия месец или седмица клиентите, които са изостанали.
  2. Мога да използвам по-напреднали техники за машинно обучение, като комбинирам два или повече алгоритъма, за да подобря общата скорост на прогнозиране.
  3. Мога да пусна това през AWS клъстера, за да видя производителността на модела и да използвам кръстосано валидиране за по-добър резултат f1.

P.S. Винаги се опитвам да следвам подход CRISP-DM в моите проекти в областта на Data Science.

Тази статия е част от проекта Udacity Data Scientist Nanodegree Project. За да видите повече за този анализ, вижте връзката към моя Github, достъпна тук.