Tensorflow v1.10 +为什么在没有检查点的情况下需要输入服务接收器功能? - python

我正在根据TensorFlow的估算器API调整我的模型。

我最近问了一个关于early stopping based on validation data的问题,除了尽早停止之外,目前还应该导出最佳模型。

似乎我对模型导出是什么以及检查点是什么的理解还不完整。

检查点是自动进行的。根据我的理解,检查点足以使估算器开始“热身”-使用如此训练的权重或错误之前的权重(例如,如果您遇到断电)。

关于检查点的好处是,除了自定义估算器(即input_fnmodel_fn)所需的代码外,我不必编写任何代码。

在给定初始化估计量的情况下,可以只调用其train方法来训练模型,而在实践中,这种方法相当乏味。人们常常想做几件事:

定期将网络与验证数据集进行比较,以确保您不会过度拟合
如果发生过度拟合,请尽早停止训练
每当网络结束时(通过达到指定数量的训练步骤或根据早期停止标准),都可以保存最佳模型。

对于刚接触“高级”估算器API的人来说,似乎需要大量低级专业知识(例如input_fn),因为如何才能使估算器做到这一点并非直截了当。

通过简单的代码重做#1可以通过将tf.estimator.TrainSpectf.estimator.EvalSpectf.estimator.train_and_evaluate一起使用来实现。

在previous question用户中,@ GPhilo阐明了如何使用tf.contrib中的半直观函数来实现#2:

tf.contrib.estimator.stop_if_no_decrease_hook(my_estimator,'my_metric_to_monitor', 10000)

(不直观,因为“提早停止不是根据未改进评估的次数来触发,而是根据特定步长范围内未改进评估的次数来触发”)。

@GPhilo-注意到它与#2不相关-还回答了如何做#3(按照原始帖子的要求)。但是,我不了解input_serving_fn是什么,为什么需要它或如何制作它。

这让我更加困惑,因为不需要这样的功能来创建检查点,也不需要估计器从检查点开始“热身”。

所以我的问题是:

检查点和导出的最佳模型之间有什么区别?
服务输入接收器功能究竟是什么?如何编写? (我花了一些时间阅读tensorflow文档,并发现不足以理解我应该如何编写一个,以及为什么我必须这样做)。
如何训练估算器,保存最佳模型,然后再加载它。

为了帮助回答我的问题,我提供了此Colab文档。

这个自包含的笔记本会生成一些虚拟数据,将其保存在TF记录中,通过model_fn具有非常简单的自定义估算器,并使用使用TF Record文件的input_fn训练该模型。因此,对于某人来说,向我解释为输入服务接收器功能我需要做些什么占位符以及如何完成第三项就足够了。

更新资料

@GPhilo首先,对于您帮助我(并希望其他人)理解此事的深思熟虑和关心,我不能低估我的感谢。

我的“目标”(激励我问这个问题)是尝试建立一个可重用的培训网络框架,这样我就可以通过另一个build_fn并继续使用(加上具有导出模型的生活质量,尽早停止,等等)。

可以在Colab中找到更新的(基于您的答案)here。

在对您的答案进行多次阅读之后,我发现现在更加混乱了:

1。

您向推理模型提供输入的方式与您用于训练的方式不同

为什么?据我了解,数据输入管道不是:

load raw —> process —> feed to model

反而:

Load raw —> pre process —> store (perhaps as tf records)
# data processing has nothing to do with feeding data to the model?
Load processed —> feed to model

换句话说,我的理解(可能是错误的)是,tf Example / SequenceExample的目的是存储准备就绪的完整单个数据实体-除了读取TFRecord文件外,无需进行其他处理。

因此,训练/评估input_fn与推论/可能有所不同(例如,从文件读取与内存中的急切/交互式评估),但是数据格式相同(推论除外,您可能只希望提供数据) 1个示例,而不是一批…)

