01.TensorFlow高级API简介

Tesseract windows标注 tensorflow标注工具_git


TensorFlow1.3之后,高级接口引入了两个重要的功能:

  • 数据集:一种创建输入管道(即将数据读入您的程序)的全新方式
  • 估算器:一种创建TensorFlow模型的高级方式。估算器包括适用于常见机器学习任务的预制模型,不过,也可以使用它们创建自己的自定义模型
    估算器形态如图:

02.TensorFlow Dataset与数据高效读写

2.1 TensorFlow Dataset与数据高效读写

Google官方给出Dataset API中的类图如下所示:

Tesseract windows标注 tensorflow标注工具_Tesseract windows标注_02

  • Dataset: 基类,包含用于创建和转换数据集的函数,允许你从内存中的数据或从python生成器初始化数据集。
  • TextLineDataset: 从文本中读取各行内容
  • FixedLengthRecordDataset: 从二进制文件中读取固定大小的记录
  • TFRecordDataset: 从TFRecord文件 中读取记录
  • Iterator: 提供一种获取一个数据集元素的方法
  • Dataset可以看做是相同类型元素的有序列表
  • 在实际使用时,单个元素可以是向量,也可以是字符串、图片、元组或字典
    TextLineDataset(处理文本)、TFRecordDataset(处理存储于硬盘的大量数据,不适合进行内存读取)、FixedLengthRecordDataset(二进制数据的处理)继承自Dataset,这几个类的方法大体一致,主要包括数据读取、元素变换、过滤,数据集拼接、交叉等。不同形式的输入如图:



    Iterator是Dataset中迭代方法的实例化,主要对数据进行访问,包括四种迭代方法,单次、可初始化、可重新初始化、可馈送等,可实现对数据集中元素的快速迭代,供模型训练使用。因此,只要掌握Dataset以及Iterator的方法,即可清楚tensorflow的数据读取方法。如图:

2.2 Estimator估算器

估算器是一种高级API,使用这种API,在训练TensorFlow时就不再像之前那样需要编写大量的样板文件代码。估算器也是非常灵活,如果不对模型有具体的要求,它允许替换默认行为。
使用估算器,您可以通过两种可能的方式构建模型:

  • 预制估算器: 这些是TensorFlow中预先定义的估算器。
  • 估算器(基类): 允许你使用model_fn函数完全掌控模型的创建方式(自定义模型)。
    估算器类图如图:

03.基于TensorFlow高级API的快速特征工程处理

3.1 特征列feature_columns

TensorFlow在高版本中自带的feature_columns有非常方便的特征工程处理函数。

  • 定义:是指一组数据的相关特征,包含了数据的相关类型和长度等信息。
  • 定位:它是原始数据与模型之间进行衔接的桥梁
  • 主要目标:对用户数据进行预处理与特征化的处理,这种技术的出现主要是原始输入数据的多样性

3.2 特征工程处理

以鸢尾花分类案例为例:

通过Estimator(鸢尾花的DNNClassifier)的feature_columns参数指定模型的输入。特征列将输入数据(由input_fn返回)与模型联系起来。

Tesseract windows标注 tensorflow标注工具_git_03


创建特征列。需调用tf.feature_columns模块的函数。如下图所示,该模块中的所有九个函数斗湖返回一个Categoricla-Columns或一个Dense-Columns对象,但却不会返回bucketized_columns,后者继承自这两个类:

Tesseract windows标注 tensorflow标注工具_Tesseract windows标注_04

3.2.1不同类型的列
(1)数值型列

鸢尾花分类器为所有输入特征调用了tf.numeric_column():SepalLengthSepalWidthPetalLengthPetalWidth。尽管tf.numeric_column()提供了可选参数,调用不含任何参数的函数仍是指定具有默认类型(tf.float32)的数字值作为模型输入的一种极简单方式。如:

# 默认是tf.floar32类型
numeric_feature_column = tf.feature_column.numeric_column(key="SepalLength")
# 使用dtype参数可以指定非默认数值数据类型。
numeric_feature_column = tf.feature_column.numeric_column(key="SepalLength",dtype=tf.float64) # 使用dtype指定tf.float64
# 默认情况下,一个数值列可以创建一个值(标量)。使用shape参数可以指定另一个形状。
numeric_feature_column = tf.feature_column.numeric_column(key="Bowling",shape=10) # tf.float32型,10个元素的向量
numeric_feature_column = tf.feature_column.numeric_column(key="MyMatrix",shape=[10,5]) # tf.float32型,10x5维度的矩阵
(2)连续值离散化/分桶

