译自:http://deeplearning.net/software/theano/extending/graphstructures.html#graphstructures

理解Theano计算原理的关键

建议阅读时间:10分钟

如果不明白内在运行机制,Theano代码的调试工作并非易事。本章就简单介绍了Theano的内部工作机理。

编写Theano code的第一步便是用符号占位符(或符号变量)书写数学表达式。表达式中的操作符包括+,-,**,sum(),tanh()等。所有这些操作符都有内部的ops。一个op表示在某种类型输入上的某种计算产生某种类型的输出,可以理解为常见编程语言中的函数定义。

Theano将符号的数学运算表示为图(Graphs)。这些图是由连接点构成,连接点(interconnected nodes)包括:Apply Node, Variable Node, 以及 Op Node。Apply Node表示将一个op应用到某些variable上。很重要的一点是,能够区分用op表示的计算的定义及其用apply表示的在实际数据上的应用。数据类型用Type的实例表示。以下是一段代码及其对应的图来展示这段代码的结构。从而可以更好的将这些细节联系在一起。

箭头表示对所指向的Python对象的引用。蓝色矩形框表示Apply /node,红色圆角矩形表示Variable Node,绿色圈表示Ops,紫色框表示类型。我们创建Variables,然后应用Apply ops到这些变量,从而得到更多Variables,我们构建二部有向无环图。Variables指向Apply Nodes,这表示函数应用,即通过变量的owner域来产生这些变量。这些Apply Node通过他们的input和output域,轮流指向他们的输入和输出Variables。Apply实例也包含outputs的引用列表,但是本文中的这幅图并没有考虑。

由于x与y两个输入的符号变量并不是其他运算的结果,所以他们的owner域都指向的是None。如果有些符号变量是其他一些运算的结果,则它的ownerfield应该像z一样指向的是另外一个蓝色框。值得注意的是,Apply实例的output域指向的是z,z的owner域指向的是Apply实例,所以是一个双向箭头。

遍历图(Traversing the Graph)

使用owner域,从输出(计算的结果)开始遍历到输入。以下面的code为例来说明:

若输入:type(y.owner),得到结果:<class 'theano.gof.graph.Apply'>,也就是说它是Apply Node,连接了op变量和输入来得到这个output。我们可以打印这个用于得到y的op的名称:

从而可知,用elementwise multiplication运算得到了y。这种Multiplication是两个输入之间的:

值得注意的是,第二个输入并不是我们所想象的2。这是因为2首先被broadcasted成为一个和x同样大小的矩阵,然后再做multiplication的运算。这种变化使用的是DimShuffle这种op:

从这个图结构,我们可以很容易理解,这种自动微分是如何进行的,以及符号关系如何被优化以获得更稳定的性能。

图结构(Graph Structure)

本节罗列了Theano内部计算图中常用的结构类型,包括:Apply, Constant, Op, Variable以及Type。

Apply. Apply Node是Theano计算图中的一种内部节点。与Variable Node不同,Apply Node通常并不能直接被终端用户所操作,可以通过Variable的owner域被访问。一个Apply Node是Apply类的实例。它表示了一个Op应用再一个或多个input上,其中每个input都是一个Variable。通常来讲,每个Op都负责知道如何从一个input Varible的列表获得Apply Node。因此,一个Apply Node可以通过调用Op.make_node(*inputs),从一个Op和一个input Variable的列表获得。与Python语言相比,一个Apply Node是一个Theano版本的函数调用,而Op则是Theano版本的函数定义。一个Apply实例包括以下三个重要的域:(1)op:Op决定了使用哪种函数或变形。(2)inputs:Variable列表,表示函数的参数。(3)outputs:Variable列表,表示函数的返回值。通过调用gof.Apply(op,inputs,outputs)来创建Apply实例。

Op.Theano中一个Op定义了在某些类型inputs上的某种类型的运算,从而产生某些类型的输出。这对应于大多数编程语言中的函数定义。从一个input Variable的列表以及一个Op,我们可以构建Apply node表示将该Op应用到这些inputs上。能够理解Op与Apply之间的区别是非常重要的:Op相当于一个函数的定义,而Apply相当于一个函数的调用和使用。如果你想用Theano的结构来解释Python语言,代码是这样的def f(x):...将产生一个名称为f的Op,而代码a=f(x)或g(f(x),5)将产生一个涉及到名为f的Op的Apply Node。

