以客戶(hù)流失數(shù)據(jù)為例,看 Tensorflow 2.0 版本如何幫助我們快速構(gòu)建表格(結(jié)構(gòu)化)數(shù)據(jù)的神經(jīng)網(wǎng)絡(luò)分類(lèi)模型。
變化
表格數(shù)據(jù),你應(yīng)該并不陌生。畢竟, Excel 這東西在咱們平時(shí)的工作和學(xué)習(xí)中,還是挺常見(jiàn)的。
在之前的教程里,我為你分享過(guò),如何利用深度神經(jīng)網(wǎng)絡(luò),鎖定即將流失的客戶(hù)。里面用到的,就是這樣的表格數(shù)據(jù)。
時(shí)間過(guò)得真快,距離寫(xiě)作那篇教程,已經(jīng)一年半了。
這段時(shí)間里,出現(xiàn)了2個(gè)重要的變化,使我覺(jué)得有必要重新來(lái)跟你談?wù)勥@個(gè)話題。
這兩個(gè)變化分別是:
首先,tflearn 框架的開(kāi)發(fā)已經(jīng)不再活躍。
tflearn 是當(dāng)時(shí)教程中我們使用的高階深度學(xué)習(xí)框架,它基于 Tensorflow 之上,包裹了大量的細(xì)節(jié),讓用戶(hù)可以非常方便地搭建自己的模型。
但是,由于 Tensorflow 選擇擁抱了它的競(jìng)爭(zhēng)者 Keras ,導(dǎo)致后者的競(jìng)爭(zhēng)優(yōu)勢(shì)凸顯。
對(duì)比二者獲得的星數(shù),已經(jīng)不在同一量級(jí)。
觀察更新時(shí)間,tflearn 已經(jīng)幾個(gè)月沒(méi)有動(dòng)靜;而 Keras 幾個(gè)小時(shí)之前,還有更新。
我們選擇免費(fèi)開(kāi)源框架,一定要使用開(kāi)發(fā)活躍、社區(qū)支持完善的。只有這樣,遇到問(wèn)題才能更低成本、高效率地解決。
看過(guò)我的《Python編程遇問(wèn)題,文科生怎么辦?》一文之后,你對(duì)上述結(jié)論,應(yīng)該不陌生。
另一項(xiàng)新變化,是 Tensorflow 發(fā)布了 2.0 版本。
相對(duì) 1.X 版本,這個(gè)大版本的變化,我在《如何用 Python 和 BERT 做中文文本二元分類(lèi)?》一文中,已經(jīng)粗略地為你介紹過(guò)了。簡(jiǎn)要提煉一下,就是:
之前的版本,以計(jì)算圖為中心。開(kāi)發(fā)者需要為這張圖服務(wù)。因此,引入了大量的不必要術(shù)語(yǔ)。新版本以人為中心,用戶(hù)撰寫(xiě)高階的簡(jiǎn)潔語(yǔ)句,框架自動(dòng)將其轉(zhuǎn)化為對(duì)應(yīng)的計(jì)算圖。
之前的版本,缺少目前競(jìng)爭(zhēng)框架(如 PyTorch 等)包含的新特性。例如計(jì)算圖動(dòng)態(tài)化、運(yùn)行中調(diào)試功能等。
但對(duì)普通開(kāi)發(fā)者來(lái)說(shuō),最為重要的是,官方文檔和教程變得對(duì)用戶(hù)友好許多。不僅寫(xiě)得清晰簡(jiǎn)明,更靠著Google Colab的支持,全都能一鍵運(yùn)行。我嘗試了 2.0 版本的一些教程樣例,確實(shí)感覺(jué)大不一樣了。
其實(shí)你可能會(huì)覺(jué)得奇怪—— Tensorflow 大張旗鼓宣傳的大版本改進(jìn),其實(shí)也無(wú)非就是向著 PyTorch早就有的功能靠攏而已嘛。那我干脆去學(xué) PyTorch 好了!
如果我們只說(shuō)道理,這其實(shí)沒(méi)錯(cuò)。然而,還是前面那個(gè)論斷,一個(gè)框架好不好,主要看是否開(kāi)發(fā)活躍、社區(qū)支持完善。這就是一個(gè)自證預(yù)言。一旦人們都覺(jué)得 Tensorflow 好用,那么 Tensorflow 就會(huì)更好用。因?yàn)闀?huì)有更多的人參與進(jìn)來(lái),幫助反饋和改進(jìn)。
看看現(xiàn)在 PyTorch 的 Github 頁(yè)面。
受關(guān)注度,確實(shí)已經(jīng)很高了。
然而你再看看 Tensorflow 的。
至少在目前,二者根本不在一個(gè)數(shù)量級(jí)。
Tensorflow 的威力,不只在于本身構(gòu)建和訓(xùn)練模型是不是好用。那其實(shí)只是深度學(xué)習(xí)中,非常小的一個(gè)環(huán)節(jié)。不信?你在下圖里找找看。
真正的問(wèn)題,在于是否有完整的生態(tài)環(huán)境支持。其中的邏輯,我在《學(xué) Python ,能提升你的競(jìng)爭(zhēng)力嗎?》一文中,已經(jīng)為你詳細(xì)分析過(guò)了。
而 Tensorflow ,早就通過(guò)一系列的布局,使得其訓(xùn)練模型可以直接快速部署,最快速度鋪開(kāi),幫助開(kāi)發(fā)者占領(lǐng)市場(chǎng)先機(jī)。
如果你使用 PyTorch ,那么這樣的系統(tǒng),是相對(duì)不完善的。當(dāng)然你可以在 PyTorch 中訓(xùn)練,然后轉(zhuǎn)換并且部署到 Tensorflow 里面。畢竟三巨頭達(dá)成了協(xié)議,標(biāo)準(zhǔn)開(kāi)放,這樣做從技術(shù)上并不困難。
但是,人的認(rèn)知帶寬,是非常有限的。大部分人,是不會(huì)選擇在兩個(gè)框架甚至生態(tài)系統(tǒng)之間折騰的。這就是路徑依賴(lài)。
所以,別左顧右盼了,認(rèn)認(rèn)真真學(xué) Tensorflow 2.0 吧。
這篇文章里面,我給你介紹,如何用 Tensorflow 2.0 ,來(lái)訓(xùn)練神經(jīng)網(wǎng)絡(luò),對(duì)用戶(hù)流失數(shù)據(jù)建立分類(lèi)模型,從而可以幫你見(jiàn)微知著,洞察風(fēng)險(xiǎn),提前做好干預(yù)和防范。
數(shù)據(jù)
你手里擁有的,是一份銀行歐洲區(qū)客戶(hù)的數(shù)據(jù),共有10000條記錄。客戶(hù)主要分布在法國(guó)、德國(guó)和西班牙。
數(shù)據(jù)來(lái)自于匿名化處理后的真實(shí)數(shù)據(jù)集,下載自 superdatascience 官網(wǎng)。
從表格中,可以讀取的信息,包括客戶(hù)們的年齡、性別、信用分?jǐn)?shù)、辦卡信息等。客戶(hù)是否已流失的信息在最后一列(Exited)。
這份數(shù)據(jù),我已經(jīng)上傳到了這個(gè)地址,你可以下載,并且用 Excel 查看。
環(huán)境
本文的配套源代碼,我放在了這個(gè) Github 項(xiàng)目中。請(qǐng)你點(diǎn)擊這個(gè)鏈接(http://t.cn/EXffmgX)訪問(wèn)。
如果你對(duì)我的教程滿(mǎn)意,歡迎在頁(yè)面右上方的 Star 上點(diǎn)擊一下,幫我加一顆星。謝謝!
注意這個(gè)頁(yè)面的中央,有個(gè)按鈕,寫(xiě)著“在 Colab 打開(kāi)” (Open in Colab)。請(qǐng)你點(diǎn)擊它。
然后,Google Colab 就會(huì)自動(dòng)開(kāi)啟。
我建議你點(diǎn)一下上圖中紅色圈出的 “COPY TO DRIVE” 按鈕。這樣就可以先把它在你自己的 Google Drive 中存好,以便使用和回顧。
Colab 為你提供了全套的運(yùn)行環(huán)境。你只需要依次執(zhí)行代碼,就可以復(fù)現(xiàn)本教程的運(yùn)行結(jié)果了。
如果你對(duì) Google Colab 不熟悉,沒(méi)關(guān)系。我這里有一篇教程,專(zhuān)門(mén)講解 Google Colab 的特點(diǎn)與使用方式。
為了你能夠更為深入地學(xué)習(xí)與了解代碼,我建議你在 Google Colab 中開(kāi)啟一個(gè)全新的 Notebook ,并且根據(jù)下文,依次輸入代碼并運(yùn)行。在此過(guò)程中,充分理解代碼的含義。
這種看似笨拙的方式,其實(shí)是學(xué)習(xí)的有效路徑。
代碼
首先,我們下載客戶(hù)流失數(shù)據(jù)集。
!wgethttps://raw.githubusercontent.com/wshuyi/demo-customer-churn-ann/master/customer_churn.csv
載入 Pandas 數(shù)據(jù)分析包。
importpandasaspd
利用 read_csv 函數(shù),讀取 csv 格式數(shù)據(jù)到 Pandas 數(shù)據(jù)框。
df=pd.read_csv('customer_churn.csv')
我們來(lái)看看前幾行顯示結(jié)果:
df.head()
顯示正常。下面看看一共都有哪些列。
df.columns
我們對(duì)所有列,一一甄別。
RowNumber:行號(hào),這個(gè)對(duì)于模型沒(méi)用,忽略
CustomerID:用戶(hù)編號(hào),這個(gè)是順序發(fā)放的,忽略
Surname:用戶(hù)姓名,對(duì)流失沒(méi)有影響,忽略
CreditScore:信用分?jǐn)?shù),這個(gè)很重要,保留
Geography:用戶(hù)所在國(guó)家/地區(qū),這個(gè)有影響,保留
Gender:用戶(hù)性別,可能有影響,保留
Age:年齡,影響很大,年輕人更容易切換銀行,保留
Tenure:當(dāng)了本銀行多少年用戶(hù),很重要,保留
Balance:存貸款情況,很重要,保留
NumOfProducts:使用產(chǎn)品數(shù)量,很重要,保留
HasCrCard:是否有本行信用卡,很重要,保留
IsActiveMember:是否活躍用戶(hù),很重要,保留
EstimatedSalary:估計(jì)收入,很重要,保留
Exited:是否已流失,這將作為我們的標(biāo)簽數(shù)據(jù)
確定了不同列的含義和價(jià)值,下面我們處理起來(lái),就得心應(yīng)手了。
數(shù)據(jù)有了,我們來(lái)調(diào)入深度學(xué)習(xí)框架。
因?yàn)楸敬挝覀冃枰褂?Tensorflow 2.0 ,而寫(xiě)作本文時(shí),該框架版本尚處于 Alpha 階段,因此 Google Colab 默認(rèn)使用的,還是 Tensorflow 1.X 版本。要用 2.0 版,便需要顯式安裝。
!pipinstall-qtensorflow==2.0.0-alpha0
安裝框架后,我們載入下述模塊和函數(shù),后文會(huì)用到。
importnumpyasnpimporttensorflowastffromtensorflowimportkerasfromsklearn.model_selectionimporttrain_test_splitfromtensorflowimportfeature_column
這里,我們?cè)O(shè)定一些隨機(jī)種子值。這主要是為了保證結(jié)果可復(fù)現(xiàn),也就是在你那邊的運(yùn)行結(jié)果,和我這里盡量保持一致。這樣我們觀察和討論問(wèn)題,會(huì)更方便。
首先是 Tensorflow 中的隨機(jī)種子取值,設(shè)定為 1 。
tf.random.set_seed(1)
然后我們來(lái)分割數(shù)據(jù)。這里使用的是 Scikit-learn 中的 train_test_split 函數(shù)。指定分割比例即可。
我們先按照80:20的比例,把總體數(shù)據(jù)分成訓(xùn)練集和測(cè)試集。
train,test=train_test_split(df,test_size=0.2,random_state=1)
然后,再把現(xiàn)有訓(xùn)練集的數(shù)據(jù),按照80:20的比例,分成最終的訓(xùn)練集,以及驗(yàn)證集。
train,valid=train_test_split(train,test_size=0.2,random_state=1)
這里,我們都指定了 random_state ,為的是保證咱們隨機(jī)分割的結(jié)果一致。
我們看看幾個(gè)不同集合的長(zhǎng)度。
print(len(train))print(len(valid))print(len(test))
驗(yàn)證無(wú)誤。下面我們來(lái)做特征工程(feature engineering)。
因?yàn)槲覀兪褂玫氖潜砀駭?shù)據(jù)(tabular data),屬于結(jié)構(gòu)化數(shù)據(jù)。因此特征工程相對(duì)簡(jiǎn)單一些。
先初始化一個(gè)空的特征列表。
feature_columns=[]
然后,我們指定,哪些列是數(shù)值型數(shù)據(jù)(numeric data)。
numeric_columns=['CreditScore','Age','Tenure','Balance','NumOfProducts','EstimatedSalary']
可見(jiàn),包含了以下列:
CreditScore:信用分?jǐn)?shù)
Age:年齡
Tenure:當(dāng)了本銀行多少年用戶(hù)
Balance:存貸款情況
NumOfProducts:使用產(chǎn)品數(shù)量
EstimatedSalary:估計(jì)收入
對(duì)于這些列,只需要直接指定類(lèi)型,加入咱們的特征列表就好。
forheaderinnumeric_columns:feature_columns.append(feature_column.numeric_column(header))
下面是比較講究技巧的部分了,就是類(lèi)別數(shù)據(jù)。
先看看都有哪些列:
categorical_columns=['Geography','Gender','HasCrCard','IsActiveMember']
Geography:用戶(hù)所在國(guó)家/地區(qū)
Gender:用戶(hù)性別
HasCrCard:是否有本行信用卡
IsActiveMember:是否活躍用戶(hù)
類(lèi)別數(shù)據(jù)的特點(diǎn),在于不能直接用數(shù)字描述。例如 Geography 包含了國(guó)家/地區(qū)名稱(chēng)。如果你把法國(guó)指定為1, 德國(guó)指定為2,電腦可能自作聰明,認(rèn)為“德國(guó)”是“法國(guó)”的2倍,或者,“德國(guó)”等于“法國(guó)”加1。這顯然不是我們想要表達(dá)的。
所以我這里編了一個(gè)函數(shù),把一個(gè)類(lèi)別列名輸入進(jìn)去,讓 Tensorflow 幫我們將其轉(zhuǎn)換成它可以識(shí)別的類(lèi)別形式。例如把法國(guó)按照 [0, 0, 1],德國(guó)按照 [0, 1, 0] 來(lái)表示。這樣就不會(huì)有數(shù)值意義上的歧義了。
defget_one_hot_from_categorical(colname):categorical=feature_column.categorical_column_with_vocabulary_list(colname,train[colname].unique().tolist())returnfeature_column.indicator_column(categorical)
我們嘗試輸入 Geography 一項(xiàng),測(cè)試一下函數(shù)工作是否正常。
geography=get_one_hot_from_categorical('Geography');geography
觀察結(jié)果,測(cè)試通過(guò)。
下面我們放心大膽地把所有類(lèi)別數(shù)據(jù)列都在函數(shù)里面跑一遍,并且把結(jié)果加入到特征列表中。
forcolincategorical_columns:feature_columns.append(get_one_hot_from_categorical(col))
看看此時(shí)的特征列表內(nèi)容:
feature_columns
6個(gè)數(shù)值類(lèi)型,4個(gè)類(lèi)別類(lèi)型,都沒(méi)問(wèn)題了。
下面該構(gòu)造模型了。
我們直接采用 Tensorflow 2.0 鼓勵(lì)開(kāi)發(fā)者使用的 Keras 高級(jí) API 來(lái)拼搭一個(gè)簡(jiǎn)單的深度神經(jīng)網(wǎng)絡(luò)模型。
fromtensorflow.kerasimportlayers
我們把剛剛整理好的特征列表,利用 DenseFeatures 層來(lái)表示。把這樣的一個(gè)初始層,作為模型的整體輸入層。
feature_layer=layers.DenseFeatures(feature_columns);feature_layer
下面,我們順序疊放兩個(gè)中間層,分別包含200個(gè),以及100個(gè)神經(jīng)元。這兩層的激活函數(shù),我們都采用 relu 。
relu 函數(shù)大概長(zhǎng)這個(gè)樣子:
model=keras.Sequential([feature_layer,layers.Dense(200,activation='relu'),layers.Dense(100,activation='relu'),layers.Dense(1,activation='sigmoid')])
我們希望輸出結(jié)果是0或者1,所以這一層只需要1個(gè)神經(jīng)元,而且采用的是 sigmoid 作為激活函數(shù)。
sigmoid 函數(shù)的長(zhǎng)相是這樣的:
模型搭建好了,下面我們指定3個(gè)重要參數(shù),編譯模型。
model.compile(optimizer='adam',loss='binary_crossentropy',metrics=['accuracy'])
這里,我們選擇優(yōu)化器為 adam 。
因?yàn)樵u(píng)判二元分類(lèi)效果,所以損失函數(shù)選的是 binary_crossentropy。
至于效果指標(biāo),我們使用的是準(zhǔn)確率(accuracy)。
模型編譯好之后。萬(wàn)事俱備,只差數(shù)據(jù)了。
你可能納悶,一上來(lái)不就已經(jīng)把訓(xùn)練、驗(yàn)證和測(cè)試集分好了嗎?
沒(méi)錯(cuò),但那只是原始數(shù)據(jù)。我們模型需要接收的,是數(shù)據(jù)流。
在訓(xùn)練和驗(yàn)證過(guò)程中,數(shù)據(jù)都不是一次性灌入模型的。而是一批次一批次分別載入。每一個(gè)批次,稱(chēng)作一個(gè) batch;相應(yīng)地,批次大小,叫做 batch_size 。
為了方便咱們把 Pandas 數(shù)據(jù)框中的原始數(shù)據(jù)轉(zhuǎn)換成數(shù)據(jù)流。我這里編寫(xiě)了一個(gè)函數(shù)。
defdf_to_tfdata(df,shuffle=True,bs=32):df=df.copy()labels=df.pop('Exited')ds=tf.data.Dataset.from_tensor_slices((dict(df),labels))ifshuffle:ds=ds.shuffle(buffer_size=len(df),seed=1)ds=ds.batch(bs)returnds
這里首先是把數(shù)據(jù)中的標(biāo)記拆分出來(lái)。然后根據(jù)把數(shù)據(jù)讀入到 ds 中。根據(jù)是否是訓(xùn)練集,我們指定要不要需要打亂數(shù)據(jù)順序。然后,依據(jù) batch_size 的大小,設(shè)定批次。這樣,數(shù)據(jù)框就變成了神經(jīng)網(wǎng)絡(luò)模型喜聞樂(lè)見(jiàn)的數(shù)據(jù)流。
train_ds=df_to_tfdata(train)valid_ds=df_to_tfdata(valid,shuffle=False)test_ds=df_to_tfdata(test,shuffle=False)
這里,只有訓(xùn)練集打亂順序。因?yàn)槲覀兿M?yàn)證和測(cè)試集一直保持一致。只有這樣,不同參數(shù)下,對(duì)比的結(jié)果才有顯著意義。
有了模型架構(gòu),也有了數(shù)據(jù),我們把訓(xùn)練集和驗(yàn)證集扔進(jìn)去,讓模型嘗試擬合。這里指定了,跑5個(gè)完整輪次(epochs)。
model.fit(train_ds,validation_data=valid_ds,epochs=5)
你會(huì)看到,最終的驗(yàn)證集準(zhǔn)確率接近80%。
我們打印一下模型結(jié)構(gòu):
model.summary()
雖然我們的模型非常簡(jiǎn)單,卻也依然包含了23401個(gè)參數(shù)。
下面,我們把測(cè)試集放入模型中,看看模型效果如何。
model.evaluate(test_ds)
依然,準(zhǔn)確率接近80%。
還不錯(cuò)吧?
……
真的嗎?
疑惑
如果你觀察很仔細(xì),可能剛才已經(jīng)注意到了一個(gè)很奇特的現(xiàn)象:
訓(xùn)練的過(guò)程中,除了第一個(gè)輪次外,其余4個(gè)輪次的這幾項(xiàng)重要指標(biāo)居然都沒(méi)變!
它們包括:
訓(xùn)練集損失
訓(xùn)練集準(zhǔn)確率
驗(yàn)證集損失
驗(yàn)證集準(zhǔn)確率
所謂機(jī)器學(xué)習(xí),就是不斷迭代改進(jìn)啊。如果每一輪下來(lái),結(jié)果都一模一樣,這難道不奇怪嗎?難道沒(méi)問(wèn)題嗎?
我希望你,能夠像偵探一樣,揪住這個(gè)可疑的線索,深入挖掘進(jìn)去。
這里,我給你個(gè)提示。
看一個(gè)分類(lèi)模型的好壞,不能只看準(zhǔn)確率(accuracy)。對(duì)于二元分類(lèi)問(wèn)題,你可以關(guān)注一下 f1 score,以及混淆矩陣(confusion matrix)。
如果你驗(yàn)證了上述兩個(gè)指標(biāo),那么你應(yīng)該會(huì)發(fā)現(xiàn)真正的問(wèn)題是什么。
下一步要窮究的,是問(wèn)題產(chǎn)生的原因。
回顧一下咱們的整個(gè)兒過(guò)程,好像都很清晰明了,符合邏輯啊。究竟哪里出了問(wèn)題呢?
如果你一眼就看出了問(wèn)題。恭喜你,你對(duì)深度學(xué)習(xí)已經(jīng)有感覺(jué)了。那么我繼續(xù)追問(wèn)你,該怎么解決這個(gè)問(wèn)題呢?
歡迎你把思考后的答案在留言區(qū)告訴我。
對(duì)于第一名全部回答正確上述問(wèn)題的讀者,我會(huì)邀請(qǐng)你作為嘉賓,免費(fèi)(原價(jià)199元)加入我本年度的知識(shí)星球。當(dāng)然,前提是你愿意。
小結(jié)
希望通過(guò)本文的學(xué)習(xí),你已掌握了以下知識(shí)點(diǎn):
Tensorflow 2.0 的安裝與使用;
表格式數(shù)據(jù)的神經(jīng)網(wǎng)絡(luò)分類(lèi)模型構(gòu)建;
特征工程的基本流程;
數(shù)據(jù)集合的隨機(jī)分割與利用種子數(shù)值保持一致;
數(shù)值型數(shù)據(jù)列與類(lèi)別型數(shù)據(jù)列的分別處理方式;
Keras 高階 API 的模型搭建與訓(xùn)練;
數(shù)據(jù)框轉(zhuǎn)化為 Tensorflow 數(shù)據(jù)流;
模型效果的驗(yàn)證;
缺失的一環(huán),也即本文疑點(diǎn)產(chǎn)生的原因,以及正確處理方法。
希望本教程對(duì)于你處理表格型數(shù)據(jù)分類(lèi)任務(wù),能有幫助。
-
神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
42文章
4809瀏覽量
102863 -
深度學(xué)習(xí)
+關(guān)注
關(guān)注
73文章
5555瀏覽量
122506 -
tensorflow
+關(guān)注
關(guān)注
13文章
330瀏覽量
61040
原文標(biāo)題:怎樣搞定分類(lèi)表格數(shù)據(jù)?有人用TF2.0構(gòu)建了一套神經(jīng)網(wǎng)絡(luò) | 技術(shù)頭條
文章出處:【微信號(hào):rgznai100,微信公眾號(hào):rgznai100】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
評(píng)論