经典机器学习 | 如何做到预流失与流失挽回?

标签: 经典 机器学习 预流 | 发表时间:2019-08-06 16:17 | 作者:腾讯技术工程
出处:https://www.jiqizhixin.com/
导语:预流失用户,即有流失倾向,但还没有开始真正流失的用户。相较于流失用户而言,预流失用户处于观望阶段,或许对现有产品有所顾虑,或许对于潜在的流向(竞品)有所顾虑,或许是在等待些什么;流失用户,即已经流失了的用户,或许是因为游戏弃坑,或许选择了其他产品,用户肯定还在玩些什么,只是不再来你这儿了。文章介绍了如何通过经典的机器学习(Machine Learning, ML)方法来寻找那些流失可能性比较高的用户、寻找那些回流意愿比较大的用户。运营同学针对这些用户就可以重点干预,降低预流失用户比例,拉高用户的回流比例。
背景

在日常游戏运营中,我们常常需要提高目标用户的留存率、提高流失用户的回流率、精准运营、节约运营资源。基于以上的述求,我们将经典机器学习实践于预流失和流失挽回两个场景。        

模型整体设计流程图如下:

预流失与流失挽回概述

1. 预流失

预流失用户,即有流失倾向,但还没有开始真正流失的用户。相较于流失用户而言,预流失用户处于观望阶段,或许对现有产品有所顾虑,或许对于潜在的流向(竞品)有所顾虑,或许是在等待些什么。

2. 流失挽回

流失用户,即已经流失了的用户,或许是因为游戏弃坑,或许选择了其他产品,用户肯定还在玩些什么,只是不再来你这儿了。

获得训练数据

1. 基础数据


基础数据是最基础也是最重要的第一步,需要我们去寻找、清洗各种原始数据,原始数据包括用户的登录数据、充值数据和用户数据几个模块。

模型训练自然是数据越多越好的。

2. 给用户打标签


预流失,判断用户是否会流失,如果上上周活跃,上周不活跃则是流失用户,label=1;反之上周活跃,则label=0。我们可以以周为单位,读取过去四周、八周或者更多的原始数据。

在流失挽回场景,label的判断逻辑正好相反,如下图所示

准备训练测试数据

1. 训练测试数据划分

根据自己的数据集大小合理的划分出三种数据,验证集在训练的时候用于模型调参,测试集在最后的最后模型所有参数设定后用于验证模型效果。

2. 正负样本均衡

如果实际数据中正负样本的比例严重不均衡,则有必要处理一下。处理办法是有放回的随机采样,code 示例如下:

# 正负样本均衡import randompos_vs_neg = 1.0pos_data_count = train_df.filter(train_df['ilabel'] == 1).count()neg_data_count = train_df.filter(train_df['ilabel'] == 0).count()gap = pos_data_count-neg_data_count*pos_vs_negprint('from ', pos_data_count, neg_data_count, 'to', pos_data_count, neg_data_count*pos_vs_neg, 'gap', gap)

if gap>0: # 正样本多,取样负样本   data_add = train_df.filter(train_df['ilabel'] == 0).sample(True, gap/neg_data_count, random.randint(1, 1000))   train_df = train_df.union(data_add)elif gap<0: # 负样本多,取样正样本   data_add = train_df.filter(train_df['ilabel'] == 1).sample(True, -gap/pos_data_count, random.randint(1, 1000))   train_df = train_df.union(data_add)    print(train_df.filter(train_df['ilabel'] == 1).count(), train_df.filter(train_df['ilabel'] == 0).count())print('balancing data finish')

(左滑可查看完整代码,下同)

特征工程

1. 特征选取


这里只简单的截取了一些常用到的特征,大家可以根据自己的场景增加各种简单特征、组合特征。日期特征需要注意一下,不同的游戏上线时间不一样、日期格式的数据也不方便运算,比如20181231,20190101,20190102其实都只差一天,但是数值上却差了很大,这里我们直接将日期转换成距今天天数,日期数据数值化,很方便后续的计算处理。        

