做爲一名數據分析師,你來到這家跨國銀行工做已經半年了。數組
今天上午,老闆把你叫到辦公室,面色凝重。瀏覽器
你內心直打鼓,覺得本身捅了什麼簍子。幸虧老闆的話讓你很快打消了顧慮。網絡
他發愁,是由於最近歐洲區的客戶流失嚴重,許多客戶都跑到了競爭對手那裏接受服務了。老闆問你該怎麼辦?dom
你脫口而出「作好客戶關係管理啊!」機器學習
老闆看了你一眼,緩慢地說「咱們想知道哪些客戶最可能在近期流失」。函數
沒錯,在有魚的地方釣魚,纔是上策。工具
你明白了本身的任務——經過數據鎖定即將流失的客戶。這個工做,確實是你這個數據分析師份內的事兒。學習
你很慶幸,這半年作了不少的數據動態採集和整理工做,使得你手頭就有一個比較完備的客戶數據集。測試
下面你須要作的,就是如何從數據中「沙裏淘金」,找到那些最可能流失的客戶。命令行
但是,該怎麼作呢?
你拿出歐洲區客戶的數據,端詳起來。
客戶主要分佈在法國、德國和西班牙。
你手裏掌握的信息,包括他們的年齡、性別、信用、辦卡信息等。客戶是否已流失的信息在最後一列(Exited)。
怎麼用這些數據來判斷顧客是否會流失呢?
以你的專業素養,很容易就判斷出這是一個分類問題,屬於機器學習中的監督式學習。可是,你以前並無作過實際項目,該如何着手呢?
別發愁,我一步步給你演示如何用Python和深度神經網絡(或者叫「深度學習」)來完成這個分類任務,幫你鎖定那些即將流失的客戶。
環境
工欲善其事,必先利其器。咱們先來安裝和搭建環境。
首先是安裝Python。
請到這個網址下載Anaconda的最新版本。
請選擇左側的Python 3.6版本下載安裝。
其次是新建文件夾,起名爲demo-customer-churn-ann,而且從這個連接下載數據,放到該文件夾下。
(注:樣例數據來自於匿名化處理後的真實數據集,下載自superdatascience官網。)
打開終端(或者命令行工具),進入demo-customer-churn-ann目錄,執行如下命令:
jupyter notebook
瀏覽器中會顯示以下界面:
點擊界面右上方的New按鈕,新建一個Python 3 Notebook,起名爲customer-churn-ann。
準備工做結束,下面咱們開始清理數據。
清理
首先,讀入數據清理最經常使用的pandas和numpy包。
import numpy as npimport pandas as pd
從customer_churn.csv裏讀入數據:
df = pd.read_csv('customer_churn.csv')
看看讀入效果如何:
df.head()
這裏咱們使用了head()函數,只顯示前5行。
能夠看到,數據完整無誤讀入。可是並不是全部的列都對咱們預測用戶流失有做用。咱們一一甄別一下:
RowNumber:行號,這個確定沒用,刪除
CustomerID:用戶編號,這個是順序發放的,刪除
Surname:用戶姓名,對流失沒有影響,刪除
CreditScore:信用分數,這個很重要,保留
Geography:用戶所在國家/地區,這個有影響,保留
Gender:用戶性別,可能有影響,保留
Age:年齡,影響很大,年輕人更容易切換銀行,保留
Tenure:當了本銀行多少年用戶,很重要,保留
Balance:存貸款狀況,很重要,保留
NumOfProducts:使用產品數量,很重要,保留
HasCrCard:是否有本行信用卡,很重要,保留
IsActiveMember:是否活躍用戶,很重要,保留
EstimatedSalary:估計收入,很重要,保留
Exited:是否已流失,這將做爲咱們的標籤數據
上述數據列甄別過程,就叫作「特徵工程」(Feature Engineering),這是機器學習裏面最經常使用的數據預處理方法。若是咱們的數據量足夠大,機器學習模型足夠複雜,是能夠跳過這一步的。可是因爲咱們的數據只有10000條,還須要手動篩選特徵。
選定了特徵以後,咱們來生成特徵矩陣X,把剛纔咱們決定保留的特徵都寫進來。
X = df.loc[:,['CreditScore', 'Geography', 'Gender', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary']]
看看特徵矩陣的前幾行:
X.head()
顯示結果以下:
特徵矩陣構建準確無誤,下面咱們構建目標數據y,也就是用戶是否流失。
y = df.Exited
y.head()
0 11 02 13 04 0Name: Exited, dtype: int64
此時咱們須要的數據基本上齊全了。可是咱們發現其中有幾列數據還不符合咱們的要求。
要作機器學習,只能給機器提供數值,而不能是字符串。但是看看咱們的特徵矩陣:
X.head()
顯然其中的Geography和Gender兩項數據都不符合要求。它們都是分類數據。咱們須要作轉換,把它們變成數值。
在Scikit-learn工具包裏面,專門提供了方便的工具LabelEncoder,讓咱們能夠方便地將類別信息變成數值。
from sklearn.preprocessing import LabelEncoder, OneHotEncoderlabelencoder1 = LabelEncoder()X.Geography= labelencoder1.fit_transform(X.Geography)labelencoder2 = LabelEncoder()X.Gender = labelencoder2.fit_transform(X.Gender)
咱們須要轉換兩列,因此創建了兩個不一樣的labelencoder。轉換的函數叫作fit_transform。
通過轉換,此時咱們再來看看特徵矩陣的樣子:
X.head()
顯然,Geography和Gender這兩列都從原先描述類別的字符串,變成了數字。
這樣是否是就完事大吉了呢?顯然,Geography和Gender這兩列都從原先描述類別的字符串,變成了數字。
不對,Gender還好說,只有兩種取值方式,要麼是男,要麼是女。咱們能夠把「是男性」定義爲1,那麼女性就取值爲0。兩種取值只是描述類別不一樣,沒有歧義。
而Geography就不一樣了。由於數據集裏面可能的國家地區取值有3種,因此就轉換成了0(法國)、1(德國)、2(西班牙)。問題是,這三者之間真的有序列(大小)關係嗎?
答案天然是否認的。咱們其實仍是打算用數值描述分類而已。可是取值有數量的序列差別,就會給機器帶來歧義。它並不清楚不一樣的取值只是某個國家的代碼,可能會把這種大小關係帶入模型計算,從而產生錯誤的結果。
解決這個問題,咱們就須要引入OneHotEncoder。它也是Scikit-learn提供的一個類,能夠幫助咱們把類別的取值轉變爲多個變量組合表示。
我們這個數據集裏,能夠把3個國家分別用3個數字組合來表示。例如法國從原先的0,變成(1, 0, 0),德國從1變成(0, 1, 0),而西班牙從2變成(0, 0, 1)。
這樣,不再會出現0和1以外的數字來描述類別,從而避免機器產生誤會,錯把類別數字當成大小來計算了。
特徵矩陣裏面,咱們只須要轉換國別這一列。由於它在第1列的位置(從0開始計數),於是categorical_features只填寫它的位置信息。
onehotencoder = OneHotEncoder(categorical_features = [1])X = onehotencoder.fit_transform(X).toarray()
這時候,咱們的特徵矩陣數據框就被轉換成了一個數組。注意全部被OneHotEncoder轉換的列會排在最前面,而後纔是那些保持原樣的數據列。
咱們只看轉換後的第一行:
X[0]
array([ 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 6.19000000e+02, 0.00000000e+00, 4.20000000e+01, 2.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.01348880e+05])
這樣,總算轉換完畢了吧?
沒有。
由於本例中,OneHotEncoder轉換出來的3列數字,其實是不獨立的。給定其中兩列的信息,你本身均可以計算出其中的第3列取值。
比如說,某一行的前兩列數字是(0, 0),那麼第三列確定是1。由於這是轉換規則決定的。3列裏只能有1個是1,其他都是0。
若是你作過多元線性迴歸,應該知道這種狀況下,咱們是須要去掉其中一列,才能繼續分析的。否則會落入「虛擬變量陷阱」(dummy variable trap)。
咱們刪掉第0列,避免掉進坑裏。
X = np.delete(X, [0], 1)
再次打印第一行:
X[0]
array([ 0.00000000e+00, 0.00000000e+00, 6.19000000e+02, 0.00000000e+00, 4.20000000e+01, 2.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.01348880e+05])
檢查完畢,如今我們的特徵矩陣處理基本完成。
可是監督式學習,最重要的是有標籤(label)數據。本例中的標籤就是用戶是否流失。咱們目前的標籤數據框,是這個樣子的。
y.head()
0 11 02 13 04 0Name: Exited, dtype: int64
它是一個行向量,咱們須要把它先轉換成爲列向量。你能夠想象成把它「豎過來」。
y = y[:, np.newaxis]y
array([[1], [0], [1], ..., [1], [1], [0]])
這樣在後面訓練的時候,他就能夠和前面的特徵矩陣一一對應來操做計算了。
既然標籤表明了類別,咱們也把它用OneHotEncoder轉換,這樣方便咱們後面作分類學習。
onehotencoder = OneHotEncoder()y = onehotencoder.fit_transform(y).toarray()
此時的標籤變成兩列數據,一列表明顧客存留,一列表明顧客流失。
y
array([[ 0., 1.], [ 1., 0.], [ 0., 1.], ..., [ 0., 1.], [ 0., 1.], [ 1., 0.]])
整體的數據已經齊全了。可是咱們不能把它們都用來訓練。
這就好像老師不該該把考試題目拿來給學生作做業和練習同樣。只有考學生沒見過的題,才能區分學生是掌握了正確的解題方法,仍是死記硬背了做業答案。
咱們拿出20%的數據,放在一邊,等着用來作測試。其他8000條數據用來訓練機器學習模型。
from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)
咱們看看訓練集的長度:
len(X_train)
8000
再看看測試集的長度:
len(X_test)
2000
確認無誤。
是否是能夠開始機器學習了?
能夠,可是下面這一步也很關鍵。咱們須要把數據進行標準化處理。由於原先每一列數字的取值範圍都各不相同,所以有的列方差要遠遠大於其餘列。這樣對機器來講,也是很困擾的。數據的標準化處理,能夠在保持列內數據多樣性的同時,儘可能減小不一樣類別之間差別的影響,可讓機器公平對待所有特徵。
咱們調用Scikit-learn的StandardScaler類來完成這一過程。
from sklearn.preprocessing import StandardScalersc = StandardScaler()X_train = sc.fit_transform(X_train)X_test = sc.transform(X_test)
注意,咱們只對特徵矩陣作標準化,標籤是不能動的。另外訓練集和測試集須要按照統一的標準變化。因此你看,訓練集上,咱們用了fit_transform函數,先擬合後轉換;而在測試集上,咱們直接用訓練集擬合的結果,只作轉換。
X_train
array([[-0.5698444 , 1.74309049, 0.16958176, ..., 0.64259497, -1.03227043, 1.10643166], [ 1.75486502, -0.57369368, -2.30455945, ..., 0.64259497, 0.9687384 , -0.74866447], [-0.5698444 , -0.57369368, -1.19119591, ..., 0.64259497, -1.03227043, 1.48533467], ..., [-0.5698444 , -0.57369368, 0.9015152 , ..., 0.64259497, -1.03227043, 1.41231994], [-0.5698444 , 1.74309049, -0.62420521, ..., 0.64259497, 0.9687384 , 0.84432121], [ 1.75486502, -0.57369368, -0.28401079, ..., 0.64259497, -1.03227043, 0.32472465]])
你會發現,許多列的方差比原先小得多。機器學習起來,會更加方便。
數據清理和轉換工做至此完成。