使用 TensorFlow Hub 和估算器構建文字分類模型

TensorFlowers發表於2018-09-05

文 / 開發技術推廣工程師 Sara Robinson

來源 | TensorFlow 公眾號

將遷移學習應用於計算機視覺模型的例子很常見,但如果將其用於文字分類,情況又會如何呢?不妨進入 TensorFlow Hub,這是通過 遷移學習來增強 TF 模型的內容庫。遷移學習是這樣一種過程:選用已藉助大量資料訓練好的已有模型的權重和變數,並將其運用於自己的資料和預測任務。

遷移學習具有諸多好處,其中之一就是您無需像從頭開始訓練時一樣提供自己的大量訓練資料。但這些已有模型又來自何處呢?這正是 TensorFlow Hub 大顯身手之處:它可以為各類模型(影象、文字等)提供現有模型檢查點的完整儲存區。在本篇博文中,我將介紹如何使用 TensorFlow Hub 文字模組構建一個模型,以根據相關描述預測電影型別。

您可以使用 Colab 在瀏覽器中執行此模型,無需任何設定。

匯入資料並進行預處理

在這個模型中,我們將使用 Kaggle 電影資料集這個出色的公共領域資源。該資料集包含逾 45000 部電影的資料,每部電影均有大量相關資料。為簡便起見,我們僅使用該資料集中的電影描述(稱作 “簡介”)和電影型別。下面是該資料集在 Kaggle 中的預覽: 注: Kaggle 電影資料集連結 www.kaggle.com/rounakbanik…

使用 TensorFlow Hub 和估算器構建文字分類模型

首先,我們將匯入要使用的內容庫,以構建此模型:

1    import numpy as np 
2    import pandas as pd
3
4    import tensorflow as tf
5    import tensorflow_hub as hub
6
7    from sklearn.preprocessing import MultiLabelBinarizer
複製程式碼

我已經將這個資料集的 CSV 檔案放在一個公共 Cloud Storage 儲存分割槽中。我們可以執行如下命令,將資料下載到我們的 Colab 例項中,並讀取為 Pandas dataframe 格式:

1    !wget 'https://storage.googleapis.com/movies_data/movies_metadata.csv'
2    data = pd.read_csv('movies_metadata.csv')
3
4    descriptions = data['overview']
5    genres = data['genres']
複製程式碼

為簡便起見,我們將可能的型別限定為下列幾種:

1    top_genres = ['Comedy', 'Thriller', 'Romance', 'Action', 'Horror', 'Crime', 'Documentary', 'Adventure', 'Science Fiction']
複製程式碼

我們將資料集限定為這些型別中描述非空白的電影,然後按照 80% 訓練和 20% 測試的比例將資料劃分為訓練資料集和測試資料集:

1    train_size = int(len(descriptions) * .8)
2
3    train_descriptions = descriptions[:train_size]
4    train_genres = genres[:train_size]
5
6    test_descriptions = descriptions[train_size:]
7    test_genres = genres[train_size:]
複製程式碼

使用 TF Hub 構建嵌入層

以 TF Hub 建立嵌入層時僅需使用極少量程式碼。我們的模型僅有一個特徵(描述),並將表示為一個嵌入列。文字嵌入提供了在向量空間表示文字內容的方法,如此一來,嵌入空間中相似的字詞或句子會更靠近(您可在此處閱讀更多相關內容)。您可以完全使用自己的資料從頭開始構建文字嵌入向量。TF Hub 可提供已使用各種文字資料訓練過的文字嵌入,因而能夠簡化這一過程。 注:文字嵌入連結 www.tensorflow.org/hub/modules…

對於英文文字,TF Hub 提供了各種已使用不同種類文字資料訓練過的嵌入:

通用語句編碼器 (Universal sentence encoder):用於較長的文字輸入 ELMo:使用十億單詞基準 (1B Word Benchmark) 訓練過的深度嵌入 神經網路語言模型 (Neural Network Language Model) 嵌入:通過 Google 新聞訓練 Word2vec:通過 Wikipedia 訓練

