基於Tensorflow的神經網路解決用戶流失概率問題

歡迎關注我們的微信公眾號「人工智慧LeadAI」(ID:atleadai)

前言

用戶流失一直都是公司非常重視的一個問題,也是AAARR中的Retention的核心問題,所以各大演算法競賽都很關注。比如最近的:KKBOX的會員流失預測演算法競賽,如何能夠搭建一個精準的模型成了大家探索的重要問題。

本文主要講解神經網路、TensorFlow的概述、如何利用python基於TensorFlow神經網路對流失用戶進行分類預測,及可能存在的一些常見問題,作為深度學習的入門閱讀比較適合。

行業做法

通常的行業預測用戶流失大概分以下幾種思路:

1、利用線性模型(比如Logistic)+非線性模型Xgboost判斷用戶是否迴流逝

這種方法有關是行業裡面用的最多的,效果也被得意驗證足夠優秀且穩定的。核心點在於特徵的預處理,Xgboost的參數挑優,擬合程度的控制,這個方法值得讀者去仔細研究一邊。問題也是很明顯的,會有一個行業baseline,基本上達到上限之後,想有有提升會非常困難,對要求精準預測的需求會顯得非常乏力。

2、規則觸發

這種方法比較古老,但是任然有很多公司選擇使用,實現成本較低而且非常快速。核心在於,先確定幾條核心的流失指標(比如近7日登錄時長),然後動態的選擇一個移動的窗口,不停根據已經流失的用戶去更新流失指標的閾值。當新用戶達到閾值的時候,觸發流失預警。效果不如第一個方法,但是實現簡單,老闆也很容易懂。

3、場景模型的預測

這個方法比較依賴於公司業務的特徵,如果公司業務有部分依賴於評論,可以做文本分析,比如我上次寫的基於word2vec下的用戶流失概率分析(jianshu.com/p/413cff5b9)。如果業務有部分依賴於登錄打卡,可以做時間線上的頻次預估。這些都是比較偏奇門易巧,不屬於通用類別的,不過當第一種方法達到上線的時候,這種方法補充收益會非常的大。

其實還有很多其它方法,我這邊也不一一列出了,這個領域的方法論還是很多的。

神經網路

核心

神經網路流程

上面這張圖片詮釋了神經網路正向傳播的流程,先通過線性變換(上圖左側)Σxw+b將線性可分的數據分離,再通過非線性變換(上圖右側)Sigmoid函數將非線性可分的數據分離,最後將輸入空間投向另一個輸出空間。

根據上面所說,我們可以知道,通過增加左側線性節點的個數,我們可以強化線性變換的力度;而通過增加層數,多做N次激活函數(比如上面提到的Sigmoid)可以增強非線性變換的能力。

通過矩陣的線性變換+激活矩陣的非線性變換,將原始不可分的數據,先映射到高緯度,再進行分離。但是這邊左側節點的個數,網路的層數選擇是非常困難的課題,需要反覆嘗試。

參數訓練

剛才我們了解了整個訓練的流程,但是如何訓練好包括線性變換的矩陣係數是一個還沒有解決的問題。

我們來看下面的過程:

input ==> Σxw+b(線性變換) ==> f(Σxw+b)(激活函數) ==> ...(多層的話重複前面過程) ==> output(到此為止,正向傳播結束,反向修正矩陣weights開始) ==> error=actual_output-output(計算預測值與正式值誤差)==>output處的梯度==>調整後矩陣weight=當前矩陣weight+errorx學習速率xoutput處的負梯度。

核心目的在於通過比較預測值和實際值來調整權重矩陣,將預測值與實際值的差值縮小。

比如:梯度下降的方法,通過計算當前的損失值的方向的負方向,控制學習速率來降低預測值與實際值間的誤差。

利用一行代碼來解釋就是

synaptic_weights += dot(inputs, (real_outputs - output) * output * (1 - output))*ηn

這邊output * (1 - output))是在output處的Sigmoid的倒數形式,η是學習速率。

weight的循環流程

神經網路流程小結

