如何用 Python 和深度神經網路發現即將流失的客戶?
本文作者 王樹義,本文首發於知乎專欄【玉樹芝蘭】,AI 研習社獲其授權轉載。
想不想了解如何用 Python 快速搭建深度神經網路,完成數據分類任務?本文一步步為你展示這一過程,讓你初步領略深度學習模型的強大和易用。
煩惱
作為一名數據分析師,你來到這家跨國銀行工作已經半年了。
今天上午,老闆把你叫到辦公室,面色凝重。
你心裡直打鼓,以為自己捅了什麼簍子。幸好老闆的話讓你很快打消了顧慮。
他發愁,是因為最近歐洲區的客戶流失嚴重,許多客戶都跑到了競爭對手那裡接受服務了。老闆問你該怎麼辦?
你脫口而出 「做好客戶關係管理啊!」
老闆看了你一眼,緩慢地說 「我們想知道哪些客戶最可能在近期流失」。
沒錯,在有魚的地方釣魚,才是上策。
你明白了自己的任務——通過數據鎖定即將流失的客戶。這個工作,確實是你這個數據分析師分內的事兒。
你很慶幸,這半年做了很多的數據動態採集和整理工作,使得你手頭就有一個比較完備的客戶數據集。
下面你需要做的,就是如何從數據中 「沙裡淘金」,找到那些最可能流失的客戶。
可是,該怎麼做呢?
你拿出歐洲區客戶的數據,端詳起來。
客戶主要分布在法國、德國和西班牙。
你手裡掌握的信息,包括他們的年齡、性別、信用、辦卡信息等。客戶是否已流失的信息在最後一列(Exited)。
怎麼用這些數據來判斷顧客是否會流失呢?
以你的專業素養,很容易就判斷出這是一個分類問題,屬於機器學習中的監督式學習。但是,你之前並沒有做過實際項目,該如何著手呢?
別發愁,我一步步給你演示如何用 Python 和深度神經網路(或者叫 「深度學習」)來完成這個分類任務,幫你鎖定那些即將流失的客戶。
環境
工欲善其事,必先利其器。我們先來安裝和搭建環境。
首先是安裝 Python。
請到這個網址(https://www.anaconda.com/download/)下載 Anaconda 的最新版本。
請選擇左側的 Python 3.6 版本下載安裝。
其次是新建文件夾,起名為 demo-customer-churn-ann,並且從這個鏈接(http://t.cn/R8woTN3)下載數據,放到該文件夾下。
(註:樣例數據來自於匿名化處理後的真實數據集,下載自 superdatascience 官網,https://www.superdatascience.com/。)
打開終端(或者命令行工具),進入 demo-customer-churn-ann 目錄,執行以下命令:
瀏覽器中會顯示如下界面:
點擊界面右上方的 New 按鈕,新建一個 Python 3 Notebook,起名為 customer-churn-ann。
準備工作結束,下面我們開始清理數據。
清理
首先,讀入數據清理最常用的 pandas 和 numpy 包。
從里讀入數據:
看看讀入效果如何:
這裡我們使用了函數,只顯示前 5 行。
可以看到,數據完整無誤讀入。但是並非所有的列都對我們預測用戶流失有作用。我們一一甄別一下:
RowNumber:行號,這個肯定沒用,刪除
CustomerID:用戶編號,這個是順序發放的,刪除
Surname:用戶姓名,對流失沒有影響,刪除
CreditScore:信用分數,這個很重要,保留
Geography:用戶所在國家 / 地區,這個有影響,保留
Gender:用戶性別,可能有影響,保留
Age:年齡,影響很大,年輕人更容易切換銀行,保留
Tenure:當了本銀行多少年用戶,很重要,保留
Balance:存貸款情況,很重要,保留
NumOfProducts:使用產品數量,很重要,保留
HasCrCard:是否有本行信用卡,很重要,保留
IsActiveMember:是否活躍用戶,很重要,保留
EstimatedSalary:估計收入,很重要,保留
Exited:是否已流失,這將作為我們的標籤數據
上述數據列甄別過程,就叫做 「特徵工程」(Feature Engineering),這是機器學習裡面最常用的數據預處理方法。如果我們的數據量足夠大,機器學習模型足夠複雜,是可以跳過這一步的。但是由於我們的數據只有 10000 條,還需要手動篩選特徵。
選定了特徵之後,我們來生成特徵矩陣 X,把剛才我們決定保留的特徵都寫進來。
看看特徵矩陣的前幾行:
顯示結果如下:
特徵矩陣構建準確無誤,下面我們構建目標數據 y,也就是用戶是否流失。
此時我們需要的數據基本上齊全了。但是我們發現其中有幾列數據還不符合我們的要求。
要做機器學習,只能給機器提供數值,而不能是字元串。可是看看我們的特徵矩陣:
顯然其中的 Geography 和 Gender 兩項數據都不符合要求。它們都是分類數據。我們需要做轉換,把它們變成數值。
在 Scikit-learn 工具包裡面,專門提供了方便的工具,讓我們可以方便地將類別信息變成數值。
我們需要轉換兩列,所以建立了兩個不同的 labelencoder。轉換的函數叫做。
經過轉換,此時我們再來看看特徵矩陣的樣子:
顯然,Geography 和 Gender 這兩列都從原先描述類別的字元串,變成了數字。
這樣是不是就完事大吉了呢?
不對,Gender 還好說,只有兩種取值方式,要麼是男,要麼是女。我們可以把 「是男性」 定義為 1,那麼女性就取值為 0。兩種取值只是描述類別不同,沒有歧義。
而 Geography 就不同了。因為數據集裡面可能的國家地區取值有 3 種,所以就轉換成了 0(法國)、1(德國)、2(西班牙)。問題是,這三者之間真的有序列(大小)關係嗎?
答案自然是否定的。我們其實還是打算用數值描述分類而已。但是取值有數量的序列差異,就會給機器帶來歧義。它並不清楚不同的取值只是某個國家的代碼,可能會把這種大小關係帶入模型計算,從而產生錯誤的結果。
解決這個問題,我們就需要引入。它也是 Scikit-learn 提供的一個類,可以幫助我們把類別的取值轉變為多個變數組合表示。
咱們這個數據集里,可以把 3 個國家分別用 3 個數字組合來表示。例如法國從原先的 0,變成,德國從 1 變成,而西班牙從 2 變成。
這樣,再也不會出現 0 和 1 之外的數字來描述類別,從而避免機器產生誤會,錯把類別數字當成大小來計算了。
特徵矩陣裡面,我們只需要轉換國別這一列。因為它在第 1 列的位置(從 0 開始計數),因而只填寫它的位置信息。
這時候,我們的特徵矩陣數據框就被轉換成了一個數組。注意所有被 OneHotEncoder 轉換的列會排在最前面,然後才是那些保持原樣的數據列。
我們只看轉換後的第一行:
這樣,總算轉換完畢了吧?
沒有。
因為本例中,OneHotEncoder 轉換出來的 3 列數字,實際上是不獨立的。給定其中兩列的信息,你自己都可以計算出其中的第 3 列取值。
好比說,某一行的前兩列數字是,那麼第三列肯定是 1。因為這是轉換規則決定的。3 列里只能有 1 個是 1,其餘都是 0。
如果你做過多元線性回歸,應該知道這種情況下,我們是需要去掉其中一列,才能繼續分析的。不然會落入 「虛擬變數陷阱」(dummy variable trap,http://t.cn/R8wKLB9)。
我們刪掉第 0 列,避免掉進坑裡。
再次列印第一行:
檢查完畢,現在咱們的特徵矩陣處理基本完成。
但是監督式學習,最重要的是有標籤 (label) 數據。本例中的標籤就是用戶是否流失。我們目前的標籤數據框,是這個樣子的。
它是一個行向量,我們需要把它先轉換成為列向量。你可以想像成把它 「豎過來」。
這樣在後面訓練的時候,他就可以和前面的特徵矩陣一一對應來操作計算了。
既然標籤代表了類別,我們也把它用 OneHotEncoder 轉換,這樣方便我們後面做分類學習。
此時的標籤變成兩列數據,一列代表顧客存留,一列代表顧客流失。
總體的數據已經齊全了。但是我們不能把它們都用來訓練。
這就好像老師不應該把考試題目拿來給學生做作業和練習一樣。只有考學生沒見過的題,才能區分學生是掌握了正確的解題方法,還是死記硬背了作業答案。
我們拿出 20% 的數據,放在一邊,等著用來做測試。其餘 8000 條數據用來訓練機器學習模型。
我們看看訓練集的長度:
再看看測試集的長度:
確認無誤。
是不是可以開始機器學習了?
可以,但是下面這一步也很關鍵。我們需要把數據進行標準化處理。因為原先每一列數字的取值範圍都各不相同,因此有的列方差要遠遠大於其他列。這樣對機器來說,也是很困擾的。數據的標準化處理,可以在保持列內數據多樣性的同時,盡量減少不同類別之間差異的影響,可以讓機器公平對待全部特徵。
我們調用 Scikit-learn 的類來完成這一過程。
注意,我們只對特徵矩陣做標準化,標籤是不能動的。另外訓練集和測試集需要按照統一的標準變化。所以你看,訓練集上,我們用了函數,先擬合後轉換;而在測試集上,我們直接用訓練集擬合的結果,只做轉換。
你會發現,許多列的方差比原先小得多。機器學習起來,會更加方便。
數據清理和轉換工作至此完成。
決策樹
如果讀過我的《貸還是不貸:如何用 Python 和機器學習幫你決策?》(http://t.cn/R8wKi3O)一文,你應該有一種感覺——這個問題和貸款審批決策很像啊!既然在該文中,決策樹很好使,我們繼續用決策樹不就好了?
好的,我們先測試一下經典機器學習演算法表現如何。
從 Scikit-learn 中,讀入決策樹工具。然後擬合訓練集數據。
然後,利用我們建立的決策樹模型做出預測。
列印預測結果:
這樣看不出來什麼。讓我們調用 Scikit-learn 的模塊,生成分析報告。
經檢測,決策樹在咱們的數據集上,表現得還是不錯的。總體的準確率為 0.81,召回率為 0.80,f1 分數為 0.81,已經很高了。對 10 個客戶做流失可能性判斷,它有 8 次都能判斷正確。
但是,這樣是否足夠?
我們或許可以調整決策樹的參數做優化,嘗試改進預測結果。
或者我們可以採用深度學習。
深度
深度學習的使用場景,往往是因為原有的模型經典機器學習模型過於簡單,無法把握複雜數據特性。
我不準備給你講一堆數學公式,咱們動手做個實驗。
請你打開這個網址(http://t.cn/R8wK0R0)。
你會看到如下圖所示的深度學習遊樂場:
右側的圖形,裡面是藍色數據,外圈是黃色數據。你的任務就是要用模型分類兩種不同數據。
你說那還不容易?我一眼就看出來了。
你看出來沒有用。通過你的設置,讓機器也能正確區分,才算數。
圖中你看到許多加減號。咱們就通過操縱它們來玩兒一玩兒模型。
首先,點圖中部上方的 "2 HIDDEN LAYERS" 左側減號,把中間隱藏層數降低為 1。
然後,點擊 "2 neurons" 上面的減號,把神經元數量減少為 1。
把頁面上方的 Activation 函數下拉框打開,選擇 「Sigmoid」。
現在的模型,其實就是經典的邏輯回歸(Logistic Regression)。
點擊左上方的運行按鈕,我們看看執行效果。
由於模型過於簡單,所以機器絞盡腦汁,試圖用一條直線切分二維平面上的兩類節點。
損失 (loss) 居高不下。訓練集和測試集損失都在 0.4 左右,顯然不符合我們的分類需求。
下面我們試試增加層數和神經元數量。這次點擊加號,把隱藏層數加回到 2,兩層神經元數量都取 2。
再次點擊運行。
經過一段時間,結果穩定了下來,你發現這次電腦用了兩條線,把平面切分成了 3 部分。
測試集損失下降到了 0.25 左右,而訓練集損失更是降低到了 0.2 以下。
模型複雜了,效果似乎更好一些。
再接再厲,我們把第一個隱藏層的神經元數量增加為 4 看看。
點擊運行,不一會兒有趣的事情就發生了。
機器用一條近乎完美的曲線把平面分成了內外兩個部分。測試集和訓練集損失都極速下降,訓練集損失甚至接近於 0。
這告訴我們,許多時候模型過於簡單帶來的問題,可以通過加深隱藏層次、增加神經元的方法提升模型複雜度,加以改進。
目前流行的劃分方法,是用隱藏層的數量多少來區分是否 「深度」。當神經網路中隱藏層數量達到 3 層以上時,就被稱為 「深度神經網路」,或者 「深度學習」。
久聞大名的深度學習,原來就是這麼簡單。
如果有時間的話,建議你自己在這個遊樂場里多動手玩兒一玩兒。你會很快對神經網路和深度學習有個感性認識。
框架
遊樂場背後使用的引擎,就是 Google 的深度學習框架 Tensorflow。
所謂框架,就是別人幫你構造好的基礎軟體應用。你可以通過調用它們,避免自己重複發明輪子,大幅度節省時間,提升效率。
支持 Python 語言的深度學習的框架有很多,除了 Tensorflow 外,還有 PyTorch, Theano 和 MXNet 等。
我給你的建議是,找到一個你喜歡的軟體包,深入學習使用,不斷實踐來提升自己的技能。千萬不要跟別人爭論哪個深度學習框架更好。一來蘿蔔白菜各有所愛,每個人都有自己的偏好;二來深度學習的江湖水很深,言多有失。說錯了話,別的門派可能會不高興喲。
我比較喜歡 Tensorflow。但是 Tensorflow 本身是個底層庫。雖然隨著版本的更迭,界面越來越易用。但是對初學者來說,許多細節依然有些過於瑣碎,不容易掌握。
初學者的耐心有限,挫折過多容易放棄。
幸好,還有幾個高度抽象框架,是建立在 Tensorflow 之上的。如果你的任務是應用現成的深度學習模型,那麼這些框架會給你帶來非常大的便利。
這些框架包括 Keras, TensorLayer 等。咱們今天將要使用的,叫做 TFlearn。
它的特點,就是長得很像 Scikit-learn。這樣如果你熟悉經典機器學習模型,學起來會特別輕鬆省力。
實戰
閑話就說這麼多,下面咱們繼續寫代碼吧。
寫代碼之前,請回到終端下,運行以下命令,安裝幾個軟體包:
執行完畢後,回到 Notebook 里。
我們呼叫 tflearn 框架。
然後,我們開始搭積木一樣,搭神經網路層。
首先是輸入層。
注意這裡的寫法,因為我們輸入的數據,是特徵矩陣。而經過我們處理後,特徵矩陣現在有 11 列,因此 shape 的第二項寫 11。
shape 的第一項,None,指的是我們要輸入的特徵矩陣行數。因為我們現在是搭建模型,後面特徵矩陣有可能一次輸入,有可能分成組塊輸入,長度可大可小,無法事先確定。所以這裡填 None。tflearn 會在我們實際執行訓練的時候,自己讀入特徵矩陣的尺寸,來處理這個數值。
下面我們搭建隱藏層。這裡我們要使用深度學習,搭建 3 層。
activation 剛才在深度學習遊樂場裡面我們遇到過,代表激活函數。如果沒有它,所有的輸入輸出都是線性關係。
Relu 函數是激活函數的一種。它大概長這個樣子。
如果你想了解激活函數的更多知識,請參考後文的學習資源部分。
隱藏層里,每一層我們都設置了 6 個神經元。其實至今為之,也不存在最優神經元數量的計算公式。工程界的一種做法,是把輸入層的神經元數量,加上輸出層神經元數量,除以 2 取整。咱們這裡就是用的這種方法,得出 6 個。
搭好了 3 個中間隱藏層,下面我們來搭建輸出層。
這裡我們用兩個神經元做輸出,並且說明使用回歸方法。輸出層選用的激活函數為 softmax。處理分類任務的時候,softmax 比較合適。它會告訴我們每一類的可能性,其中數值最高的,可以作為我們的分類結果。
積木搭完了,下面我們告訴 TFlearn,以剛剛搭建的結構,生成模型。
有了模型,我們就可以使用擬合功能了。你看是不是跟 Scikit-learn 的使用方法很相似呢?
注意這裡多了幾個參數,我們來解釋一下。
:數據訓練幾個輪次。
:每一次輸入給模型的數據行數。
:訓練過程中要不要列印結果。
以下就是電腦輸出的最終訓練結果。其實中間運行過程看著更激動人心,你自己試一下就知道了。
我們看到訓練集的損失 (loss) 大概為 0.4 左右。
打開終端,我們輸入
然後在瀏覽器里輸入
可以看到如下界面:
這是模型訓練過程的可視化圖形,可以看到準確度的攀升和損失降低的曲線。
打開 GRAPHS 標籤頁,我們可以查看神經網路的結構圖形。
我們搭積木的過程,在此處一目了然。
評估
訓練好了模型,我們來嘗試做個預測吧。
看看測試集的特徵矩陣第一行。
我們就用它來預測一下分類結果。
列印出來看看:
我們看看實際標籤數據:
客戶果然沒有流失。這個預測是對的。
但是一個數據的預測正確與否,是無法說明問題的。我們下面跑整個測試集,並且使用 evaluate 函數評價模型。
在測試集上,準確性達到 84.15%,好樣的!
希望在你的努力下,機器做出的準確判斷可以幫助銀行有效鎖定可能流失的客戶,降低客戶的流失率,繼續日進斗金。
說明
你可能覺得,深度學習也沒有什麼厲害的嘛。原先的決策樹演算法,那麼簡單就能實現,也可以達到 80% 以上的準確度。寫了這麼多語句,深度學習結果也無非只提升了幾個百分點而已。
首先,準確度達到某種高度後,提升是不容易的。這就好像學生考試,從不及格到及格,付出的努力並不需要很高;從 95 分提升到 100,卻是許多人一輩子也沒有完成的目標。
其次,在某些領域裡,1% 的提升意味著以百萬美元計的利潤,或者幾千個人的生命因此得到拯救。
第三,深度學習的崛起,是因為大數據的環境。在許多情況下,數據越多,深度學習的優勢就越明顯。本例中只有 10000 條記錄,與 「大數據」 的規模還相去甚遠。
學習資源
如果你對深度學習感興趣,推薦以下學習資源。
首先是教材。
第一本是 Deep Learning(http://t.cn/R8w9frZ),絕對的經典。
第二本是 Hands-On Machine Learning with Scikit-Learn and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems(http://t.cn/R8w9Xcz),深入淺出,通俗易懂。
其次是 MOOC。
推薦吳恩達 (Andrew Ng) 教授在 Coursera 上的兩門課程。
一門是機器學習。這課推出有年頭了,但是非常有趣和實用。具體的介紹請參考拙作《機器學習哪裡有這麼玄?》(http://t.cn/R8w99RP)以及《如何用 MOOC 組合掌握機器學習?》。
一門是深度學習。這是個系列課程,包括 5 門子課程。今年推出的新課,自成體系,但是最好有前面那門課程作為基礎。
討論
你對深度學習感興趣嗎?之前有沒有做過深度學習項目?你掌握了哪些深度學習框架?有沒有什麼建議給初學者?歡迎留言,把心得分享給大家,我們一起交流討論。
如果本文可能對你身邊的親友有幫助,也歡迎你把本文通過微博或朋友圈分享給他們。讓他們一起參與到我們的討論中來。
TAG:AI研習社 |