谷歌機器學習實戰的7個步驟:用於結構化資料的TensorFlow示例

iyacontrol發表於2020-05-18

網際網路上有很多很棒的機器學習教程。但是,它們大多數都專注於機器學習的特定部分,例如,探索資料,建立模型,訓練和評估。其中很少有人介紹構建機器學習模型的完整步驟。

最受歡迎的文章之一概述了進行機器學習的步驟,這是Google雲端平臺推出的郭玉峰的《機器學習的7個步驟》。

提出了以下七個步驟:

  1. Gathering data
  2. Preparing data (and exploring data)
  3. Choosing a model
  4. Training
  5. Evaluation
  6. Hyperparameter tuning
  7. Prediction (and save model)

在本文中,我們將實踐上述步驟,並從頭開始構建機器學習模型。

定義問題和環境設定

在開始討論細節之前,對於任何機器學習專案,我們要做的第一件事是為我們的機器學習模型定義問題。

對於本教程,我們將使用Kaggle的Titanic Dataset。這是一個非常著名的資料集,通常是學生學習機器學習的第一步。

假設我們被要求建立一個可以預測泰坦尼克號生存時間的系統。

環境設定

為了執行本教程,您需要安裝

TensorFlow 2, TensorBoard 2, numpy, pandas, matplotlib, seaborn

它們都可以直接通過PyPI安裝,我強烈建議建立一個新的虛擬環境。最好避免使用base(root),因為它可能會破壞系統。

有關建立Python虛擬環境的教程,您可以看一下:

https://towardsdatascience.com/create-virtual-environment-using-virtualenv-and-add-it-to-jupyter-notebook-6e1bf4e03415​towardsdatascience.com

1. Gathering data

定義好問題後,就該進行機器學習的第一步,那就是收集資料。這一步是最重要的,因為您收集的資料的質量和數量將直接決定您的預測模型的質量。

在本教程中,資料將來自Kaggle。讓我們匯入一些庫並載入資料以開始使用:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

讓我們將train.csv和test.csv檔案載入到pandas DataFrame中。

df_train_raw = pd.read_csv('data/titanic/train.csv')
df_test_raw = pd.read_csv('data/titanic/test.csv')
df_train_raw.head()

preview of Titanic data

Data Dictionary from Kaggle

2. Preparing data

讓我們從一些探索性資料分析(EDA)開始。我們將從檢查缺失值開始。

2.1 Missing values

我們可以使用seaborn來建立一個簡單的熱圖,以檢視缺少的值:

sns.heatmap(df_train_raw.isnull(), 
            yticklabels=False, 
            cbar=False, 
            cmap='viridis')

output of seaborn heatmap plot for missing values

年齡,機艙和出發缺少值。年齡缺失的比例可能很小,不足以用某種形式的估算合理地替代。檢視“機艙”列,該資料似乎缺少太多值,無法做有用的事情。我們可能會在以後放下機艙,或將其更改為其他功能,例如“機艙已知:1或0”。出發的比例很小,在本教程中,我們保留它。

2.2 Visualizing some more of the data

讓我們繼續視覺化更多資料:

sns.countplot(x='Survived', data=df_train_raw, palette='RdBu_r')

plot of Survived

sns.countplot(x='Survived', 
              hue='Sex', 
              data=df_train_raw,
              palette='RdBu_r')

sns.countplot(x='Survived',
              hue='Pclass', 
              data=df_train_raw,
              palette='rainbow')

sns.distplot(df_train_raw['Age'].dropna(),
             kde=True,
             color='darkred',
             bins=30)

sns.countplot(x='SibSp',data=df_train_raw)

df_train_raw['Fare'].hist(color='green', 
                          bins=40, 
                          figsize=(8,4))

2.3 Data cleaning

我們想用某種形式的估算來代替失蹤的時代。一種方法是填寫所有乘客的平均年齡。但是,我們可以對此有所瞭解,並按旅客等級檢查平均年齡。例如:

sns.boxplot(x='Pclass',
            y='Age',
            data=df_train_raw,
            palette='winter')

