wshuyi的個人博客分享 http://www.ueservicedoffices.com/u/wshuyi

博文

你的機器“不肯”學習,怎么辦? 精選

已有 4204 次閱讀 2019-5-1 08:30 |系統分類:科研筆記| python, 機器學習, 數據科學

給你講講機器學習數據預處理中,歸一化(normalization)的重要性。

前情回顧

Previously, on 玉樹芝蘭 ……

我給你寫了一篇《如何用 Python 和 Tensorflow 2.0 神經網絡分類表格數據?》,為你講解了 Tensorflow 2.0 處理結構化數據的分類。

結尾處,我給你留了一個問題。

把測試集輸入模型中,檢驗效果。結果是這樣的:

model.evaluate(test_ds)

準確率接近80%,看起來很棒,對嗎?

但是,有一個疑問:

注意這張截圖。訓練的過程中,除了第一個輪次外,其余4個輪次的這幾項重要指標居然都沒變!

它們包括:

  • 訓練集損失

  • 訓練集準確率

  • 驗證集損失

  • 驗證集準確率

所謂機器學習,就是不斷迭代改進。

如果每一輪下來,結果都一模一樣,這里八成有鬼。

我給了你提示:

看一個分類模型的好壞,不能只看準確率(accuracy)。對于二元分類問題,你可以關注一下 f1 score,以及混淆矩陣(confusion matrix)。

這段時間,你通過思考,發現問題產生原因,以及解決方案了嗎?

從留言的反饋來看,有讀者能夠正確指出了問題。

但很遺憾,我沒有能見到有人提出正確和完整的解決方案。

這篇文章,咱們就來談談,機器為什么“不肯學習”?以及怎么做,才能讓它“學得進去”。

環境

本文的配套源代碼,我放在了這個 Github 項目中。請你點擊這個鏈接http://t.cn/ESJmj4h)訪問。

如果你對我的教程滿意,歡迎在頁面右上方的 Star 上點擊一下,幫我加一顆星。謝謝!

注意這個頁面的中央,有個按鈕,寫著“在 Colab 打開”(Open in Colab)。請你點擊它。

然后,Google Colab 就會自動開啟。

我建議你點一下上圖中紅色圈出的 “COPY TO DRIVE” 按鈕。這樣就可以先把它在你自己的 Google Drive 中存好,以便使用和回顧。

Colab 為你提供了全套的運行環境。你只需要依次執行代碼,就可以復現本教程的運行結果了。

如果你對 Google Colab 不熟悉,沒關系。我這里有一篇教程,專門講解 Google Colab 的特點與使用方式。

為了你能夠更為深入地學習與了解代碼,我建議你在 Google Colab 中開啟一個全新的 Notebook ,并且根據下文,依次輸入代碼并運行。在此過程中,充分理解代碼的含義。

這種看似笨拙的方式,其實是學習的有效路徑。

代碼

請你在 Colab Notebook 里,找到這一條分割線:

用鼠標點擊它,然后從菜單里面選擇 Runtime -> Run Before

運行結束后,你會獲得如下圖的結果:

如何用 Python 和 Tensorflow 2.0 神經網絡分類表格數據?》一文的結果已經成功復現。

下面我們依次來解讀后面的語句。

首先,我們利用 Keras API 中提供的 predict 函數,來獲得測試集上的預測結果。

pred = model.predict(test_ds)

但是請注意,由于我們的模型最后一層,用的激活函數是 sigmoid , 因此 pred 的預測結果,會是從0到1區間內的小數。

而我們實際需要輸出的,是整數0或者1,代表客戶“流失”(1)或者“未流失”(0)。

幸好, numpy 軟件包里面,有一個非常方便的函數 rint ,可以幫助我們四舍五入,把小數變成整數。

pred = np.rint(pred)

我們來看看輸出結果:

pred

有了預測輸出結果,下面我們就可以用更多的方法,檢驗分類效果了。

根據前文的提示,這里我們主要用到兩項統計功能:

  • 分類報告

  • 混淆矩陣

我們先從 Scikit-learn 軟件包導入對應的功能。

from sklearn.metrics import classification_report, confusion_matrix

然后,我們對比測試集實際標記,即 test['Exited'] ,和我們的預測結果。

print(classification_report(test['Exited'], pred))

這里,你立刻就能意識到出問題了——有一個分類,即“客戶流失”(1)里,三項重要指標(precision, recall 和 f1-score)居然都是0!

