[软件工程与实践]lingvo学习
2021SC@SDUSC
读论文
论文链接
lingvo组成成分
Models : 模型是一个或多个任务的抽象集合。对于单任务模型,模型和任务可认为相同,对于多任务模型,模型控制任务间的变量共享以及训练模型的采样。
Tasks : 任务是一个完整的优化问题的规范,如图像分类、语音识别。它包含input generator、多层神经网络、损失函数和优化器,并在每个训练步骤中更新模型参数。
Layers : 表示可能具有可训练参数的任意函数。层可以包含其他层作为子层。如SoftMax、LSTM、注意力机制。
Input Generator : lingvo input generator专门用于序列,允许在多个桶中批处理不同长度的输入,并自动将它们填充到相同的长度。由此可看出lingvo应用的不是循环神经网络。支持跨多个输入文件的大型数据集。
Params : 参数对象包含模型的超参数。可将其视为本地版本的 tf.flags。层、任务和模型都是根据其参数中的规范构建的。参数是分层的,这意味着对象的参数可以包含参数配置子对象。
Experiment Configurations : 每个实验都在自己的类中定义,完全定义了实验的各个方面,从学习率、优化器参数等超参数,到影响模型图结构的选项,到输入数据集等杂项选项。
这些独立的配置类使得跟踪每个实验使用的参数和复制过去的实验变得很容易。它还允许配置从其他配置继承。
所有的实验参数都注册在一个中央注册表中,并且可以通过它的名称来引用,如image.mnist.LeNet5。
Job Runners : Lingvo的训练机制分为不同的工作。例如,Controller作业负责编写检查点,而Evaler作业在最新检查点上评估模型。
NestedMap : NestedMap是用于任意结构化数据的通用字典结构,类似于 tf.contrib.framework.nest。它被广泛应用于Lingvo来传递数据。代码中的大多数python对象都是Tensor、BaseLayer的子类或NestedMap的实例。
Custom Ops : Lingvo支持用c++编写的自定义op内核,以获得高性能的代码。例如,自定义操作用于输入管道、波束搜索和标记化。
核心lingvo API
参数
Params类是一个字典,其中显式定义了用于配置的键。在创建对象时应该定义键,并且试图访问或修改会引发异常的不存在的键。在实践中,每个Layer都有一个Params类方法,它创建一个新的Params对象,并定义用于配置具有合理默认值的层的键。然后,在单独的实验配置类中,这些默认值被特用于实验的值覆盖。
层
为了构造一个层,需要这些层的Params的实例。
参数包括如下细节:
- cls:the layer’s class
- name: the layer’s name
- params_init: 该层创建的变量的初始化方法。
因为类包含在参数中,以下构造层的方法是等价的:
p = SomeLayerClass.Params()
layer = SomeLayerClass(p) # 调用构造函数
layer = p.cls(p) # 参数调用构造函数
所有层都有一个FProp()函数,该函数在计算的前向步骤中被调用。子层可以使用self在构造函数中创建。createcchild ('child_name ', child_params)
,它们可以被self.child_name
引用。
变量管理
每个层创建并管理自己的变量。
变量是通过调用self.CreateVariable()
在层的__init__()方法中创建的,它注册self.vars的
变量和self.theta
变量的值(self.theta
可能在变换之后,比如添加变分噪声)。在FProp()中,由于它可能在分布式训练中在不同的设备上执行,出于性能原因,最好通过传递给函数的theta参数而不是self.vars或self.theta。
变量的位置由cluster.GetPlacer()
函数决定。默认策略是将每个变量放在分配的字节最少的参数服务器上。对于模型并行性,基于显式策略例如可采用可变范围。
显式管理变量比使用tf . get_variable
变量有很多好处:
- 它支持诸如重量噪声之类的研究思路
- variable_scope构造可能容易出错且可读性较差,例如意外的变量复用
- 对于同步复制训练,在同一台机器上的多个作业之间共享权重是很尴尬的。
输入处理
Lingvo支持纯文本或TFRecord格式的输入。序列输入可以通bucket_upper_bound和bucket_batch_limit参数按长度进行bucket。
可以为文本输入指定分词器。可用分词器包括VocabFileTokenizer作为文件提供的查询表,BpeTokenizer用于字节对编译,WpnTokenizer用于word-piece模型。
输入文件模式应该通过file_pattern
参数指定为 type: glob_pattern
。输入处理器应该实现
_datasourcefromfileppattern()方法,该方法返回一个操作,该操作在执行时从文件中读取并返回一些张量。通常,这个操作是使用RecordProcessor接口实现的自定义c++操作。这个操作返回的张量可以通过调用_BuildDataSource()来检索,并且可以用来填充由InputBatch()方法返回的输入批处理NestedMap。最后,批级预处理也可以在PreprocessInputBatch()中实现。
除了使用自定义的RecordProcessor op外,还可以通过generic_input op在Python中直接定义输入处理器。
模型配准
配置类位于lingvo/tasks/<task>/params/<param>.py
中,用对于单任务模型的典型情况@model_registry.RegisterSingleTaskModel
注释。该注释将类添加到模型注册中心,其键值为<task >.< param >.< classname >
(如 image.mnist.LeNet5
)。该类应该是SingleTaskModelParams的子类,并实现Task()方法,该方法返回一个配置Task的Params实例。注册代码将自动将Task包装到SingleTaskModel中。类还应该实现Train()、Test(),可能还有Dev()方法。这些方法返回一个配置输入生成器的Params实例,并表示不同的数据集。
如
@model_registry . RegisterSingleTaskModel
class MyTaskParams ( base_model_params . SingleTaskModelParams ):
@classmethod
def Train ( cls ):
... # Input params .
@classmethod
def Task ( cls ):
p = my_model . MyNetwork . Params ()
p. name = 'my_task '
...
return p
从控制行重载参数
可以使用——model_params_override或——model_params_file_override标志覆盖特定运行的任何超参数的值。
这使得启动超参数调优的类似工作变得很简单。
断言
py_utils.py包含用于运行时断言值和形状的函数,以及用于检测nan的CheckNumerics()。可以使用命令行——enable_assert = false禁用断言。类似地,可以使用——enable_check_numerics = false禁用CheckNumerics。
代码布局
- lingvo
- trainer.py
- model_imports.py
- core
- base_input_generator.py
- base_layer.py
- base_model.py
- cluster.py
- hyperparams.py
- attention.py, layers.py, rnn_cell.py, rnn_layers.py
- optimizer.py
- py_utils.py
- recurrent.py
- slummary_utils.py
- ops
- record_*.*
- py_x_ops.py
- x_ops.cc
- tasks
- + params
- tools