1. 程式人生 > >利用深度學習建立流失模型

利用深度學習建立流失模型

客戶流失分析

失去一個老使用者會帶來巨大的損失,大概需要公司拉新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() 

我們首先使用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['registrationTime']

df['lastLoginTime'] = now_time-df['lastLoginTime']
df['registrationTime']

在DataFrame物件中,可以直接對2個時間格式資料進行相減,得到時間間隔。但是這個不是數值型,我們還需要進行處理。

先根據業務邏輯把最近登入時間缺失的部分替換為註冊時間。

#把最近登入時間列的空值替換為同索引行註冊時間列的值
df.loc[df['lastLoginTime'].isnull(),'lastLoginTime']=df[df['lastLoginTime'].isnull()]['registrationTime']
df['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.head()
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行的資料作為大家練習使用)上傳到百度網盤。大家可以自行下載。

原始碼:

#!/usr/bin/env python
# coding: utf-8

# 讀取使用者流失測試資料

# In[90]:


#載入pandas包來讀取csv格式的資料集
import pandas as pd
#把 csv格式的資料集匯入到DataFrame物件中
df = pd.read_csv('C:/Users/36540/Desktop/lossertest.csv',  header = 0)
df.head() 


# 首先要對元資料進行資料清理,需要把所有資料轉化為數值型資料

# In[91]:


#把totalPaiedAmount列也就是使用者付款金額的缺失值替換為0
df['totalPaiedAmount'] = df['totalPaiedAmount'].fillna(0)
df['totalBuyCount'] = df['totalBuyCount'].fillna(0)


# In[92]:


#利用pandas中的to_datetime函式把字串的日期變為時間序列
df['registrationTime'] = pd.to_datetime(df['registrationTime'], format='%Y-%m-%d %H:%M:%S')
df['registrationTime'] 


# In[93]:


#同理轉化為實踐序列
df['lastLoginTime'] = pd.to_datetime(df['lastLoginTime'], format='%Y-%m-%d %H:%M:%S') 
df['lastLoginTime']


# In[94]:


import datetime
#獲取當前時間
now_time = datetime.datetime.now()
now_time


# In[95]:


#把資料序列轉化為距今的時間間隔
df['registrationTime'] = now_time-df['registrationTime']
df['registrationTime']


# In[96]:


df['lastLoginTime'] = now_time-df['lastLoginTime']
df['registrationTime']


# In[97]:


#把最近登入時間列的空值替換為同索引行註冊時間列的值
df.loc[df['lastLoginTime'].isnull(),'lastLoginTime']=df[df['lastLoginTime'].isnull()]['registrationTime']
df['registrationTime']


# In[98]:


#因為資料量有點大,取前1w行資料測試下
df = df.iloc[0:1000]


# In[100]:


#把時間間隔轉化為數值型的天數
j = 0
for i in df['registrationTime']:
    df = df.replace(df['registrationTime'][j],i.days)
    j += 1
    


# In[101]:


j = 0
for i in df['lastLoginTime']:
    df = df.replace(df['lastLoginTime'][j],i.days)
    j += 1


# In[ ]:


#不知道為什麼這樣操作就會報錯,歡迎大家研究研究
for i in range(0,df['registrationTime']):
    df = df.replace(df['registrationTime'][i],df['registrationTime'][i].days)
    
#最後建議大家在mysql直接把時間轉化為時間天數間隔,pandas的處理效率實在是太慢了


# In[102]:


#對資料集進檢查,看看是否還有缺失值
df[df.isnull().values==True]


# In[103]:


#把第一列無用的使用者ID列刪除
df = df.iloc[:,1:]


# In[104]:


df.head()


# In[105]:


df.info()


# In[106]:


#把輸入輸出項確定下
y = df.iloc[:,-1]
x = df.iloc[:,:-1]


# In[107]:


x.shape


# In[108]:


y.shape


# 區分訓練與測試資料集

# In[109]:


#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)


# In[110]:


x_train.shape


# In[111]:


y_train.shape


# In[112]:


x_test.shape


# In[113]:


y_test.shape


# 尺度標準化

# In[114]:


#使用sklearn把資料集進行尺度標準化
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
x_train = sc.fit_transform(x_train)
x_test = sc.fit_transform(x_test)


# In[115]:


x_test


# 訓練ANN

# In[116]:


#使用keras包搭建人工神經網路
import keras
#序貫(Sequential)模型包
from keras.models import Sequential
#神經網路層
from keras.layers import Dense
#優化器
from keras.optimizers import SGD


# In[117]:


#建立一個空的神經網路模型
classifier = Sequential()


# In[118]:


#建立輸入層
classifier.add(Dense(units = 3, kernel_initializer = 'uniform', activation = 'relu', input_dim = 6))
#建立輸出層
classifier.add(Dense(units = 1, kernel_initializer = 'uniform', activation = 'sigmoid'))


# In[119]:


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))


# 評估模型

# In[120]:


y_pred = classifier.predict(x_test)


# In[121]:


y_pred


# In[122]:


y_pred = (y_pred > 0.5)


# In[123]:


y_pred.shape


# In[124]:


y_pred.flatten().astype(int)


# In[125]:


sum(y_pred.flatten().astype(int) == y_test) / len(y_test)


# In[126]:


from sklearn.metrics import accuracy_score
accuracy_score(y_test, y_pred )


# In[127]:


from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pred )


# In[128]:


cm


# In[129]:


from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred))


# In[ ]:




提取碼:9ii2