您所選擇的預訓練文字嵌入是您模型中的一個超引數,所以最好用不同的文字嵌入進行試驗,看看哪個的準確性最高。先從用與您的文字最接近的文字訓練過的模型開始。由於我們的電影描述都是較長的輸入,因此,我發現使用通用語句編碼器嵌入的準確性最高。這可以將我們的描述編碼為高維文字向量。請注意,這一特定模型很大,會佔用 1GB 容量。 注:通用語句編碼器連結 www.tensorflow.org/hub/modules…

我們可以使用 hub.text_embedding_column,以一行程式碼為該層建立一個特徵列,並向其傳遞我們層的名稱 (“movie_descriptions”) 和要使用的 TF Hub 模型網址:

1    description_embeddings = hub.text_embedding_column( 
2        "movie_descriptions", 
3        module_spec="https://tfhub.dev/google/universal-sentence-encoder/2" 
4    ) 
複製程式碼

請注意,該單元正在下載預訓練過的嵌入,因此需要一些時間來執行。

此操作最大的好處在於,我們無需進行任何預處理,即可將文字描述饋送至預訓練過的字詞嵌入。如果從頭開始構建此模型,我們就需要自己將描述轉換為向量,但使用 TF Hub 列,我們可以將描述字串直接傳遞至模型。

將標籤變為 multi-hot 編碼 由於一部電影往往具有多種型別,所以我們的模型會為每部電影返回多個可能的標籤。我們的型別目前是每部電影有一個字串列表(例如 [‘Action’, ‘Adventure’])。由於每個標籤的長度必須相同,所以我們要將這些列表轉換為由 1 和 0(與特定描述中的型別相對應)組成的 multi-hot 向量。動作冒險片的 multi-hot 向量如下所示: 注:多個可能的標籤連結 en.wikipedia.org/wiki/Multi-…

1    # Genre lookup, each genre corresponds to an index    
2    top_genres = ['Comedy', 'Thriller', 'Romance', 'Action', 'Horror', 'Crime', 'Documentary', 'Adventure', 'Science Fiction']
3
4    # Multi-hot label for an action and adventure movie 
5    [0 0 0 1 0 0 0 1 0]
複製程式碼

要用短短几行程式碼將字串標籤轉換為 multi-hot 向量,我們需要使用名為 MultiLabelBinarizer 的 Scikit Learn 實用程式:

1    encoder = MultiLabelBinarizer()    
2    encoder.fit_transform(train_genres)    
3    train_encoded = encoder.transform(train_genres)    
4    test_encoded = encoder.transform(test_genres)    
5    num_classes = len(encoder.classes_) 
複製程式碼

您可以列印 encoder.classes_,檢視模型預測的所有字串類列表。

構建並訓練 DNNEstimator 模型

針對我們的模型,我們將使用 DNNEstimator 構建能夠返回 multi-hot 向量的深度神經網路,這是因為每部電影會具有 0 個或多個可能的標籤(這與每個輸出正好有一個標籤的模型不同)。我們傳遞至 DNNEstimator 的第一個引數稱作 head,且此引數會定義我們的模型預期具有的標籤型別。我們希望我們的模型可以輸出多個標籤,所以我們在這裡使用 multi_label_head:

1    multi_label_head = tf.contrib.estimator.multi_label_head( 
2            num_classes,
3            loss_reduction=tf.losses.Reduction.SUM_OVER_BATCH_SIZE
4    )
複製程式碼

現在,當我們例項化 DNNEstimator 時,便可對其進行傳遞。hidden_units 參數列示我們網路中的層數。此模型有 2 個層,第一層有 64 個神經元,第二層有 10 個。層數和層大小是超引數,所以您應當嘗試不同的值,看看哪個最適合您的資料集。最後,我們將特徵列傳遞至估算器。在本例中,我們只有一個特徵列(即描述),而且在上文中已將其定義為 TF Hub 嵌入列,所以在此我們可以將其作為列表傳遞:

1    estimator = tf.contrib.estimator.DNNEstimator(
2            head=multi_label_head, 
3            hidden_units=[64,10],
4            feature_columns=[description_embeddings]
5    )  
複製程式碼

