基于机器学习预测用户流失

 

一、背景和目标

     用户运营是CRM运营中重中之重的一项工作,在人工智能时代,我们可以探索用AI帮助我们做一些用户运营的工作,之前我写了几篇关于快消行业与AI技术相结合的文章:

1. 利用RFM模型对餐饮客户进行分析

2.利用Apriori关联算法看看客户最喜欢买什么

3.利用ARMA算法对销售进行预测

4.利用深度学习和机器学习对餐饮客户进行分类

本次探索一下利用AI来预测用户流失,整体过程如下:

基于机器学习预测用户流失_第1张图片

二、数据采集和准备

   数据采集分为几个模块:用户基本信息、消费产品偏好、消费渠道偏好、LBS信息、优惠使用信息、积分信息、等级信息等。

1、维度采集:

   数据存储在数据库中,根据采集信息,写SQL形成维度数据,实际上类似于用户画像的萃取过程。这里以SQL server为例,选择用户最喜欢的时间段、用的最多的渠道和最喜欢的产品,只选择一部分SQL作为示例,一共采集33个维度信息:

 --计算每个用户订单渠道、订单时间段、产品类型
select * into #product_prefer
 from(
  select userid,'orderchannel'as tag,orderchannel,count(1) ordercount
  from #orderdetail
 group by userid,orderchannel
 union all
  select userid,'daypart'as tag,daypart,count(1) ordercount
  from #orderdetail
 group by userid,daypart
 union all
   select userid, 'product_type'as tag,product_type,count(1) ordercount
  from #orderdetail
 group by userid, product_type
  )y

 --取出每个用户使用最多的订单渠道、最多时间段和最喜欢产品
 select *
 into  #base_tmp
from
  (select *,ROW_NUMBER() OVER ( PARTITION BY userid,tag order by userid,ordercount DESC ) 'rank' FROM  #product_prefer  ) a
where [rank] = 1
 --列转行形成用户标签
select userid,max(product_type)product_type,max(daypart)dayparts,max(orderchannel)orderchannel
from(
SELECT *
FROM #base_emp
PIVOT(max(orderchannel) FOR tag IN("product_type","daypart","orderchannel")) AS T 
)x
group by userid

2、标签生成

 因为是采用有监督学习,所以需要有标签,那怎么怎么生成呢? 我们可以采集截止2018年的维度数据作为训练数据,然后2019年没有消费的会员定义为流失会员,有消费的会员定义为活跃会员,这样标签就有了。用2020年的数据作为验证,这样就解决了标签和验证的问题。最终采集的数据大概是这样的:

三、数据预处理

1、数据映射

因为我们很多维度是文本,需要转化为数字才可以用于训练,这里用labelencoder进行映射,最后把原始的带文本的dataframe转成纯数字的dataframe。

from sklearn.preprocessing import LabelEncoder,OneHotEncoder,StandardScaler
from collections import defaultdict

d=defaultdict(LabelEncoder)
x=X.apply(lambda z:d[z.name].fit_transform(z))
y=LabelEncoder().fit_transform(Y)
mergerdata=pd.concat([x,pd.DataFrame(y,columns=['labels'])],axis=1,ignore_index=False)  #把标签合并成一张dataframe

2、数据探索

因为特征很多,我们需要选择对结果影响比较大的特征进行训练,排除一些无用的特征,先用人工筛选的方法探索看看。

a.看看性别和消费时间段对客户流失的影响:

import matplotlib.pyplot as plt
import seaborn as sns

data=pd.read_csv(r'E:\Python\train.csv')#.fillna(0)
#数据探索
f,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,10))
plt.subplot(1,2,1)
sex=sns.countplot(x='sex',hue='labels',data=data)
plt.xlabel='sex'
plt.title='Distribe by sex'

plt.subplot(1,2,2)
dayparts=sns.countplot(x='dayparts',hue='labels',data=data)
plt.xlabel='dayparts'
plt.title='Distribe by dayparts'
plt.show()

基于机器学习预测用户流失_第2张图片

可以看到性别对消费影响不是很大,午餐时间段客户流失较多。

b.特征之间相关性

使用热力图显示特征之间的相关性

