特征组合&特征交叉 (Feature Crosses)

写在前面:之前收藏了一个网友些的谷歌机器学习总结教程(感恩),可是突然断更了,只能自己补完后面的笔记了。

特征组合也叫特征交叉
特征组合也叫特征交叉
特征组合也叫特征交叉(说三遍)

合成特征 (synthetic feature)和特征组合(Feature Crosses)不太一样,特征交叉是特征组合的一个子集。

合成特征 (synthetic feature)

一种特征,不在输入特征之列,而是从一个或多个输入特征衍生而来。通过标准化或缩放单独创建的特征不属于合成特征。合成特征包括以下类型:

  • 将一个特征与其本身或其他特征相乘(称为特征组合)。
  • 两个特征相除。
  • 对连续特征进行分桶,以分为多个区间分箱。

特征组合 (feature cross):对非线性规律进行编码

  • 通过将单独的特征进行组合(相乘或求笛卡尔积)而形成的合成特征。特征组合有助于表示非线性关系。

对于下面的非线性问题。线性学习器画的任何一条线都不能很好地预测树的健康状况。

《特征组合&特征交叉 (Feature Crosses)》

要解决上图所示的非线性问题,可以创建一个特征组合。特征组合是指通过将两个或多个输入特征相乘来对特征空间中的非线性规律进行编码的合成特征。“cross”(组合)这一术语来自 cross product(向量积)。我们通过将 与 组合来创建一个名为x3的特征组合:
x3 = x1x2

我们像处理任何其他特征一样来处理这个新建的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。然后,如果您对这些独热编码进行特征组合,则会得到可解读为逻辑连接的二元特征,如下所示:

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)
    原文作者:kugua233
    原文地址: https://segmentfault.com/a/1190000014799038
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