有时候,我们不希望将数字直接提供给模型,而是根据数值范围将它拆分成不同的桶(离散化)。例如,假设存在表示房子建造年份的原始数据。我们可以将年份拆分成以下四个存储分区,而不是以标量数值列表示年份

Tesseract windows标注 tensorflow标注工具_Tesseract windows标注_05


模型将通过以下方式表示存储分区:

日期范围

表示形式

<1960

[1,0,0,0]

>=1960 and <1980

[0,1,0,0]

>=1980 and <2000

[0,0,1,0]

>2000

[0,0,0,1]

既然数字对模型来说是一种非常有效的输入,为什么还要将它拆分成这样的分类值呢?
请注意,分类可以将一个数字拆分为一个四元素矢量。离散化处理使得特征可以展开成4个特征,因此模型现在可以学习四个单独的权重,而不是仅仅学习一个,模型的表达能力会增强。与一个权重相比,四个权重可以创建信息更丰富的模型。更重要的是,由于只有一个元素置位(1),其它三个元素清零(0),离散化/分桶可以让模型清楚地区分不同的年份区间,如果我们仅仅使用一个数字作为输入,模型无法区分类别。所以存储分区可以为模型提供可以用来学习的其他重要的信息。下面的代码演示了如何创建分桶特征(离散化):

# 创建分桶特征前,我们先创建了一个数值列
numeric_feature_column = tf.feature_column.numeric_column("Year") # 原始连续值输入
# 按照给定的3个边界对数据进行离散化
bucketized_feature_column = tf.feature_column.bucketized_column(
                            souce_column = numeric_feature_column,     # numeric_column型的原始数据
                            boundaries = [1960,1980,2000])             # 给定边界
(3)分类标识列/label encoder

分类标识列是一种特殊形式的存储分区化列,对应的实际是我们在机器学习特征工程中提到过的lebal encoder。

Tesseract windows标注 tensorflow标注工具_Tesseract windows标注_06


独热向量编码的好处是,模型可以在分类标识中为每个类别学习单独的权重。调用tf.feature_column.categorical_column_with_identity()来实现一个分类标识列。如:

# Create a categorical output for input "feature_name_from_input_fn",which must be of integer type.Value is expected to # # be >= 0 and < num_buckets
identity_feature_column = tf.feature_column.categorical_column_with_identity(
                          key = 'feature_name_from_input_fn'
                          num_buckets = 4) # Value [0,4]
(4)分类词汇列/one-hot encodeing

我们无法将字符串直接输入模型。相反,我们必须先将字符串映射到数字或分类值。分类词汇列提供了一种以独热矢量表示字符串的好方法。例如我们有一个商品字段“product_class”包含下面的不同商品类型取值

Tesseract windows标注 tensorflow标注工具_git_07


分类词汇列是一种枚举版本的分类标识列。TensorFlow提供了两个不同放入函数来创建分类词汇列:

  • tf.feature_column.categorical_column_with_vocabulary_list():函数可以根据一个显式词汇列表将每个字符串映射到一个整数。如:
# 相当于把所有的取值平铺开
vocabulary_feature_column = tf.feature_column.categorical_column_with_vocabulary_list(
                            key = "feature_column_from_input_fn"
                            vocabulary_list = ["kitchenware","electronics","sports"])
  • tf.feature_column.categorical_column_with_vocabulary_file():前面的函数明显有一个缺陷,当词汇列表较长时,需要进行很多输入工作。对于这些情况可以调用此函数,它可以将词汇放在一个单独的文件中。
# 直接给定product_class.txt这个文件,囊括所有的“词汇”
vocabulary_feature_column = tf.feature_column.categorical_column_with_vocabulary_file(
                            key = "feature_column_from_input_fn"
                            vocabulary_file = "product_class.txt"
                            vocabulary_size = 3)
 # 文件中是一个类(词汇)一行
(5)使用哈希存储分区限制类别/cate feature hash

