这一讲我们来学习 Tensorflow 的基本操作。不出意外这是这门课程最枯燥的一节,因为我们需要学习很多基础的概念,但之后就会有趣很多!
更新历史
- 2019.08.05: 添加关于 Python Property 的文章链接
- 2019.08.04: 完成初稿
从简单的程序开始
代码地址,我们在上面代码做的操作有:
- 屏蔽 TF 的编译告警
- 写入 Graph 的结果,方便用 Tensorboard 展示
执行一下,然后打开 tensorboard 看看
1 | python 1_basic_operation.py |
Contants 常量
定义为 tf.constant(value, dtype=None, shape=None, name='Const', verify_shape=False)
常量的相关代码参考 这里,以下是关键要点:
- 常量的广播 broadcasting 与 Numpy 类似
tf.zeros
与numpy.zeros
类似- 可以使用
tf.zeros_like
函数生成与输入 tensor 形状一样的 tensor - 同样的道理适用于
tf.ones
与tf.ones_like
- 用指定值填充指定的形状
tf.fill(dims, value, name=None)
- 生成数组可以用
tf.lin_space(start, stop, num, name=None)
或者tf.range(start, limit=None, delta=1, dtype=None, name='range')
,但是注意,这个数组并不可以用来遍历 - 随机生成常量的函数不少,具体见代码
- 如果要控制随机性,可以通过
tf.set_random_seed
来固定种子,保证每次结果一致 - 常量会被保存在 graph 的定义中,滥用会使得载入 graph 变得很费时。所以只把 contant 用于基础类型,而数据集的部分,放到变量或者 reader 中,这样就可以用内存来存储,速度更快
Operations 操作
主要分以下几类
- 针对元素的数学操作: Add, Sub, Mul, Div, Exp, Log, Greater, Less, Equal, …
- 数组操作: Concat, Slice, Split, Constant, Rank, Shape, Shuffle, …
- 矩阵操作: MatMul, MatrixInverse, MatrixDeterminant, …
- 状态操作: Variable, Assign, AssignAdd, …
- 神经网络模块: SoftMax, Sigmoid, ReLU, Convolution2D, MaxPool, …
- Checkpoint 操作: Save, Restore
- 队列和同步操作: Enqueue, Dequeue, MutexAcquire, MutexRelease, …
- 流控制操作: Merge, Switch, Enter, Leave, NextIteration
数学运算操作
比较标准,与 numpy 很类似,如
- 绝对值
tf.abs
- 取反
tf.negative
- 返回数值的符号(就是判断正负)
tf.sign
- 求倒数
tf.reciprocal
- 求平方
tf.square
- 取整
tf.round
- 开平方
tf.sqrt
- 求平方根的倒数
tf.rsqrt
- 求幂
tf.pow
- 求 e 的指数
tf.exp
- 除法有花式
div
,具体参考代码
Data Types 数据类型
代码参考 这里,以下是关键要点:
- 直接用 Python 原生的类型有:boolean, numeric(int, float), string
- 标量会被认为是 0 维张量
- 1 维数组会被认为是 1 维张量
- 2 维数字会被认为是 2 维张量
- TF 和 Numpy 无缝对接
tf.int32 == np.int32 => True
- 可以直接向 TF 传递 numpy 的数据类型
- 在 Session 中,获取 Tensor 会返回 numpy ndarray
可能的话尽可能使用 TF 的数据类型,因为:
- 用 python 原始类型的话,TF 需要去推断 python 类型
- 对于 numpy array 来说,无法使用 GPU 加速计算
Variables 变量
代码参考 这里,以下是关键要点:
- 尽量使用
tf.get_variable
而非tf.Variable
来创建变量,因为tf.Variable
和tf.Constant
不同,并不是一个 operation,而是一个类,包含了多个 operation,所以建议用更加像访问类的方式来访问 - 在使用变量之前,一定要进行初始化,比较省事的方式就是
sess.run(tf.global_variables_initializer()
,注意,initializer 是一个 operation,所以需要用 sess 来执行 - 也可以使用
sess.run(tf.variables_initializer([s, m]))
来初始化部分变量 - 也可以使用
sess.run(W.initializer)
只初始化一个变量 - 可以用 eval 函数来获取值
tf.Variable.assign
是一个 operation,需要保存下来然后再 sess 中执行才可以生效,其他的操作还有assign_add
和assign_sub
- 每个 session 中的变量是独立的,不会互相影响
- 可以通过
tf.Graph.control_dependencies(control_inputs)
来控制哪些 operations 先执行
Placeholders 占位符
还记得一个 TF 程序的两个步骤吗:
- 组装一个 Graph
- 使用个 session 来执行 Graph 中的 operation
也就是说,我们是在不知道要计算什么值之前,就已经设定好了要如何计算这些值。这样一来,我们就可以之后再把要计算的值传过去。我们先来看一段代码:
1 | # tf.placeholder(dtype, shape=None, name=None) |
这里有一点要注意下,shape=None
表示该位置可以接受任何的值,好处在于构建 graph 的时候很轻松,但是,但是,在调试的时候,会很让人摸不着头脑。更重要的是,这样会导致后面的 shape 推断无法进行,进而导致很多 operations 无法进行。
Lazy Loading 的陷阱
啥是 Lazy Loading?就是指当真正用到这个对象的时候,才进行创建或初始化。
先看一个正常的例子:
1 | x = tf.Variable(10, name='x') |
然后我们比较一下 lazy loading 的版本,这里只是没有把 z 放到 session 外定义,省一行代码:
1 | x = tf.Variable(10, name='x') |
如何解决这个问题?请做好如下几点:
- 能提前定义的都提前定义好!这是所有计算图框架都需要注意的事情!
- Use Python property to ensure function is also loaded once the first time it is called.更多可以参考 这篇文章,写得非常棒!
下期预告
- Linear regression
- 控制流
- tf.data
- Optimizer
- Logistic regression on MNIST