TensorFlow学习(3)特征列的学习

因为比较菜,所以记录在保证没有歧义和准确的前提下尽量的口语化。
参考文章:https://www.tensorflow.org/get_started/premade_estimators

什么叫特征列

我们可以肤浅的将机器学习模型理解成一个算法函数,算法的本质中有几个特性:其中一个是要求必须要有输入参数(装逼的说法叫刺激),然后需要有一个输出。这里输出的参数就是特征,输出就是预测结果。重点来了,就是参数是要有约定的,主要是指数据类型的约定,因为模型不可能无限度的接收任何类型的数据,毕竟现在的计算机技术还是只能处理有限集合的问题,为了约束类型保证模型的处理能力和效率,因为在模型算法的内部多是特征进行乘法和加法的运算,所以TF选择了对数值类型的支持,这里包括整数和浮点数。但是事实上对于现实中的问题,我们采集到的数据绝对不可能是完全的数值类型,比如一个学生的特征包括:年龄、性别、身高、体重等,这里的性别是一个非数值类型的特征,这里我们可以转化成数值类型:

男性 - [1, 0]
女性 - [0, 1]

通过类似这样的手段,我们可以转化、提炼、标准化我们的特征集合,从而更好的进行算法计算。特征列是TF的一个概念,用来将采集到的数据进行规范约束,从而可以进行TF所支持的类型的特征被模型所运算。

TF提供的特征列类型

TF的特征列构造由tensorflow.feature_column模块来提供,生成的数据将会作为模型的feature_columns参数被传递给模型。下面将会介绍9个feature_column模块提供的特征列类型(图片来自TF官方文档):
这里写图片描述
在调用feature_column模块提供的格式化函数时会返回一个 Categorical-Column 或 Dense-Column 类型的对象,从字面上理解一种是“明确类型”一种被叫做“稠密类型”或者叫“混合类型”,下面分介绍九种类型的每一种的特点。

数值列(tf.feature_column.numeric_column)

通俗点讲就是全部都是由数值构成的特征集合适合用这种方式来表达,在前篇文章中鸢尾花分类模型中的花萼、花蕊的长和宽都是可以直接用数值来表示的。虽然 tf.numeric_column 提供可选参数,但在没有任何参数的情况下调用 tf.numeric_column(如下所示)也是一种将具有默认数据类型 (tf.float32) 的数值指定为模型输入的不错方式:

# Defaults to a tf.float32 scalar.
numeric_feature_column = tf.feature_column.numeric_column(key="SepalLength")

要指定一个非默认的数值数据类型,请使用 dtype 参数。例如:

# Represent a tf.float64 scalar.
numeric_feature_column = tf.feature_column.numeric_column(key="SepalLength",
                                                          dtype=tf.float64)

默认情况下,数值列会创建单个值(标量)。使用 shape 参数指定另一种形状。例如:

# Represent a 10-element vector in which each cell contains a tf.float32.
vector_feature_column = tf.feature_column.numeric_column(key="Bowling",
                                                         shape=10)

# Represent a 10x5 matrix in which each cell contains a tf.float32.
matrix_feature_column = tf.feature_column.numeric_column(key="MyMatrix",
                                                         shape=[10,5])

总结:这是一种最简单的特征列类型,没啥好总结的,基本操作。。。

分桶列(tf.feature_column.bucketized_column)

通常,您不会直接向模型馈送数字,相反,您会根据数值范围将其值分为不同的类别。为此,请创建一个分桶列。以表示房屋建造年份的原始数据为例。我们并非以标量数值列表示年份,而是将年份分成下列四个分桶:
这里写图片描述
模型将按以下方式表示这些分桶:

< 1960                      [1, 0, 0, 0]
>= 1960 and < 1980          [0, 1, 0, 0]
>= 1980 and < 2000          [0, 0, 1, 0]
> 2000                      [0, 0, 0, 1]

为什么要将数字(一个完全有效的模型输入)拆分为分类值?请注意,该分类将单个输入数字分成了一个四元素矢量。因此,模型现在可以学习四个单独的权重,而非仅仅一个;相比一个权重,四个权重能够创建一个内容更丰富的模型。更重要的是,借助分桶,模型能够清楚地区分不同年份类别,因为仅设置了一个元素 (1),其他三个元素则被清除 (0)。当我们仅将单个数字(年份)用作输入时,模型只能学习线性关系。因此,分桶为模型提供了可用于学习的额外灵活性。
创建桶分特征列的方法:

# First, convert the raw input to a numeric column.
numeric_feature_column = tf.feature_column.numeric_column("Year")