2. 特征处理


2.1 缺失值填充

在预流失场景中,我们针对登录数据、充值数据做了填0处理,针对日期时间数据做填最大值处理。

2.2 zscore标准化

不同特征的取值范围对模型训练的影响不言而喻,我们有必要对许多特征做一次标准化,比如登陆次数、充值金额等等。

2.3 onehot处理

对于枚举类型的特征,最常用的编码就是OneHot,比如性别。

训练模型

1. 模型选择

预测流失Score和回流Score有许许多多的模型可以选择,本文以LR为例,早点介绍如何在生产过程中实践经典机器学习算法。LR详细介绍参考以下两个链接

Logistic regression Docs

pyspark.ml.classification.LogisticRegression APIs

2. 模型调参

使用验证集数据对模型进行调参,以下是LR的可配置参数

aggregationDepth: suggested depth for treeAggregate (>= 2). (default: 2)elasticNetParam: the ElasticNet mixing parameter, in range [0, 1]. For alpha = 0, the penalty is an L2 penalty. For alpha = 1, it is an L1 penalty. (default: 0.0)family: The name of family which is a description of the label distribution to be used in the model. Supported options: auto, binomial, multinomial (default: auto)featuresCol: features column name. (default: features)fitIntercept: whether to fit an intercept term. (default: True)labelCol: label column name. (default: label, current: ilabel)maxIter: max number of iterations (>= 0). (default: 100, current: 1000)predictionCol: prediction column name. (default: prediction)probabilityCol: Column name for predicted class conditional probabilities. Note: Not all models output well-calibrated probability estimates! These probabilities should be treated as confidences, not precise probabilities. (default: probability, current: score)rawPredictionCol: raw prediction (a.k.a. confidence) column name. (default: rawPrediction)regParam: regularization parameter (>= 0). (default: 0.0, current: 0.03)standardization: whether to standardize the training features before fitting the model. (default: True)threshold: Threshold in binary classification prediction, in range [0, 1]. If threshold and thresholds are both set, they must match.e.g. if threshold is p, then thresholds must be equal to [1-p, p]. (default: 0.5, current: 0.6)thresholds: Thresholds in multi-class classification to adjust the probability of predicting each class. Array must have length equal to the number of classes, with values > 0, excepting that at most one value may be 0. The class with largest value p/t is predicted, where p is the original probability of that class and t is the class's threshold. (undefined)tol: the convergence tolerance for iterative algorithms (>= 0). (default: 1e-06)

其中常用的参数如最大迭代次数maxIter=1000、正则项regParam=0.03,阈值threshold=0.6

离线模型评估

1. 评估指标

离线评估的指标有AUC、准确率、召回率、F1值

AUC的介绍可以查看博客AUC,ROC我看到的最透彻的讲解,AUC用来衡量一个模型的性能。准确率指的是预测为正样本中有多少是预测对了,召回率指的是有多少正样本被预测出来了。F1值是权衡准确率和召回率的一个数值。准确率、召回率、F1值随阈值的改变而改变,根据产品的实际场景合理的选择阈值。

实现demo,提供三种计算AUC的办法

'''模型评估'''# 模型评估## 训练数据的AUCprint("train auc is %.6f" %lr_model.summary.areaUnderROC)

## 方法一,使用pyspark.mllib.evaluation.BinaryClassificationMetrics来计算AUC# BinaryClassificationMetrics的参数是 RDD of (score, label) pairs,其中score表示预测为1的概率binary_metrics = BinaryClassificationMetrics(   result_df.selectExpr(['ilabel', 'score']) \   .rdd.map(lambda x: (float(x['score'][1]), float(x['ilabel']))))print("test auc is %.6f" %binary_metrics.areaUnderROC)

## 方法二,使用pyspark.ml.evaluation.BinaryClassificationEvaluator来计算AUCfrom pyspark.ml.evaluation import BinaryClassificationEvaluatorbinary_evaluator = BinaryClassificationEvaluator(rawPredictionCol='rawPrediction', labelCol='ilabel', metricName='areaUnderROC')print("test auc is %.6f" %binary_evaluator.evaluate(result_df))