我們可以看到,較高階層的較富裕乘客往往年齡較大,這是有道理的。我們將使用這些平均年齡值根據年齡的Pclass進行估算。

def impute_age(cols):
    Age = cols[0]
    Pclass = cols[1]
    
    if pd.isnull(Age):
        if Pclass == 1:
            return 37
        elif Pclass == 2:
            return 29
        else:
            return 24
    else:
        return Age

現在,我們應用該功能並檢查其是否有效:

# Make a copy for test only
train_copy = df_train_raw.copy() 
train_copy['Age'] = train_copy[['Age','Pclass']]
   .apply(impute_age, axis=1)
# check that heat map again
sns.heatmap(train_copy.isnull(), 
            yticklabels=False, 
            cbar=False, 
            cmap='viridis')

非常好! impute_age()有效。讓我們繼續進行轉換,並刪除“機艙”列。

2.4 Converting Categorical Features

我們需要將分類功能轉換為一鍵編碼。否則,我們的機器學習演算法將無法直接將這些功能作為輸入。

讓我們使用info()檢查列資料型別:

df_train_raw.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 712 entries, 0 to 711
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  712 non-null    int64  
 1   Survived     712 non-null    int64  
 2   Pclass       712 non-null    int64  
 3   Name         712 non-null    object 
 4   Sex          712 non-null    object 
 5   Age          566 non-null    float64
 6   SibSp        712 non-null    int64  
 7   Parch        712 non-null    int64  
 8   Ticket       712 non-null    object 
 9   Fare         712 non-null    float64
 10  Cabin        168 non-null    object 
 11  Embarked     710 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 66.9+ KB

有5列具有物件資料型別的列。其中,不需要名稱,機票和機艙。另外,根據上面看到的資料字典,我們注意到Pclass是分類資料。讓我們做一個函式preprocessing()來保留這些有用的數字功能,並將Pclass,Sex和Embarked轉換為一鍵編碼。

讓我們應用該功能並建立訓練和測試資料集以構建我們的機器學習模型。

x_train = preprocessing(df_train_raw)
y_train = df_train_raw['Survived'].values
x_test = preprocessing(df_test_raw)
y_test = df_test_raw['Survived'].values
print("x_train.shape =", x_train.shape )
print("x_test.shape =", x_test.shape )

通過在上面執行,您應該獲得如下訓練和測試資料集的形狀:

x_train.shape = (712, 13)
x_test.shape = (179, 13)

讓我們看看x_train.head()的資料:

我們的資料已準備好用於模型。

3. Choose a model

在TensorFlow 2.0中有三種方法來實現神經網路架構:

  • Sequential API: 是使用Keras入門和執行的最簡單方法。
  • Functional API: 適用於更復雜的模型。
  • Model Subclassing: 完全可自定義,使您能夠實現自己的模型自定義前向傳遞。

為簡單起見,讓我們使用最簡單的方法:帶有Sequential()的Sequential API。讓我們繼續前進,構建一個具有3個密集層的神經網路。每一層中的所有引數均已進行硬編碼,如下所示:

import tensorflow as tf 
from tensorflow.keras import models, layers
tf.keras.backend.clear_session()
model = models.Sequential()
model.add(layers.Dense(10, activation='relu', input_shape=(13,)))
model.add(layers.Dense(20, activation='relu' ))
model.add(layers.Dense(1, activation='sigmoid'))
model.summary()

下面是model.summary() 的輸出:

4. Training

首先,讓我們使用model.compile()配置模型:

  • 使用優化程式隨機梯度下降(SGD)
  • 使用二進位制交叉熵損失函式(binary_crossentropy)進行二進位制分類
  • 為簡單起見,請在培訓和測試期間使用“準確性”作為我們的評估指標來評估模型

對於訓練,有三種方法可以訓練Keras模型:

  • 對模型使用model.fit()以獲取固定數量的紀元。
  • 使用model.train_on_batch() 可以只對一個批次進行一次訓練。
  • 建立自定義訓練迴圈。

在本教程中,讓我們繼續最簡單的方式model.fit()。

