特徵組合&特徵交叉 (Feature Crosses)

寫在前面:以前收藏了一個網友些的谷歌機器學習總結教程(感恩),但是忽然斷更了,只能本身補完後面的筆記了。git

特徵組合也叫特徵交叉
特徵組合也叫特徵交叉
特徵組合也叫特徵交叉(說三遍)github

合成特徵 (synthetic feature)和特徵組合(Feature Crosses)不太同樣,特徵交叉是特徵組合的一個子集。算法

合成特徵 (synthetic feature)

一種特徵,不在輸入特徵之列,而是從一個或多個輸入特徵衍生而來。經過標準化或縮放單首創建的特徵不屬於合成特徵。合成特徵包括如下類型:網絡

  • 將一個特徵與其自己或其餘特徵相乘(稱爲特徵組合)。
  • 兩個特徵相除。
  • 對連續特徵進行分桶,以分爲多個區間分箱。

特徵組合 (feature cross):對非線性規律進行編碼

  • 經過將單獨的特徵進行組合(相乘或求笛卡爾積)而造成的合成特徵。特徵組合有助於表示非線性關係。

對於下面的非線性問題。線性學習器畫的任何一條線都不能很好地預測樹的健康情況。機器學習

clipboard.png

要解決上圖所示的非線性問題,能夠建立一個特徵組合。特徵組合是指經過將兩個或多個輸入特徵相乘來對特徵空間中的非線性規律進行編碼的合成特徵。「cross」(組合)這一術語來自 cross product(向量積)。咱們經過將 與 組合來建立一個名爲x3的特徵組合:
x3 = x1x2ide

咱們像處理任何其餘特徵同樣來處理這個新建的x3特徵組合。線性公式變爲:
y = b + w1x1 + w2x2 + w3x3函數

雖然w3表示非線性信息,但您不須要改變線性模型的訓練方式來肯定w3的值。學習

特徵組合的種類

經過採用隨機梯度降低法,能夠有效地訓練線性模型。所以,在使用擴展的線性模型時輔以特徵組合一直都是訓練大規模數據集的有效方法。咱們能夠建立不少不一樣種類的特徵組合。例如:優化

  • [A X B]:將兩個特徵的值相乘造成的特徵組合。
  • [A x B x C x D x E]:將五個特徵的值相乘造成的特徵組合。
  • [A x A]:對單個特徵的值求平方造成的特徵組合。

特徵組合 (Feature Crosses):組合獨熱矢量

在實踐中,機器學習模型不多會組合連續特徵。不過,機器學習模型卻常常組合獨熱特徵矢量,將獨熱特徵矢量的特徵組合視爲邏輯鏈接.例如,假設咱們具備如下兩個特徵:國家/地區和語言。對每一個特徵進行獨熱編碼會生成具備二元特徵的矢量,這些二元特徵可解讀爲 country=USA, country=France 或language=English,language=Spanish。而後,若是您對這些獨熱編碼進行特徵組合,則會獲得可解讀爲邏輯鏈接的二元特徵,以下所示:google

country:usa AND language:spanish

再舉一個例子,假設您對緯度和經度進行分箱,得到單獨的獨熱 5 元素特徵矢量。例如,指定的緯度和經度能夠表示以下:

binned_latitude = [0, 0, 0, 1, 0]
binned_longitude = [0, 1, 0, 0, 0]

假設您對這兩個特徵矢量建立了特徵組合:

binned_latitude X binned_longitude

此特徵組合是一個 25 元素獨熱矢量(24 個 0 和 1 個 1)。該組合中的單個 1 表示緯度與經度的特定鏈接。而後,您的模型就能夠了解到有關這種鏈接的特定關聯性。

假設咱們更粗略地對緯度和經度進行分箱,以下所示:

binned_latitude(lat) = [
  0  < lat <= 10
  10 < lat <= 20
  20 < lat <= 30
]

binned_longitude(lon) = [
  0  < lon <= 15
  15 < lon <= 30
]

針對這些粗略分箱建立特徵組合會生成具備如下含義的合成特徵:

binned_latitude_X_longitude(lat, lon) = [
  0  < lat <= 10 AND 0  < lon <= 15
  0  < lat <= 10 AND 15 < lon <= 30
  10 < lat <= 20 AND 0  < lon <= 15
  10 < lat <= 20 AND 15 < lon <= 30
  20 < lat <= 30 AND 0  < lon <= 15
  20 < lat <= 30 AND 15 < lon <= 30
]

如今,假設咱們的模型須要根據如下兩個特徵來預測狗主人對狗狗的滿意程度:

  • 行爲類型(吠叫、叫、偎依等)
  • 時段

若是咱們根據這兩個特徵構建如下特徵組合:

[behavior type X time of day]

咱們最終得到的預測能力將遠遠超過任一特徵單獨的預測能力。例如,若是狗狗在下午 5 點主人下班回來時(快樂地)叫喊,可能表示對主人滿意度的正面預測結果。若是狗狗在凌晨 3 點主人熟睡時(也許痛苦地)哀叫,可能表示對主人滿意度的強烈負面預測結果。