我們基本準備就緒,很快就可以開始訓練模型了。在訓練估算器例項之前,我們需要定義訓練輸入函式。輸入函式可以將我們的資料與模型聯絡起來。在這裡,我們將使用 numpy_input_fn,並將我們的資料作為 Numpy 陣列饋送至模型:

1    # Format our data for the numpy_input_fn    
2    features = {    
3        "descriptions": np.array(train_descriptions)    
4    }    
5    labels = np.array(train_encoded)    
6    
7    train_input_fn = tf.estimator.inputs.numpy_input_fn(
8            features,    
9            labels,    
10            shuffle=True,    
11            batch_size=32,    
12            num_epochs=20    
13    )
複製程式碼

我們輸入函式中的 batch_size 和 num_epochs 引數都是超函式。batch_size 可告知我們的模型在一次迭代中會有多少示例傳遞至模型,而 num_epochs 是指我們的模型完成整個訓練集的次數。

現在可以開始訓練我們的模型了。只用一行程式碼即可:

1    estimator.train(inpu
```t_fn=train_input_fn)

為了評估模型的準確性,我們用自己的測試資料建立一個 eval 函式 input_function,然後呼叫 estimator.evaluate():


複製程式碼

1 eval_input_fn = tf.estimator.inputs.numpy_input_fn({"descriptions": np.array(test_descriptions).astype(np.str)}, test_encoded.astype(np.int32), shuffle=False)
2
3 estimator.evaluate(input_fn=eval_input_fn)


此模型的 AUC 達到 91.5%,而查準率/查全率為 74%。您的結果可能稍有不同。


## 使用我們已訓練的模型生成預測結果
現在到了最精彩的部分:根據我們的模型從未見過的資料生成預測結果。首先,我們設定一個包含一些描述的陣列(我從 IMDB 中獲取這些描述):


複製程式碼

1 raw_test = [
2 "An examination of our dietary choices and the food we put in our bodies. Based on Jonathan Safran Foer's memoir.", # Documentary
3 "A teenager tries to survive the last week of her disastrous eighth-grade year before leaving to start high school.", # Comedy
4 "Ethan Hunt and his IMF team, along with some familiar allies, race against time after a mission gone wrong." # Action, Adventure
5 ]


然後,我們定義預測輸入函式並呼叫 predict():


複製程式碼

1 predict_input_fn = tf.estimator.inputs.numpy_input_fn({"descriptions": np.array(raw_test).astype(np.str)}, shuffle=False) 2 3 results = estimator.predict(predict_input_fn)

最後,我們可以迭代訪問結果,並顯示為每部電影找到的前 2 個型別及其置信度值:

1 for movie_genres in results:
2 top_2 = movie_genres['probabilities'].argsort()[-2:][::-1]
3 for genre in top_2:
4 text_genre = encoder.classes_[genre]
5 print(text_genre + ': ' + str(round(movie_genres['probabilities'][genre] * 100, 2)) + '%')


我們的模型能夠正確標記上述所有電影描述。


## 使用入門
想用 TF Hub 開始構建自己的模型嗎?請參閱此文件和教程。您可以在 GitHub 或 Colab 上找到本文所述的完整模型程式碼。在之後的博文中,我會介紹如何匯出此模型,以用於 TensorFlow Serving 或 Cloud ML Engine,並構建可根據新描述生成預測結果的應用。
注:文件連結
https://www.tensorflow.org/hub/
教程連結
https://www.tensorflow.org/hub/tutorials/text_classification_with_tf_hub
GitHub 連結
https://github.com/tensorflow/workshops/blob/master/extras/tfhub-text/movie-classification.ipynb
Colab 連結
https://colab.research.google.com/github/tensorflow/workshops/blob/master/extras/tfhub-text/movie-classification.ipynb

如果您有疑問或反饋,請通過 Twitter 聯絡我 (@SRobTweets)。
注:@SRobTweets 連結
https://twitter.com/srobtweets複製程式碼

相關文章