1數據集獲取(有監督數據整理)

2神經網路參數確定,有多少層,多少個節點,激活函數是什麼,損失函數是什麼

3數據預處理,pca,標準化,中心化,特徵壓縮,異常值處理

4初始化網路權重

5網路訓練

5.1正向傳播

5.2計算loss

5.3計算反向梯度

5.4更新梯度

5.5重新正向傳播

這邊只是簡單介紹了神經網路的基礎知識,針對有一定基礎的朋友喚醒記憶,如果純小白用戶,建議從頭開始認真閱讀理解一遍過程,避免我講的有偏頗的地方對你進行誤導。

TensorFlow

理論上講,TensorFlow工具可以單獨寫一本書,用法很多而且技巧性的東西也非常的復

雜,這邊我們主要作為工具進行使用,遇到新技巧會在code中解釋,但不做全書的梳理,建議去買一本《TensorFlow實戰Google深度學習框架》,簡單易懂。

TensorFlow是谷歌於2015年11月9日正式開源的計算框架,由Jeff Dean領導的谷歌大腦團隊改編的DistBelief得到的,在ImageNet2014、YouTube視頻學習,語言識別錯誤率優化,街景識別,廣告,電商等等都有了非常優秀的產出,是我個人非常喜歡的工具。

除此之外,我在列出一些其他的框架工具供讀者使用:

接下來看一下最基本的語法,方便之後我們直接貼代碼的時候可以輕鬆閱讀。

  • 張量:可以理解為多維array 或者 list,time決定張量是什麼

tf.placeholder(time,shape,name)

  • 變數:同一時刻下的不變的數據

    tf.Variable(value,name)
  • 常量:永遠不變的常值

    tf.constant(value)
  • 執行環境開啟與關閉,在環境中才能運行TensorFlow語法

    sess=tf.Session()

    sess.close()

    sess.run(op)
  • 初始化所有權重:類似於變數申明

    tf.initialize_all_variables()
  • 更新權重:

    tf.assign(variable_to_be_updated,new_value)
  • 加值行為,利用feed_dict裡面的值來訓練[output]函數

    sess.run([output],feed_dict={input1:value1,input2:value2})

    利用input1,input2,來跑output的值
  • 矩陣乘法,類似於dot

    tf.matmul(input,layer1)

  • 激活函數,relu

    tf.nn.relu(),除此之外,還有tf.nn.sigmoid,tf.nn.tanh等等

用戶流式分析

說了那麼多前置的鋪墊,讓我們來真實的面對我們需要解決的問題:

首先,我們拿到了用戶是否流失的歷史數據集20724條,流失與飛流失用戶佔比在1:4,這部分數據需要進行一下預處理,這邊就不細講預處理過程了,包含缺失值填充(分層填充),異常值處理(isolation foest),數據平衡(tomek link),特徵選擇(xgboost importance),特徵變形(normalizing),特徵分布優化等等,工程技巧我之前的文章都有講解過,不做本文重點。

taiking is cheap,show me the code.

#author:shataowein#time:20170924n#基礎包載入nimport numpy as npnimport pandas as pdnimport tensorflow as tfnimport mathnfrom sklearn.cross_validation import train_test_split n

#數據處理ndata = pd.read_table(/Users/slade/Desktop/machine learning/data/data_all.txt)ndata = data.iloc[:,1:len(data.columns)]ndata1 = (data - data.mean())/data.std()nlabels = data[tag] items = data1.iloc[:,1:len(data1.columns)]nall_data = pd.concat([pd.DataFrame(labels),items],axis = 1)n#數據集切分成訓練集和測試集,佔比為0.8:0.2n train_X,test_X,train_y,test_y = train_test_split(items,labels,test_size = 0.2,random_state = 0) #pandas讀取進來是dataframe,轉換為ndarray的形式ntrain_X = np.array(train_X)ntest_X = np.array(test_X)n#我將0或者1的預測結果轉換成了[0,1]或者[1,0]的對應形式,讀者可以不轉n train_Y = [] for i in train_y: nif i ==0: ntrain_Y.append([0,1]) n else: ntrain_Y.append([1,0])ntest_Y = [] for i in test_y: n if i ==0: n test_Y.append([0,1]) nelse: n test_Y.append([1,0])n