# Convert DataFrame into np array
x_train = np.asarray(x_train)
y_train = np.asarray(y_train)
# Get around with KMP duplicate issue
import os
os.environ['KMP_DUPLICATE_LIB_OK']='True'
# Use binary cross entropy loss function for binary classification
model.compile(optimizer='sgd',
            loss='binary_crossentropy',
            metrics=['accuracy'])
history = model.fit(x_train,y_train,
                    batch_size= 64,
                    epochs= 30,
                    validation_split=0.2
                   )

如果一切執行順利,我們應該得到如下輸出。

Train on 569 samples, validate on 143 samples
Epoch 1/30
569/569 [==============================] - 1s 2ms/sample - loss: 0.5568 - accuracy: 0.7206 - val_loss: 0.6139 - val_accuracy: 0.6713
Epoch 2/30
569/569 [==============================] - 0s 91us/sample - loss: 0.5639 - accuracy: 0.7047 - val_loss: 0.6212 - val_accuracy: 0.6643
Epoch 3/30
569/569 [==============================] - 0s 112us/sample - loss: 0.5705 - accuracy: 0.6907 - val_loss: 0.6379 - val_accuracy: 0.6573
Epoch 4/30
569/569 [==============================] - 0s 109us/sample - loss: 0.5538 - accuracy: 0.7065 - val_loss: 0.6212 - val_accuracy: 0.6713
......
......
Epoch 30/30
569/569 [==============================] - 0s 102us/sample - loss: 0.5597 - accuracy: 0.7065 - val_loss: 0.6056 - val_accuracy: 0.7203

5. Model Evaluation

訓練結束後,就可以使用“模型評估”來檢視模型是否良好。模型評估通常涉及:

  • 繪製損失和準確性指標的進度圖
  • 針對從未用於訓練的資料測試我們的模型。這是我們前面放置的測試資料集df_test發揮作用的地方。

讓我們建立一個函式plot_metric()來繪製指標。

%matplotlib inline
%config InlineBackend.figure_format = 'svg'
def plot_metric(history, metric):
    train_metrics = history.history[metric]
    val_metrics = history.history['val_'+metric]
    epochs = range(1, len(train_metrics) + 1)
    plt.plot(epochs, train_metrics, 'bo--')
    plt.plot(epochs, val_metrics, 'ro-')
    plt.title('Training and validation '+ metric)
    plt.xlabel("Epochs")
    plt.ylabel(metric)
    plt.legend(["train_"+metric, 'val_'+metric])
    plt.show()

通過執行plot_metric(history,'loss')繪製損失進度。

通過執行plot_metric(history,'accuracy')繪製準確性進度。

針對測試資料集測試我們的模型:

# Convert DataFrame into np array
x_test = np.asarray(x_test)
y_test = np.asarray(y_test)
model.evaluate(x = x_test,y = y_test)

而且我們應該得到具有損失和準確性的輸出,如下所示:

179/1 [====] - 0s 43us/sample - loss: 0.5910 - accuracy: 0.6760
[0.5850795357586951, 0.67597765]

6. Hyperparameter tuning

太酷了,我們已經對第一個機器學習模型進行了評估。現在該看看我們是否可以通過任何方式進一步改進它。我們可以通過旋轉超引數來做到這一點。當我們進行第一次訓練時,我們隱式假設了一些引數,現在是時候回過頭來測試這些假設並嘗試其他值了。

對於本教程,我們只關注模型中以下三個超引數的實驗:

  • 第一密集層中的單位數
  • 第二致密層中的單位數
  • 優化器

6.1 Experiment setup

首先,首先載入TensorBoard notebook擴充套件程式:

# Load the TensorBoard notebook extension
%load_ext tensorboard

然後,新增一條語句以清除上一次執行中的所有日誌。如果您不清除儀表盤,則會弄亂儀表盤。

# Clear any logs from previous runs
!rm -rf ./logs/

匯入TensorBoard HParams外掛:

from tensorboard.plugins.hparams import api as hp

列出要嘗試的值,並將實驗配置記錄到TensorBoard。

  • 第一層中的單元數為51020
  • 第二層的單元數為102040
  • adamsgd用於優化程式

6.2 Adapt TensorFlow runs to log hyperparameters and metrics

