產(chǎn)品如同蓄水池骏令,用戶好比池中之水蔬捷。池子中每時每刻都有新用戶源源不斷地加入,也有一部分用戶選擇離開。如果用戶流失超過新用戶的補給周拐,且速度越來越快铡俐、規(guī)模越來越大時,產(chǎn)品如若不警惕妥粟,蓄水池遲早會干涸审丘。
不合理的周期造成預(yù)測準(zhǔn)確率低且不平衡,我們需要不斷嘗試周期劃分勾给,在保證整體準(zhǔn)確率的情況下尋求流失與留存準(zhǔn)確率最佳的平衡點滩报,才能更為準(zhǔn)確地同時預(yù)測流失及留存情況。
流失比較經(jīng)典的定義是“一段時間內(nèi)未進(jìn)行關(guān)鍵行為的用戶”播急,關(guān)鍵點在于如何界定時間周期(流失周期)和關(guān)鍵行為(流失行為)露泊。
用戶回訪率 = 回訪用戶數(shù) ÷ 流失用戶數(shù) × 100%
通過流失預(yù)警模型,我們可以獲得產(chǎn)品一系列功能模塊或指標(biāo)對流失留存的影響因子旅择,并計算出每個用戶的流失概率惭笑。通過影響因子,我們可以對流失原因有所了解生真,在此基礎(chǔ)上進(jìn)行深入研究和確認(rèn)沉噩,結(jié)合用戶反饋的頻率、專家意見等確定改版的優(yōu)先級柱蟀。
區(qū)分出可能流失的用戶是為了提高挽留策略的針對性川蒙,提高效率與減少成本,實現(xiàn)精細(xì)化運營——這也是流失模型的核心價值所在长已。
從用戶使用的輕重程度出發(fā)(如上圖)畜眨,在通過模型計算出用戶未來的流失概率后,將使用App的頻率和時長作為用戶輕重度的劃分標(biāo)準(zhǔn)术瓮,結(jié)合用戶流失留存預(yù)期康聂,將用戶劃分為高價值、重點發(fā)展胞四、重點轉(zhuǎn)化恬汁、有待挽留等幾種類型,分析每個類型用戶不同的行為特點和使用痛點辜伟,采取針對性的運營策略氓侧。
當(dāng)然,流失模型也可結(jié)合付費維度進(jìn)行研究导狡。先篩選出極有可能將會流失的用戶约巷,再根據(jù)購買頻次和付費金額來進(jìn)行細(xì)分:從未付費的用戶可通過優(yōu)惠券、促銷活動或超低價商品吸引回訪旱捧、促成首單購買独郎;少量付費且客單價低的用戶可以精準(zhǔn)推送符合個性化偏好的商品,或者推薦符合該用戶消費層次的超值商品;多次付費的老用戶囚聚,可以增加會員專屬優(yōu)惠靖榕,通過回饋激勵增強用戶粘性,延長使用周期顽铸。
以上只是流失模型的兩個層面的應(yīng)用茁计,在不同項目中還可以結(jié)合多種方式對用戶進(jìn)行精細(xì)化運營。模型準(zhǔn)確性高的話谓松,可以用更少的成本星压、對用戶更少的干擾來留住更有價值的用戶。
下面舉例:電信公司希望針對客戶的信息預(yù)測其流失可能性
從機器學(xué)習(xí)的分類來講鬼譬, 這是一個監(jiān)督問題中的分類問題娜膘。 具體來說, 是一個二分類問題优质。
數(shù)據(jù)預(yù)處理
讀取數(shù)據(jù):
from __future__ import division
import pandas as pd
import numpy as np
ds = pd.read_csv('F:\churn.csv')
col_names = ds.columns.tolist()
print("Column names:")
print(col_names)
ds.shape
變量說明:
subscriberID="個人客戶的ID"
churn="是否流失:1=流失";
Age="年齡"
incomeCode="用戶居住區(qū)域平均收入的代碼"
duration="在網(wǎng)時長"
peakMinAv="統(tǒng)計期間內(nèi)最高單月通話時長"
peakMinDiff="統(tǒng)計期間結(jié)束月份與開始月份相比通話時長增加數(shù)量"
posTrend="該用戶通話時長是否呈現(xiàn)出上升態(tài)勢:是=1"
negTrend="該用戶通話時長是否呈現(xiàn)出下降態(tài)勢:是=1"
nrProm="電話公司營銷的數(shù)量"
prom="最近一個月是否被營銷過:是=1"
curPlan="統(tǒng)計時間開始時套餐類型:1=最高通過200分鐘竣贪;2=300分鐘;3=350分鐘巩螃;4=500分鐘"
avPlan="統(tǒng)計期間內(nèi)平均套餐類型"
planChange="統(tǒng)計結(jié)束時和開始時套餐的變化:正值代表套餐檔次提升演怎,負(fù)值代表下降,0代表不變"
posPlanChange="統(tǒng)計期間是否提高套餐:1=是"
negPlanChange="統(tǒng)計期間是否降低套餐:1=是"
call_10086="撥打10086的次數(shù)"
查看前5行
ds.head()
整個數(shù)據(jù)集有3463條數(shù)據(jù)避乏, 20個維度爷耀,第二項是分類是否流失。
查看數(shù)據(jù)類型:
ds.info()
全為浮點型數(shù)據(jù)拍皮,不需要數(shù)值轉(zhuǎn)換
首先查看因變量中各類別的比例差異歹叮,通過餅圖:
import matplotlib.pyplot as plt
# 數(shù)據(jù)集中是否違約的客戶比例
# 為確保繪制的餅圖為圓形,需執(zhí)行如下代碼
plt.axes(aspect = 'equal')
# 中文亂碼和坐標(biāo)軸負(fù)號的處理
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
#重命名因變量
ds.rename(columns={'churn':'y'},inplace=True)
# 統(tǒng)計客戶是否違約的頻數(shù)
counts = ds.y.value_counts()
# 繪制餅圖
plt.pie(x = counts, # 繪圖數(shù)據(jù)
labels=pd.Series(counts.index).map({0:'不流失',1:'流失'}), # 添加文字標(biāo)簽
autopct='%.1f%%' # 設(shè)置百分比的格式铆帽,這里保留一位小數(shù)
)
# 顯示圖形
plt.show()
總體來說咆耿,兩個類別的比例不算失衡。
拆分?jǐn)?shù)據(jù)
# 將數(shù)據(jù)集拆分為訓(xùn)練集和測試集
# 導(dǎo)入第三方包
from sklearn import model_selection
from sklearn import ensemble
from sklearn import metrics
# 排除數(shù)據(jù)集中的ID變量和因變量锄贼,剩余的數(shù)據(jù)用作自變量X
X = ds.drop(['y'], axis = 1)
y = ds.y
# 數(shù)據(jù)拆分
X_train,X_test,y_train,y_test = model_selection.train_test_split(X,y,test_size = 0.3, random_state = 1234)
法一:利用ROC(只涉及l(fā)ogistic和隨機森林)
Logistic模型
建模
from sklearn import linear_model
#利用訓(xùn)練集建模
sklearn_logistic=linear_model.LogisticRegression()
sklearn_logistic.fit(X_train,y_train)
#返回模型的各個參數(shù)
print(sklearn_logistic.intercept_,sklearn_logistic.coef_)
預(yù)測構(gòu)建混淆矩陣
# 模型預(yù)測
sklearn_predict = sklearn_logistic.predict(X_test)
# 預(yù)測結(jié)果統(tǒng)計
pd.Series(sklearn_predict).head()
pd.Series(sklearn_predict).value_counts()
判斷為不流失的為1039個
# 導(dǎo)入第三方模塊
from sklearn import metrics
# 混淆矩陣
cm = metrics.confusion_matrix(y_test, sklearn_predict, labels = [0,1])
cm
繪制ROC曲線
Accuracy = metrics.scorer.accuracy_score(y_test, sklearn_predict)
Sensitivity = metrics.scorer.recall_score(y_test, sklearn_predict)
Specificity = metrics.scorer.recall_score(y_test, sklearn_predict, pos_label=0)
print('模型準(zhǔn)確率為%.2f%%:' %(Accuracy*100))
print('正例覆蓋率為%.2f%%' %(Sensitivity*100))
print('負(fù)例覆蓋率為%.2f%%' %(Specificity*100))
整體的預(yù)測準(zhǔn)確率一般
# y得分為模型預(yù)測正例的概率
y_score = sklearn_logistic.predict_proba(X_test)[:,1]
# 計算不同閾值下票灰,fpr和tpr的組合值,其中fpr表示1-Specificity宅荤,tpr表示Sensitivity
fpr,tpr,threshold = metrics.roc_curve(y_test, y_score)
# 計算AUC的值
roc_auc = metrics.auc(fpr,tpr)
# 繪制面積圖
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
# 添加邊際線
plt.plot(fpr, tpr, color='black', lw = 1)
# 添加對角線
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
# 添加文本信息
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
# 添加x軸與y軸標(biāo)簽
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
# 顯示圖形
plt.show()
低于0.8,認(rèn)定回歸模型是不合理的
隨機森林模型
from sklearn import ensemble
# 構(gòu)建隨機森林
RF_class = ensemble.RandomForestClassifier(n_estimators=200, random_state=1234)
# 隨機森林的擬合
RF_class.fit(X_train, y_train)
# 模型在測試集上的預(yù)測
RFclass_pred = RF_class.predict(X_test)
# 模型的準(zhǔn)確率
print('模型在測試集的預(yù)測準(zhǔn)確率:\n',metrics.accuracy_score(y_test, RFclass_pred))
# 計算繪圖數(shù)據(jù)
y_score = RF_class.predict_proba(X_test)[:,1]
fpr,tpr,threshold = metrics.roc_curve(y_test, y_score)
roc_auc = metrics.auc(fpr,tpr)
# 繪圖
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
plt.plot(fpr, tpr, color='black', lw = 1)
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
plt.show()
遠(yuǎn)遠(yuǎn)高于0.8浸策,認(rèn)為模型合理
再挑選出重要因素
# 變量的重要性程度值
importance = RF_class.feature_importances_
# 構(gòu)建含序列用于繪圖
Impt_Series = pd.Series(importance, index = X_train.columns)
# 對序列排序繪圖
Impt_Series.sort_values(ascending = True).plot('barh')
plt.show()
取出重要性比較高的變量再利用交叉驗證選擇參數(shù)建模
# 取出重要性比較高的自變量建模
predictors = list(Impt_Series[Impt_Series>0.015].index)
predictors
重新建模
# 隨機森林的擬合
RF_class.fit(X_train[predictors], y_train)
# 模型在測試集上的預(yù)測
RFclass_pred = RF_class.predict(X_test[predictors])
# 模型的準(zhǔn)確率
print('模型在測試集的預(yù)測準(zhǔn)確率:\n',metrics.accuracy_score(y_test, RFclass_pred))
# 計算繪圖數(shù)據(jù)
y_score = RF_class.predict_proba(X_test[predictors])[:,1]
fpr,tpr,threshold = metrics.roc_curve(y_test, y_score)
roc_auc = metrics.auc(fpr,tpr)
# 繪圖
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
plt.plot(fpr, tpr, color='black', lw = 1)
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
plt.show()
遠(yuǎn)遠(yuǎn)高于0.8且準(zhǔn)確率上升冯键,認(rèn)為模型合理
法二:利用誤差均值(多個模型循環(huán)比較)
# prepare models
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
from sklearn import linear_model
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn import naive_bayes
from sklearn import svm
models = []
models.append(('LR', linear_model.LogisticRegression()))
models.append(('LDA', LinearDiscriminantAnalysis()))
models.append(('KNN', KNeighborsClassifier()))
models.append(('CART', DecisionTreeClassifier()))
models.append(('NB', naive_bayes.GaussianNB()))
models.append(('SVM', svm.SVC()))
# evaluate each model in turn
results = []
names = []
scoring = 'accuracy'
for name, model in models:
kfold = KFold(n_splits=10, random_state=7)
cv_results = cross_val_score(model, X_test, y_test, cv=kfold, scoring=scoring)
results.append(cv_results)
names.append(name)
msg = "%s: %f (%f)" % (name, cv_results.mean(), cv_results.std())
print(msg)
# boxplot algorithm comparison
fig = plt.figure()
fig.suptitle('Algorithm Comparison')
ax = fig.add_subplot(111)
plt.boxplot(results)
ax.set_xticklabels(names)
plt.show()
可以看出,LDA判別效果最好
但可以利用集成學(xué)習(xí)庸汗,例如隨機森林
from sklearn.ensemble import RandomForestClassifier
num_trees = 100
max_features = 3
kfold = KFold(n_splits=10, random_state=7)
model = RandomForestClassifier(n_estimators=num_trees, max_features=max_features)
results = cross_val_score(model, X_test, y_test, cv=kfold)
print(results.mean())
則隨機森林的模型更加合理