1. 程式人生 > >[教程10]TensorFlow線性模型教程

[教程10]TensorFlow線性模型教程

了解 air into tip 必須 輸出 ros 自己的 rmi

[教程10]TensorFlow線性模型教程

在本教程中,我們將使用TensorFlow中的tf.estimator API來解決二進制分類問題:根據年齡,性別,教育和職業(特征)等個人的普查數據,我們將嘗試預測人每年賺取5萬多美元(目標標簽)。我們將訓練邏輯回歸模型,並給出個人信息,我們的模型將輸出0到1之間的數字,這可以解釋為個人年收入超過5萬美元的可能性。

閱讀人口普查數據

我們將使用的數據集是 普查收入數據集您可以 手動下載 培訓數據測試數據,或使用如下代碼:

import tempfile
import urllib
train_file = tempfile.NamedTemporaryFile()
test_file 
= tempfile.NamedTemporaryFile() urllib.urlretrieve("https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data", train_file.name) urllib.urlretrieve("https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test", test_file.name)

CSV文件下載後,讓我們將它們讀入 熊貓數據幀。

由於任務是二進制分類問題,我們將構建一個名為“label”的標簽列,如果收入超過50K,則其值為1,否則為0。

train_labels = (df_train["income_bracket"].apply(lambda x: ">50K" in x)).astype(int)
test_labels = (df_test["income_bracket"].apply(lambda x: ">50K" in x)).astype(int)

接下來,我們來看看數據框架,看看我們可以使用哪些列來預測目標標簽。列可以分為兩種類型 - 分類和連續列:

  • 如果一個列的值只能是有限集合中的一個類別,那麽該列稱為分類例如,一個人的本國(美國,印度,日本等)或教育程度(高中,大學等)是分類欄目。
  • 如果其值可以是連續範圍內的任何數值,則該
    列稱為
    連續例如,一個人的資本收益(例如$ 14,084)是一個連續的列。

以下是“普查收入”數據集中列的列表:

列名稱類型描述
年齡 連續 個人的年齡
workclass 明確的 個人擁有的雇主類型(政府,軍事,私人等)。
fnlwgt 連續 人口普查員認為觀察值(樣本重量)的人數。該變量不會被使用。
教育 明確的 為個人所取得的最高水平的教育。
education_num 連續 數字形式最高的教育水平。
婚姻狀況 明確的 個人的婚姻狀況
占用 明確的 個人的職業。
關系 明確的 妻子,自己的孩子,丈夫,不在家,其他相對,未婚。
種族 明確的 白人,亞太島民族,美印愛斯基摩人,其他黑人。
性別 明確的 女人男人。
資本收益 連續 資本收益記錄。
capital_loss 連續 資本損失記錄。
hours_per_week 連續 每周工作時數。
祖國 明確的 個人原籍國。
收入 明確的 “> 50K”或“<= 50K”,這意味著該人是否每年賺取超過$ 50,000。

將數據轉換成傳感器

構建tf.estimator模型時,通過Input Builder函數指定輸入數據。此構建器函數將不會被調用,直到它後來傳遞給tf.estimator.Estimator方法,如trainevaluate此函數的目的是構造以tf.Tensors或tf.SparseTensors 形式表示的輸入數據更詳細地說,Input Builder函數返回以下對象

  1. feature_cols:從特征列名稱到TensorsSparseTensors
  2. label:A Tensor包含標簽列。

feature_cols將在下一節中使用鍵的構造列。因為我們想用不同的數據調用trainevaluate方法,我們定義一個方法,返回基於給定數據的輸入函數。請註意,在構建TensorFlow圖時,將不會在運行圖時調用返回的輸入函數。返回的是將輸入數據表示為TensorFlow計算的基本單位a Tensor(或SparseTensor)。

我們使用該tf.estimator.inputs.pandas_input_fn方法從熊貓數據幀創建輸入函數。火車或測試數據幀中的每個連續列將被轉換成一個Tensor,通常是表示密集數據的良好格式。對於分類數據,我們必須將數據表示為a SparseTensor該數據格式適用於表示稀疏數據。代表輸入數據的另一種更先進的方法是構造一個 表示文件或其他數據源輸入和讀取器,並以TensorFlow運行圖形方式遍歷文件。

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)
  # remove NaN elements
  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)

模型的選擇和工程特征

選擇和制作右側的特征列是學習有效模型的關鍵。特征柱可以是在原來的數據幀(讓我們稱它們為原料的列中的任一個基本特征的列)的基礎上在一個或多個堿基列(我們稱它們限定一些轉化或創建任何新列得出的特征列)。基本上,“特征列”是可用於預測目標標簽的任何原始或派生變量的抽象概念。

基本分類特征列

要定義分類功能的功能列,我們可以CategoricalColumn使用tf.feature_column API 創建一個 如果您知道列的所有可能的特征值的集合,並且只有其中的幾個可以使用categorical_column_with_vocabulary_list列表中的每個鍵將從0開始分配一個自動增量ID。例如,對於gender 列,我們可以通過執行以下操作將特征字符串“Female”分配給整數ID為0,將“Male”分配給1。

gender = tf.feature_column.categorical_column_with_vocabulary_list(
    "gender", ["Female", "Male"])

如果我們不提前知道一組可能的價值呢?不是問題 我們可以用

categorical_column_with_hash_bucket
occupation = tf.feature_column.categorical_column_with_hash_bucket(
    "occupation", hash_bucket_size=1000)

將會發生的是,occupation 在訓練中遇到這些特征列,每個可能的值將被哈希到一個整數ID。見下面的示例圖:

ID特征
...
9 "Machine-op-inspct"
...
103 "Farming-fishing"
...
375 "Protective-serv"
...