我們的模型非常簡單:3個密集層。儘管不再對超引數進行硬編碼,但程式碼看起來很熟悉。相反,超引數在hyparams字典中提供,並在整個訓練功能中使用:

對於每次執行,請記錄具有超引數和最終精度的hparams摘要:

def run(run_dir, hparams):
  with tf.summary.create_file_writer(run_dir).as_default():
    hp.hparams(hparams)  # record the values used in this trial
    accuracy = train_test_model(hparams)
    tf.summary.scalar(METRIC_ACCURACY, accuracy, step=1)

6.3 Start runs and log them

現在,我們可以嘗試進行多個實驗,並使用不同的一套超級血壓計來訓練每個實驗。為簡單起見,讓我們使用網格搜尋來嘗試離散引數的所有組合以及實值引數的上限和下限。

session_num = 0
for num_units_one in HP_NUM_UNITS_ONE.domain.values:
  for num_units_two in HP_NUM_UNITS_TWO.domain.values:
    for optimizer in HP_OPTIMIZER.domain.values:
      hparams = {
          HP_NUM_UNITS_ONE: num_units_one,
          HP_NUM_UNITS_TWO: num_units_two,
          HP_OPTIMIZER: optimizer,
      }
      run_name = "run-%d" % session_num
      print('>> Starting trial: %s' % run_name)
      print({h.name: hparams[h] for h in hparams})
      run('logs/hparam_tuning/' + run_name, hparams)
      session_num += 1

如果一切執行順利,我們應該得到如下輸出:

6.4 Visualize the results in TensorBoard’s HParams plugin

執行完成後,開啟終端並cd進入專案目錄。然後,現在可以通過在終端中執行以下命令來開啟HParams儀表板:

admin@Mac:~/Code/WorkSpace/machine-learning/tf2
⇒  tensorboard --logdir logs/hparam_tuning
Serving TensorBoard on localhost; to expose to the network, use a proxy or pass --bind_all
TensorBoard 2.0.0 at http://localhost:6006/ (Press CTRL+C to quit)

在瀏覽器中開啟儀表板,然後直接轉到HPARAMS-> PARALLEL COORDINATES VIEW

通過檢視“平行座標”檢視,然後在精度軸上單擊並拖動,可以選擇精度最高的執行。

當這些執行通過不同的超引數時,我們可以得出以下結論:

  • 第一層5個單元
  • 第二層10個單位
  • “ adam”優化器

在這些實驗中表現最好。

7. Prediction (and save model)

現在,就準確度而言,我們已經有了最好的機器學習模型。最後一步是使用此模型進行預測或推斷。這是所有這些工作的重點,在那裡機器學習的價值得以實現。我們最終可以使用我們的模型來預測乘客是否存活。

使用模型進行預測:

model.predict(x_test[0:10])
array([[0.56895125],
       [0.37735564],
       [0.5005745 ],
       [0.60003537],
       [0.5371451 ],
       [0.36402294],
       [0.49169463],
       [0.49049523],
       [0.4984674 ],
       [0.1470165 ]], dtype=float32)

使用該模型為輸入樣本生成類別預測。

model.predict_classes(x_test[0:10])
array([[1],
       [0],
       [1],
       [1],
       [1],
       [0],
       [0],
       [0],
       [0],
       [0]], dtype=int32)

最後,我們可以將整個模型儲存到單個HDF5檔案中:

model.save('data/keras_model.h5')

並載入通過save()儲存的模型:

model = models.load_model('data/keras_model.h5')
# Predict class
model.predict_classes(x_test[0:10])

結論

本文是一個快速教程,主要向所有人展示如何將Google的機器學習的7個步驟付諸實踐。我試圖避免使用許多機器學習概念,並儘量簡化本教程。

在實際的應用程式中,它們還有很多要考慮的地方。例如,選擇評估指標,特徵縮放,選擇有意義的特徵,拆分資料集,處理過度擬合和欠擬合等。此外,本教程僅適用於結構化資料,而實際資料並不總是結構化資料,所有諸如影像,音訊或文字之類的東西都是非結構化資料。

PS:本文屬於翻譯,原文

相關文章