TF1 Hub 格式

TensorFlow Hub 于 2018 年推出时,提供了一种类型的资产:TF1 Hub 格式,用于导入到 TensorFlow 1 程序中。

此页面说明了如何使用 hub.Module 类和关联的 API 在 TF1(或 TF2 的 TF1 兼容模式)中使用 TF1 Hub 格式。(典型用法是通过将一个或多个 TF1 Hub 格式的模型与 tf.compat.layerstf.layers 结合,构建一个 tf.Graph,可能在 TF1 Estimator 内)。

TensorFlow 2 的用户(在 TF1 兼容模式之外)必须使用 带有 hub.load()hub.KerasLayer 的新 API。新 API 加载新的 TF2 SavedModel 资产类型,但对在 TF2 中加载 TF1 Hub 格式的支持也受到限制

使用 TF1 Hub 格式的模型

实例化 TF1 Hub 格式的模型

通过使用其 URL 或文件系统路径(如)的字符串创建 hub.Module 对象,将 TF1 Hub 格式的模型导入到 TensorFlow 程序中

m = hub.Module("path/to/a/module_dir")

注意:此处了解有关其他有效句柄类型的更多信息。

这会将模块的变量添加到当前 TensorFlow 图表中。运行它们的初始化程序会从磁盘读取其预训练值。同样,表和其他状态也会添加到图表中。

缓存模块

从 URL 创建模块时,模块内容会被下载并缓存在本地系统临时目录中。可以使用 TFHUB_CACHE_DIR 环境变量覆盖模块的缓存位置。有关详细信息,请参阅 缓存

应用模块

实例化后,可以将模块 m 从张量输入调用到张量输出,就像 Python 函数一样,可以调用零次或多次

y = m(x)

每次此类调用都会向当前 TensorFlow 图表添加操作,以从 x 计算 y。如果这涉及具有训练权重的变量,则这些变量将在所有应用程序之间共享。

模块可以定义多个命名的签名,以便允许以多种方式应用(类似于 Python 对象具有方法的方式)。模块的文档应描述可用的签名。上面的调用应用了名为 "default" 的签名。可以通过将名称传递给可选的 signature= 参数来选择任何签名。

如果签名有多个输入,则必须将其作为字典传递,其中键由签名定义。同样,如果签名有多个输出,则可以通过传递 as_dict=True 将其作为字典检索,其中键由签名定义(如果 as_dict=False,则键 "default" 用于返回的单个输出)。因此,应用模块的最通用形式如下所示

outputs = m(dict(apples=x1, oranges=x2), signature="fruit_to_pet", as_dict=True)
y1 = outputs["cats"]
y2 = outputs["dogs"]

调用方必须提供签名定义的所有输入,但没有要求使用模块的所有输出。TensorFlow 仅运行模块中最终作为 tf.Session.run() 中目标的依赖项的部分。实际上,模块发布者可以选择为高级用途(如中间层的激活)提供各种输出以及主要输出。模块使用者应妥善处理其他输出。

尝试替代模块

每当针对同一任务有多个模块时,TensorFlow Hub 鼓励为其配备兼容的签名(接口),以便尝试不同的模块就像将模块句柄作为字符串值超参数进行更改一样容易。

为此,我们维护了一系列针对热门任务推荐的 通用签名

创建新模块

兼容性说明

TF1 Hub 格式面向 TensorFlow 1。它仅在 TensorFlow 2 中得到 TF Hub 的部分支持。请考虑以新的 TF2 SavedModel 格式发布。

TF1 Hub 格式在语法层面类似于 TensorFlow 1 的 SavedModel 格式(相同的文件名和协议消息),但在语义上有所不同,以便允许模块重用、组合和重新训练(例如,资源初始化器的不同存储、元图的不同标记约定)。在磁盘上区分它们最简单的方法是是否存在 tfhub_module.pb 文件。

一般方法

要定义一个新模块,发布者使用一个函数 module_fn 调用 hub.create_module_spec()。此函数使用 tf.placeholder() 构建表示模块内部结构的图,以便调用者提供输入。然后,它通过多次调用 hub.add_signature(name, inputs, outputs) 来定义签名。

例如

def module_fn():
  inputs = tf.placeholder(dtype=tf.float32, shape=[None, 50])
  layer1 = tf.layers.dense(inputs, 200)
  layer2 = tf.layers.dense(layer1, 100)
  outputs = dict(default=layer2, hidden_activations=layer1)
  # Add default signature.
  hub.add_signature(inputs=inputs, outputs=outputs)

...
spec = hub.create_module_spec(module_fn)

可以使用 hub.create_module_spec() 的结果(而不是路径)在特定 TensorFlow 图中实例化模块对象。在这种情况下,没有检查点,模块实例将使用变量初始化器。

可以通过 export(path, session) 方法将任何模块实例序列化到磁盘。导出模块会将其定义与 session 中其变量的当前状态一起序列化到传递的路径中。这可以在首次导出模块时以及导出微调模块时使用。

为了与 TensorFlow Estimators 兼容,hub.LatestModuleExporter 从最新检查点导出模块,就像 tf.estimator.LatestExporter 从最新检查点导出整个模型一样。

模块发布者应尽可能实现通用签名,以便消费者可以轻松交换模块并找到最适合其问题的模块。

实际示例

查看我们的文本嵌入模块导出器,了解如何从通用文本嵌入格式创建模块的真实示例。

微调

将导入模块的变量与其周围模型的变量一起训练称为微调。微调可以提高质量,但会增加新的复杂性。我们建议消费者在探索更简单的质量调整后才考虑微调,并且仅在模块发布者建议时才进行微调。

对于消费者

要启用微调,请使用 hub.Module(..., trainable=True) 实例化模块,使其变量可训练,并导入 TensorFlow 的 REGULARIZATION_LOSSES。如果模块有多个图变体,请确保选择适合训练的变体。通常,带有 {"train"} 标记的变体就是合适的。

选择一种不会破坏预训练权重的训练方案,例如,比从头开始训练时更低的学习率。

对于发布者

为了让消费者更容易进行微调,请注意以下事项

  • 微调需要正则化。您的模块使用 REGULARIZATION_LOSSES 集合导出,该集合将您的 tf.layers.dense(..., kernel_regularizer=...) 等选择放入使用者从 tf.losses.get_regularization_losses() 中获取的内容。优先使用这种方式定义 L1/L2 正则化损失。

  • 在发布者模型中,避免通过 tf.train.FtrlOptimizertf.train.ProximalGradientDescentOptimizer 和其他邻近优化器的 l1_l2_regularization_strength 参数定义 L1/L2 正则化。这些参数不会与模块一起导出,并且全局设置正则化强度可能不适合使用者。除了在宽(即稀疏线性)或宽深模型中使用 L1 正则化外,应该可以改用单独的正则化损失。

  • 如果您使用 dropout、批量归一化或类似的训练技术,请将其超参数设置为在许多预期用途中有意义的值。dropout 率可能必须根据目标问题的过度拟合倾向进行调整。在批量归一化中,动量(又称衰减系数)应足够小,以便使用小数据集和/或大批次进行微调。对于高级使用者,请考虑添加一个签名,以公开对关键超参数的控制。