## 方法三,使用BinaryLogisticRegressionSummary来计算AUC,注意,直接传入test data,不需要提前预测print("test auc is %.6f" %lr_model.evaluate(test_df).areaUnderROC)

tn = result_df.filter(result_df['ilabel'] == 0).filter(result_df['prediction'] == 0).count()fp = result_df.filter(result_df['ilabel'] == 0).filter(result_df['prediction'] == 1).count()fn = result_df.filter(result_df['ilabel'] == 1).filter(result_df['prediction'] == 0).count()tp = result_df.filter(result_df['ilabel'] == 1).filter(result_df['prediction'] == 1).count()print(tn, fp, fn, tp)precision = tp*1.0 / (fp+tp)recall = tp*1.0 / (fn+tp)f1 = 2*precision*recall / (precision+recall)print("test precision is %.6f" %(precision))print("test recall is %.6f" %(recall))print("test f1 is %.6f" %(f1))

2. 学习曲线

通过分析学习曲线,可以诊断高偏差、高方差问题


高偏差,欠拟合,随着训练样本数量增加,最后测试集、验证集的误差都停在高位,彼此很接近。
尝试解决办法:获得更多的特征、增加多项式特征、减少正则化程度λ。


高方差,过拟合,测试集和验证集的误差逐渐接近,但还是有一定的距离,随着样本数的增加误差正在逐渐趋于稳定。
尝试解决办法:更多的训练样本、减少特征的数量、增加正则化程度λ。

预测数据

1. 获得预测数据

预流失场景中预测数据为本周活跃的用户,预测其是否会在下一周流失;流失场景中预测数据为本周流失用户,预测其是否会在下周回流。

2. 预测数据分组

首先,将预测数据分成模型预测、随机两组,模型预测组用模型预测Score值,随机预测组用rand的方法输出Score值,再比较Score值与阈值的大小来判断当前样本为正或者负;

然后,将预测后的数据分成2*2组,一个是线上干预组,另一组是线上不干预的对照组,用于对比线上干预的效果。

3. 上线效果分析

如上图所示,效果分析分为模型效果和干预效果两个维度

3.1 模型效果

分析模型效果时我们需要控制变量,排除干预、不干预的影响。预期模型预测的准确率普遍要好于随机预测的准确率

同干预的情况下,对比A组和C组的准确率;同不干预的情况下,对比B组和D组的准确率

3.2 干预效果

同样需要排除不同策略预测的影响,预期干预组的留存率或者回流率要普遍好于对照组的留存率或回流率。

同模型预测情况下,对比A组和B组的留存率;同随机预测模型情况下,对比C组和D组的留存率

小结

将全流程串起来,给出如下demo

from pyspark.sql import SparkSessionfrom pyspark.ml import Pipelinefrom pyspark.ml.classification import LogisticRegressionfrom pyspark.ml.feature import StandardScaler, OneHotEncoder, HashingTF, Tokenizer, VectorAssemblerfrom pyspark.ml.evaluation import BinaryClassificationEvaluator

# 训练数据# 预测数据# 预测结果表

# 需要做缺失值处理的特征missing_value_zero_features = ['lilastactdate' ...]missing_value_max_features = ['dilastactdate' ...]# z_scorez_score_features = ['lilastactdate' ...]# one_hot_dataone_hot_features = ['idayact14_loss' ...]print('args init finish')   def get_data():     # 获得训练数据   session = SparkSession \           .builder \           .appName("prelost-lr") \           .getOrCreate()   train_df = ...

   # 缺失值过滤   train_df = train_df.dropna(subset=['sopenid', 'iuin']) \       .na.fill(0, missing_value_zero_features) \       .na.fill(9999, missing_value_max_features)          # 测试集和训练集   (raw_train_data, raw_test_data) = train_df.randomSplit([0.7, 0.3], seed=43)   print('train data count is', raw_train_data.count())   print('test data count is', raw_test_data.count())   return raw_train_data, raw_test_data

