文本主要记录feature columns
的相关内容。feature columns
是原始数据鱼Estimator之间的媒介,其内容比较丰富,可以将各种各样的原始数据转换为Estimator可以用的格式。无论如何,深度神经网络可以处理的数据类型一定是数字。但是在实际使用时,原始输入的数据可能并不是数值型的,可能是类别或者其他非数值数据。为了让深度神经网络可以接收并处理各种各样的原始数据,就需要对使用tf.feature_column
这个模块来创建模型可使用的各种feature columns
。这里主要介绍下图涉及的九个方法:
1. Numeric column
tf.feature_column.numeric_column
主要处理的是原始数据是实数(默认为tf.float32),这样的特征值模型可以直接使用,并不需要做其他任何转换。调用方法如下所示:
numeric_feature_column = tf.feature_column.numeric_column(key="SpalLength")
上述调用默认使用tf.float32作为数据类型,也可以通过dtype
来指定数据类型,如下所示:
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],
)
2. Bucketized column
当我们需要对数值在一定范围内将其分为不同的类型时,就需要创建Bucketized column,主要使用tf.feature_column.bucketized_column
。例如以人的年龄数据为例,我们并不以其真实年龄值作为特征值,而是将年龄分为如下几个bucket中:
年龄范围 | 特征表示 |
---|---|
<18 | 0 |
>=18 && <30 | 1 |
>= 30 && <45 | 2 |
>= 45 && < 60 | 3 |
>=60 | 4 |
示例如下:
# 首先将原始数据转换成numeric column
numeirc_feature_column = tf.feature_column.numeric_column("age")
# 然后,将numeric column转换成buckeized column
bucketized_feature_column = tf.feature_column.bucketized_column(
source_column = numeric_feature_column,
boundries = [18, 30, 45, 60]
)
这里需要注意的是要分成5个buckets,boundries
需要指定4个分界就可以了。
3. Categorical identity column
tf.feature_column.categorical_column_with_identity
用来实现分类标识列。可以将其视为一同特殊的bucketized column,在bucketized column中一个bucket代表一系列的原始值,在categorical identity column中每个bucket表示唯一的整数。例如,要将2, 4, 6, 7四个数字对应的categorical identity column如下表所示:
原始数值 | 特征表示 |
---|---|
2 | 0 |
4 | 1 |
6 | 2 |
7 | 3 |
示例代码如下:
identity_feature_column = tf.feature_column.categorical_column_with_identity(
key = "my_feature",
num_buckets = 4,
)
4. Categorical vocabulate column
有时候,特征的原始输入是字符串,模型并不能处理字符串,这里就需要将字符串映射到数组或者分类值。其中,categorical vocabulate column提供了一种将字符串表示为one-hot向量的方法,如下表所示:
原始字符串 | 特征表示 |
---|---|
kitchenware | 0 |
ele | [1 |
sports | 2 |
这里主要可以通过如下两个接口来实现:
tf.feature_column.categorical_column_with_vocabulary_list
-
tf.feature_column.categorical_column_with_vocabulary_file
示例代码如下:
vocabulary_feature_column = tf.feature_column.categorical_column_with_vocabulary_list(
key = "my_feature",
vocabulary_list = ["kitchenware", "ele", "sports"]
)
当词汇表非常长的时候,vocabulary_list将会是一个非常长的list。这里就可以改用tf.feature_column.categorical_column_with_vocabulary_file
,tensorflow会从一个单独的文件中获取词汇列表,代码如下:
vocabulary_feature_column = tf.feature_column.categorical_column_with_vocabulary_file(
key = "my_feature",
vocabulary_file = "vocabulary_file.txt",
vocabulary_size = 3,
其中vocabulary_file.txt
中每一个单词占一行,一共3行,内容如下:
kitchenware
ele
sports
5. Hashed Column
上述处理的类别都是比较少的类别,当类别非常多时,无法为每个词汇单独设置一个one-hot向量。于是就可以使用tf.feature_column.categorical_column_with_hash_bucket
将各个类别映射到不同的整数。这种方式不可避免的将不同的类别映射到同一整数,不过这并不会对模型的处理产生负面影响。具体做法是,首先计算原始数据的hash值;然后用hash值对hash_buckets_size
取模运算,这样就将原始数据映射到了hash_buckets_size
范围内的整数类别中了。如下图所示:
代码如下:
hash_feature_column = tf.feature_column.categorical_column_with_hash_bucket(
key = "my_feature",
hash_buckets_size = 100,
)
6. Crossed column
tf.feature_column.crossed_columns
用来将多个特征组合成一个特征。假设我们希望模型计算佐治亚州亚特兰大的房地产价格。这个城市的房地产价格在不同位置差异很大。在确定对房地产位置的依赖性方面,将纬度和经度表示为单独的特征用处不大;但是,将纬度和经度组合为一个特征则可精确定位位置。假设我们将亚特兰大表示为一个 100x100 的矩形网格区块,按纬度和经度的特征组合标识全部 10000 个区块。借助这种特征组合,模型可以针对与各个区块相关的房价条件进行训练,这比单独的经纬度信号强得多。具体代码如下:
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, ...)
crossed column可以对任意特这个进行组合,除了categorical_column_with_hash_bucket
,因为crossed_column
会对输入进行hash处理。简单来说其处理过程是这样的,将原特征组合在一起,然后计算hash,最后对hash值取hash_bucket_size
模。
7. Indicator and embedding column
类别值 | one-hot表示 |
---|---|
0 | [1, 0, 0, 0] |
1 | [0, 1, 0, 0] |
2 | [0, 0, 1, 0] |
3 | [0, 0, 0, 1] |
indicator column和embedding column并不直接处理特征,而是将分类视为输入。
indicator column将每个类别转换成one-hot向量,如下表所示
类别值 | one-hot表示 |
---|---|
0 | [1, 0, 0, 0] |
1 | [0, 1, 0, 0] |
2 | [0, 0, 1, 0] |
3 | [0, 0, 0, 1] |
示例代码如下:
categorical_column = = tf.feature_column.categorical_column_with_vocabulary_list(
key = "my_feature",
vocabulary_list = ["kitchenware", "ele", "sports"]
)
indicator_column = tf.feature_column.indicator_column(categorical_column)
如果我们的输入有百万甚至亿级的类别时,使用indicator column就不合适了,模型也根本没办法处理(内存存不下了)。这是可以使用embedding column来解决这个问题。embedding column是将多维one-hot向量表示为低维普通向量,其中的元素并不是0和1而是一个确定的数值。同时embedding column的维度要远远小于indicator column,而且可以更加丰富的表现分类的内容。
接下来看一下embedding column和indicator column之间的区别。假设输入样本包含多个不同的词(取自仅有81个词的有限词汇表)。假设数据集在4个不同的样本中提供了如下输入词:
dog
spoon
scissors
guitar
下图分别说明了embedding column和indicator column的处理流程:
embedding column中的值是在训练期间进行分配的。embedding column可以增强模型的功能,因为在训练过程中模型学习了类别之间的其他关系。关于embedding后续还会详细介绍。embedding column的维度是由原始类别的个数决定,一般采用如下公式进行确定:
embedding_dimensions = number_of_categories**0.25
代码示例如下所示:
categorical_column = ... # Create any categorical column
# Represent the categorical column as an embedding column.
# This means creating a one-hot vector with one element for each category.
embedding_column = tf.feature_column.embedding_column(
categorical_column=categorical_column,
dimension=dimension_of_embedding_vector)