Files
ailearning/docs/da/116.md
2020-10-19 21:08:55 +08:00

474 lines
8.9 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Theano 基础
In [1]:
```py
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
```
首先导入 `theano` 及其 `tensor` 子模块(`tensor`,张量):
In [2]:
```py
import theano
# 一般都把 `tensor` 子模块导入并命名为 T
import theano.tensor as T
```
```py
Using gpu device 1: Tesla K10.G2.8GB (CNMeM is disabled)
```
`tensor` 模块包含很多我们常用的数学操作,所以为了方便,将其命名为 T。
## 符号计算
`theano` 中,所有的算法都是用符号计算的,所以某种程度上,用 `theano` 写算法更像是写数学(之前在[04.06 积分](../04\. scipy/04.06 integration in python.ipynb)一节中接触过用 `sympy` 定义的符号变量)。
`T.scalar` 来定义一个符号标量:
In [3]:
```py
foo = T.scalar('x')
```
In [4]:
```py
print foo
```
```py
x
```
支持符号计算:
In [5]:
```py
bar = foo ** 2
print bar
```
```py
Elemwise{pow,no_inplace}.0
```
这里定义 `foo` 是 $x$`bar` 就是变量 $x^2$,但显示出来的却是看不懂的东西。
为了更好的显示 `bar`,我们使用 `theano.pp()` 函数(`pretty print`)来显示:
In [6]:
```py
print theano.pp(bar)
```
```py
(x ** TensorConstant{2})
```
查看类型:
In [7]:
```py
print type(foo)
print foo.type
```
```py
<class 'theano.tensor.var.TensorVariable'>
TensorType(float32, scalar)
```
## theano 函数
有了符号变量,自然可以用符号变量来定义函数,`theano.function()` 函数用来生成符号函数:
```py
theano.function(input, output)
```
其中 `input` 对应的是作为参数的符号变量组成的列表,`output` 对应的是输出,输出可以是一个,也可以是多个符号变量组成的列表。
例如,我们用刚才生成的 `foo``bar` 来定义函数:
In [8]:
```py
square = theano.function([foo], bar)
```
使用 `square` 函数:
In [9]:
```py
print square(3)
```
```py
9.0
```
也可以使用 `bar``eval` 方法,将 `x` 替换为想要的值,`eval` 接受一个字典作为参数,键值对表示符号变量及其对应的值:
In [10]:
```py
print bar.eval({foo: 3})
```
```py
9.0
```
## theano.tensor
除了 `T.scalar()` 标量之外,`Theano` 中还有很多符号变量类型,这些都包含在 `tensor`(张量)子模块中,而且 `tensor` 中也有很多函数对它们进行操作。
* `T.scalar(name=None, dtype=config.floatX)`
* 标量shape - ()
* `T.vector(name=None, dtype=config.floatX)`
* 向量shape - (?,)
* `T.matrix(name=None, dtype=config.floatX)`
* 矩阵shape - (?,?)
* `T.row(name=None, dtype=config.floatX)`
* 行向量shape - (1,?)
* `T.col(name=None, dtype=config.floatX)`
* 列向量shape - (?,1)
* `T.tensor3(name=None, dtype=config.floatX)`
* 3 维张量shape - (?,?,?)
* `T.tensor4(name=None, dtype=config.floatX)`
* 4 维张量shape - (?,?,?,?)
`shape` 中为 1 的维度支持 `broadcast` 机制。
除了直接指定符号变量的类型(默认 `floatX`),还可以直接在每类前面加上一个字母来定义不同的类型:
* `b` int8
* `w` int16
* `i` int32
* `l` int64
* `d` float64
* `f` float32
* `c` complex64
* `z` complex128
例如 `T.dvector()` 表示的就是一个 `float64` 型的向量。
除此之外,还可以用它们的复数形式一次定义多个符号变量:
```py
x,y,z = T.vectors('x','y','z')
x,y,z = T.vectors(3)
```
In [11]:
```py
A = T.matrix('A')
x = T.vector('x')
b = T.vector('b')
```
`T.dot()` 表示矩阵乘法: $$y = Ax+b$$
In [12]:
```py
y = T.dot(A, x) + b
```
`T.sum()` 表示进行求和: $$z = \sum_{i,j} A_{ij}^2$$
In [13]:
```py
z = T.sum(A**2)
```
来定义一个线性函数,以 $A,x,b$ 为参数,以 $y,z$ 为输出:
In [14]:
```py
linear_mix = theano.function([A, x, b],
[y, z])
```
使用这个函数:
$$ A = \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix}, x = \begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix}, b = \begin{bmatrix} 4 \\ 5 \end{bmatrix} $$In [15]:
```py
print linear_mix(np.array([[1, 2, 3],
[4, 5, 6]], dtype=theano.config.floatX), #A
np.array([1, 2, 3], dtype=theano.config.floatX), #x
np.array([4, 5], dtype=theano.config.floatX)) #b
```
```py
[array([ 18., 37.], dtype=float32), array(91.0, dtype=float32)]
```
这里 `dtype=theano.config.floatX` 是为了与 `theano` 设置的浮点数精度保持一致,默认是 `float64`,但是在 `GPU` 上一般使用 `float32` 会更高效一些。
我们还可以像定义普通函数一样,给 `theano` 函数提供默认值,需要使用 `theano.Param` 类:
In [16]:
```py
linear_mix_default = theano.function([A, x, theano.Param(b, default=np.zeros(2, dtype=theano.config.floatX))],
[y, z])
```
计算默认参数下的结果:
In [17]:
```py
print linear_mix_default(np.array([[1, 2, 3],
[4, 5, 6]], dtype=theano.config.floatX), #A
np.array([1, 2, 3], dtype=theano.config.floatX)) #x
```
```py
[array([ 14., 32.], dtype=float32), array(91.0, dtype=float32)]
```
计算刚才的结果:
In [18]:
```py
print linear_mix_default(np.array([[1, 2, 3],
[4, 5, 6]], dtype=theano.config.floatX), #A
np.array([1, 2, 3], dtype=theano.config.floatX), #x
np.array([4, 5], dtype=theano.config.floatX)) #b
```
```py
[array([ 18., 37.], dtype=float32), array(91.0, dtype=float32)]
```
## 共享的变量
`Theano` 中可以定义共享的变量,它们可以在多个函数中被共享,共享变量类似于普通函数定义时候使用的全局变量,同时加上了 `global` 的属性以便在函数中修改这个全局变量的值。
In [19]:
```py
shared_var = theano.shared(np.array([[1.0, 2.0], [3.0, 4.0]], dtype=theano.config.floatX))
print shared_var.type
```
```py
CudaNdarrayType(float32, matrix)
```
可以通过 `set_value` 方法改变它的值:
In [20]:
```py
shared_var.set_value(np.array([[3.0, 4], [2, 1]], dtype=theano.config.floatX))
```
通过 `get_value()` 方法返回它的值:
In [21]:
```py
print shared_var.get_value()
```
```py
[[ 3\. 4.]
[ 2\. 1.]]
```
共享变量进行运算:
In [22]:
```py
shared_square = shared_var ** 2
f = theano.function([], shared_square)
print f()
```
```py
[[ 9\. 16.]
[ 4\. 1.]]
```
这里函数不需要参数,因为共享变量隐式地被认为是一个参数。
得到的结果会随这个共享变量的变化而变化:
In [23]:
```py
shared_var.set_value(np.array([[1.0, 2], [3, 4]], dtype=theano.config.floatX))
print f()
```
```py
[[ 1\. 4.]
[ 9\. 16.]]
```
一个共享变量的值可以用 `updates` 关键词在 `theano` 函数中被更新:
In [24]:
```py
subtract = T.matrix('subtract')
f_update = theano.function([subtract], shared_var, updates={shared_var: shared_var - subtract})
```
这个函数先返回当前的值,然后将当前值更新为原来的值减去参数:
In [25]:
```py
print 'before update:'
print shared_var.get_value()
print 'the return value:'
print f_update(np.array([[1.0, 1], [1, 1]], dtype=theano.config.floatX))
print 'after update:'
print shared_var.get_value()
```
```py
before update:
[[ 1\. 2.]
[ 3\. 4.]]
the return value:
<CudaNdarray object at 0x7f7f3c16a6f0>
after update:
[[ 0\. 1.]
[ 2\. 3.]]
```
## 导数
`Theano` 的一大好处在于它对符号变量计算导数的能力。
我们用 `T.grad()` 来计算导数,之前我们定义了 `foo``bar` (分别是 $x$ 和 $x^2$,我们来计算 `bar` 关于 `foo` 的导数(应该是 $2x$
In [26]:
```py
bar_grad = T.grad(bar, foo) # 表示 bar (x^2) 关于 foo (x) 的导数
print bar_grad.eval({foo: 10})
```
```py
20.0
```
再如,对之前的 $y = Ax + b$ 求 $y$ 关于 $x$ 的雅可比矩阵(应当是 $A$
In [27]:
```py
y_J = theano.gradient.jacobian(y, x)
print y_J.eval({A: np.array([[9.0, 8, 7], [4, 5, 6]], dtype=theano.config.floatX), #A
x: np.array([1.0, 2, 3], dtype=theano.config.floatX), #x
b: np.array([4.0, 5], dtype=theano.config.floatX)}) #b
```
```py
[[ 9\. 8\. 7.]
[ 4\. 5\. 6.]]
```
`theano.gradient.jacobian` 用来计算雅可比矩阵,而 `theano.gradient.hessian` 可以用来计算 `Hessian` 矩阵。
## `R-op` 和 `L-op`
Rop 用来计算 $\frac{\partial f}{\partial x}v$Lop 用来计算 $v\frac{\partial f}{\partial x}$
一个是雅可比矩阵与列向量的乘积,另一个是行向量与雅可比矩阵的乘积。
In [28]:
```py
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])
```
```py
[ 2\. 2.]
```