無論我們選擇哪種方式定義一個SparseColumn,每個特征字符串將通過查找固定映射或散列來映射到整數ID。請註意,散列碰撞是可能的,但可能不會顯著影響模型質量。在引擎蓋下,LinearModel該類負責管理映射和創建tf.Variable以存儲每個功能ID的模型參數(也稱為模型權重)。模型參數將通過後面的模型訓練過程學習。

我們將采取類似的技巧來定義其他分類功能:

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"
    ])
native_country = tf.feature_column.categorical_column_with_hash_bucket(
    "native_country", hash_bucket_size=1000)

基本連續特征列

類似地,我們可以NumericColumn為模型中要使用的每個連續特征列定義一個

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")

通過Bucketization進行連續分類

有時,連續特征與標簽之間的關系不是線性的。作為一個假設的例子,一個人的收入可能隨著職業生涯的早期階段的年齡而增長,那麽增長可能會在某個時候慢一些,最後退休後的收入就會下降。在這種情況下,將raw age作為實值特征列可能不是一個好的選擇,因為模型只能學習三種情況之一:

  1. 隨著年齡的增長,收入總是以一定的速度增長(正相關),
  2. 收入總是隨著年齡的增長而下降(負相關),或
  3. 無論什麽年齡(無相關性),收入保持不變

如果我們想要分別了解收入和每個年齡組之間的細粒度關聯,我們可以利用桶化Bucketization是將連續特征的整個範圍劃分成一組連續的倉/桶的過程,然後根據該值所在的桶將原始數值特征轉換為桶ID(作為分類特征)。因此,我們可以定義一個bucketized_column以上age為:

age_buckets = tf.feature_column.bucketized_column(
    age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])

哪裏boundaries是桶邊界的列表。在這種情況下,有10個邊界,導致11個年齡組的桶(從17歲及以下,18-24,25-29,...,到65歲以上)。

用交叉列相交多個列

單獨使用每個基本特征列可能不足以解釋數據。例如,不同職業的教育與標簽(賺取> 50,000美元)的相關性可能會有所不同。因此,如果我們只學了一個模型的權重education="Bachelors"education="Masters",我們就無法捕捉到每一個教育,職業組合(例如區分education="Bachelors" AND occupation="Exec-managerial"education="Bachelors" AND occupation="Craft-repair")。要了解不同功能組合之間的差異,我們可以向模型添加交叉的特征列

education_x_occupation = tf.feature_column.crossed_column(
    ["education", "occupation"], hash_bucket_size=1000)

我們也可以創建CrossedColumn超過兩列。每個構成列可以是基本特征列,分類(SparseColumn),分段實值特征列(BucketizedColumn)或甚至另一個CrossColumn這裏有一個例子:

age_buckets_x_education_x_occupation = tf.feature_column.crossed_column(
    [age_buckets, "education", "occupation"], hash_bucket_size=1000)

定義邏輯回歸模型

在處理輸入數據並定義所有特征列之後,我們現在可以將它們全部放在一起,構建一個Logistic回歸模型。在上一節中,我們看到了幾種類型的基礎和派生的功能列,其中包括:

  • CategoricalColumn
  • NumericColumn
  • BucketizedColumn
  • CrossedColumn

所有這些都是抽象FeatureColumn類的類,並且可以添加到feature_columns模型字段中:

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)
]

model_dir = tempfile.mkdtemp()
m = tf.estimator.LinearClassifier(
    model_dir=model_dir, feature_columns=base_columns + crossed_columns)

該模型還自動學習一個偏離項,它控制預測,而不需要觀察任何特征(參見“邏輯回歸如何運作”以獲得更多的解釋)。學習的模型文件將被存儲model_dir

培訓和評估我們的模型

在將所有功能添加到模型之後,現在來看看如何實際訓練模型。訓練一個模型只是一個使用tf.estimator API的單行:

# set num_epochs to None to get infinite stream of data.
m.train(
    input_fn=input_fn(train_file_name, num_epochs=None, shuffle=True),
    steps=train_steps)

在模型訓練後,我們可以評估我們的模型在預測保持數據的標簽方面有多好:

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.83557522,這意味著精度是83.6%。隨意嘗試更多的功能和變革,看看你能做得更好!

如果您希望看到一個有效的端對端示例,您可以下載我們的 示例代碼並設置model_type標誌wide

添加正則化以防止過度配合

正則化是一種用於避免過度擬合的技術當您的模型對其進行培訓的數據執行情況良好時,會發生過度擬合,但對於模型以前未見過的測試數據(如實時流量)更糟。通常,當模型過於復雜時,通常發生過擬合,例如相對於觀察到的訓練數據的數量具有太多的參數。正則化允許您控制您的模型的復雜性,並使模型更可概括為看不見的數據。

在線性模型庫中,您可以將L1和L2正則化添加到模型中:

m = tf.estimator.LinearClassifier(
    model_dir=model_dir, feature_columns=base_columns + crossed_columns,
    optimizer=tf.train.FtrlOptimizer(
      learning_rate=0.1,
      l1_regularization_strength=1.0,
      l2_regularization_strength=1.0),
    model_dir=model_dir)

L1和L2正則化之間的一個重要區別在於L1正則化趨向於使模型權重保持為零,創建較為稀疏的模型,而L2正則化也試圖使模型權重接近零但不一定為零。因此,如果增加L1正則化的強度,則您將具有較小的模型大小,因為許多模型權重將為零。當特征空間非常大但稀疏時,並且當存在阻止您提供太大模型的資源約束時,這通常是希望的。

在實踐中,您應該嘗試L1,L2正則化強度的各種組合,並找到最佳控制過擬合的最佳參數,並為您提供所需的模型大小。

技術分享

[教程10]TensorFlow線性模型教程