Type.Theano中的Type表示的是对可能的数据对象的约束集合。这些约束允许Theano来对C代码进行裁剪,以能够处理它们,并能对计算图进行优化。举例来说,theano.tensor包中的irow类型定义了下述对数据的约束,也就是说具有irow类型的Variable需要包含:(1)必须是一个numpy.ndarray类型的实例(即isinstance(x,numpy.ndarray));(2)必须是一个32位整数类型的array(str(x.dtype)=='int32');(3)必须是1*N大小(len(x.shape)==2 and x.shape[0]==1)。知道了这些约束,Theano可以在产生例如加法这样的C代码的过程中声明正确的数据类型,包含维度上的正确循环次数。值得注意的是Theano的Type并不等同于Python的type或类。实际上,Theano中irow和dmatrix本质上都是numpy.ndarray类型,按该类型来做运算和存储数据,但是Theano的Type确实有些不同之处。例如,dmatrix的约束结合是:(1)必须是一个numpy.ndarray类型的实例(isinstance(x,numpy.ndarray));(2)必须是一个64位浮点数的array(str(x.dtype)=='float64');(3)必须是大小为M*N的,对M或N没有限制(len(x.shape)==2)。这些约束是与irow的不同。有些情况theano Type可以完全对应到Python类型的情况,如这里的double对应Python的float。除非特别指明,我们说的Type都是指Theano Type。

Variable.Variable是Theano中主要的数据结构。我们操作的符号输入是Variables,我们使用各种各样的符号在这些inputs得到的结果也是Variables。举个例子来说明:import theano x=theano.tensor.ivector() y=-x。其中x与y都是Variables,也就是Variable类的实例。这里x与y的类型都是theano.tensor.ivector。不像x,y是经过计算得到的Variable。y是运算的输出Variable,x对应的是它的输入Variable。计算本身用另外一种Node,即Apply Node来表示,可以通过y.owner来访问。更具体来讲,一个Variable是Theano中的一个基本结构,它表示的是在计算过程中的某个点。通常来讲,它是Variable类的或它的子类的实例。一个Variable r包含以下四个重要的域:(1)type: 运算中Variable所采用的值的类型;(2)owner:要么是None要么是Apply Node(当Variable是output时);(3)index:r在owner.outputs中的索引owner.outputs[index]=r,若owner=None即可忽略;(4)name: 在pretty-printing以及debugging中使用的字符串。Variable有一个特殊的子类:Constant。

Constant.Constant是一个具有额外域的Variable,该域为data,仅有一次设置机会。当在计算图(Computation Graph)中用作Op的input时,假设该input总会取data域中的值。进一步讲,假设Op在任何情况下都不会改变这种输入。这就意味着Constant适合于好多优化:C代码中的constant内联,constant folding等。一个Constant不必要通过function函数来指定一个输入Variable的列表。如果这样做的话会带来异常。

图结构扩展(Graph Structures Extension)

当我们开始编译Theano函数时,我们也计算一些额外的信息。本节描述了一些可用信息的一部分。图在编译之初被cloned,所以在编译过程中任何改动并不会影响图结构。每个变量接受一个称为clients的新域。这个域是对图中每个使用该Variable的位置的引用列表。若该长度为0,也就意味着该Variable没有被使用。该变量被使用的每个位置用一个二元组表示。有两种类型的二元组:(1)第一个元素为Apply Node,第二个元素为index:var.clients[*][0].inputs[index]或fgraph.outputs[index]就是这个变量;(2)第一个元素为字符串Output,意思是函数的输出为该变量,第二个元素为index:var.clients[*][0].inpus[index]或fgraph.outputs[index]就是这个变量。

自动微分(Automatic Differentiation)

一旦有了图结构,自动计算微分变得容易。唯一要做的事情就是调用tensor.grad(),该方法从输出开始回溯到输入,通过所有的Apply Nodes来遍历图,apply nodes是那些定义了图中要做哪些运算的节点。对于每个这样的Apply Node,它的op定义了如何计算该node的输出对输入的梯度。值得注意的是若一个op并没有提供这种信息,那么认为梯度没有定义。为了得到整个图的输出对输入的梯度,需要使用链式法则对这些梯度进行组合。该tutorial的下一章会对微分做详细介绍。

优化(Optimization)

当编译一个Theano function时,我们需要给theano.function提供的一个图结构(从输出Variable,我们可以遍历整个图,到达输入Variable。)。整个图结构不但展示了如何从输入计算得到输出,而且给我们提供了对计算进行改进的可能。theano的优化方式时识别并用其它以更快的速度或更稳定的产生相同结果的pattern替换其中的一些pattern。优化也能探测相似的子图,从而确保同一值不被计算两次,或者重构部分图以适应到GPU版本。例如,Theano中一个简单的优化时用x来代替pattern xy/y。更详尽的介绍见文档。