線性學習器能夠很好地擴展到大量數據。對大規模數據集使用特徵組合是學習高度複雜模型的一種有效策略。神經網絡可提供另外一種策略。
特徵組合 (Feature Crosses):Playground 練習

代碼部分練習 學習目標:

  • 經過添加其餘合成特徵來改進線性迴歸模型(這是前一個練習的延續)
  • 使用輸入函數將 Pandas DataFrame 對象轉換爲 Tensors,並在 fit() 和 predict() 中調用輸入函數
  • 使用 FTRL 優化算法進行模型訓練
  • 經過獨熱編碼、分箱和特徵組合建立新的合成特徵

代碼部分仍是原來的部分,不作任何改變。須要的改變的是將原來的SGD梯度降低訓練學習器換成了TFRL訓練學習器。
FTRL算法融合了RDA算法能產生稀疏模型的特性和SGD算法能產生更有效模型的特性,也就是說能學習出有效的且稀疏的模型。
理解FTRL

my_optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)

換爲

my_optimizer = tf.train.FtrlOptimizer(learning_rate=learning_rate)

使用分桶特徵列訓練模型

分桶(分箱)特徵
分桶也稱爲分箱。

例如,咱們能夠將 population 分爲如下 3 個分桶:

bucket_0 (< 5000):對應於人口分佈較少的街區
bucket_1 (5000 - 25000):對應於人口分佈適中的街區
bucket_2 (> 25000):對應於人口分佈較多的街區
根據前面的分桶定義,如下 population 矢量:

[[10001], [42004], [2500], [18000]]
將變成如下通過分桶的特徵矢量:

[[1], [2], [0], [1]]
這些特徵值如今是分桶索引。請注意,這些索引被視爲離散特徵。一般狀況下,這些特徵將被進一步轉換爲獨熱編碼表示,但這是以透明方式實現的。

爲分桶特徵定義特徵列,咱們可使用 bucketized_column(而不是使用 numeric_column),該列將數字列做爲輸入,並使用 boundardies 參數中指定的分桶邊界將其轉換爲分桶特徵。如下代碼爲 households 和 longitude 定義了分桶特徵列;get_quantile_based_boundaries 函數會根據分位數計算邊界,以便每一個分桶包含相同數量的元素。

def get_quantile_based_boundaries(feature_values, num_buckets):
  boundaries = np.arange(1.0, num_buckets) / num_buckets
  quantiles = feature_values.quantile(boundaries)
  return [quantiles[q] for q in quantiles.keys()]

# Divide households into 7 buckets.
households = tf.feature_column.numeric_column("households")
bucketized_households = tf.feature_column.bucketized_column(
  households, boundaries=get_quantile_based_boundaries(
    california_housing_dataframe["households"], 7))

# Divide longitude into 10 buckets.
longitude = tf.feature_column.numeric_column("longitude")
bucketized_longitude = tf.feature_column.bucketized_column(
  longitude, boundaries=get_quantile_based_boundaries(
    california_housing_dataframe["longitude"], 10))

在前面的代碼塊中,兩個實值列(即 households 和 longitude)已被轉換爲分桶特徵列。剩下的任務是對其他的列進行分桶,而後運行代碼來訓練模型。您能夠採用各類啓發法來肯定分桶的範圍。本練習使用了分位數技巧,經過這種方式選擇分桶邊界後,每一個分桶將包含相同數量的樣本。

def construct_feature_columns():
  """Construct the TensorFlow Feature Columns.

  Returns:
    A set of feature columns
  """ 
  households = tf.feature_column.numeric_column("households")
  longitude = tf.feature_column.numeric_column("longitude")
  latitude = tf.feature_column.numeric_column("latitude")
  housing_median_age = tf.feature_column.numeric_column("housing_median_age")
  median_income = tf.feature_column.numeric_column("median_income")
  rooms_per_person = tf.feature_column.numeric_column("rooms_per_person")
  
  # Divide households into 7 buckets.
  bucketized_households = tf.feature_column.bucketized_column(
    households, boundaries=get_quantile_based_boundaries(
      training_examples["households"], 7))

  # Divide longitude into 10 buckets.
  bucketized_longitude = tf.feature_column.bucketized_column(
    longitude, boundaries=get_quantile_based_boundaries(
      training_examples["longitude"], 10))
  
  # Divide latitude into 10 buckets.
  bucketized_latitude = tf.feature_column.bucketized_column(
    latitude, boundaries=get_quantile_based_boundaries(
      training_examples["latitude"], 10))

  # Divide housing_median_age into 7 buckets.
  bucketized_housing_median_age = tf.feature_column.bucketized_column(
    housing_median_age, boundaries=get_quantile_based_boundaries(
      training_examples["housing_median_age"], 7))
  
  # Divide median_income into 7 buckets.
  bucketized_median_income = tf.feature_column.bucketized_column(
    median_income, boundaries=get_quantile_based_boundaries(
      training_examples["median_income"], 7))
  
  # Divide rooms_per_person into 7 buckets.
  bucketized_rooms_per_person = tf.feature_column.bucketized_column(
    rooms_per_person, boundaries=get_quantile_based_boundaries(
      training_examples["rooms_per_person"], 7))
  
  feature_columns = set([
    bucketized_longitude,
    bucketized_latitude,
    bucketized_housing_median_age,
    bucketized_households,
    bucketized_median_income,
    bucketized_rooms_per_person])
  
  return feature_columns