#转独热码
d=defaultdict(LabelEncoder)
x=X.apply(lambda z:d[z.name].fit_transform(z))
y=LabelEncoder().fit_transform(Y)
#查看相关性
corr=x.corr()
plt.figure(figsize=(20,20))
ax=sns.heatmap(corr,xticklabels=corr.columns,yticklabels=corr.columns,cmap='YlGnBu_r',annot=True) #annot显示数字标注cmap="YlGnBu":数字越大,颜色越深"
plt.show()

基于机器学习预测用户流失_第3张图片

从上图的颜色,可以看到特征之间的相关性,颜色越深越相关。

如果遇到报错: 

Traceback (most recent call last):
TypeError: '<' not supported between instances of 'str' and 'int'During handling of the above exception, another exception occurred:
TypeError: Encoders require their input to be uniformly strings or numbers. Got ['int', 'str']

这是因为object类型有可以包含str,int等,需要统一标准化,可以通过如下方式解决:

方法一:X[cat]=X[cat].astype('category')   把对应列转行category类型,转成str也行。

方法二:X=X.apply(lambda z:z.astype(str)) 全部当成str处理

c.查看各个特征与用户流失之间的相关性:

#特征与用户流失之间的相关性
labelrelation=mergerdata.corr()['labels'].sort_values(ascending=False).plot(kind='bar')
plt.show()

基于机器学习预测用户流失_第4张图片

从上图可以看到各个特征与用户流失之间的关系,中间部分的值接近于0,说明对用户流失影响不大,特征可以舍弃。

3.特征选择

上面是用人工或者图的方式来判断特征对用户流失的影响,但是sklearn还提供了其他方法可以判断,比如PCA主成分分析,SelectKBest,随机森林等方式。本次选择用随机森林来选择特征

代码:

#随机森林选择特征值
threshold=0 #根据自己需求设置阈值
rf=RFC(n_estimators=100,random_state=0,n_jobs=-1) # random_state 设置随机种子,n_jobs 为CPU数,-1可以调用所有内核
rf.fit(x,y)
select_feature=[]
for i in sorted(zip(rf.feature_importances_,mergerdata.columns),reverse=True):
    #print(i)
    if i[0]>threshold:
        select_feature.append(i[1])
x=x[select_feature] #选择大于阈值的特征作为新的训练数据
print(select_x.head())

基于机器学习预测用户流失_第5张图片

按特征重要性顺序输出,设置好阈值,我们就可以选择多少个维度进行训练了,PS这是个很吃内存的过程,我设置1000棵树,跑大概1000w数据,用了接近50个G的运存,如果内存吃不消,可以控制一下参数,比如减少树数量,减少树深度。。。。。 

4、数据标准化和划分验证测试集

因为数据可能有大小差异,所以对数据进行标准化。用standardScaler函数直接标准化就行。划分训练集可以用StratifiedShuffleSplit可以分层抽样也可以直接用常见的train_test_split划分

select_x=StandardScaler().fit_transform(select_x)
#划分数据集和测试集
#用StratifiedShuffleSplit可以分层抽样
# for train_index, test_index in StratifiedShuffleSplit(n_splits=5, test_size=0.2, random_state=0).split(select_x, y):
#     #print("train:", train_index, "test:", test_index)
#     x_train,x_test=select_x.iloc[train_index], select_x.iloc[test_index]
#     y_train,y_test=y[train_index], y[test_index]
 #直接抽样
x_train, x_test, y_train, y_test = train_test_split(select_x, y, test_size = 0.3, random_state = 0)

到此为止,我们已经完成了训练数据的准备。

四、机器学习

1.确定参数

利用网格搜索法确定最佳参数

#利用网格搜索法确定最佳参数
max_depth = [2, 3, 4, 5, 6]
min_samples_split = [2, 4, 6, 8, 10]
min_samples_leaf = [2, 4, 6, 8, 10]
parameters = {'max_depth':max_depth, 'min_samples_split':min_samples_split, 'min_samples_leaf':min_samples_leaf}
grid = GridSearchCV(estimator=DTC(), param_grid=parameters, cv=10)
grid.fit(x_train, y_train)
print(grid.best_params_)

所以我们树的形状就是上面的参数。

2.开始训练

这里选用决策树、GDBT和随机森林三种机器学习方法训练数据,并且把模型压缩之后保存本地,用法可以看我之前写的sklearn 入门,把各个机器学习用法用一遍

