音乐服务流失用户预测
项目简介
项目数据集是一个音乐服务的用户日志,包含了用户信息,歌曲信息,用户活动,时间戳等。大小128M。需要通过数据集中信息,预测出可能流失的用户,以便后续对相应用户采取挽留措施
项目思路
为了预测可能流失的用户,对日志进行分析,探索并提取与用户流失相关的变量;根据变量,使用Spark建立机器学习模型进行预测。具如下:
1.加载所需的库并实例化
2.加载与清洗数据
3.探索性数据分析
4.构建预特征
5.建模预测
6.结论汇总
项目实现
1.加载所需的库并实例化
加载所需的库:
项目涉及的库
1、pyspark.sql:进行类似SQL操作
2、pyspark.ml:进行机器学习
3、pandas 、numpy:对dataframe操作
4、matplotlib、seaborn: 绘图
5、time:记录代码块运行时间的库
实例化
spark=SparkSession.builder.getOrCreate()
2.加载与清洗数据
加载数据集
原始数据集是json格式,由于需上传github,压缩为bz2格式。
df=spark.read.json('mini_sparkify_event_data.json.bz2')
评估数据集
对数据先查看整体情况,再查看重点希望了解的列的情况。
1、查看整体情况:
(1)查看数据前几行的值,了解数据集概况,对数据集有整体认识。主要使用show()函数
(2)查看列数、每列的名称以及类型,并结合以上了解每列的含义。主要使用printSchema()函数
(3)查看数据行数。主要使用.count()函数
通过以上观察,我们可了解到:数据集共有286500行,18列;主要包含了用户信息,歌曲信息,用户活动,时间戳等信息。变量含义推测如下:
|-- artist: string (歌手)|-- auth: string (含义暂不明确)|-- firstName: string (名字)|-- gender: string (性别)|-- itemInSession: long (含义暂不明确)|-- lastName: string (姓氏)|-- length: double (听歌时长)|-- level: string (等级)|-- location: string (地区)|-- method: string (具体含义暂不明确)|-- page: string (页面)|-- registration: long (注册时间)|-- sessionId: long (页面ID)|-- song: string (歌名)|-- status: long (含义暂不明确)|-- ts: long (含义暂不明确)|-- userAgent: string (用户使用平台信息)|-- userId: string (用户ID)
2、查看某一列的值分布:
通过dropDuplicates()去重查看唯一值;sort对于有数值的进行排序,便于查看
df.select('userId').dropDuplicates().sort('userId').show()
+------+
|userId|
+------+
| |
| 10|
| 100|
|100001|
|100002|
+------+
only showing top 5 rows
通过对各列进行查看,我们发现:
userId列存在非NA的空值,需要删除
清理数据集
处理空值
先通过dropna(),处理userId、sessionId列空值;
df_clean=df.dropna(how="any",subset=["userId","sessionId"])
再通过filter(), 去除有空字符的行.
df_clean=df_clean.filter(df["userId"]!="")
3.探索性数据分析
建立注销客户的标签
项目提示使用churn作为模型的标签, 并且建议使用Cancellation Confirmation事件来定义客户流失.。
1、标记注销事件:新建一列churn_event列,标记page中的Cancellation Confirmation事件
2、标记注销用户:新建一列churn_user列,标记注销用户。具体方法是,只要用户churn_event中有标记注销,该用户所有的churn列均标记为注销
建立注销客户的标签
定义好客户流失后, 进行探索性数据分析, 观察留存用户和流失用户的行为。绘图观察主要使用了直方图、小提琴图。相比箱线图,小提琴图更能看出密度分布
1、注销与用户添加播放列表数量的关系
#提取Add to Playlist数据,查看用户添加至播放列表数量的分布
lifetime_songs=df_clean.where('page=="Add to Playlist"').groupby(['userId','churn_user']).count().toPandas()
#绘制小提琴图
ax=sns.violinplot(data=lifetime_songs,x='churn_user',y='count')
- 相比于非注销用户,注销用户将歌曲添加至播放列表的数量较少,且数量的分布相对集中,其小提琴图形相对扁平
2、是否注销与添加好友数量关系
#提取Add Friend数据,观察用户添加好友分布
add_friend=df_clean.where('page=="Add Friend"').groupby(['userId','churn_user']).count().toPandas()
#绘制小提琴图
ax=sns.violinplot(data=add_friend,x='churn_user',y='count')
- 相比于非注销用户,注销用户添加好友的数量大多处于较低水平;非注销用户添加好友数量从高水平到低水平均有分布,且非注销用户添加好友数量最大值远远大于注销用户的最大值
3、是否注销与性别关系
#提取性别与用户ID列,观察注销与性别间关系
gender_churn=df_clean.dropDuplicates(["userId","gender"]).groupby(["churn_user","gender"]).count().toPandas()
#绘制直方图
ax=sns.barplot(x='gender',y='count',hue='churn_user',data=gender_churn)
- 男性用户注销账户的绝对人数以及比例均比女性大
4.构建特征
变量选择
结合经验及以上的分析,构建以下变量:
1、听歌情况方面的变量:
- 用户听歌数量:听歌数量越大,说明用户愿意使用该服务,注销几率越小。
- 用户单次(同一sessionId)听歌最大数量:单次听歌数量越大,说明用户愿意使用该服务,注销几率越小
- 播放的歌手数量:播放过的歌手数量越多,侧面说明用户听歌越多,越愿意使用该服务,注销几率越小。
2、从page中提取动作建立变量: - 差评量:差评越多,说明用户不喜欢该服务,注销几率越大。
- 添加播放列表量:用户将歌曲加进播放列表,一般可说明用户喜欢该音乐;添加的量越多,用户愿意使用该服务的可能性越大,注销可能性越小。
- 添加好友量:添加好友量越多,说明用于越愿意在改服务中交友分享,注销几率越小。
3、其他 - 用户等级:用户曾经有付费,说明用户对该服务还是感兴趣的,注销几率相对小
变量提取
1用户听歌数量
获取每个用户点击页面NextSong的数量信息计数,获得用户添加进播放列表数量
feature_1=df_clean.select('userId','page').where(df_clean.page=="NextSong").groupBy('userId').count().withColumnRenamed('count','song_total')
2用户单次(同一sessionId)听歌最大数量
获取每个sessionId点击页面NextSong数量信息并计数,并按用户求最大值,可获得用户单次(同一sessionId)听歌最大数量
feature_5=df_clean.where('page=="NextSong"').groupBy('userId','sessionId').count().groupBy(['userId']).agg({'count':'max'}).withColumnRenamed('max(count)','max_songs_played')
3播放的歌手数量
#获取每个用户点击页面NextSong时的artist信息并计数,可获得用户听过的歌手数量
feature_6=df_clean.filter(df_clean.page=="NextSong").select("userId","artist").dropDuplicates().groupby("userId").count().withColumnRenamed("count","artist_total")
4差评量
获取每个用户点击页面Thumbs Down的数量信息计数,可获得用户差评量
feature_4=df_clean.select('userID','page').where(df_clean.page=='Thumbs Down').groupBy('userId').count().withColumnRenamed('count','Thumbs Down')
5添加播放列表量
获取每个用户点击页面Add to Playlist的数量信息计数,可获得用户添加进播放列表数量
feature_2=df_clean.select('userId','page').where(df_clean.page=='Add to Playlist').groupBy('userId').count().withColumnRenamed('count','add_to_playlist')
6添加好友量
获取每个用户点击页面Add Friend的数量信息计数,可获得用户添加好友书量
feature_3=df_clean.select('userId','page').where(df_clean.page=='Add Friend').groupBy('userId').count().withColumnRenamed('count','add_friend')
7是否曾经付费/等级
将level中free/paid转换为0/1;只有用户曾经付费,标记为1
windowval_feature=Window.partitionBy('userId')
feature_7=df_clean.select('userId','level').replace(['free', 'paid'],['0','1'],'level').select('userId', col('level').cast('int'))
feature_7=feature_7.withColumn('level_max',max('level').over(windowval_feature)).drop('level').dropDuplicates()
整理标签列
后续建模时,真实标记列默认为label列,将churn_user列重命名为label
label=df_clean.select('userId',col('churn_user').alias('label')).dropDuplicates()
变量聚合
1、通过join将变量连接,选用并集
df_feature=feature_1.join(feature_2,'userId','outer')\.join(feature_3,'userId','outer')\.join(feature_4,'userId','outer')\.join(feature_5,'userId','outer')\.join(feature_6,'userId','outer')\.join(feature_7,'userId','outer')\.join(label,'userId','outer')
2、无值的,用0填充
df_feature=df_feature.fillna(0)
3、删除索引
df_feature=df_feature.drop('userId')
5.建模预测
模型选用逻辑回归、支持向量机与随机森林。根据项目说明,选用 F1 score 作为主要优化指标。
准备数据
将数据转换为向量形式,标准化,并分成训练集、测试集和验证集
#用VectorAssembler将数据集转换为可供模型计算的结构(向量形式)
cols=["song_total","add_to_playlist","add_friend","Thumbs Down","max_songs_played","artist_total","level_max"]
assembler=VectorAssembler(inputCols=cols,outputCol="features_vec")
df_feature=assembler.transform(df_feature)#用StandardScaler标准化数据
scaler=StandardScaler(inputCol="features_vec",outputCol="features",withStd=True)
scalerModel=scaler.fit(df_feature)
df_feature=scalerModel.transform(df_feature)#按60%,40%,40%比例拆分为训练集、测试集和验证集
train,validation,test=data.randomSplit([0.6,0.2,0.2],seed=42)
模型选择
模型选择思路
- 选用逻辑回归、支持向量机、随机森林进行对比,这几个模型一般不需要很多参数调整就可以达到不错的效果。他们的优缺点如下:
1、逻辑回归:优点:计算速度快;缺点:容易产生欠拟合
2、支持向量机:数据量较小情况下解决机器学习问题。缺点:对缺失数据敏感
3、随机森林:优点:有抗过拟合能力。通过平均决策树,降低过拟合的风险性。缺点:大量的树结构会占用大量的空间和利用大量时间
模型训练
- Random Forest
#创建并训练模型,通过time()记录训练时间
rf=RandomForestClassifier(seed=42)#初始化
start=time()#开始时间
model_rf=rf.fit(train)#训练
end=time()#结束时间
print('The training process took{} second'.format(end-start))#验证模型效果
results_rf=model_rf.transform(validation)#验证集上预测
evaluator=MulticlassClassificationEvaluator(predictionCol="prediction")#评分器
print('Random Forest:')
print('F-1 Score:{}'.format(evaluator.evaluate(results_rf,{evaluator.metricName:"f1"})))#计算F-1 Score
- LogisticRegression、LinearSVC
逻辑回归、支持向量机模型代码与随机森林与结构基本一致
计算结果
- LogisticRegression模型:F-1 Score为0.7096;耗时121s
- LinearSVC模型:F-1 Score为0.7096;耗时214s
- Random Forest模型:F-1 Score0.7096;耗时215s
LogisticRegression、Random Forest的F-1 Score一致,且较LinearSVC的高。为了避免过拟合,选取Random Forest作为最终模型,选用并通过调节模型参数尝试获取更优模型
模型调优
调优思路
- 如上所述,选用Random Forest进行调优。
- 使用3折交叉验证及参数网络对模型进行调优。
- 选择FI-score作为衡量模型
指标选择
1、指标对比
- Accuracy预测正确的结果占总样本的百分比,评估模型准确程度;
- Recall在实际为正的样本中被预测为正样本的概率,评估模型能预测覆盖到多少正例;
- Precision在所有被预测为正的样本中实际为正的样本的概率,评估预测为正样本的结果中,我们有多少把握可以预测正确
- F1 Score同时考虑了Recall与 Precision。
2、指标选择
- 因为流失客户在总的客户量中占比较少,数量类别不平衡。如采用accuracy作为指标,如果模型一直预测客户不流失,很容易就可以得到很好的结果,而该结果没有使用上意义
- 由于Recall与Precision可能存在矛盾:如果预测比较“谨慎”,Precision提升,但是覆盖正例范围减少,Recall减小。为了覆盖更多正例“大胆”预测,Recall上升,但Precision会减少。
- 就这个项目而言,如果我们为了找到更多的流失客户(扩大Recall),我们要把很多的资源用在挽留本来就不会流失的客户上(Precision低),如果我们为了精准挽留客户(提高Precision),便会导致很多没预测到的流失客户流失(Recall上升)。
- 为了综合考虑,我们选用F1 Score指标
*参数调优
- 对随机森林的两个参数:树的最大深度(maxDepth)、训练的树的数量(numTrees)进行了优化。
- 对于最大深度尝试10及20两个值,对于树的数量尝试50及100两个值。
- 原代码的基础上,对训练部分的代码做调整。
rf=RandomForestClassifier()#初始化模型
f1_evaluator=MulticlassClassificationEvaluator(metricName='f1')#选用f1-score来衡量优劣
paramGrid=ParamGridBuilder().addGrid(rf.maxDepth,[10,20]).addGrid(rf.numTrees,[50,100]).build()#建立可选参数的网络,主要对maxDepth、numTrees调整
crossval_rf=CrossValidator(estimator=rf,estimatorParamMaps=paramGrid,evaluator=f1_evaluator,numFolds=3)#3折交叉验证
cvModel_rf=crossval_rf.fit(train)#训练
- 调优训练结果中,(10,50)的F1-score:0.7438;(10,100)的F1-score:0.7515;(20,50)的F1-score:0.7438;(20,100)的F1-score:0.7515。
- 其中maxDepth=10;numTrees=100,以及maxDepth=20;numTrees=100的F1-score较高,为最优参数。选择最优参数,对验证集进行预测
*调优结果
选择最优结果在验证集预测
results_rf_cv=cvModel_rf.transform(validation)
- 对比调优前后的模型在验证集上预测结果,调优前F-1 Score0.7096;调优后F-1 Score:0.7227,F-1 Score有提升
- 故使用调优后模型在测试集进行最终预测
对测试集预测
在测试集预测
results_final=cvModel_rf.transform(test)
evaluator=MulticlassClassificationEvaluator(predictionCol="prediction")
print('Test:')
print('Accuracy:{}'.format(evaluator.evaluate(results_final,{evaluator.metricName:"accuracy"})))
print('F-1 Score:{}'.format(evaluator.evaluate(results_final,{evaluator.metricName:"f1"})))
在测试集上运算后:F-1 Score:0.6591,和在验证集的结果上相比,F-1 Score有下降。模型存在在过拟合
6.结论汇总
总结&反思
过程总结
- 这个项目中,我们建立了一个预测流失用户的模型。
- 在数据集中,我们删除了没有用户ID和sessionID的数据;对流失用户建立了标识,并结合对特征与是否流失间关系的探索,并建立了7个特征
- 然后我们选择3个模型:逻辑回归,SVM和随机森林进行比较。根据比较结果。选择了随机森林预测最后结果。
- 接着我们使用交叉验证和参数网络搜索调优随机森林的参数,对测试集进行预测。预测结果F-1 Score:0.6591
遇到的挑战
1、指标的构建。
- 一方面,原始数据集中,可以直接用于预测的特征并不多,需要构建。本次构建的变量,大多需要从page中记录的动作提取
- 另一方面,即便有现成的特征,也需要作一定转换。如level是现成的特征,但是一些用户既有付费的数据,也有免费的数据,无法说明用户到底是付费还是不付费。需要处理后才使用
- 最后,构建哪些特征,需要经验积累。尽管可以通过逐一构建变量,逐一对比效果,但是需要耗费很多时间。
2、参数调优
- 从原理上,尝试的参数组合越多,便越能知道调节的方向。但是,对过多的参数的尝试,程序很长的时间都跑不出结果,时间成本变高了
过程反思
- 对比各未经优化的模型间F-1 Score,各模型相差不大。随机森林调优前后的提升也并不大。后续想有较大幅度提升,除了选取更优模型,更多可能需要从创建更合适的特征变量入手
- 数据集中,现成的可用于预测的特征并不多;我们需要重新构造特征来预测流失用户。而从数据集中构造变量,除了需要探索、熟悉手上的数据;还需要经验与知识的积累
改进
1、相比于完整数据集(12G),数据量并不大。如果进一步增加数据量,可以得到预测效果更好的模型
2、用于预测流失用户的特征进一步增加完善,找到与数据集相关性更强的特征,以提升模型性能。如增加用户注销账户时的的等级作为特征;或者对未理解未探索的特征进一步研究
3、选用决策树、梯度提升树等其他算法,观察accuracy与f1分数变化,对比已使用的算法,选取更优模型
参考文献
机器学习概念回顾、精确率、召回率、F1-score、准确率、AUC、ROC曲线
精确率、召回率、F1 值、ROC、AUC 各自的优缺点是什么
随机森林介绍、关键参数分析
音乐服务流失用户预测相关推荐
- 音乐平台Sparkify流失用户预测
Sparkify 流失用户分析 Sparkify是一个国外的音乐平台,本文将介绍预测Sparkify流失用户的过程.我们使用的数据是Sparkify的用户使用log,其中包含用户听的歌曲,时长,艺术家 ...
- 用户都跑了,你却还分不清流失用户和流失率
PMCAFF(www.pmcaff.com):互联网产品社区,是百度,腾讯,阿里等产品经理的学习交流平台.定期出品深度产品观察,互联产品研究首选. 外包大师(www.waibaodashi.com): ...
- 银行流失用户分析及预测模型
自学的一个银行流失客户预警的小项目 0.引言-银行流失用户分析 银行客户流失是指银行的客户终止在该行的所有业务,并销号.但在实际运营中,对于具体业务部门,银行客户流失可以定位为特定的业务终止行为. 商 ...
- 10年运营老兵,教你召回流失用户
运营干货,5000余字,教你低成本召回流失用户 < 背景 > 电商平台A成立近10年,算不上行业TOP,但在细分领域发展不错. 2018年不景气,App日活从年初50万一路下跌,到现在运营 ...
- 华为分析服务| 基于用户生命周期 寻找增长机会点
在当下的环境里,几乎所有应用都面临着巨大的用户增长挑战.这其中最主要的原因是互联网的人口红利衰退.用户数量以及增速越来越低,甚至在部分垂类里出现了负增长.电商.生活.游戏等行业的竞争也不断加大,同时, ...
- 企业网络推广期间对于易流失用户群体企业网络推广有话说
对于企业网站来说,在搜索引擎中的推广运营而言,企业网站持续获取用户才是维持企业网络推广长期运营的根本,在获取用户的同时也要做好防止用户流失的措施,避免企业网站出现"铁打的网站流水的用户&qu ...
- 451 Research发布《2019年数据中心服务和基础设施预测》
日前,451 research 发布<2019年数据中心服务和基础设施预测>报告,对数据中心发展趋势做出5项关键预测. 趋势一 数据中心供应商将与CSP厂商加强合作 据451 resear ...
- 谁在为网易云音乐2亿用户的即时通讯保驾护航?
继2015年7月用户数破亿后,网易云音乐的发展与口碑一直让业界瞩目.近日,网易云音乐更是在北京的发布会上对外宣布用户数突破2亿,同比增长超过100%. 网易云音乐副总裁丁博透露,目前网易云音乐曲库收录 ...
- 微软关闭音乐服务器,微软关闭Zune音乐服务 Zune播放器变为MP3
腾讯科技讯 11月16日消息,据外电报道,下载音乐或者串流播放网络歌曲,但Zune播放器仍可作为音乐继续播放器使用. 微软推出首款Zune音乐播放器和相应的Zune数字音乐服务还要追溯到2006年,但 ...
最新文章
- 面试大法——算法、Python、机器学习等笔试面经资源|干货收藏
- HTML table 标签的 frame 属性
- MAC EI Capitan上更新系统自带SVN版本号(关闭SIP方能sudo rm)
- centos mysql 5.6.19_Centos5.8 安装 MySQL5.6.19
- 不用临时变量交换两个变量的值
- html网页放大时文字不换行_WEB前端-html基础
- matlab heaviside,Matlab编写的Lyapunov指数计算程序汇总.doc
- redis内存知识点
- android 字体淡入淡出,如何让文字在Android中淡入淡出?
- mysql_use_result与mysql_store_result异同点
- c语言汉诺塔问题详解
- Silverlight:针式打印机文字模糊的改善办法
- OSI七层协议模型与记忆口诀
- 20 个有用的 Go 语言微服务开发框架吐血总结!!!
- readxmls r语言_R语言批量爬取NCBI基因注释数据
- 内核层读写应用层文件,使用filp_open函数——完美
- 换算rem的宽度和高度不生效 chrome字体最小为12px
- MOSFET管驱动电路图
- 量子计算机治愈癌症,如果量子计算机实现了,癌症可以治愈吗?
- 关于 电脑分配IP地址可以连接局域网但无法上互联网 的解决方法