到目前为止,我们仅介绍了少量几个类别。例如我们的product_class示例仅有3个类别。但是,类别的数量通常可以非常大,以至于无法为每个词汇或整数使用单独的类别,因为这样会消耗大量内存。
对于这些情况,我们可以将问题反过来问问自己,“我愿意为输入使用多少个类别?”事实上tf.feature_column.categorical_column_with_vocabulary_hash_buckets()函数可以指定类别数量。例如,以下显式了此函数如何计算输入哈希值,然后使用模数运算符将其置于一个hash_buckets_size类别中:

# Create categorical output for input "feature_name_from_input_fn".
# Category becomes:hash_value("feature_name_from_input_fn") % hash_buckets_size
hashed_feature_column = tf.feature_column.categorical_column_with_vocabulary_hash_buckets(
                        key = "feature_name_from_input_fn"
                        hash_buckets_size = 100) # The number kf catehories

注意:

这里的操作,我们在将不同的值强制到一组较小的类别中。这意味着:两个很可能不相关的输入将被映射到同一个类别,这对神经网络来说也是一样的。下图说明了这个难题,显示kitchenware和sports都分配获得了类别(哈希存储分区)12:

Tesseract windows标注 tensorflow标注工具_git_08


与机器学习中的许多有悖常理的现象一样,哈希通常可以在实践中很好地运行。这是因为哈希类别为模型提供了一些分割。模型可以使用更多特征来进一步将kitchenware和sports分开。

(6)交叉特征

这里介绍最后一个分类列允许我们将多个输入特征组合成一个。组合特征(更广为人知的说法是特征交叉)让模型可以专门针对特征组合表示的任何意义学习单独的权重。

例如,假设我们希望构建一个模型预估佐治亚州亚特兰大的房地产价格。这个城市的房地场价格因位置不同而相差很大。以单独的特征表示维度和精在确定房地产为止相关性中不是很有用;不过,将经度和纬度组合到一个特征中可以确定位置。假设我们一一个100x100大小的矩形剖面网格表示亚特兰大,通过经纬度交叉来确定10000个剖面。这个组合让模型可以拾取与各个剖面相关的定价条件,与单独的经纬度相比,这样可以提供更强大的信息。

下图显示我们的平面图,其中包含城市四个角落的经度和纬度值:

Tesseract windows标注 tensorflow标注工具_数据_09

  • 组合特征一定是对离散的特征去做组合的,连续值做不了组合,因此在做组合特征前先把连续值离散化。
    为了解决问题,我们使用之前介绍过的一字儿特征列组合,以及tf.feature_column.crossed_column()函数。
# In our input_fn,we convert input longitude and latitude to integer values in the range(0,100).
def input_fn():
    # Using Datasets,reaed the input values fir longitude and latitude
    latitude = ... # A tf.float32 value
    longitude = ... # A tf.float32 value
    
    # In our example we just reture our lat_int,long_int features.
    # The dictionary of a compele program would probably have more keys.
    return { "latitude": latitude,"longitude":longitude,...},lables
# As can be see from the map,we want to split the latitude range [33.641336,33.887157] into 100 buckets,to do this we # # use np.linspace to get a list of 99 numbers between min and max of this range. 
# Useing this list we can bucketize latitude into 100 buckets. 
latitude_buckets = list(np.linspace(33.641336,33.887157,99))
latitude_fc = tf.feature_column.bucketized_column(
              tf.feature_column.numeric_column("latitude"),
              latitude_buckets)
# Do the same bucketization for longitude as done for latitude
longitude_buckets = list(np.linspace(33.641336,33.887157,99))
longitude_fc = tf.feature_column.bucketized_column(
              tf.feature_column.numeric_column("longitude"),
              longitude_buckets)
# Create a feature crossof fc_longitude x fc_latitude
fc_san_francisco_boxed = tf.feature_column.crossed_column(
                         key = [latitude_fc,longitude_fc],
                         hash_bucket_size = 1000) # No precise rule,maybe 1000 buckets will be good?

可以从以下信息之一创建特征交叉:

  • 特征名称:即从input_fn返回的dict中的名称
  • 任何分类列,除categorical_column_with_hash_bucket