def features_clear():   # Configure an ML pipeline, which consists of some stages   # 将所有的z_score_features特征合并起来一起做归一化   z_score_vector_assember = VectorAssembler(inputCols=z_score_features, outputCol="z_score_features")   z_score_standard_scaler = StandardScaler(inputCol=z_score_vector_assember.getOutputCol(), outputCol="z_score_features_scaled")

   # 组合所有特征到features字段里   features = [z_score_standard_scaler.getOutputCol()] + one_hot_features   features_vector_assember = VectorAssembler(inputCols=features, outputCol="features")      lr = LogisticRegression(maxIter=1000, regParam=0.03, threshold=0.6, labelCol="ilabel", probabilityCol="score")

   model_pipeline = Pipeline(stages=[z_score_vector_assember, z_score_standard_scaler,                                        features_vector_assember, lr])   print('features clear pipeline config finish')   return model_pipeline

def model_train(model_pipeline, raw_train_data):   model = model_pipeline.fit(raw_train_data)   print('model train finish')   return model   def model_evaluator(model, data):   result_data = model.transform(data)

   binary_evaluator = BinaryClassificationEvaluator(rawPredictionCol='rawPrediction', labelCol='ilabel', metricName='areaUnderROC')   print("auc is %.6f" %binary_evaluator.evaluate(result_data))

   tn = result_data.filter(result_data['ilabel'] == 0).filter(result_data['prediction'] == 0).count()   fp = result_data.filter(result_data['ilabel'] == 0).filter(result_data['prediction'] == 1).count()   fn = result_data.filter(result_data['ilabel'] == 1).filter(result_data['prediction'] == 0).count()   tp = result_data.filter(result_data['ilabel'] == 1).filter(result_data['prediction'] == 1).count()   print(tn, fp, fn, tp)   precision = tp*1.0 / (fp+tp)   recall = tp*1.0 / (fn+tp)   f1 = 2*precision*recall / (precision+recall)   print("precision is %.6f" %(precision))   print("recall is %.6f" %(recall))   print("f1 is %.6f" %(f1))

def model_predict():   result_data = lr_model.transform(test_data)   tdw_util = TDWUtil(tdw_user_name, tdw_user_pwd, tdw_db)   tdw_util.dropPartition(tdw_predict_result_tbl, tdw_predict_result_tbl_pri_partition, level=0)   tdw_util.createListPartition(tdw_predict_result_tbl, tdw_predict_result_tbl_pri_partition, tdw_predict_result_tbl_pri_date, 0)   tdw.saveToTable(result_data, tdw_predict_result_tbl, tdw_predict_result_tbl_pri_partition)   print('save data into tdw finish')   

raw_train_data, raw_test_data = get_data()model_pipeline = features_clear()model = model_train(model_pipeline, raw_train_data)print('------------------ train model ------------------')model_evaluator(model, raw_train_data)print('------------------ predict data ------------------')model_evaluator(model, raw_test_data)

相关 [经典 机器学习 预流] 推荐:

经典机器学习 | 如何做到预流失与流失挽回?

- - 机器之心
导语:预流失用户,即有流失倾向,但还没有开始真正流失的用户. 相较于流失用户而言,预流失用户处于观望阶段,或许对现有产品有所顾虑,或许对于潜在的流向(竞品)有所顾虑,或许是在等待些什么;流失用户,即已经流失了的用户,或许是因为游戏弃坑,或许选择了其他产品,用户肯定还在玩些什么,只是不再来你这儿了. 文章介绍了如何通过经典的机器学习(Machine Learning, ML)方法来寻找那些流失可能性比较高的用户、寻找那些回流意愿比较大的用户.

6 大经典机器学习数据集,3w+ 用户票选得出,建议收藏

