TensorFlow廣度和深度學習的教程
在這篇文章中,我們將會介紹如何使用 TF.Learn API 同時訓練一個廣度線性模型和一個深度前饋神經網絡。這種方法結合了記憶和泛化的優(yōu)勢。它在一般的大規(guī)?;貧w和具有稀疏輸入特性的分類問題(例如,分類特征存在一個很大的可能值域)上很有效。如果你有興趣學習更多關于廣度和深度學習如何工作的問題,請參考 研究論文 Wide & Deep Spectrum of Models現在,我們來看一個簡單的例子。
上圖展示了廣度模型(具有稀疏特征和轉換性質的 logistic 回歸模型),深度模型(具有一個嵌入層和多個隱藏層的前饋神經網絡),廣度和深度模型(兩者的聯合訓練)的區(qū)別比較。在高層級里,只需要通過以下三個步驟就能使用 TF.Learn API 配置廣度,深度或廣度和深度模型。
-
選擇廣度部分的特征:選擇要使用的稀疏基本列和交叉列。
-
選擇深度部分的特征:選擇連續(xù)列,每個分類列的嵌入維度和隱藏層大小。
-
將它們一起放入廣度和深度模型(DNNLinearCombinedClassifier)。
安裝
如果想要嘗試本教程中的代碼:
1.安裝 TensorFlow ,請前往此處。
2.下載 教程代碼。
3.安裝 pandas 數據分析庫。因為本教程中需要使用 pandas 數據。雖然 tf.learn 不要求 pandas,但是它支持 pandas。安裝 pandas:
a. 獲取 pip:
- # Ubuntu/Linux 64-bit
- $ sudo apt-get install python-pip python-dev
- # Mac OS X
- $ sudo easy_install pip
- $ sudo easy_install --upgrade six
b. 使用 pip 安裝 pandas
- $ sudo pip install pandas
如果你在安裝過程中遇到問題,請前往 pandas 網站上的 說明 。
4.執(zhí)行以下命令來訓練教程中描述的線性模型:
- $ python wide_n_deep_tutorial.py --model_type=wide_n_deep
請繼續(xù)閱讀,了解此代碼如何構建其線性模型。
定義基本特征列
首先,定義我們使用的基本分類和連續(xù)特征的列。這些列將被作為模型的廣度部分和深度部分的構件塊。
- import tensorflow as tf
- gender = tf.feature_column.categorical_column_with_vocabulary_list(
- "gender", ["Female", "Male"])
- education = tf.feature_column.categorical_column_with_vocabulary_list(
- "education", [
- "Bachelors", "HS-grad", "11th", "Masters", "9th",
- "Some-college", "Assoc-acdm", "Assoc-voc", "7th-8th",
- "Doctorate", "Prof-school", "5th-6th", "10th", "1st-4th",
- "Preschool", "12th"
- ])
- marital_status = tf.feature_column.categorical_column_with_vocabulary_list(
- "marital_status", [
- "Married-civ-spouse", "Divorced", "Married-spouse-absent",
- "Never-married", "Separated", "Married-AF-spouse", "Widowed"
- ])
- relationship = tf.feature_column.categorical_column_with_vocabulary_list(
- "relationship", [
- "Husband", "Not-in-family", "Wife", "Own-child", "Unmarried",
- "Other-relative"
- ])
- workclass = tf.feature_column.categorical_column_with_vocabulary_list(
- "workclass", [
- "Self-emp-not-inc", "Private", "State-gov", "Federal-gov",
- "Local-gov", "?", "Self-emp-inc", "Without-pay", "Never-worked"
- ])
- # 展示一個哈希的例子:
- occupation = tf.feature_column.categorical_column_with_hash_bucket(
- "occupation", hash_bucket_size=1000)
- native_country = tf.feature_column.categorical_column_with_hash_bucket(
- "native_country", hash_bucket_size=1000)
- # 連續(xù)基列
- age = tf.feature_column.numeric_column("age")
- education_num = tf.feature_column.numeric_column("education_num")
- capital_gain = tf.feature_column.numeric_column("capital_gain")
- capital_loss = tf.feature_column.numeric_column("capital_loss")
- hours_per_week = tf.feature_column.numeric_column("hours_per_week")
- # 轉換
- age_buckets = tf.feature_column.bucketized_column(
- age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])
廣度模型:具有交叉特征列的線性模型
廣度模型是一個具有稀疏和交叉特征列的線性模型:
- base_columns = [
- gender, native_country, education, occupation, workclass, relationship,
- age_buckets,
- ]
- crossed_columns = [
- tf.feature_column.crossed_column(
- ["education", "occupation"], hash_bucket_size=1000),
- tf.feature_column.crossed_column(
- [age_buckets, "education", "occupation"], hash_bucket_size=1000),
- tf.feature_column.crossed_column(
- ["native_country", "occupation"], hash_bucket_size=1000)
- ]
具有交叉特征列的廣度模型可以有效地記憶特征之間的稀疏交互。也就是說,交叉特征列不能概括沒有在訓練數據中出現的特征組合。讓我們采用嵌入方式來添加一個深度模型來修復這個問題。
深度模型:嵌入式神經網絡
深度模型是一個前饋神經網絡,如前圖所示。每一個稀疏,高維度分類特征首先都會被轉換成一個低維度密集的實值矢量,通常被稱為嵌入式矢量。這些低維度密集的嵌入式矢量與連續(xù)特征相連,然后在正向傳遞中饋入神經網絡的隱藏層。嵌入值隨機初始化,并與其他模型參數一起訓練,以***化減少訓練損失。如果你有興趣了解更多關于嵌入的知識,請在查閱教程 Vector Representations of Words 或在 Wikipedia 上查閱 Word Embedding。
我們將使用 embedding_column 配置分類嵌入列,并將它們與連續(xù)列連接:
- deep_columns = [
- tf.feature_column.indicator_column(workclass),
- tf.feature_column.indicator_column(education),
- tf.feature_column.indicator_column(gender),
- tf.feature_column.indicator_column(relationship),
- # 展示一個嵌入例子
- tf.feature_column.embedding_column(native_country, dimension=8),
- tf.feature_column.embedding_column(occupation, dimension=8),
- age,
- education_num,
- capital_gain,
- capital_loss,
- hours_per_week,
- ]
嵌入的 dimension 越高,自由度就越高,模型將不得不學習這些特性的表示。為了簡單起見,我們設置所有特征列的維度為 8。從經驗上看,關于維度的設定***是從 \log_{2}(n) 或 k\sqrt[4]{n} 值開始,這里的 n 代表特征列中唯一特征的數量,k 是一個很小的常量(通常小于10)。
通過密集嵌入,深度模型可以更好的概括,并更好對之前沒有在訓練數據中遇見的特征進行預測。然而,當兩個特征列之間的底層交互矩陣是稀疏和高等級時,很難學習特征列的有效低維度表示。在這種情況下,大多數特征對之間的交互應該為零,除了少數幾個,但密集的嵌入將導致所有特征對的非零預測,從而可能過度泛化。另一方面,具有交叉特征的線性模型可以用更少的模型參數有效地記住這些“異常規(guī)則”。
現在,我們來看看如何聯合訓練廣度和深度模型,讓它們優(yōu)勢和劣勢互補。
將廣度和深度模型結合為一體
通過將其最終輸出的對數幾率作為預測結合起來,然后將預測提供給 logistic 損失函數,將廣度模型和深度模型相結合。所有的圖形定義和變量分配都已經被處理,所以你只需要創(chuàng)建一個 DNNLinearCombinedClassifier:
- import tempfile
- model_dir = tempfile.mkdtemp()
- m = tf.contrib.learn.DNNLinearCombinedClassifier(
- model_dir=model_dir,
- linear_feature_columns=wide_columns,
- dnn_feature_columns=deep_columns,
- dnn_hidden_units=[100, 50])
訓練和評估模型
在訓練模型之前,請先閱讀人口普查數據集,就像在 《TensorFlow Liner Model Tutorial》 中所做的一樣。 輸入數據處理的代碼再次為你提供方便:
- import pandas as pd
- import urllib
- # 為數據集定義列名
- CSV_COLUMNS = [
- "age", "workclass", "fnlwgt", "education", "education_num",
- "marital_status", "occupation", "relationship", "race", "gender",
- "capital_gain", "capital_loss", "hours_per_week", "native_country",
- "income_bracket"
- ]
- def maybe_download(train_data, test_data):
- """Maybe downloads training data and returns train and test file names."""
- if train_data:
- train_file_name = train_data
- else:
- train_file = tempfile.NamedTemporaryFile(delete=False)
- urllib.request.urlretrieve(
- "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data",
- train_file.name) # pylint: disable=line-too-long
- train_file_name = train_file.name
- train_file.close()
- print("Training data is downloaded to %s" % train_file_name)
- if test_data:
- test_file_name = test_data
- else:
- test_file = tempfile.NamedTemporaryFile(delete=False)
- urllib.request.urlretrieve(
- "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test",
- test_file.name) # pylint: disable=line-too-long
- test_file_name = test_file.name
- test_file.close()
- print("Test data is downloaded to %s"% test_file_name)
- return train_file_name, test_file_name
- def input_fn(data_file, num_epochs, shuffle):
- """Input builder function."""
- df_data = pd.read_csv(
- tf.gfile.Open(data_file),
- names=CSV_COLUMNS,
- skipinitialspace=True,
- engine="python",
- skiprows=1)
- # 移除 NaN 元素
- df_data = df_data.dropna(how="any", axis=0)
- labels = df_data["income_bracket"].apply(lambda x: ">50K" in x).astype(int)
- return tf.estimator.inputs.pandas_input_fn(
- x=df_data,
- y=labels,
- batch_size=100,
- num_epochs=num_epochs,
- shuffle=shuffle,
- num_threads=5)
閱讀數據之后,你可以訓練并評估模型:
- # 將 num_epochs 設置為 None,以獲得***的數據流
- m.train(
- input_fn=input_fn(train_file_name, num_epochs=None, shuffle=True),
- steps=train_steps)
- # 在所有數據被消耗之前,為了運行評估,設置 steps 為 None
- results = m.evaluate(
- input_fn=input_fn(test_file_name, num_epochs=1, shuffle=False),
- steps=None)
- print("model directory = %s" % model_dir)
- for key in sorted(results):
- print("%s: %s" % (key, results[key]))
輸出的***行應該類似 accuracy: 0.84429705。我們可以看到使用廣度和深度模型將廣度線性模型精度約 83.6% 提高到了約 84.4%。如果你想看端對端的工作示例,你可以下載我們的 示例代碼。
請注意,本教程只是一個小型數據基的簡單示例,為了讓你快速熟悉 API。如果你有大量具有稀疏特征列和大量可能特征值的數據集,廣度和深度學習將會更加強大。此外,請隨時關注我們的研究論文,以了解更多關于在實際中廣度和深度學習在大型機器學習方面如何應用的思考。