#模型得分
def func(clf,filename):
    clf.fit(x_train, y_train)
    joblib.dump(value=clf,filename=filename,compress=True )  # 模型保存
    score = clf.score(x_test, y_test)
    return score
print('决策树结果为:{}'.format(func(DTC(min_samples_leaf=2,max_depth=6,min_samples_split=2),'DTC.gz')))
print('GDBT结果为:{}'.format(func(GDBC(n_estimators=200,max_depth=6,min_samples_leaf=2,min_samples_split=2),'GDBC.gz')))
print('随机森林结果为:{}'.format(func(RFC(n_estimators=200,max_depth=6,min_samples_leaf=2,min_samples_split=2),'RFC.gz')))

基于机器学习预测用户流失_第6张图片

这个结果准确度太高,应该是过拟合了。接下来应用真实生成的数据来测试结果

3.用户预测

选择刚才保存的模型,直接选用生产数据来预测,对比真实结果。

import pandas as pd
from sklearn.preprocessing import LabelEncoder,OneHotEncoder,StandardScaler
from collections import defaultdict
import joblib
import pyodbc

#1.数据预处理阶段
sql ='''
select xxxxx from
xxxxx
'''
conn = get_db_connection()
print('数据库链接成功')
data=pd.read_sql(sql,conn).fillna(0)
print('读取成功')
X=data.iloc[:,1:17]
Y=data.iloc[:,17]
#预处理
d=defaultdict(LabelEncoder)
X=X.apply(lambda z:z.astype(str)) #为了避免报错
x=X.apply(lambda z:d[z.name].fit_transform(z))
y=LabelEncoder().fit_transform(Y)
x_test=StandardScaler().fit_transform(x)#数据标准化


#用户流失预测
#加载随机森林模型
RFC=joblib.load('RFC.gz')
y_predict = RFC.predict(x_test)
result=pd.DataFrame({'predict':y_predict,'actual':y})
result['accuracy']=(result['predict']==result['actual'])
c=result['accuracy'].value_counts().iloc[0]/len(result)
print('随机森林预测结果准确率为{:.2f}%'.format(c*100))
#加载决策树模型
DTC=joblib.load('DTC.gz')
y_predict = DTC.predict(x_test)
result=pd.DataFrame({'predict':y_predict,'actual':y})
result['accuracy']=(result['predict']==result['actual'])
c=result['accuracy'].value_counts().iloc[0]/len(result)
print('决策树预测结果准确率为{:.2f}%'.format(c*100))
#加载GDBC模型
DTC=joblib.load('GDBC.gz')
y_predict = DTC.predict(x_test)
result=pd.DataFrame({'predict':y_predict,'actual':y})
result['accuracy']=(result['predict']==result['actual'])
c=result['accuracy'].value_counts().iloc[0]/len(result)
print('GDBT预测结果准确率为{:.2f}%'.format(c*100))

基于机器学习预测用户流失_第7张图片

总结: 

这个结果正确率不太高,可能原因是标签采集不太全面,作为一次的用户预测尝试,后面可以试试多采集用户标签,然后调调参数看看能不能有更精准的结果。等正确率提高了,可以与业务部分合作,提供定向的营销手段避免用户流失

 

完整代码:

import pandas as pd
from sklearn.feature_selection import SelectKBest,chi2
from sklearn.preprocessing import LabelEncoder,OneHotEncoder,StandardScaler
from collections import defaultdict
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.feature_selection import SelectFromModel
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier as GDBC
from sklearn.tree import DecisionTreeClassifier as DTC
from sklearn.externals import joblib
from sklearn.model_selection import StratifiedShuffleSplit,GridSearchCV
import pyodbc

#1.数据预处理阶段
#data=pd.read_sql(sql,conn).fillna(0) #从数据库获取数据
data=pd.read_csv(r'E:\Python\train.csv').fillna(0) #从本地获取数据
pd.set_option('display.max_columns', None) #打印全部行,避免省略号
X=data.iloc[:,1:33]
Y=data.iloc[:,33]

