利用深度學習建立流失模型
客戶流失分析
失去一個老用戶會帶來巨大的損失,大概需要公司拉新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[ ]:
鏈接:https://pan.baidu.com/s/14wbOA9pz-o9pEehOgZOxRQ
提取碼:9ii2
機器學習 深度學習
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。