我同意“输入管道不是模型本身的一部分”。但是,在我看来,我的思维方式显然是错误的,使用估算器,我应该能够为它提供一批训练用的样本和一个单独的示例(或批处理)以进行推理。

旁白:“评估时,您不需要渐变,而需要其他输入函数。 ”,唯一的区别(至少就我而言)是您读取的文件?

我对TF指南很熟悉,但是我没有发现它有用,因为我不清楚我需要添加哪些占位符以及需要添加哪些其他操作来转换数据。

如果我用记录训练我的模型并且只想推断密集的张量怎么办?

切线地,在给定的tf记录界面要求用户多次定义如何在不同上下文中从tf记录文件写入特征或从中提取特征的情况下,我可以在链接的指南子表中找到该示例。此外,鉴于TF团队已明确表示他们对记录tf记录几乎没有兴趣,因此对我而言,基于tf记录构建的任何文档同样具有启发性。

关于tf.estimator.export.build_raw_serving_input_receiver_fn
占位符叫什么?输入?您是否可以通过编写等效的tf.estimator.export.build_raw_serving_input_receiver_fn来显示serving_input_receiver_fn的类似物
关于带有输入图像的示例serving_input_receiver_fn。您如何知道将特征称为“图像”,将接收器张量称为“ input_data”?那是(后者)标准吗?
如何使用signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY命名导出。

参考方案

检查点和导出的最佳模型之间有什么区别?

检查点至少是一个文件,其中包含在特定时间点获取的特定图的所有变量的值。
通过特定的图,我的意思是,当加载回检查点时,TensorFlow的作用是循环遍历图中定义的所有变量(正在运行的session中的一个)并在具有以下内容的检查点文件中搜索一个变量:与图中的名称相同。对于继续训练,这是理想的选择,因为您的图形在每次重新启动之间始终看起来相同。

导出的模型有不同的用途。导出模型的想法是,完成训练后,您希望获得一些可以用于推理的东西,其中不包含训练所特有的所有(繁重)部分(一些示例:梯度计算,全局步骤变量,输入管道...)。
而且,他的要点是关键,通常,您向推理模型提供输入的方式与您在训练中使用的方式不同。为了进行培训,您有一个输入管道,可将数据加载,预处理并将数据馈送到网络。此输入管道不是模型本身的一部分,可能需要更改以进行推断。这是使用Estimator进行操作时的关键点。

为什么需要服务输入接收器功能?

为了回答这个问题,我将首先退后一步。为什么我们在所有广告中都需要输入函数,它们是什么? TF的Estimator虽然可能不如其他建模网络的直观方式,但具有很大的优势:它们通过输入函数和模型函数将模型逻辑和输入处理逻辑明确分开。

模型处于三个不同的阶段:训练,评估和推理。对于最常见的用例(或者至少我目前能想到的所有用例),在TF中运行的图在所有这些阶段中都会有所不同。该图是输入预处理,模型以及在当前阶段运行模型所需的所有机械的组合。

希望有一些示例可以进一步阐明:训练时,您需要渐变以更新权重,运行训练步骤的优化器,监视事情进展的各种指标,从训练集中获取数据的输入管道等。 。评估时,您不需要渐变,而需要其他输入函数。当进行推理时,您只需要模型的前部,输入函数也将有所不同(没有tf.data.*内容,但通常只是一个占位符)。

Estimator中的每个阶段都有其自己的输入功能。您熟悉培训和评估的知识,推论只是您的serving input receiver函数。在TF术语中,“服务”是打包训练后的模型并将其用于推理的过程(有一个完整的TensorFlow服务系统可用于大规模操作,但这超出了这个问题,您很可能根本就不需要它)。

是时候引用TF guide on the topic了:

在训练期间,input_fn()会摄取数据并准备供以下人员使用
该模型。在投放时,serving_input_receiver_fn()
接受推理请求并为模型做好准备。这个
函数具有以下目的:

要将占位符添加到服务系统将要提供的图表中
与推理请求。
添加转换所需的任何其他操作
数据从输入格式转换为预期的特征张量
模型。

现在,服务输入功能规范取决于您计划如何将输入发送到图形。

如果要将数据打包在(序列化的)tf.Example中(类似于TFRecord文件中的记录之一),则您的服务输入函数将具有字符串占位符(用于示例中的序列化字节) ),并且需要有关如何解释该示例的规范,以提取其数据。如果这是您想要的方式,我邀请您看一下上面链接的指南中的示例,它本质上显示了如何设置如何解释示例并对其进行解析以获取输入数据的规范。

相反,如果您打算将输入直接馈送到网络的第一层,则仍然需要定义服务输入功能,但是这次它将仅包含一个占位符,该占位符将直接插入网络。 TF提供了一个功能来实现:tf.estimator.export.build_raw_serving_input_receiver_fn

那么,您实际上是否需要编写自己的输入函数?如果您需要的是占位符,否。只需将build_raw_serving_input_receiver_fn与适当的参数一起使用即可。如果需要更高级的预处理,那么可以,您可能需要编写自己的预处理程序。在这种情况下,它将看起来像这样:

def serving_input_receiver_fn():
  """For the sake of the example, let's assume your input to the network will be a 28x28 grayscale image that you'll then preprocess as needed"""
  input_images = tf.placeholder(dtype=tf.uint8,
                                         shape=[None, 28, 28, 1],
                                         name='input_images')
  # here you do all the operations you need on the images before they can be fed to the net (e.g., normalizing, reshaping, etc). Let's assume "images" is the resulting tensor.

  features = {'input_data' : images} # this is the dict that is then passed as "features" parameter to your model_fn
  receiver_tensors = {'input_data': input_images} # As far as I understand this is needed to map the input to a name you can retrieve later
  return tf.estimator.export.ServingInputReceiver(features, receiver_tensors)

如何训练估算器,保存最佳模型,然后再加载?

您的model_fn采用mode参数,以便您有条件地构建模型。例如,在您的合作实验室中,您总是有一个优化器。这是错误的,因为它只应存在于mode == tf.estimator.ModeKeys.TRAIN处。

其次,您的build_fn具有一个无意义的“输​​出”参数。此函数应表示您的推理图,仅将您将在推理中馈入的张量作为输入,并返回logit /预测。
因此,我假定outputs参数不存在,因为build_fn签名应为def build_fn(inputs, params)

此外,您定义model_fn以将features作为张量。尽管可以做到这一点,但这不仅限制了您只能输入一个内容,而且使serving_fn变得复杂(您不能使用罐装的build_raw_...,而需要编写自己的并返回TensorServingInputReceiver)。我将选择更通用的解决方案,并假设您的model_fn如下(为简洁起见,我省略了变量范围,请根据需要添加):

def model_fn(features, labels, mode, params): 
  my_input = features["input_data"]
  my_input.set_shape(I_SHAPE(params['batch_size']))

  # output of the network
  onet = build_fn(features, params)
  predicted_labels = tf.nn.sigmoid(onet)
  predictions = {'labels': predicted_labels, 'logits': onet}
  export_outputs = { # see EstimatorSpec's docs to understand what this is and why it's necessary.
       'labels': tf.estimator.export.PredictOutput(predicted_labels),
       'logits': tf.estimator.export.PredictOutput(onet) 
  } 
  # NOTE: export_outputs can also be used to save models as "SavedModel"s during evaluation.

  # HERE is where the common part of the graph between training, inference and evaluation stops.
  if mode == tf.estimator.ModeKeys.PREDICT:
    # return early and avoid adding the rest of the graph that has nothing to do with inference.
    return  tf.estimator.EstimatorSpec(mode=mode, 
                                       predictions=predictions, 
                                       export_outputs=export_outputs)

  labels.set_shape(O_SHAPE(params['batch_size']))      

  # calculate loss 
  loss = loss_fn(onet, labels)

  # add optimizer only if we're training
  if mode == tf.estimator.ModeKeys.TRAIN:
    optimizer = tf.train.AdagradOptimizer(learning_rate=params['learning_rate'])
  # some metrics used both in training and eval
  mae = tf.metrics.mean_absolute_error(labels=labels, predictions=predicted_labels, name='mea_op')
  mse = tf.metrics.mean_squared_error(labels=labels, predictions=predicted_labels, name='mse_op')
  metrics = {'mae': mae, 'mse': mse}
  tf.summary.scalar('mae', mae[1])
  tf.summary.scalar('mse', mse[1])

  if mode == tf.estimator.ModeKeys.EVAL:
    return tf.estimator.EstimatorSpec(mode, loss=loss, eval_metric_ops=metrics, predictions=predictions, export_outputs=export_outputs)

  if mode == tf.estimator.ModeKeys.TRAIN:
    train_op = optimizer.minimize(loss, global_step=tf.train.get_global_step())
    return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op, eval_metric_ops=metrics, predictions=predictions, export_outputs=export_outputs)