当特征列latitude_fclongitude交叉时,TensorFlow将创建10000个(latitude_fc,longitude_fc)组合。
函数tf.feature_column.crossed_column将这些组合执行hash计算,然后通过hash_bucket_size执行模数运算,讲结果插入类别中。如之前讨论,执行哈希和模数函数可能会导致类别冲突,不过在实践中,执行特征交叉扔可以为模型的学习能力提供有效值。
不过需要注意的是,很多时候,在创建特征交叉时,我们通常仍需要在模型中包含原始(非交叉)特征。例如,不仅提供(latitude,longitude)特征交叉,还需要以单独的特征形式提供latitudelongitude。单独的latitudelongitude特征将帮助模型分隔包含不同特征交叉的分桶特征。

(7)指示器列和嵌入列(embedding)

指示器列和嵌入列永远不会在特征上运行,而是将分类作为输入。使用指示器列时,我们将告知TensorFlow准确执行我们在分类product_class示例中看到的操作。即指示器列将每个类别作为独热矢量中的一个元素处理,其中,匹配类别的值为1,其余为0:

Tesseract windows标注 tensorflow标注工具_git_10


创建指示器列:

categorical_column = ... # Create any type of categorical column,see Figure 3
# Represent the categorical column as an indicator column.
# This means createing a one-hot vector with one element for each category.
indicator_column  = tf.feature_column.indicator_column(categorical_column)

现在,假设我们不只有三个可能的类别,而是由100万个,或者有10亿个。由于多种原因,在类别数量增大时,使用指示器列训练神经网络将变得不可行。我们可以使用嵌入列来克服此限制,与将数据表示为具有许多维度的独热矢量不同,嵌入列将数据表示为一个更低维度的普通矢量,其中,每个单元格都可以包含任意数字,而不仅仅是0或1。通过为每个单元格允许更丰富的数字组合,与指示器列相比,嵌入列可以包含少得多的单元格数。
我们来看一个比较指示器列和嵌入列的示例。假设我们的输入示例包含一个有限组合中的不同单词,这个组合仅有81个单词。再假设数据集再4个单独的示例中提供一下输入单词:

  • “dog”
  • “spoon”
  • “scissors”
  • “guitar”
    在这种情况下,下图说明了嵌入列或指示器列的处理路径。

    在处理一个示例时,一个categorical_column_with…函数会将示例字符串映射到数字分类值。例如,一个函数会将"spoon"映射到[32]。然后,可以通过以下两种方式之一表示这些数字分类值:
  • 作为指示器列(indicator column)。函数会将每个数字分类值转换成一个81元素矢量(因为我们的组合包含81个单词),在分类值(9,32,79,80)的索引中放置1,其他位置放置0。
  • 作为嵌入列(embedding column)。一个函数使用数字分类值(9,32,79,80)作为查表的索引。查找表中的每个显示位置都包含一个3元素矢量。
    嵌入列相当于一种映射,把离散的表示,映射到向量空间中,并希望挖掘出某种关联关系。
    示例中矢量的大小为什么是3?以下“公式”提供了与嵌入维度数量有关的一般经验法则:
embedding_dimensions = number_of_categories ** 0.25  # 81 ** 0.25 = 3

这只是一个一般准则,其实我们可以随意设置嵌入维度的数量。调用tf.feature_column.embeding_column()来创建embeding_column。嵌入矢量的维度取决于上面介绍的问题,但常用值可以从最低的3开始,一直到300甚至更大。

categorical_column = ... # Create any type of categorical column,see Figure 3
# Represent the categorical column as an embedding column.
# This means createing a one-hot vector with one element for each category.
indicator_column  = tf.feature_column.embedding_column(
                    categorical_column = categorical_column
                    dimension = dimension_of_embedding_vector)

注意,indicator_column可以是multi-hot的,不一定是one-hot。

name = indicator_column(categorical_column_with_vocabulary_list('name',['bob','george','wanda'])
columns = [name, ...]
features = tf.parse_example(...,features = make_parse_example_spec(cloumns))
dense_tensor = input_layer(features,columns)
dense_tensor == [[1,0,0]] # If "name" bytes_list is ["bob"]
dense_tensor == [[1,0,1]] # If "name" bytes_list is ["bob","wanda"]
dense_tensor == [[2,0,0]] # If "name" bytes_list is ["bob","bob"]