功能列

    本文档是官方TensorFlow的改编版指导功能列

    本文档详细介绍了特征列以及如何使用TensorFlow将它们用作神经网络的输入。特征列非常丰富,使您能够将不同范围的原始数据转换为神经网络可以使用的格式,使实验变得容易。

    深度神经网络能处理什么样的数据?答案当然是数字(例如,tf float32美元).毕竟,神经网络中的每个神经元都要对权重和输入数据进行乘法和加法运算。然而,实际的输入数据通常包含非数值(分类)数据。例如,考虑一个product_class可以包含以下三个非数值的特性:

    • 厨房用具
    • 电子产品
    • 体育

    ML模型通常将分类值表示为简单的向量,其中1表示有值,0表示没有值。例如,当product_class设置为sports时,ML模型通常表示product_class作为[0, 0, 1]意义:

    • 0:没有厨具
    • 0:表示没有电子器件
    • 1:体育是存在的

    因此,尽管原始数据可以是数字的或分类的,但ML模型将所有特征表示为数字。

    本文档解释了tfdataset中可用的9个特性列。如下图所示,所有9个函数都返回acategorical_column或者一个dense_column对象,除了bucketized_column,继承自两个类:

    特征列方法分为两大类和一类混合方法。

    让我们更详细地看看这些函数。

    功能规格接口

    我们要用feature_spec接口。的feature_spec接口是一种抽象,它使在R中定义特征列更加容易。

    你可以初始化feature_spec:

    图书馆(tfdatasets)hearts_dataset < -tensor_slices_dataset(心)规范< -feature_spec(hearts_dataset目标.)

    然后我们添加步骤以进行定义feature_columns.读了feature_spec更多信息请参见维内特。

    数字列

    我们使用step_numeric_column将数字列添加到规范

    规范% > %step_numeric_column(年龄)
    # #──功能规范──────────────────────────────────────────────────────────────────────────────# # feature_spec 1步骤。步骤# #安装:假# #───────────────────────────────────────────────────────────────────────────────────────# # StepNumericColumn:年龄# #──密集特性────────────────────────────────────────────────────────────────────────────# #功能规范必须安装之前,我们可以检测出密集的特性。

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

    #表示一个10元素的向量,每个单元格包含一个tf$float32。规范% > %step_numeric_column(保龄球,形状=10#表示一个10x5的矩阵,其中每个单元格包含一个tf$float32。规范% > %step_numeric_column(my_matrix形状=c105))

    一个很好的特性step_numeric_column您还可以指定规范化函数。当使用标量时,拟合时将从数据中学习比例常数feature_spec

    #使用定义张量流操作的函数。规范% > %step_numeric_column(年龄、normalizer_fn =函数(x (x)-10/5#使用一个标量规范% > %step_numeric_column(年龄、normalizer_fn =scaler_standard())

    Bucketized列

    通常,您不希望直接将数字输入到模型中,而是根据数值范围将其值划分为不同的类别。为此,创建一个bucketized_column.例如,考虑代表房屋建造年份的原始数据。我们可以将该年分为以下四个部分,而不是用标量数字列表示该年:

    将年度数据分为四个桶。

    为什么要将一个数字(模型的一个完全有效的输入)分割为一个类别值?注意,分类将单个输入数字分割为一个四元素向量。因此,模型现在可以学习四个单独的权重,而不是一个;四个权重比一个权重创建的模型更丰富。更重要的是,桶化使模型能够清楚地区分不同的年份类别,因为只设置了其中一个元素(1),而清除了其他三个元素(0)。例如,当我们只使用单个数字(一年)作为输入时,线性模型只能学习到线性关系。因此,桶为模型提供了可用于学习的额外灵活性。

    下面的代码演示了如何创建桶化特性:

    首先,将原始输入转换为数字列。规范< -规范% > %step_numeric_column(年龄)#然后,bucket化数字列。规范< -规范% > %step_bucketized_column(年龄、边界=c30.5070))

    注意,指定一个三元素边界向量将创建一个四元素桶化向量。

    分类标识列

    类别标识列为tfdatasets什么因素换句话说,它们可以被视为桶化列的特殊情况。在传统的桶化列中,每个桶表示一个值范围(例如,从1960到1979)。在类别标识列中,每个桶表示一个惟一的整数。例如,假设您想表示整数范围[0,4)。也就是说,您想表示整数0、1、2或3。在这种情况下,类别标识映射是这样的:

    一个类别标识列映射。注意,这是一种单热编码,而不是二进制数字编码。

    与桶化列一样,模型可以学习类别标识列中每个类的单独权重。例如,不使用字符串来表示product_class,让我们用唯一的整数值表示每个类。那就是:

    • 0 =“厨具”
    • 1 =“电子”
    • 2 =“体育”

    调用step_categorical_column_with_identity将类别标识列添加到feature_spec.例如:

    #为一个名为my_feature_b的整数特性创建分类输出# my_feature_b的值必须是>= 0和< num_buckets规范< -规范% > %step_categorical_column_with_identity(my_feature_bnum_buckets =4

    分类词汇表列

    我们不能直接向模型输入字符串。相反,我们必须首先将字符串映射到数值或类别值。分类词汇表列提供了一种将字符串表示为单热向量的好方法。例如:

    将字符串值映射到词汇表列。

    如您所见,类别词汇表列是类别标识列的枚举版本。tfdatasets提供两个不同的函数来创建分类词汇表列:

    • step_categorical_column_with_vocabulary_list
    • step_categorical_column_with_vocabulary_file

    categorical_column_with_vocabulary_list根据显式词汇表将每个字符串映射为整数。例如:

    规范< -规范% > %step_categorical_column_with_vocabulary_list需要,vocabulary_list =c“固定”“正常”“可逆”

    请注意,vocabulary_list参数在R中是可选的,在拟合时将发现词汇表featture_spec这帮我们节省了很多打字的时间。

    还可以将词汇表放在单独的文件中,每个词汇表元素都应该包含一行。然后你可以使用:

    规范< -规范% > %step_categorical_column_with_vocabulary_file(需要vocabulary_file =“thal.txt”

    散列的列

    到目前为止,我们只处理了少量的类别。例如,我们的product_class示例只有3个类别。但是,通常情况下,类别的数量可能非常大,以至于不可能为每个词汇表单词或整数拥有单独的类别,因为这将消耗太多的内存。对于这些情况,我们可以反过来问,“我愿意有多少类别作为我的输入?”事实上,step_categorical_column_with_hash_bucket函数使您能够指定类别的数目。对于这种类型的特征列,模型计算输入的哈希值,然后使用模运算符将其放入hash_bucket_size类别之一,如下面的伪代码所示:

    # pseudocode feature_id = hash(raw_feature) % hash_bucket_size

    的代码feature_columnfeature_spec可能看起来像这样:

    规范< -规范% > %step_categorical_column_with_hash_bucket(需要hash_bucket_size =One hundred.

    此时,你可能会理所当然地认为:“这太疯狂了!”毕竟,我们将不同的输入值强制用于更小的类别集合。这意味着两个可能不相关的输入将被映射到同一个类别,因此对神经网络意味着相同的东西。下图说明了这种困境,显示厨房用具和运动都被分配到类别(哈希桶)12:

    用哈希桶表示数据。

    与机器学习中的许多反直觉现象一样,事实证明哈希在实践中通常工作得很好。这是因为哈希分类为模型提供了一些分离。该模型可以使用额外的功能,进一步将厨房用具与运动用品区分开来。

    交叉列

    将特征组合成单个特征,即特征交叉,使模型能够学习每个特征组合的单独权重。

    更具体地说,假设我们希望我们的模型计算佐治亚州亚特兰大市的房地产价格。这个城市的房地产价格因地段不同而有很大差别。将纬度和经度表示为单独的特征在识别房地产位置依赖性方面不是很有用;然而,跨越纬度和经度的单一特征可以精确定位位置。假设我们将亚特兰大表示为一个由100x100个矩形部分组成的网格,通过经纬度的特征交叉来标识10,000个部分中的每个部分。这个特征交叉使模型能够训练与每个单独路段相关的定价条件,这是一个比单独的经纬度强得多的信号。

    下图是我们的平面图,城市角落的经纬度值用红色文字表示:

    亚特兰大的地图。想象一下这张地图被分成10000个大小相等的部分。

    对于该解决方案,我们使用了前面看到的bucketized_column和step_crossed_column函数。

    规范< -feature_spec(数据集,目标latitute+经度)% > %step_numeric_column(纬度、经度)% > %step_bucketized_column(纬度,边界=c(latitude_edges))% > %step_bucketized_column(经度边界=c(longitude_edges))% > %step_crossed_columnlatitude_longitude =c(经度、纬度),hash_bucket_size =One hundred.

    你可以从以下任何一种创建一个特性交叉:

    • 任何分类列,除了categorical_column_with_hash_bucket(因为crossed_column会哈希输入)。

    当特征栏latitude_bucket_fc而且longitude_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

    如前所述,执行哈希和模函数会限制类别的数量,但会导致类别冲突;也就是说,多个(纬度、经度)特征交叉将在同一个散列桶中结束。但是在实践中,执行特征交叉仍然为模型的学习能力增加了显著的价值。

    有些违背直觉的是,当创建特性交叉时,您通常仍然应该在模型中包括原始(未交叉的)特性(如前面的代码片段)。独立的纬度和经度特征帮助模型区分在交叉特征中发生散列冲突的示例。

    指标和嵌入列

    指示器列和嵌入列从来不直接作用于特性,而是将分类列作为输入。

    当使用指示器列时,我们告诉TensorFlow做我们在分类product_class示例中看到的事情。也就是说,指示符列将每个类别视为one-hot vector中的一个元素,其中匹配的类别的值为1,其余的为0:

    用指示器列表示数据。

    下面是如何创建一个指标列:

    规范< -feature_spec(数据集,目标.)% > %step_categorical_column_with_vocabulary_list(product_class)% > %step_indicator_column(product_class)

    现在,假设我们不是只有三个可能的类,而是有一百万个。或者可能是十亿。由于一些原因,随着类别数量的增加,使用指标列训练神经网络变得不可行。

    我们可以使用嵌入列来克服这个限制。嵌入列不是将数据表示为具有多个维度的单一热点向量,而是将数据表示为较低维度的普通向量,其中每个单元格可以包含任何数字,而不仅仅是0或1。通过允许为每个单元格提供更丰富的数字面板,嵌入列包含的单元格要比指示列少得多。

    让我们来看一个比较指示符和嵌入列的示例。假设我们的输入示例由来自有限的仅有81个单词的不同单词组成。进一步假设数据集在4个单独的例子中提供了以下输入单词:

    • “狗”
    • “勺”
    • “剪刀”
    • “吉他”

    在这种情况下,下图说明了嵌入列或指示符列的处理路径。

    嵌入列将分类数据存储在比指示符列更低维的向量中。(我们只是在嵌入向量中放入随机数;训练决定了实际的数字。)

    在处理示例时,categorical_column_with_[…]函数之一将示例字符串映射到数值类别值。例如,函数将“spoon”映射到[32]。(32来自于我们的想象——实际值取决于映射函数。)然后,你可以用以下两种方式之一表示这些数值类别值:

    • 作为一个指示列。一个函数将每个数字类别值转换为一个包含81个元素的向量(因为我们的调色板由81个单词组成),将1放在类别值的索引(0、32、79、80)中,并将0放在所有其他位置。

    • 作为嵌入列。函数使用数值分类值(0、32、79、80)作为查找表的索引。查找表中的每个槽都包含一个3元素向量。

    嵌入向量中的值是如何神奇地赋值的?实际上,作业是在训练过程中完成的。也就是说,模型学习将输入的数字类别值映射到嵌入向量值的最佳方法,以便解决问题。嵌入列增加了模型的能力,因为嵌入向量从训练数据中学习类别之间的新关系。

    为什么在我们的例子中,嵌入向量的大小是3 ?好吧,下面的“公式”提供了一个关于嵌入维数的一般经验法则:

    embedding_dimensions = number_of_categories * * 0.25

    也就是说,嵌入向量的维数应为类别数的4次根。由于本例中的词汇表大小为81,因此推荐的维度数为3:

    3 = 81 * 0.25 *

    注意:这只是一个一般的指导方针;您可以随意设置嵌入维度的数量。

    调用step_embedding_column按照以下代码片段的建议创建embedding_column:

    规范< -feature_spec(数据集,目标.)% > %step_categorical_column_with_vocabulary_list(product_class)% > %step_embedding_column(product_class尺寸=3.

    嵌入是机器学习中的一个重要课题。这些信息只是为了让您开始使用它们作为特征列。

    把专栏传给Keras

    在创建和拟合一个feature_spec对象,可以使用dense_features从规范中获得密性特征。然后您可以使用layer_dense_features函数来创建一个自动初始化值的层。

    这里有一个例子来说明它是如何工作的:

    图书馆(keras)图书馆(dplyr)
    ## ##附加包:'dplyr'
    以下对象从'package:stats'被屏蔽:## ## filter, lag
    以下对象从'package:base'被屏蔽:## ## intersect, setdiff, setequal, union
    规范< -feature_spec(心,目标.)% > %step_numeric_columnall_numeric(),-cp,-restecg,-exang,-性,-的边后卫,normalizer_fn =scaler_standard()% > %step_categorical_column_with_vocabulary_list(需要)% > %step_bucketized_column(年龄、边界=c182530.35404550556065))% > %step_indicator_column(需要)% > %step_embedding_column(需要尺寸=2% > %step_crossed_columnc(需要bucketized_age),hash_bucket_size =10% > %step_indicator_column(crossed_thal_bucketized_age)规范< -适合(规范)输入< -layer_input_from_dataset(心% > %选择-目标)输出< -输入% > %layer_dense_featuresfeature_columns =dense_features(规范)% > %layer_dense单位=1激活=“乙状结肠”模型< -keras_model(输入、输出)模型% > %编译损失=“binary_crossentropy”优化器=“亚当”指标=“准确性”模型% > %适合x =% > %选择-目标),y =目标,validation_split =0.2
    # #火车在242个样本,验证61个样本# #时代1/10 # # 32/242  [==>...........................] -η:6 s -损失:0.8009 - 0.3438精度:242/242  [==============================] - 1 s 5 ms /样本-损失:0.7781 -准确性:0.4298 - val_loss: 0.7310 - 0.5574 val_accuracy: # #时代2/10 # # 32/242  [==>...........................] -η:0 -损失:0.7911 - 0.3750精度:242/242  [==============================] - 0 212 /样本-损失:0.7587 -准确性:0.4545 - val_loss: 0.7162 - 0.5738 val_accuracy: # #时代3/10 # # 32/242  [==>...........................] -η:0 -损失:0.6832 - 0.5312精度:242/242  [==============================] - 0 121 /样本-损失:0.7407 -准确性:0.4752 - val_loss: 0.7025 - 0.5902 val_accuracy: # #时代4/10 # # 32/242  [==>...........................] -η:0 -损失:0.7768 - 0.3750精度:242/242  [==============================] - 0 128 /样本-损失:0.7231 -准确性:0.4959 - val_loss: 0.6894 - 0.5738 val_accuracy: # #时代5/10 # # 32/242  [==>...........................] -η:0 -损失:0.6872 - 0.5312精度:242/242  [==============================] - 0 120 /样本-损失:0.7068 -准确性:0.5372 - val_loss: 0.6767 - 0.5738 val_accuracy: # #时代6/10 # # 32/242  [==>...........................] -η:0 -损失:0.6885 - 0.6250精度:242/242  [==============================] - 0 127 /样本-损失:0.6911 -准确性:0.5661 - val_loss: 0.6647 - 0.5902 val_accuracy: # #时代7/10 # # 32/242  [==>...........................] -η:0 -损失:0.6150 - 0.7188精度:242/242  [==============================] - 0 119 /样本-损失:0.6762 -准确性:0.6033 - val_loss: 0.6535 - 0.6066 val_accuracy: # #时代8/10 # # 32/242  [==>...........................] -η:0 -损失:0.6720 - 0.5625精度:242/242  [==============================] - 0 117 /样本-损失:0.6624 -准确性:0.6322 - val_loss: 0.6428 - 0.6230 val_accuracy: # #时代9/10 # # 32/242  [==>...........................] -η:0 -损失:0.6823 - 0.5938精度:242/242  [==============================] - 0 122 /样本-损失:0.6494 -准确性:0.6529 - val_loss: 0.6328 - 0.6393 val_accuracy: # #时代10/10 # # 32/242  [==>...........................] -η:0 -损失:0.6710 - 0.5625精度:242/242  [==============================] - 0 115 /样本-损失:0.6365 -准确性:0.6529 - val_loss: 0.6233 - val_accuracy: 0.6393