现在,要设置导出部分,在您对train_and_evaluate的调用完成之后:

1)定义您的服务输入功能:

serving_fn = tf.estimator.export.build_raw_serving_input_receiver_fn(
                                       {'input_data':tf.placeholder(tf.float32, [None,#YOUR_INPUT_SHAPE_HERE (without batch size)#])})

2)将模型导出到某个文件夹

est.export_savedmodel('my_directory_for_saved_models', serving_fn)

这会将估算器的当前状态保存到您指定的任何位置。如果需要特定的检查点,请在调用export_savedmodel之前加载它。
这将在“ my_directory_for_saved_models”中保存一个预测图,其中包含调用导出函数时估算器具有的训练参数。

最后,您可能不希望冻结图形(查找freeze_graph.py)并对其进行优化以进行推理(查找optimize_for_inference.py和/或transform_graph),以获得冻结的*.pb文件,然后可以将其加载并用于推理你希望。

编辑:在更新中为新问题添加答案

边注:

我的“目标”(激励我提出这个问题)是尝试建立一个
可重复使用的培训网络框架,这样我就可以通过
不同的build_fn和go(加上具有以下质量特征的生活
导出模型,提前停止等)。

如果可以的话,请务必将其发布在GitHub上的某个地方并将其链接到我。我一直试图将相同的东西启动并运行一段时间,结果并不像我希望的那样好。

问题1:

换句话说,我的理解(也许是错误的)是
tf Example / SequenceExample的要点是存储一个完整的
单个基准实体准备就绪-无需其他处理
比从TFRecord文件读取。

实际上,通常不是这种情况(尽管从理论上讲,您的方法也完全可以)。
您可以将TFRecords视为一种(详细记录的方式)以紧凑的方式存储数据集。例如,对于图像数据集,记录通常包含压缩的图像数据(如组成jpeg / png文件的字节),其标签和一些元信息。然后,输入管道读取一条记录,对其进行解码,根据需要对其进行预处理,然后将其馈送到网络。当然,您可以在生成TFRecord数据集之前移动解码和预处理,并在示例中存储随时可提供的数据,但是数据集的规模将非常庞大。

特定的预处理管道是一个在阶段之间进行更改的示例(例如,您可以在训练管道中进行数据扩充,而在其他阶段则没有)。当然,在某些情况下,这些管道是相同的,但总的来说并非如此。

关于一旁:

“评估时,您不需要渐变,您需要
不同的输入功能。 ”,唯一的区别(至少就我而言)
从中读取文件?

在您的情况下可能是这样。但是,再次假设您使用的是数据增强:您需要在eval期间将其禁用(或者更好的是完全不使用它),这会改变您的管道。

问题2:如果我使用记录训练模型并只想推断密集的张量怎么办?

这就是为什么您将管道与模型分开的原因。
该模型将张量作为输入并对其进行操作。张量是占位符,还是将子图从Example转换为张量的子图的输出,那是属于框架的细节,而不是模型本身。

分割点是模型输入。该模型期望张量(或更常见的情况是name:tensor项的字典)作为输入,并使用该张量构建其计算图。输入的来源由输入函数决定,但是只要所有输入函数的输出具有相同的接口,就可以根据需要交换输入,并且模型将简单地获取并使用它。

因此,回顾一下,假设您使用“示例”进行训练/评估并使用密集张量进行预测,那么您的“训练”和“评估”输入函数将建立一个管道,该管道从某处读取示例,将其解码为张量,然后将其返回给模型以用作输入。另一方面,您的预测输入函数只需为模型的每个输入设置一个占位符,然后将其返回给模型,因为它假定您将把准备好要馈送到网络的数据放入占位符中。

问题3:

您将占位符作为build_raw_serving_input_receiver_fn的参数传递,因此您选择其名称:

tf.estimator.export.build_raw_serving_input_receiver_fn(                                               
    {'images':tf.placeholder(tf.float32, [None,28,28,1], name='input_images')})

问题4:

代码中有一个错误(我混淆了两行),字典的键应该是input_data(我修改了上面的代码)。
字典中的键必须是用于从features中的model_fn检索张量的键。在model_fn中,第一行是:

my_input = features["input_data"]

因此密钥是'input_data'
根据receiver_tensor中的键,我仍然不太确定该角色是什么,所以我的建议是尝试设置一个与features中的键不同的名称,并检查该名称在何处显示。

问题5:

我不确定自己是否了解,我将在澄清后对其进行编辑

扩展Python时可以使用C++功能吗? - c++

Python手册说您可以在C和C++中为Python创建模块。使用C++时可以利用类和模板之类的东西吗?它不会与其他库和解释器产生不兼容吗? 参考方案 挂钩函数的实现是用C还是用C++实现都没有关系。实际上,我已经看过一些Python扩展,这些扩展有效利用C++模板甚至Boost库。没问题。 :-)

在Python和C++之间传输数据而无需写入Windows和Unix文件 - python

我有预先存在的python和C ++文件,其中python文件定义了许多点,而C ++代码利用其现有库进行了所需的计算。最终产品是C ++代码写入的文件。我正在寻找一种在python中获取2000点列表的方法,将其传递给函数,然后执行所有C ++代码并输出我需要的文件。其他注意事项。这必须是可以在Linux或Windows机器上工作的东西,并且最少安装新插件…

将Python嵌入C++应用程序 - c++

上下文:我们一直面临的一个持续问题是对我们的市场数据应用程序进行单元测试。这些应用程序坐下来观察从提要中检索到的数据并执行某些操作。一些很难触发的关键事件很少发生,并且测试人员很难在所有情况下验证我们的应用程序是否正常运行,因此我们必须依靠单元测试。这些系统通常通过在事件发生时发出回调(进入我们的应用程序)来工作,然后由我们负责处理此事件。 我设想的解决方案…

Python的C++名称处理库 - c++

Improve this question 我想在Python程序中修改和分解C++函数名。有没有类似的东西可用?我搜索了几个小时,也许我很幸运在这里... 参考方案 您很可能不想在Python中执行此操作。顺便说一句,您可能不应该从DLL中导出错误的名称,因为这样会使具有不同编译器的人难以使用。如果必须使用变形的名称,则只需在Python代码中对其进行硬编…

用于Python和C++应用程序的简单但快速的IPC方法? - c++

我有一个同时使用Python和C++代码的GNU Radio应用程序。我希望能够表示事件的C++代码。如果它们在同一范围内,我通常会使用一个简单的布尔值,但是代码是分开的,因此需要某种形式的共享内存。有问题的代码对性能至关重要,因此需要一种有效的方法。我最初考虑的是可由Python和C++访问的共享内存段。因此,我可以在python代码中设置一个标志,并从C…