我們用同樣的數據查看混淆矩陣,看看到底發生了什么。

print(confusion_matrix(test['Exited'], pred))

混淆矩陣的讀法是,行代表實際分類,列代表預測分類,分別從0到1排列。

上圖中,矩陣的含義就是:模型預測,所有測試集數據對應的輸出都是0;其中預測成功了1585個(實際分類就是0),預測錯誤415個(實際分類其實是1)。

也就是說,咱們費了半天勁,訓練出來的模型只會傻乎乎地,把所有分類結果都設置成0.

在機器學習里,這是一個典型的笨模型(dummy model)。

如果咱們的測試集里面,標簽分類0和1的個數是均衡的(一樣一半),那這種笨模型,應該獲得 50% 的準確率。

然而,我們實際看看,測試集里面,分類0(客戶未流失)到底占多大比例:

len(test[test['Exited'] == 0])/len(test)

結果是:

0.7925

這個數值,恰恰就是《如何用 Python 和 Tensorflow 2.0 神經網絡分類表格數據?》一文里面,我們在測試集上獲得了準確率。

一開始我們還認為,將近80%的準確率,是好事兒。

實際上,這模型著實很傻,只有一根筋。

設想我們拿另外一個測試集,里面只有 1% 的標注是類別0,那么測試準確率也就只有 1% 。

為了不冤枉模型,咱們再次確認一下。

使用 numpy 中的 unique 函數,查看一下預測結果 pred 中,到底有幾種不同的取值。

np.unique(pred)

結果是:

array([0.], dtype=float32)

果不其然,全都是0.

果真是“人工不智能”!

分析

問題出在哪里呢?

模型根本就沒有學到東西。

每一輪下來,結果都一樣,毫無進步。

說到這里,你可能會有疑惑:

老師,是不是你講解出錯了?

兩周前,我在 UNT 給學生上課的時候,他們就提出來了這疑問。

我早有準備,立即布置了一個課堂練習。

讓他們用這套流程,處理另外的一個數據集。

這個數據集你也見過,就是我在《貸還是不貸:如何用Python和機器學習幫你決策?》里面用過的貸款審批數據。

我把數據放在了這個鏈接http://t.cn/ESJ3x3o),你如果感興趣的話,不妨也試著用前文介紹的流程,自己跑一遍。

學生們有些無奈地做了這個練習。

他們的心理活動大概是這樣的:

你教的這套流程,連演示數據都搞不定,更別說練習數據了。做了也是錯的。是不是先糾正了錯誤,再讓我們練?

然后,當運行結果出來的時候,我在一旁,靜靜看著他們驚詫、沉思,以至于抓狂的表情。

同一套流程,在另外的數據上使用,機器確實學習到了規律。

數據集的細節里面,藏著什么魔鬼?

歸一

直接說答案:

流程上確實有問題。數值型數據沒有做歸一化(normalization)。

歸一化是什么?

就是讓不同特征列上的數值,擁有類似的分布區間。

最簡單的方法,是根據訓練集上的對應特征,求 Z 分數。

Z 分數的定義是:

  • Mean 是均值

  • Standard Deviation 是標準差

為什么一定要做這一步?

回顧一下咱們的數據。

我這里用紅色標出來了所有數值特征列。

看看有什么特點?

對,它們的分布迥異。

NumOfProducts 的波動范圍,比起 Balance 或者 EstimatedSalary,要小得多。

機器學習,并不是什么黑科技。

它的背后,是非常簡單的數學原理。

最常用的迭代方法,是梯度下降(Gradient descent)。如下圖所示:

其實就是奔跑著下降,找局部最優解。

如果沒跑到,繼續跑。

如果跑過轍了,再跑回來。

但問題在于,你看到的這張圖,是只有1維自變量的情況。

咱們觀察的數據集,僅數值型數據,就有6個。因此至少是要考察這6個維度。

不好意思,我無法給你繪制一個六維圖形,自己腦補吧。

但是注意,對這六個維度,咱們用的,卻是同一個學習速率(learning rate)。

就好像同一個老師,同時給6個學生上數學課。

如果這六個維度分布一致,這樣是有意義的。

這也是為什么大多數學校里面,都要分年級授課。要保證授課對象的理解能力,盡量相似。

但假如這“6個學生”里,有一個是愛因斯坦,一個是阿甘。

你說老師該怎么講課?

