[教程10]TensorFlow線性模型教程
[教程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)
接下來,我們來看看數據框架,看看我們可以使用哪些列來預測目標標簽。列可以分為兩種類型 - 分類和連續列:
- 如果一個列的值只能是有限集合中的一個類別,那麽該列稱為分類。例如,一個人的本國(美國,印度,日本等)或教育程度(高中,大學等)是分類欄目。
- 如果其值可以是連續範圍內的任何數值,則該
以下是“普查收入”數據集中列的列表:
列名稱 | 類型 | 描述 |
---|---|---|
年齡 | 連續 | 個人的年齡 |
workclass | 明確的 | 個人擁有的雇主類型(政府,軍事,私人等)。 |
fnlwgt | 連續 | 人口普查員認為觀察值(樣本重量)的人數。該變量不會被使用。 |
教育 | 明確的 | 為個人所取得的最高水平的教育。 |
education_num | 連續 | 數字形式最高的教育水平。 |
婚姻狀況 | 明確的 | 個人的婚姻狀況 |
占用 | 明確的 | 個人的職業。 |
關系 | 明確的 | 妻子,自己的孩子,丈夫,不在家,其他相對,未婚。 |
種族 | 明確的 | 白人,亞太島民族,美印愛斯基摩人,其他黑人。 |
性別 | 明確的 | 女人男人。 |
資本收益 | 連續 | 資本收益記錄。 |
capital_loss | 連續 | 資本損失記錄。 |
hours_per_week | 連續 | 每周工作時數。 |
祖國 | 明確的 | 個人原籍國。 |
收入 | 明確的 | “> 50K”或“<= 50K”,這意味著該人是否每年賺取超過$ 50,000。 |
將數據轉換成傳感器
構建tf.estimator模型時,通過Input Builder函數指定輸入數據。此構建器函數將不會被調用,直到它後來傳遞給tf.estimator.Estimator方法,如train
和evaluate
。此函數的目的是構造以tf.Tensor
s或tf.SparseTensor
s 形式表示的輸入數據。更詳細地說,Input Builder函數返回以下對象
feature_cols
:從特征列名稱到Tensors
或SparseTensors
。label
:ATensor
包含標簽列。
feature_cols
將在下一節中使用鍵的構造列。因為我們想用不同的數據調用train
和evaluate
方法,我們定義一個方法,返回基於給定數據的輸入函數。請註意,在構建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
作為實值特征列可能不是一個好的選擇,因為模型只能學習三種情況之一:
- 隨著年齡的增長,收入總是以一定的速度增長(正相關),
- 收入總是隨著年齡的增長而下降(負相關),或
- 無論什麽年齡(無相關性),收入保持不變
如果我們想要分別了解收入和每個年齡組之間的細粒度關聯,我們可以利用桶化。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線性模型教程