X=X.drop(['membername'],axis=1)
#数据探索
f,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,10))
plt.subplot(1,2,1)
sex=sns.countplot(x='sex',hue='labels',data=data)
plt.xlabel='sex'
plt.title='Distribe by sex'
plt.subplot(1,2,2)
dayparts=sns.countplot(x='dayparts',hue='labels',data=data)
plt.xlabel='dayparts'
plt.title='Distribe by dayparts'
plt.show()
#转独热码
d=defaultdict(LabelEncoder)
x=X.apply(lambda z:d[z.name].fit_transform(z))
y=LabelEncoder().fit_transform(Y)
mergerdata=pd.concat([x,pd.DataFrame(y,columns=['labels'])],axis=1,ignore_index=False)  #把标签合并成一张dataframe

#查看特征相关性
corr=mergerdata.corr()
plt.figure(figsize=(20,20))
ax=sns.heatmap(corr,xticklabels=corr.columns,yticklabels=corr.columns,cmap='YlGnBu_r',annot=True) #annot显示数字标注cmap="YlGnBu":数字越大,颜色越深"
plt.show()
#特征与用户流失之间的相关性
labelrelation=mergerdata.corr()['labels'].sort_values(ascending=False).plot(kind='bar')
plt.show()
#PCA选择特征值
# pca=PCA()
# pca.fit(x)
# print(pca.components_)
# print(pca.explained_variance_ratio_)
#Kbest选择最佳特征值
# test = SelectKBest(score_func=chi2, k=20)
# fit = test.fit(x,y)
# features = fit.transform(x)
# print(features[0:21, :])
#随机森林选择特征值
threshold=0.001
rf=RFC(n_estimators=100,random_state=0,n_jobs=-1) # random_state 设置随机种子,n_jobs 为CPU数,-1可以调用所有内核
rf.fit(x,y)
select_feature=[]
for i in sorted(zip(rf.feature_importances_,mergerdata.columns),reverse=True):
    print(i)
    if i[0]>threshold:
        select_feature.append(i[1])
#根据随机森林的结果选择特征组成新的dataframe
select_x=x[select_feature]
select_x=StandardScaler().fit_transform(select_x)#数据标准化
#划分数据集和测试集
#用StratifiedShuffleSplit可以分层抽样
# for train_index, test_index in StratifiedShuffleSplit(n_splits=5, test_size=0.2, random_state=0).split(select_x, y):
#     #print("train:", train_index, "test:", test_index)
#     x_train,x_test=select_x.iloc[train_index], select_x.iloc[test_index]
#     y_train,y_test=y[train_index], y[test_index]
x_train, x_test, y_train, y_test = train_test_split(select_x, y, test_size = 0.3, random_state = 0) #直接抽样

#2.机器学习阶段
#利用网格搜索法确定最佳参数
max_depth = [2, 3, 4, 5, 6]
min_samples_split = [2, 4, 6, 8, 10]
min_samples_leaf = [2, 4, 6, 8, 10]
parameters = {'max_depth':max_depth, 'min_samples_split':min_samples_split, 'min_samples_leaf':min_samples_leaf}
grid = GridSearchCV(estimator=DTC(), param_grid=parameters, cv=10)
grid.fit(x_train, y_train)
print(grid.best_params_)
#模型得分
def func(clf,filename):
    clf.fit(x_train, y_train)
    joblib.dump(value=clf,filename=filename,compress=True )  # 模型保存
    score = clf.score(x_test, y_test)
    return score
print('决策树结果为:{}'.format(func(DTC(min_samples_leaf=2,max_depth=6,min_samples_split=2),'DTC.gz')))
print('GDBT结果为:{}'.format(func(GDBC(n_estimators=200,max_depth=6,min_samples_leaf=2,min_samples_split=2),'GDBC.gz')))
print('随机森林结果为:{}'.format(func(RFC(n_estimators=200,max_depth=6,min_samples_leaf=2,min_samples_split=2),'RFC.gz')))
#用户流失预测
RFC=joblib.load('RFC.gz')
y_predict = RFC.predict(x_test)
result=pd.DataFrame({'predict':y_predict,'actual':y})
result['accuracy']=(result['predict']==result['actual'])
c=result['accuracy'].value_counts().iloc[0]/len(result)
print('随机森林预测结果准确率为{:.2f}%'.format(c*100))

参考博客:

Python建立客户流失预测模型

基于决策树的用户流失分析与预测

 

你可能感兴趣的:(Python,机器学习,数据分析)