下面我們就要開始正式開始訓練神經網路了.

input_node = 9 #輸入的feature的個數,也就是input的維度noutput_node = 2 #輸出的[0,1]或者[1,0]的維度n layer1_node = 500 #隱藏層的節點個數,一般在255-1000之間,讀者可以自行調整nbatch_size = 200 #批量訓練的數據,batch_size越小訓練時間越長,訓練效果越準確(但會存在過擬合)nlearning_rate_base = 0.8 #訓練weights的速率ηnregularzation_rate = 0.0001 #正則力度ntraining_steps = 10000 #訓練次數,這個指標需要類似grid_search進行搜索優化 #設定之後想要被訓練的x及對應的正式結果y_nx = tf.placeholder(tf.float32,[None,input_node])ny_ = tf.placeholder(tf.float32,[None,output_node])n #input到layer1之間的線性矩陣nweight weight1 = tf.Variable(tf.truncated_normal([input_node,layer1_node],stddev=0.1))n#layer1到output之間的線性矩陣nweight weight2 = tf.Variable(tf.truncated_normal([layer1_node,output_node],stddev=0.1))n#input到layer1之間的線性矩陣的偏置nbiases1 = tf.Variable(tf.constant(0.1,shape = [layer1_node]))n#layer1到output之間的線性矩陣的偏置nbiases2 = tf.Variable(tf.constant(0.1,shape=[output_node]))n#正向傳播的流程,線性計算及激活函數relu的非線性計算得到resultndef interence(input_tensor,weight1,weight2,biases1,biases2): n layer1 = tf.nn.relu(tf.matmul(input_tensor,weight1)+biases1) nresult = tf.matmul(layer1,weight2)+biases2 nreturn resultny = interence(x,weight1,weight2,biases1,biases2)n

正向傳播完成後,我們要反向傳播來修正weight

global_step = tf.Variable(0,trainable = False)n#交叉熵,用來衡量兩個分布之間的相似程度ncross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels = y_,logits=y) cross_entropy_mean = tf.reduce_mean(cross_entropy)n#l2正則化,這部分的理論分析可以參考我之前寫的:http://www.jianshu.com/p/4f91f0dcba95 regularzer = tf.contrib.layers.l2_regularizer(regularzation_rate)nregularzation = regularzer(weight1) + regularzer(weight2)n #損失函數為交叉熵+正則化nloss = cross_entropy_mean + regularzationn#我們用learning_rate_base作為速率η,來訓練梯度下降的loss函數解ntrain_op = tf.train.GradientDescentOptimizer(learning_rate_base).minimize(loss,global_step = global_step)n #y是我們的預測值,y_是真實值,我們來找到y_及y(比如[0.1,0.2])中最大值對應的index位置,判斷y與y_是否一致ncorrection = tf.equal(tf.argmax(y,1),tf.argmax(y_,1))n #如果y與y_一致則為1,否則為0,mean正好為其準確率naccurary = tf.reduce_mean(tf.cast(correction,tf.float32))n

模型訓練結果

#初始化環境,設置輸入值,檢驗值ninit = tf.global_variables_initializer()nsess = tf.Session()nsess.run(init)nvalidate_feed = {x:train_X,y_:train_Y}ntest_feed = {x:test_X,y_:test_Y}n #模型訓練,每到1000次彙報一次訓練效果n for i in range(training_steps): nstart = (i*batch_size)%len(train_X) nend = min(start+batch_size,16579) nxs = train_X[start:end] nys = train_Y[start:end] nif i%1000 ==0: nvalidate_accuary = sess.run(accurary,feed_dict = validate_feed) nprint the times of training is %d, and the accurary is %s %(i,validate_accuary) sess.run(train_op,feed_dict = {x:xs,y_:ys})n

訓練的結果如下:

2017-09-24 12:11:28.409585: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasnt compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.n2017-09-24 12:11:28.409620: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasnt compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations.n2017-09-24 12:11:28.409628: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasnt compiled to use AVX2 instructions, but these are available on your machine and could speed up CPU computations.n2017-09-24 12:11:28.409635: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasnt compiled to use FMA instructions, but these are available on your machine and could speed up CPU computations.nthe times of training is 0, and the accurary is 0.736775nthe times of training is 1000, and the accurary is 0.99246nthe times of training is 2000, and the accurary is 0.993003nthe times of training is 3000, and the accurary is 0.992943nthe times of training is 4000, and the accurary is 0.992943nthe times of training is 5000, and the accurary is 0.99234nthe times of training is 6000, and the accurary is 0.993124nthe times of training is 7000, and the accurary is 0.992943nthe times of training is 8000, and the accurary is 0.993124nthe times of training is 9000, and the accurary is 0.992943n

初步看出,在訓練集合上,準確率在能夠99%以上,讓我們在看看測試集效果。

test_accuary = sess.run(accurary,feed_dict = test_feed)n

Out[5]: 0.99034983,也是我們的測試數據集效果也是在99%附近,可以看出這個分類的效果還是比較高的。

除次之外,我們還可以得到每個值被預測出來的結果,也可以通過工程技巧轉換為0-1的概率:

result_y = sess.run(y,feed_dict={x:train_X})nresult_y_update=[]nfor i in result_y:n if i[0]>=i[1]:n result_y_update.append([1,0])n else:n result_y_update.append([0,1])n

==>

Out[7]: narray([[-1.01412344, 1.21461654],n [-3.66026735, 3.81834102],n [-3.78952932, 3.79097509],n ..., n [-3.71239662, 3.65721083],n [-1.59250259, 1.89412308],n [-3.35591984, 3.24001145]], dtype=float32)n

以上就實現了如果用TensorFlow裡面的神經網路技巧去做一個分類問題,其實這並不TensorFlow的全部,傳統的Bp神經網路,SVM也可以到達近似的效果,在接下來的文章中,我們將繼續看到比如CNN圖像識別,LSTM進行文本分類,RNN訓練不均衡數據等複雜問題上面的優勢。

可能存在的問題

在剛做神經網路的訓練前,要注意一下是否會犯以下的錯誤。

1、數據是否規範化

模型計算的過程時間長度及模型最後的效果,均依賴於input的形式。大部分的神經網路訓練過程都是以input為1的標準差,0的均值為前提的;除此之外,在算梯度算反向傳播的時候,過大的值有可能會導致梯度消失等意想不到的情況,非常值得大家注意。

2、batch的選擇

在上面我也提了,過小的batch會增加模型過擬合的風險,且計算的時間大大增加。過大的batch會造成模型的擬合能力不足,可能會被局部最小值卡住等等,所以需要多次選擇並計算嘗試。

3、過擬合的問題

是否在計算過程中只考慮了損失函數比如交叉熵,有沒有考慮l2正則、l2正則,或者有沒有進行dropout行為,是否有必要加入雜訊,在什麼地方加入雜訊(weight?input?),需不需要結合Bagging或者bayes方法等。

4、激活函數的選擇是否正確

比如relu只能產出>=0的結果,是否符合最後的產出結果要求。比如Sigmoid的函數在數據離散且均大於+3的數據集合上會產生梯度消失的問題,等等。

到這裡,我覺得一篇用TensorFlow來訓練分類模型來解決用戶流失這個問題就基本上算是梳理完了。很多簡單的知識點我沒有提,上面這些算是比較重要的模塊,希望對大家有所幫助,最後謝謝大家的閱讀。

推薦閱讀:

求推薦人工智慧特別是神經網路方面的書?
人工智慧聊天機器人,微軟小冰就夠了
強化學習經典入門書的讀書筆記系列--第三篇(上)
計算機視覺之我見,碧空的cv之旅開篇
聽說你剛中了NIPS?恭喜(研究德撲、老鼠鬍鬚等AI的都入圍了)

TAG:TensorFlow | 神经网络 |