- - SegmentFault 最新的文章
内容一览:本期汇总了超神经下载排名众多的 6 个数据集,涵盖图像识别、机器翻译、遥感影像等领域. 这些数据集质量高、数据量大,经历人气认证值得收藏码住. 关键词:数据集 机器翻译 机器视觉. 数据集是机器学习模型训练的基础,优质的公开数据集对于模型训练效果、研究成果可靠度等具有重要意义. HyperAI超神经自上线以来,为数据科学从业者提供了大量优质的公开数据集.

机器学习五步走

- - 我爱机器学习
经常会有人问“我该如何在机器学习方面更进一步,我不知道我接下来要学什么了. 一般我都会给出继续钻研教科书的答案. 每当这时候我都会收到一种大惑不解的表情. 但是进步确实就是持续的练习,保持较强的求知欲,并尽你可能的完成具有挑战性的工作. 因为他是为数不多的几种可以让你真真让你获取坚实知识的媒介. 是的,你可以选择选一门课,注册MOOC,参加一些讨论班.

机器学习之路

- - 我爱机器学习
自从答应简二毛博士将自己的机器学习历程和心得分享给大家至今,转眼间半年已经过去了,感谢简博士分享和开源精神的鼓舞,这也正是本系列关于机器学习介绍博客的动力来源. 之前有些网友,师弟们问我,学习机器学习怎么入手,从看什么书开始. 如果你只愿意看一本书,那么推荐Bishop的PRML,全名Pattern Recognition and Machine Learning. 这本书是机器学习的圣经之作,尤其对于贝叶斯方法,介绍非常完善.

机器学习算法Boosting

- - 标点符
机器学习通常会被分为2大类:监督学习和非监督学习. 在监督学习中,训练数据由输入和期望的输出组成,然后对非训练数据进行预测输出,也就是找出输入x与输出y之间的函数关系F:y = F(x). 根据输出的精确特性又可以分为分类和回归. 分类和回归的区别在于输出变量的类型. 定量输出称为回归,或者说是连续变量预测.

Mahout实现的机器学习算法

- - ITeye博客
使用命令:mahout -h.   在Mahout实现的机器学习算法见下表:. EM聚类(期望最大化聚类). 并行FP Growth算法. 并行化了Watchmaker框架. 非Map-Reduce算法. 扩展了java的Collections类. Mahout最大的优点就是基于hadoop实现,把很多以前运行于单机上的算法,转化为了MapReduce模式,这样大大提升了算法可处理的数据量和处理性能.

国内的机器学习几个人

- - 丕子
转: http://blog.csdn.net/playoffs/article/details/7588597. 推荐几个机器学习和数据挖掘领域相关的中国大牛:. 李航:http://research.microsoft.com/en-us/people/hangli/,是MSRA Web Search and Mining Group高级研究员和主管,主要研究领域是信息检索,自然语言处理和统计学习.

Apache Mahout 0.8 发布,机器学习库

- - 开源中国社区最新新闻
Apache Mahout 0.8 发布了,Apache Mahout 是 Apache Software Foundation (ASF) 开发的一个全新的开源项目,其主要目标是创建一些可伸缩的机器学习算法,供开发人员在 Apache 在许可下免费使用. 该项目已经发展到了它的最二个年头,目前只有一个公共发行版.

Ruby的机器学习项目

- - 阳志平的网志-技术类
作者是辉瑞公司的小牛,可惜烂尾了. 我在改啊改啊,可惜现在是商业项目,暂时不能放出改掉的部分. 对了,顺便做个小广告,去年组织翻译的一本小书:社会网络分析:方法与实践. 已经上市了,感兴趣的可以翻翻:. 社会网络分析:探索关系背后的科学与技术. treat:自然语言处理. 类似于igraph,也是桥接处理nlp.

机器学习该如何入门

- - CSDN博客综合推荐文章
  对于这个问题的解释,说实话我很有压力,因为在分享篇文章之前就有朋友告诉我,这个百度上一搜一大片,还需要你讲吗. 正如同一千个读者眼里有一千个林黛玉一样,我解释的当然是我个人自从读研到工作这么多年对机器学习的学习到应用过程的独特见解.   首先我们看下图了解一下机器学习在AI(Artificial Intelligence 人工智能)领域的地位.