分桶後運行結果:

_ = train_model(
    learning_rate=1.0,
    steps=500,
    batch_size=100,
    feature_columns=construct_feature_columns(),
    training_examples=training_examples,
    training_targets=training_targets,
    validation_examples=validation_examples,
    validation_targets=validation_targets)

特徵組合

組合兩個(或更多個)特徵是使用線性模型來學習非線性關係的一種聰明作法。在咱們的問題中,若是咱們只使用 latitude 特徵進行學習,那麼該模型可能會發現特定緯度(或特定緯度範圍內,由於咱們已經將其分桶)的城市街區更可能比其餘街區住房成本高昂。longitude 特徵的狀況與此相似。可是,若是咱們將 longitude 與 latitude 組合,產生的組合特徵則表明一個明確的城市街區。若是模型發現某些城市街區(位於特定緯度和經度範圍內)更可能比其餘街區住房成本高昂,那麼這將是比單獨考慮兩個特徵更強烈的信號。

目前,特徵列 API 僅支持組合離散特徵。要組合兩個連續的值(好比 latitude 或 longitude),咱們能夠對其進行分桶。

若是咱們組合 latitude 和 longitude 特徵(例如,假設 longitude 被分到 2 個分桶中,而 latitude 有 3 個分桶),咱們實際上會獲得 6 個組合的二元特徵。當咱們訓練模型時,每一個特徵都會分別得到本身的權重。

使用特徵組合訓練模型

在模型中添加 longitude 與 latitude 的特徵組合,訓練模型,而後肯定結果是否有所改善。
可參閱有關 crossed_column() 的 TensorFlow API 文檔,瞭解如何爲您的組合構建特徵列。hash_bucket_size 能夠設爲 1000。

def construct_feature_columns():
  """Construct the TensorFlow Feature Columns.

  Returns:
    A set of feature columns
  """ 
  households = tf.feature_column.numeric_column("households")
  longitude = tf.feature_column.numeric_column("longitude")
  latitude = tf.feature_column.numeric_column("latitude")
  housing_median_age = tf.feature_column.numeric_column("housing_median_age")
  median_income = tf.feature_column.numeric_column("median_income")
  rooms_per_person = tf.feature_column.numeric_column("rooms_per_person")
  
  # Divide households into 7 buckets.
  bucketized_households = tf.feature_column.bucketized_column(
    households, boundaries=get_quantile_based_boundaries(
      training_examples["households"], 7))

  # Divide longitude into 10 buckets.
  bucketized_longitude = tf.feature_column.bucketized_column(
    longitude, boundaries=get_quantile_based_boundaries(
      training_examples["longitude"], 10))
  
  # Divide latitude into 10 buckets.
  bucketized_latitude = tf.feature_column.bucketized_column(
    latitude, boundaries=get_quantile_based_boundaries(
      training_examples["latitude"], 10))

  # Divide housing_median_age into 7 buckets.
  bucketized_housing_median_age = tf.feature_column.bucketized_column(
    housing_median_age, boundaries=get_quantile_based_boundaries(
      training_examples["housing_median_age"], 7))
  
  # Divide median_income into 7 buckets.
  bucketized_median_income = tf.feature_column.bucketized_column(
    median_income, boundaries=get_quantile_based_boundaries(
      training_examples["median_income"], 7))
  
  # Divide rooms_per_person into 7 buckets.
  bucketized_rooms_per_person = tf.feature_column.bucketized_column(
    rooms_per_person, boundaries=get_quantile_based_boundaries(
      training_examples["rooms_per_person"], 7))
  
  # YOUR CODE HERE: Make a feature column for the long_x_lat feature cross
  long_x_lat = tf.feature_column.crossed_column(
  set([bucketized_longitude, bucketized_latitude]), hash_bucket_size=1000) 
  
  feature_columns = set([
    bucketized_longitude,
    bucketized_latitude,
    bucketized_housing_median_age,
    bucketized_households,
    bucketized_median_income,
    bucketized_rooms_per_person,
    long_x_lat])
  
  return feature_columns

程序運行:

_ = train_model(
    learning_rate=1.0,
    steps=500,
    batch_size=100,
    feature_columns=construct_feature_columns(),
    training_examples=training_examples,
    training_targets=training_targets,
    validation_examples=validation_examples,
    validation_targets=validation_targets)
相關文章
相關標籤/搜索