愛因斯坦聽得舒服的進度,阿甘早就跟不上了。

阿甘能接受的進度,愛因斯坦聽了可能無聊到想撞墻。

最后老師決定——太難了,我不教了!

于是誰都學不到東西了。

對應到我們的例子,就是因為數據分布差異過大,導致不論往哪個方向嘗試改變參數,都按下葫蘆浮起瓢,越來越糟。

于是模型判定,呆在原地不動,是最好的策略。

所以,它干脆不學了。

怎么辦?

這個時候,就需要歸一化了。

對應咱們這個不恰當的舉例,就是在課堂上,老師要求每個人都保持每天一單位(unit)的學習進度。

只不過,愛因斯坦的一單位,是100頁書。

阿甘同學……兩行,還能接受吧?

新代碼

請你點擊這個鏈接http://t.cn/ESJBJHW)訪問更新后的代碼。

按照之前的方式,點擊“在 Colab 打開”(Open in Colab)。使用 “COPY TO DRIVE” 按鈕,存放在你自己的 Google Drive 中。

對比觀察后,你會發現,改動只有1個代碼段落。

就是把原先的數值型特征采集從這樣:

for header in numeric_columns:
  feature_columns.append(feature_column.numeric_column(header))

變成這樣:

for header in numeric_columns:
  feature_columns.append(
      feature_column.numeric_column(
          header,
          normalizer_fn=lambda x: (tf.cast(x, dtype=float)-train[header].mean())/train[header].std()))

尤其要注意,我們要保證平均值和標準差來自于訓練集。只有這樣,才能保證模型對驗證集和測試集的分布一無所知,結果的檢驗才有意義。否則,就如同考試作弊一樣。

這就是為了歸一化,你所需做的全部工作。

這里我們依然保持原先的隨機種子設定。也就是凡是使用了隨機函數的功能(訓練集、驗證集和測試集的劃分等),都與更新代碼之前完全一致。

這樣做,改變代碼前后的結果才有可對比性。

下面我們使用菜單欄里面的 "Run All" 運行一下代碼。

之后查看輸出。

首先我們可以注意到,這次的訓練過程,數值終于有變化了。

因為其他變量全都保持一致。所以這種變化,沒有別的解釋,只能是因為使用了歸一化(normalization)。

我們更加關心的,是這次的分類報告,以及混淆矩陣。

分類報告是這樣的:

注意這一次,類別1上面的幾項指標,終于不再是0了。

混淆矩陣中,類別1里,也有36個預測正確的樣本了。

成功了!

……

別急著歡呼。

雖然機器在學習和改進,但是效果好像也不是很好嘛。例如類別1的 Recall 簡直慘不忍睹。

有沒有什么辦法改進呢?

這個問題,就需要你了解如何微調模型,以及超參數的設定了。

我把推薦的學習資料,放在了公眾號的對應文章里,歡迎查看。

小結

這篇文章里,我為你介紹了以下知識點:

  • 分類模型性能驗證(尤其是 Accuracy 之外的)評測指標;

  • 預處理過程中數值數據歸一化(Normalization)的重要性;

  • 如何在 Tensorflow 2.0 的數據預處理和特征抽取中使用歸一化;

  • 如何利用模型預測分類結果,并且使用第三方軟件包功能快速統計匯報。

希望上述內容,能對你使用深度神經網絡進行機器學習有幫助。

祝深度學習愉快!

延伸閱讀

你可能也會對以下話題感興趣。點擊鏈接就可以查看。

喜歡請點贊和打賞。還可以微信關注和置頂我的公眾號“玉樹芝蘭”(nkwangshuyi)。

如果你對 Python 與數據科學感興趣,不妨閱讀我的系列教程索引貼《如何高效入門數據科學?》,里面還有更多的有趣問題及解法。

題圖:來自于 freepixels




http://www.ueservicedoffices.com/blog-377709-1176491.html

上一篇:文科生 Python 與數據科學入門教材推薦
下一篇:數據科學入門后,該做什么?

5 劉鋼 黃永義 李劍超 強濤 毛宏

該博文允許注冊用戶評論 請點擊登錄 評論 (1 個評論)

數據加載中...

Archiver|手機版|科學網 ( 京ICP備14006957 )

GMT+8, 2019-5-18 11:01

Powered by ScienceNet.cn

Copyright © 2007- 中國科學報社

返回頂部
时时彩平台