Theano入门笔记1:Theano中的Graph Structure的更多相关文章

  1. Theano入门笔记2:scan函数等

    1.Theano中的scan函数 目前先弱弱的认为:相当于symbolic的for循环吧,或者说计算图上的for循环,也可以用来替代repeat-until. 与scan相比,scan_checkpo ...

  2. Theano学习笔记:Theano的艰辛安装体验

    http://www.cnblogs.com/hanahimi/p/4127026.html

  3. 母函数入门笔记(施工中…

    定义:对于一个数列,它的母函数(即生成函数)为   为了对这个准确求值,我们设    举一个简单的例子 例1 对于数列 他的生成函数为 ,那么应用一下等比数列求和公式 这里由于 所以当时 那么   例 ...

  4. Theano 学习笔记(一)

    Theano 学习笔记(一) theano 为什么要定义共享变量? 定义共享变量的原因在于GPU的使用,如果不定义共享的话,那么当GPU调用这些变量时,遇到一次就要调用一次,这样就会花费大量时间在数据 ...

  5. Ruby小白入门笔记之 <Gemfile 文件>

    因为初学Ruby,四处查资料无果,才来的贴出亲自试过的操作,覆盖整个个人入门笔记博客中,故所有的操作,都以最明了的方式阐述,当你创建完一个新的Rails应用后,你发现JAVA中我们可以编写maven聚 ...

  6. Theano学习笔记(一)——代数

    标量相加 import theano.tensor as T from theano import function x = T.dscalar('x') y = T.dscalar('y') z = ...

  7. Theano入门——CIFAR-10和CIFAR-100数据集

    Theano入门——CIFAR-10和CIFAR-100数据集 1.CIFAR-10数据集介绍 CIFAR-10数据集包含60000个32*32的彩色图像,共有10类.有50000个训练图像和1000 ...

  8. Theano Graph Structure

    Graph Structure Graph Definition theano's symbolic mathematical computation, which is composed of: A ...

  9. Theano入门神经网络(三)

    附录一个:Keras学习随笔 http://blog.csdn.net/niuwei22007/article/details/49045909 参考 <Python Machine Learn ...

随机推荐

  1. SpringBootSecurity学习(07)网页版登录整合JDBC

    数据库中定义用户 前面我们定义用户是在配置文件和代码中定义死的默认用户,一般在开发中是不会这样做的,我们的用户都是来自我们的用户表,存储在数据库中.操作数据库的技术有很多,spring securit ...

  2. 《JAVA高并发编程详解》-Thread对象的启动

    当我们用关键字new创建一个Thread对象时,此时它并不处于执行状态,因为没有调用start方法启动该线程,那么线程的状态为NEW状态,NEW状态通过start方法进入RUNNABLE状态. 线程一 ...

  3. nodejs的交互式解释器模式常用命令

    node - 进入交互器交互器解释模式 ctrl + c - 退出当前终端 ctrl + c 按下两次 - 退出 Node REPL ctrl + d - 退出 Node REPL 向上/向下 键 - ...

  4. ASP.NET WebApi 学习与实践系列(2)---WebApi 路由请求的理解

    目录 写在前面 WebApi Get 请求 1.无参数的请求 2.一个参数的请求 3.多个参数的请求 4.实体参数的请求 WebApi Post 请求 1.键值对请求 2.dynamic 动态类型请求 ...

  5. 手写MQ框架(三)-客户端实现

    一.背景 书接手写MQ框架(二)-服务端实现  ,前面介绍了服务端的实现.但是具体使用框架过程中,用户肯定是以客户端的形式跟服务端打交道的.客户端的好坏直接影响了框架使用的便利性. 虽然框架目前是通过 ...

  6. ASUS笔记本,更换了固态硬盘,重装系统前后开机都自动进入BIOS界面

    解决方法:advanced标签中sata configration回车进入,如有识别硬盘设备,按F9恢复BIOS默认设置,按F10保存后重启. 如有自行安装过系统,Security-Secure Bo ...

  7. selenium获取元素

    1.获取窗口titledriver.title2.获取urldriver.current_url3.获取窗口截图driver.get_screenshot_as_file('window.png')4 ...

  8. TOML配置文件

    Toml是一种易读.mini语言,由github前CEO,Tom创建.Tom's Obvious, Minimal Language. TOML致力于配置文件的小型化和易读性.wiki:https:/ ...

  9. ireport(1.2.7)的IllegalAccessError异常

    IllegalAccessError异常: Exception in thread "main" java.lang.IllegalAccessError: tried to ac ...

  10. UML——从类图到C++

    简易软件开发流程 实践中,use case and description.class diagram与sequence diagram三者搭配,几乎是UML项目的基本类型,所以在分工或外包的设计文档 ...