
基础In :%matplotlib inlineimport numpy as npimport matplotlib.pyplot as plt首先, 把那个东西导入进来, 还有它的子模块, 就是张量这个部分:In :import theano# 一般都把 tensor 子模块导入并命名为 Timport theano.tensor as TUsing gpu device 1: Tesla K10.G2.8GB (CNMeM is disabled)这个模块里面包含了我们平时会经常用到的一些数学方面的操作, 于是为了图个方便, 我们就给它起了一个名字叫作 T。符号计算在这个过程中, 全部的算法都要靠那种用符号来做计算的办法来实现。因此在某种特定的意义上面来讲。使用Sympy来写算法这件事。它其实更像是在做数学方面的书写工作。这个情况在前面的那个04.06章节里面讲积分的部分当中就已经有所接触过了。那时候大家就见识过如何使用Sympy去定义出那些符号变量了。我们要通过使用字母T来对一个符号类型的标量进行定义这个操作。In :foo T.scalar(x)In :print foox该功能支持用户对符号进行计算操作。In :bar foo ** 2print barElemwise{pow,no_inplace}.0我们在这里将 foo 这个玩意儿定义成了 $x$, 接着把 bar 定义成了一个代表着变量 $x^2$ 的存在, 可是呢, 最终出现在显示屏上面的内容居然是一些令人完全无法理解、摸不着头脑的奇怪符号和组合。为了更好的显示 bar我们使用 .pp() 函数 print来显示In :print theano.pp(bar)(x ** TensorConstant{2})我们来查看一下这个类型吧。In :print type(foo)print foo.typetheano.tensor.var.TensorVariableTensorType(float32, scalar)函数当用户拥有了符号变量的时候, 他们就可以使用这些符号变量来对函数进行定义工作, 而为了生成所谓的符号函数这一目的, 则需要通过使用那个特定的.() 函数来进行。theano.function(input, output)其中 input 是那些当作参数用的符号变量所摆出来的一个列表, 另一边则是用来表示输出的那个地方这个输出的东西可以仅仅只是一个单独的符号, 也可以是由好多个符号变量联合构成的一个列表。例如我们用刚才生成的 foo 和 bar 来定义函数In :square theano.function([foo], bar)使用 函数In :print square(3)9.0你也可以去调用 bar 这个对象的 eval 方法, 把这个 x 替换成你想要的那个值, 在调用的时候, 需要给 eval 一个字典当参数, 让字典里面的键和值一一对应, 来分别代表那个符号变量, 以及它应该对应成的具体值。In :print bar.eval({foo: 3})9.0我们要说的这个东西, 它不单单就只有那个 T.() 这种标量, 在中间的这个地方, 还存在着非常多各种各样的符号变量的类型, 这些个东西, 全部都包含在那个 张量这个子模块的里面, 而且, 在这个当中, 也有着非常多的函数, 这些函数都是去对它们执行操作的。T.T是一个对象, 它里面有个属性叫name是None, 还有一个dtype也是空的。然后T.row它也有一个name是None, 还有dtype是空的。接下来T.col它同样有一个name是None并且dtype也是个空的东西。再后面的括号里写的都是name等于None还有dtype也是这种什么都没有的状态这样的形式一直不断重复出现下去的这样的内容信息。除了可以直接去指定那个符号变量所属的类型, 而这个类型呢, 默认的那个形式是被省略掉的这种情况之外, 另外有一种办法就是可以去直接在每一个类别的前面位置那里去加上一个单独的字母出来, 然后以此来做到对不同的类型的定义工作:另外呢还有一种办法, 那就是用它们的那种复数的形式, 在同一个操作里面去一次性地定义出好多个符号变量的, 这种方式也是存在的:x,y,z T.vectors(x,y,z)x,y,z T.vectors(3)In :A T.matrix(A)x T.vector(x)b T.vector(b)T.这个调用方法 dot 代表的含义是矩阵乘法运算, 其具体的形式可以写成 y Axb。In :y T.dot(A, x) bT.sum() 表示进行求和z \sum{i,j} A{ij}^2In :z T.sum(A**2)现在我们来定义一个线性函数, 这个以A,x,b为参数, 并且以y,z为输出的这样一个东西。In :linear_mix theano.function([A, x, b],[y, z])使用这个函数现在我来把这个数学公式转换成更直白的说法, A矩阵里面有两排数字, 上面一排是1、2和3, 下面一排是4、5还有6, x是一个竖着写的列向量, 它的值是1在最上面, 然后是2, 最下面是3, b也是一个竖着的列向量, 它只包括两个数, 上面的数是4, 下面的数是5。In :print linear_mix(np.array([[1, 2, 3],[4, 5, 6]], dtypetheano.config.floatX), #Anp.array([1, 2, 3], dtypetheano.config.floatX), #xnp.array([4, 5], dtypetheano.config.floatX)) #b[array([ 18., 37.], dtypefloat32), array(91.0, dtypefloat32)]此处需要指定 dtype 变量的数值, 其主要目的在于确保该参数与系统内部预先设置的浮点数精度保持一致, 不过默认状态下这个值通常是空的或者没有明确指代, 然而在实际操作的过程中, 如果使用的是 GPU 进行计算的话, 一般会倾向于选择另外一种更高的精度标准, 因为这样做在效率方面会有更出色的表现。在我们可以为这个函数赋予一个默认值的这种做法方面, 它就像是我们去定义一个普通的函数那样简单, 而为了达成这一点, 我们必须要去运用那个叫做 .Param 的类:In :linear_mix_default theano.function([A, x, theano.Param(b, defaultnp.zeros(2, dtypetheano.config.floatX))],[y, z])我们需要进行的操作是先计算出在默认的参数设置之下的最终结果。In :print linear_mix_default(np.array([[1, 2, 3],[4, 5, 6]], dtypetheano.config.floatX), #Anp.array([1, 2, 3], dtypetheano.config.floatX)) #x[array([ 14., 32.], dtypefloat32), array(91.0, dtypefloat32)]计算刚才的结果In :print linear_mix_default(np.array([[1, 2, 3],[4, 5, 6]], dtypetheano.config.floatX), #Anp.array([1, 2, 3], dtypetheano.config.floatX), #xnp.array([4, 5], dtypetheano.config.floatX)) #b[array([ 18., 37.], dtypefloat32), array(91.0, dtypefloat32)]共享的变量在程序里面可以定义那种共享的变量, 然后它们可以在多个函数中被一起使用, 这些共享变量其实就类似于在普通函数定义的时候所使用的那个全局变量, 只不过另外还加上了一个特殊的属性, 这个属性的作用就是为了能够在具体的函数内部去修改那个全局变量的真实值。In :shared_var theano.shared(np.array([[1.0, 2.0], [3.0, 4.0]], dtypetheano.config.floatX))print shared_var.typeCudaNdarrayType(float32, matrix)可以通过这个方法来改变它的值: 。In :shared_var.set_value(np.array([[3.0, 4], [2, 1]], dtypetheano.config.floatX))它是通过调用这个方法来把值给返回出来的。In :print shared_var.get_value()[[ 3. 4.][ 2. 1.]]共享变量的运算。In :shared_square shared_var ** 2f theano.function([], shared_square)print f()[[ 9. 16.][ 4. 1.]]这里所说的函数并不需要去设置参数, 这是因为共享变量这个东西, 在大家的心里头, 是被默认当作一个参数来予以考虑的。最后所得到的那些结果, 会随着这个共享变量的变化情况, 产生相应的变化:In :shared_var.set_value(np.array([[1.0, 2], [3, 4]], dtypetheano.config.floatX))print f()[[ 1. 4.][ 9. 16.]]那个共享变量的一个值可以利用一个相应的关键词, 在一个函数内部去进行修改和更新操作。In :subtract T.matrix(subtract)f_update theano.function([subtract], shared_var, updates{shared_var: shared_var - subtract})这个函数会先将当前的值返回出来, 然后将当前的值更新为原来的值减去参数。In :print before update:print shared_var.get_value()print the return value:print f_update(np.array([[1.0, 1], [1, 1]], dtypetheano.config.floatX))print after update:print shared_var.get_value()before update:[[ 1. 2.][ 3. 4.]]the return value:CudaNdarray object at 0x7f7f3c16a6f0after update:[[ 0. 1.][ 2. 3.]]导数它有一个特别明显的优点, 也就是它能够去算出符号变量的导数这件事。我们使用 T.grad() 这个函数来实现导数的计算, 而此前大家已经分别定义好了 foo 和 bar 这两个对象, 其中 foo 对应的值是 $x$, 而 bar 对应的值为 $x^2 $, 所以现在接着就可以利用上述工具来算出 bar 对 foo 求导的结果, 理论上得出的数值就等于 $2x$。In :bar_grad T.grad(bar, foo) # 表示 bar (x^2) 关于 foo (x) 的导数print bar_grad.eval({foo: 10})20.0另外呢, 针对前面那个表达式 $y Ax b$, 我们需要去算出 $y$ 关于 $x$ 的雅可比矩阵, 按照常理来说, 这个结果应该就是 $A$ 才对。In :y_J theano.gradient.jacobian(y, x)print y_J.eval({A: np.array([[9.0, 8, 7], [4, 5, 6]], dtypetheano.config.floatX), #Ax: np.array([1.0, 2, 3], dtypetheano.config.floatX), #xb: np.array([4.0, 5], dtypetheano.config.floatX)}) #b[[ 9. 8. 7.][ 4. 5. 6.]]其中, 前一个部分是用来计算雅可比矩阵的, 而后一个部分可以用来计算相关的矩阵。所谓的是R-op与L-op。Rop 这个工具, 主要被拿来去做那个 f 除以 x 再乘以 v 这样一个事情的计算工作。而 Lop 呢则是专门用来处理 v 乘以 f 除以 x 这种格式的计算的。一方面是那个雅可比矩阵跟列向量的乘积, 另外一方面是行向量跟那个雅可比矩阵的乘积。In :W T.dmatrix(W)V T.dmatrix(V)x T.dvector(x)y T.dot(x, W)JV T.Rop(y, W, V)f theano.function([W, V, x], JV)print f([[1, 1], [1, 1]], [[2, 2], [2, 2]], [0,1])[ 2. 2.]