客戶流失分析
失去一個老使用者會帶來巨大的損失,大概需要公司拉新10個新使用者才能予以彌補。如何預測客戶即将流失,讓公司采取合适的挽回措施,是每個公司都要關注的重點問題。
目标
利用類神經網絡建構使用者流失分析模型,以預測使用者是否有流失的可能。
工具
Jupyter Notebook :一個對于資料分析師來說特别合适的Python編輯器,強烈推薦大家去使用。
Python:在機器學習時代,Python是最受歡迎的機器學習語言。有很多機器學習的庫,可以友善高效的去實作機器學習。
主要用到的Python包
pandas:是基于 Numpy 建構的含有更進階資料結構和工具的資料分析包。能很友善的進行各種資料清洗。是每個資料分析師必學的Python包之一。
sklearn:是機器學習中一個常用的第三方包,裡面對一些常用那個的機器學習方法進行了封裝,使得大家能夠更加簡單的使用機器學習的方法。本文主要用這個包進行訓練資料集和測試資料集的拆分以及資料尺度的标準化。
Keras:是一個高層神經網絡API,Keras由純Python編寫而成并基Tensorflow、Theano以及CNTK後端。本文是基于Tensorflow後端建構神經網絡模型。Tensorflow是谷歌開發的一個開源的人工智能庫。
接下來我們真正進入實戰部分:
1.讀取使用者流失測試資料
#載入pandas包來讀取csv格式的資料集
import pandas as pd
#把 csv格式的資料集導入到DataFrame對象中
df = pd.read_csv('C:/Users/36540/Desktop/lossertest.csv', header = 0)
df.head()
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLwMDOmlDN1kjNxQ2MyITMlVjN4I2N3MWMmZmZyEGMyIDZkdjY1UjYy8CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
我們首先使用pandas包把csv格式的資料集導入DataFrame對象中,大概介紹下資料集的對象,從左到右分别是,使用者ID、國家、注冊時間、B類使用者标記、最近登入時間、購買次數、購買金額、流失标記。
2.資料清洗
我們需要把所有的資料轉化為數值型的資料,且沒有缺失值。
#把totalPaiedAmount列也就是使用者付款金額的缺失值替換為0
df['totalPaiedAmount'] = df['totalPaiedAmount'].fillna(0)
df['totalBuyCount'] = df['totalBuyCount'].fillna(0)
根據業務邏輯,首先把使用者付款次數和付款金額的缺失值替換為0。
#利用pandas中的to_datetime函數把字元串的日期變為時間序列
df['registrationTime'] = pd.to_datetime(df['registrationTime'], format='%Y-%m-%d %H:%M:%S')
df['registrationTime']
直接導入的pandas的資料是字元串格式的時間,我們需要将資料轉化為時間序列格式。這裡用到pandas自帶的to_datetime函數,可以友善快速的把字元串轉化為時間序列。
#同理最近登入時間也轉化為實踐序列
df['lastLoginTime'] = pd.to_datetime(df['lastLoginTime'], format='%Y-%m-%d %H:%M:%S')
df['lastLoginTime']
根據業務邏輯需要把時間轉化為距今的時間間隔。
import datetime
#擷取目前時間
now_time = datetime.datetime.now()
now_time
根據datetime包,擷取目前的時間。
#把資料序列轉化為距今的時間間隔
df['registrationTime'] = now_time-df['registrationTime']
df['lastLoginTime'] = now_time-df['lastLoginTime']
在DataFrame對象中,可以直接對2個時間格式資料進行相減,得到時間間隔。但是這個不是數值型,我們還需要進行處理。
先根據業務邏輯把最近登入時間缺失的部分替換為注冊時間。
#把最近登入時間列的空值替換為同索引行注冊時間列的值
df.loc[df['lastLoginTime'].isnull(),'lastLoginTime']=df[df['lastLoginTime'].isnull()]['registrationTime']
根據pandas中自帶的isnull可以很友善的替換缺失值。
#因為資料量有點大,取前1w行資料測試下
df = df.iloc[0:1000]
#把時間間隔轉化為數值型的天數
j = 0
for i in df['registrationTime']:
df = df.replace(df['registrationTime'][j],i.days)
j += 1
建立一個for循環把所有的時間隔間轉化為數值型的時間隔間天數,.days函數可以友善擷取時間隔間的天數。經過我是實踐發現,Python對于這個轉化的處理速度很慢。是以我就取了前1000條資料進行測試處理。建議大家還是在mysql中直接用時間函數擷取時間差天數,資料庫中的處理速度快了很多。我50W+的資料隻要10幾秒就可以完成。
#不知道為什麼這樣操作就會報錯,歡迎大家研究研究
for i in range(0,df['registrationTime']):
df = df.replace(df['registrationTime'][i],df['registrationTime'][i].days)
我本來是這樣編寫for循環的,不知道為什麼運作幾條就報錯。差了很多資料也沒找到原因。也歡迎大家研究研究。找到原因可以評論或者私信我。
到這裡資料清洗也就基本完成了,我來最後檢查一遍,資料集是否還有缺失值。
#對資料集進檢查,看看是否還有缺失值
df[df.isnull().values==True]
可以發現,還有缺失值的列已經不存在了。接下來就把第一列對于結果無關的使用者ID列删除。
#把第一列無用的使用者ID列删除
df = df.iloc[:,1:]
資料清洗步驟就全部完成了,我再來看看資料集現在的樣子,來最終檢查一遍處理結果。
df.info()
可以發現所有的資料都已經變成float64或者 int64,已經達到了我們處理的目的。
接下來把輸入輸出項确定下,前6列是輸入的名額,最後一列流失标記是輸出項。
#把輸入輸出項确定下
y = df.iloc[:,-1]
x = df.iloc[:,:-1]
x.shape
y.shape
可以發現輸入項是1000行資料,6列。輸出是1000行數,1列。
區分訓練與測試資料集
#sklearn把資料集拆分成訓練集和測試集
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.33, random_state = 123)
x_train.shape
y_train.shape
x_test.shape
y_test.shape
利用sklearn包中的train_test_split函數可以很友善的區分訓練集和測試集。test_size代表測試的大小,0.33也就是訓練集和測試集的比為3:1,random_state代表區分的随機标準,這個如果不确定的話,每次拆分的結果也就是不一樣,這屬性是為了資料可以複現。大家不要使用123,可以随意填寫。從上圖可以看到,資料已經被拆分為670行和330行2個資料集了。
尺度标準化
所有神經網絡的輸入層必須進行标準處理,因為不同列的大小是不一樣,這樣的話沒法進行對比。是以需要對資料集進行标準化處理。
#使用sklearn把資料集進行尺度标準化
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
x_train = sc.fit_transform(x_train)
x_test = sc.fit_transform(x_test)
x_test
sklearn包中的StandardScaler函數可以友善對資料進行去均值和方差歸一化處理。首先定義一個對象,sc = StandardScaler(),然後把資料集放進去就可以直接輸出一個标準化完成的資料集。輸出的資料集如上圖所示。
訓練ANN
#使用keras包搭建人工神經網絡
import keras
#序貫(Sequential)模型包
from keras.models import Sequential
#神經網絡層
from keras.layers import Dense
#優化器
from keras.optimizers import SGD
#建立一個空的神經網絡模型
classifier = Sequential()
我們利用keras包來交輕松的完成人工神經網絡的搭建。首先載入一個序貫(Sequential)模型。序貫模型是多個網絡層的線性堆疊,也就是“一條路走到黑”。可以通過向
Sequential
模型傳遞一個layer的list來構造該模型,也可以通過
.add()
方法一個個的将layer加入模型中。本文采用.add()方法将2層神經網絡輸入模型中。優化器的選擇是SGD,因為本來資料量比較小,而且訓練次數也不多,是以選擇最賤簡答的SGD。平時對于性能的有要求的可以使用Adam優化器。
#建立輸入層
classifier.add(Dense(units = 3, kernel_initializer = 'uniform', activation = 'relu', input_dim = 6))
#建立輸出層
classifier.add(Dense(units = 1, kernel_initializer = 'uniform', activation = 'sigmoid'))
将神經網絡的輸入輸出層添加到模型中。
Dense就是常用的全連接配接層,所實作的運算是
output = activation(dot(input, kernel)+bias)
。
參數
units:大于0的整數,代表該層的輸出次元。一般為輸入項的一半,但是真正合适的值還是要經過多次訓練才能得出。
activation:激活函數,為預定義的激活函數名(參考激活函數),或逐元素(element-wise)的Theano函數。如果不指定該參數,将不會使用任何激活函數(即使用線性激活函數:a(x)=x)。本文用的relu和sigmoid。都是最基礎的。
bias_initializer:偏置向量初始化方法,為預定義初始化方法名的字元串,或用于初始化偏置向量的初始化器。不同的層可能使用不同的關鍵字來傳遞初始化方法,一般來說指定初始化方法的關鍵字。本文用的Glorot均勻分布初始化方法,又成Xavier均勻初始化,參數從[-limit, limit]的均勻分布産生,其中limit為
sqrt(6 / (fan_in + fan_out))
。fan_in為權值張量的輸入單元數,fan_out是權重張量的輸出單元數。
形如(batch_size, ..., input_dim)的nD張量,最常見的情況為(batch_size, input_dim)的2D張量。
classifier.compile(loss='binary_crossentropy',
optimizer=SGD(),
metrics=['accuracy'])
history = classifier.fit(x_train, y_train,
batch_size=10,
epochs=100,
validation_data=(x_test, y_test))
然後設定模型的損失函數loss為binary_crossentropy(亦稱作對數損失,logloss)。目标函數,或稱損失函數,是編譯一個模型必須的兩個參數之一。
優化器選擇了SGD,也就是最簡單基礎的一個優化器。
性能評估子產品提供了一系列用于模型性能評估的函數,這些函數在模型編譯時由
metrics
關鍵字設定。性能評估函數類似與目标函數, 隻不過該性能的評估結果講不會用于訓練。
Keras以Numpy數組作為輸入資料和标簽的資料類型。訓練模型一般使用
fit
函數。把訓練集輸入,然後batch_size選擇每次訓練數量,epochs是訓練的次數。validation_data驗證的資料集。
最後看到上面的訓練結果loss為0.0973,acc為0.9612。這個結果已經是一個比較好的結果。
評估模型
y_pred = classifier.predict(x_test)
y_pred
利用predict把測試集的結果輸出來,輸出的是0-1的機率值,我可以假設大于0.5為流失,把結果轉化為0和1和結果。0.5隻是一個大概的值,最合适的話還是要自己去測試得出。
y_pred = (y_pred > 0.5)
y_pred.shape
y_pred.flatten().astype(int)
最終把結果轉化為0和1和,通過flatten吧資料轉化為一維的資料,并且利用astype(int)把True和False轉化為0和1。
from sklearn.metrics import accuracy_score
accuracy_score(y_test, y_pred )
根據accuracy_score直接得到結果,可以發現結果為0.9727,這個資料是好的結果。準确率有97%。但是我們僅僅看着資料是不夠的,因為假如1000個人裡隻有50個流失,那我全部亂猜為不流失,這樣準确率也有95%。是以要再看看流失和非流失的準确率。
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pred )
cm
可以發現非流失使用者全部猜對,但是流失的隻對了3個。說明模型對于非流失使用者的準确性還需要提高。結果看看更加詳細的結果。
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred))
利用classification_report函數直接擷取結果。我們觀察結果可以發現,流失使用者的f1-score隻有0.40.這是比較小的值,還有很大的提高空間。雖然全部使用者的準确率97%,看上去很美好,實際一拆分的結果并不如人意。當然這裡隻是一個測試的結果,後續我們可以增加輸入層的資料名額,增加訓練的次數去提高準确率。
今天的文章就到這裡啦,我會把模型的代碼和資料(可以提供1000行的脫敏資料作為大家練習使用)上傳到百度網盤。大家可以自行下載下傳。
原文釋出時間為:2018-10-24
本文作者:王向君
本文來自雲栖社群合作夥伴“
Python愛好者社群”,了解相關資訊可以關注“
”。