计算图(ComputationGraph):表达式相当于一种隐含的计算图对象的一部分,该计算图定义了需要进行的计算是什么。DyNet 目前假定在任意一个时刻只有一个计算图存在。尽管计算图是 DyNet 内部工作的核心,但从使用者的角度来看,唯一需要负责做的是为每个训练样本创建一个新的计算图。 用 DyNet 中实现并训练一个模型的整体流程可描述如下: 创建一个模型; 向模型里增加必要的参数(Parameters)和查找表参数(LookupParameters); 创建一个训练器(Trainer)对象,并使之与模型(Model)相关联; 对每个样本(example): (a) 创建一个新的计算图(ComputationGraph),并且建立一个表达式(Expression)来填充该计算图,该表达式用来表示针对这个样本想要进行的计算。 (b) 通过调用最终表达式的 value() 或者 npvalue() 函数,计算整个图前向计算的结果。 (c) 如果训练的话,计算损失函数的表达式,并使用它的 backward() 函数来进行反向传播。 (d) 使用训练器对模型的参数进行更新。 与像 Theano 和 TensorFlow 这样的静态声明库对比可以发现,创建一个图的步骤落在每一个样本的循环里。这有利于使用户为每个实例(instance)灵活地创建新的图结构,并使用他们掌握的编程语言中的流控句法(flow control syntax,比如迭代(iteration))来做这些。当然,它也增加了对图结构速度的要求,即它要足够快,不能变成负担,我们会在§4 中进一步阐述。 3.2 高层面的示例 为了在更高层次说明 DyNet 的编码范式,我们用 Python 演示了一个 DyNet 程序的例子,如图 1 所示。这个程序显示了为一个简单分类器进行最大似然训练的过程,这个分类器为每个需要它预测的类计算一个向量分数,然后返回这个得分最高的类 ID 以及这个最高分。我们假定每个训练样本是一个(输入和输出)对,其中输入是一个二词索引的元组,输出是一个指示正确类的数。 图 1:一个使用 DyNet 的 Python API 进行训练和测试的例子。 在头两行,我们导入(import)适当的库。在第 3 行,我们初始化 DyNet 模型,并为相关参数分配内存空间,但是不初始化它们。在第 4—6 行,我们向模型里添加我们的参数,这个过程会因为使用的模型不同而不一样。这里我们增加一个 20 × 100 的权重矩阵、一个 20 维的偏置向量和一个查找表(嵌入表)——该查找表的词汇量大小为 20000 项映射到 50 维向量。在第 7 行,我们初始化了一个训练器(在这个例子中是一个简单的随机梯度降(SGD)训练器),这个训练器被用来更新模型参数。在第 8 行中,我们对数据进行多次训练和测试。 从第 9 行开始,我们对训练数据进行迭代。第 10 行,清除当前计算图的内容,开始一个空的计算图,为后续的计算做准备。第 11-13 行,我们创建一个图,这个图会为每个训练实例计算一个分数向量(这个过程会因为模型的不同而不同)。这里我们首先访问模型中的权重矩阵和偏置向量参数(W_p 和 b_p),并把它们加到图中,也就是这个代码例子中用到的表达式中(W 和 b_p)。然后我们根据输入的 id 来查找两个向量,拼接它们,然后做一个线性变换和 softmax,这样就创建了和计算相对应的表达式。接下来,我们在第 14 行创建一个与损失有关的表达式——对正确记分结果做一次 softmax 后的负对数似然估计。在第 15 行,我们计算前向图的结果,在第 16 行,我们计算后向的,并累计模型变量中参数的梯度。在第 17 行,我们根据 SGD 的更新规则更新这些参数,并清掉之前的累计梯度。 接下来,从第 18 和 19 行开始,我们遍历测试数据并测量准确度。在第 20-23 行,我们又一次清除计算图以及定义计算测试数据分数的表达式,方式和我们在训练数据中做的一样。在第 24 行,我们开始计算并把结果数据放到一个 NumPy 的数组里。在第 25 和 26 行,我们检查是否正确的数据是最高分的那个,如果是的话就把它算作是一个正确的结果。最后第 27 行,我们把本次迭代的测试准确度 print 出来。 3.3 动态图构建(Dynamic Graph Construction)的两个示例 图 2:树结构递归神经网络(tree-structured recursive neural network)的一个例子
图 3:动态流控制的一个示例。 4 后台工作 (责任编辑:本港台直播) |