# Then, bucketize the numeric column on the years 1960, 1980, and 2000.
bucketized_feature_column = tf.feature_column.bucketized_column(
    source_column = numeric_feature_column,
    boundaries = [1960, 1980, 2000])

请注意,指定一个三元素边界矢量可创建一个四元素分桶矢量。
总结:从上面的描述中可以看出为啥要将已经是数值类型的特征数据集转换成区间类型,原因有俩:

  • 一个样本的特征集合在这个特征上可以让模型接收到4个数值,虽然有效位只有一个,但是这可以丰富模型,额,其实说白了,还是让模型接收的特征更多。
  • 构造特征的非线性关系,可能非线性关系会提高模型的复杂程度从而提高精度?
  • 桶分类型是一种独热矢量特征

分类标识列(tf.feature_column.categorical_column_with_identity)

可将分类标识列视为分桶列的一种特殊情况。在传统的分桶列中,每个分桶表示一系列值(例如,从 1960 年到 1979 年)。在分类标识列中,每个分桶表示一个唯一整数。例如,假设您想要表示整数范围 [0, 4)。也就是说,您想要表示整数 0、1、2 或 3。在这种情况下,分类标识映射如下所示:
这里写图片描述
与分桶列一样,模型可以在分类标识列中学习每个类别各自的权重。例如,我们使用唯一的整数值来表示每个类别,而不是使用某个字符串来表示。例如在籍贯上可以这样标识,即:

0 - 北京
1 - 重庆
2 - 成都
3 - 香港
4 - 澳门
5 - 台湾

调用 tf.feature_column.categorical_column_with_identity 以实现分类标识列。例如:

# Create categorical output for an integer feature named "my_feature_b",
# The values of my_feature_b must be >= 0 and < num_buckets
identity_feature_column = tf.feature_column.categorical_column_with_identity(
    key='my_feature_b',
    num_buckets=4) # Values [0, 4)

# In order for the preceding call to work, the input_fn() must return
# a dictionary containing 'my_feature_b' as a key. Furthermore, the values
# assigned to 'my_feature_b' must belong to the set [0, 4).
def input_fn():
    ...
    return ({ 'my_feature_a':[7, 9, 5, 2], 'my_feature_b':[3, 1, 2, 2] },
            [Label_values])

总结:主要用于将非数值类型(总类型是有限集合)的特征进行数值化,这也是一种独热矢量特征。

分类词汇列(tf.feature_column.categorical_column_with_vocabulary_list、file)

我们不能直接向模型中输入字符串。相反,我们必须首先将字符串映射到数值或分类值。分类词汇列提供了一种将字符串表示为独热矢量的好方法。例如:
这里写图片描述
您可以看到,分类词汇列就像是分类标识列的枚举版本。TensorFlow 提供了两种不同的函数来创建分类词汇列:
tf.feature_column.categorical_column_with_vocabulary_list
tf.feature_column.categorical_column_with_vocabulary_file
categorical_column_with_vocabulary_list 根据明确的词汇表将每个字符串映射到一个整数。例如:

# Given input "feature_name_from_input_fn" which is a string,
# create a categorical feature by mapping the input to one of
# the elements in the vocabulary list.
vocabulary_feature_column =
    tf.feature_column.categorical_column_with_vocabulary_list(
        key="a feature returned by input_fn()",
        vocabulary_list=["kitchenware", "electronics", "sports"])

上面的函数非常简单,但它有一个明显的缺点。那就是,当词汇表很长时,需要输入的内容太多了。对于此类情况,请改为调用 tf.feature_column.categorical_column_with_vocabulary_file,以便将各个词汇放在单独的文件中。例如:

# Given input "feature_name_from_input_fn" which is a string,
# create a categorical feature to our model by mapping the input to one of
# the elements in the vocabulary file
vocabulary_feature_column =
    tf.feature_column.categorical_column_with_vocabulary_file(
        key="a feature returned by input_fn()",
        vocabulary_file="product_class.txt",
        vocabulary_size=3)

product_class.txt 应该让每个词汇元素各占一行。在我们的示例中:

kitchenware
electronics
sports

总结:主要用于大量文本类型特征处理时,将特征处理为数值的场景

经过哈希处理的列(tf.feature_column.categorical_column_with_hash_bucket)

到目前为止,我们处理的示例都包含很少的类别。但是通常,类别的数量非常大,以至于无法为每个词汇或整数设置单独的类别,因为这会消耗太多内存。对于此类情况,我们可以反问自己:“我愿意为我的输入设置多少类别?”实际上,tf.feature_column.categorical_column_with_hash_bucket 函数使您能够指定类别的数量。对于这种类型的特征列,模型会计算输入的哈希值,然后使用模运算符将其置于其中一个 hash_bucket_size 类别中,如以下伪代码所示:

# pseudocode
feature_id = hash(raw_feature) % hash_buckets_size

创建 feature_column 的代码可能如下所示:

hashed_feature_column =
    tf.feature_column.categorical_column_with_hash_bucket(
        key = "some_feature",
        hash_buckets_size = 100) # The number of categories

此时,您可能会认为:“这太疯狂了!”,这种想法很正常。毕竟,我们是将不同的输入值强制划分成更少数量的类别。这意味着,两个可能不相关的输入会被映射到同一个类别,这样一来,神经网络也会面临同样的结果。下图显示了这一困境,即厨具和运动用品都被分配到类别(哈希分桶)12:
这里写图片描述
与机器学习中的很多反直觉现象一样,事实证明哈希技术经常非常有用。这是因为哈希类别为模型提供了一些分隔方式。模型可以使用其他特征进一步将厨具与运动用品分隔开来。
总结:从上面的描述中可以很明显可看出来这种特征类型的适用场景。

组合列(tf.feature_column.crossed_column)

通过将多个特征组合为一个特征(称为特征组合),模型可学习每个特征组合的单独权重。

更具体地说,假设我们希望模型计算佐治亚州亚特兰大的房地产价格。这个城市的房地产价格在不同位置差异很大。在确定对房地产位置的依赖性方面,将纬度和经度表示为单独的特征用处不大;但是,将纬度和经度组合为一个特征则可精确定位位置。假设我们将亚特兰大表示为一个 100x100 的矩形网格区块,按纬度和经度的特征组合标识全部 10000 个区块。借助这种特征组合,模型可以针对与各个区块相关的房价条件进行训练,这比单独的经纬度信号强得多。

下图展示了我们的计划(以红色文本显示城市各角落的纬度和经度值):
这里写图片描述
为了解决此问题,我们同时使用了先前介绍的 bucketized_columntf.feature_column.crossed_column 函数。

def make_dataset(latitude, longitude, labels):
    assert latitude.shape == longitude.shape == labels.shape

    features = {'latitude': latitude.flatten(),
                'longitude': longitude.flatten()}
    labels=labels.flatten()

    return tf.data.Dataset.from_tensor_slices((features, labels))

# Bucketize the latitude and longitude usig the `edges`
latitude_bucket_fc = tf.feature_column.bucketized_column(
    tf.feature_column.numeric_column('latitude'),
    list(atlanta.latitude.edges))

longitude_bucket_fc = tf.feature_column.bucketized_column(
    tf.feature_column.numeric_column('longitude'),
    list(atlanta.longitude.edges))

# Cross the bucketized columns, using 5000 hash bins.
crossed_lat_lon_fc = tf.feature_column.crossed_column(
    [latitude_bucket_fc, longitude_bucket_fc], 5000)

fc = [
    latitude_bucket_fc,
    longitude_bucket_fc,
    crossed_lat_lon_fc]

# Build and train the Estimator.
est = tf.estimator.LinearRegressor(fc, ...)

您可以根据下列任意内容创建一个特征组合:

  • 特征名称;也就是 input_fn 返回的 dict 中的名称。
  • 任意分类列,categorical_column_with_hash_bucket 除外(因为 crossed_column 会对输入进行哈希处理)。

当特征列 latitude_bucket_fclongitude_bucket_fc 组合时,TensorFlow 会为每个样本创建 (latitude_fc, longitude_fc) 对。这会生成完整的概率网格,如下所示:

 (0,0),  (0,1)...  (0,99)
 (1,0),  (1,1)...  (1,99)
   ...     ...       ...
(99,0), (99,1)...(99, 99)

不同之处在于,完整的网格仅对词汇有限的输入而言可追踪。crossed_column 仅构建 hash_bucket_size 参数所请求的数字,而不是构建这个可能庞大的输入表。特征列通过在输入元组上运行哈希函数,然后使用 hash_bucket_size 进行模运算,为索引分配一个样本。

如前所述,执行哈希函数和模函数会限制类别的数量,但会导致类别冲突;也就是说,多个(纬度、经度)特征组合最终位于同一个哈希分桶中。但实际上,执行特征组合对于模型的学习能力仍具备重大价值。

有些反直觉的是,在创建特征组合时,通常仍应在模型中包含原始(未组合)特征(如前面的代码段中所示)。独立的纬度和经度特征有助于模型区分组合特征中发生哈希冲突的样本。
总结:总结来说就是将两个或两个以上的特征组合起来构造成一个特征,主要适用于单个特征在独立使用时基本无法表达意义的场景,构造起来稍微有点麻烦,需要慢慢理一下 T_T。。。

  • 20
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值