Basic operations, constants, variables
Control dependencies
Data pipeline
TensorBoard
本节学习来源斯坦福大学cs20课程,有关自学与组队学习笔记,将会放于github仓库与本公众号发布,欢迎大家star与转发,收藏!
cs20是一门对于深度学习研究者学习Tensorflow的课程,今天学习了二节,非常有收获,并且陆续将内容写入jupytebook notebook中,有关这个源代码及仓库地址,大家可以点击阅读原文或者直接复制下面链接!
直通车: https://github.com/Light-City/Translating_documents
目录
第一个TensorFlow程序TensorBoard可视化如何运行可视化的图?可视化计算图Constants, Sequences, Variables, Ops常量特殊值填充张量常量作为序列随机生成的变量算法操作神奇的除法Tensorflow数据类型尽可能使用TF DType常数有什么问题?变量创建变量tf.constant 与 tf.Variable区别初始化变量Eval()tf.Variable.assign()assign_add() and assign_sub()每个会话都维护自己的变量副本控制依赖关系Placeholder快速提醒Placeholders为什么占位符?占位符使用?使用字典向占位符补充值Variable和placeholder有什么区别呢?Placeholders 是有效操作如果想要提供多个数据点怎么办?is_feedable给TF操作喂数据对测试非常有帮助,输入虚拟值以测试大图的部分什么是懒加载?
import tensorflow as tf
a = tf.constant(2)
b = tf.constant(3)
x = tf.add(a, b)
with tf.Session() as sess:
print(sess.run(x))
输出:
5
上述运行会出现警告:Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
# 如果你有一个GPU,你不应该关心AVX的支持,因为大多数昂贵的操作将被分派到一个GPU设备上(除非明确地设置)。
# 在这种情况下,您可以简单地忽略此警告:
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
a = tf.constant(2)
b = tf.constant(3)
x = tf.add(a, b)
with tf.Session() as sess:
print(sess.run(x))
输出:
5
定义计算图
a=tf.constant(2)
b=tf.constant(3)
x=tf.add(a,b)
在图形定义之后和运行会话之前创建摘要编写器
writer=tf.summary.FileWriter('./graphs',tf.get_default_graph())
with tf.Session() as sess:
print(sess.run(x))
writer.close
输出:
5
输出:
<bound method FileWriter.close of <tensorflow.python.summary.writer.writer.FileWriter object at 0x7f0bbeb5d518>>
1.如果您使用jupyter notebook进行Tensorboard代码编写,那么可以安装一个jupyer-tensorboard库,关于这个库的更多内容,请查阅:https://github.com/lspvic/jupyter_tensorboard
安装这个库:pip(3) install jupyter-tensorboard
重启Jupyter,勾选生成的event文件夹,会出现tensorboard,然后即可打开tensorboard,如果弹出浏览器阻止,开启允许即可!
当然你也可以通过jupyter直接运行后,通过命令端运行:
tensorboard --logdir=./graphs/ --port 6066
2.如果您使用pycharm,那么就直接运行代码后,然后在命令端运行上述命令即可!
import tensorflow as tf
a = tf.constant(2)
b = tf.constant(3)
x = tf.add(a, b)
writer = tf.summary.FileWriter('./graphs', tf.get_default_graph())
writer.close()
上述运行后,变量名是默认给的,const,const1,那如何修改呢?
直接给定变量一个name属性即可!
a = tf.constant(2, name='a')
b = tf.constant(3, name='b')
x = tf.add(a, b, name='add')
writer = tf.summary.FileWriter('./graphs', tf.get_default_graph())
with tf.Session() as sess:
print(sess.run(x)) # >> 5
输出:
5
原型:
tf.constant(
value,
dtype=None,
shape=None,
name='Const',
verify_shape=False
)
import tensorflow as tf
a = tf.constant([2,2],name='a')
b = tf.constant([[0,1],[2,3]],name='b')
类似于Numpy的广播机制
x = tf.multiply(a, b, name='mul')
with tf.Session() as sess:
print(sess.run(x))
输出:
[[0 2]
[4 6]]
1.原型:
tf.zeros(shape, dtype=tf.float32, name=None)
创建一个shape的的张量,并且为每个元素设置为0
类似于numpy.zeros
tf.zeros([2,3],tf.int32)
输出:
<tf.Tensor 'zeros:0' shape=(2, 3) dtype=int32>
2.原型:
tf.zeros_like(input_tensor, dtype=None, name=None, optimize=True)
创建一个形状和类型的张量(除非指定了type)作为input_tensor,但所有元素都是零。
类似于numpy.zeros_like
如果输入张量是[[0, 1], [2, 3], [4, 5]]
,那么将会创建同shape,全为0的张量。即:[[0, 0], [0, 0], [0, 0]]
3.原型:
tf.ones(shape, dtype=tf.float32, name=None)
tf.ones_like(input_tensor, dtype=None, name=None, optimize=True)
分别类似于:numpy.ones
与numpy.ones_like
4.原型:
tf.fill(dims, value, name=None)
创建一个用标量值填充的张量
类似于:numpy.full
tf.fill([2, 3], 8)
输出:
<tf.Tensor 'Fill:0' shape=(2, 3) dtype=int32>
1.原型:
tf.lin_space(start, stop, num, name=None)
tf.lin_space(10.0, 13.0, 4) # [10. 11. 12. 13.]
输出:
<tf.Tensor 'LinSpace:0' shape=(4,) dtype=float32>
2.原型:
tf.range(start, limit=None, delta=1, dtype=None, name='range')
注意:这个不与numpy的序列相同,Tensor对象是不可迭代的,也就是不能有如下操作:
for _ in tf.range(4): # typeError
tf.range(3,18,3)# [3 6 9 12 15]
输出:
<tf.Tensor 'range:0' shape=(5,) dtype=int32>
tf.range(5) # [0 1 2 3 4]
输出:
<tf.Tensor 'range_1:0' shape=(5,) dtype=int32>
tf.random_normal
tf.truncated_normal
tf.random_uniform
tf.random_shuffle
tf.random_crop
tf.multinomial
tf.random_gamma
设置随机种子:
tf.set_random_seed(seed)
tf.abs
tf.sqrt
等等
类似于numpy
a = tf.constant([2, 2], name='a')
b = tf.constant([[0, 1], [2, 3]], name='b')
with tf.Session() as sess:
print(sess.run(tf.div(b, a)))
print(sess.run(tf.divide(b, a)))
print(sess.run(tf.truediv(b, a)))
print(sess.run(tf.floordiv(b, a)))
# print(sess.run(tf.realdiv(b, a))) # Error: only works for real values
print(sess.run(tf.truncatediv(b, a)))
print(sess.run(tf.floor_div(b, a)))
[[0 0]
[1 1]]
[[0. 0.5]
[1. 1.5]]
[[0. 0.5]
[1. 1.5]]
[[0 0]
[1 1]]
[[0 0]
[1 1]]
[[0 0]
[1 1]]
TensorFlow 采用 Python 原生类型: 布尔, 数值(int, float), 字符串
单个值将转换为0-d张量(或标量),值列表将转换为1-d张量(向量),值列表将转换为2-d 张量(矩阵)等。
t_0 = 19 # scalars are treated like 0-d
tf.zeros_like(t_0) # 0
tf.ones_like(t_0) # 1
输出:
<tf.Tensor 'ones_like:0' shape=() dtype=int32>
t_1 = [b"apple", b"peach", b"grape"] # 1-d arrays are treated like 1-d tensors
tf.zeros_like(t_1) # [b'' b'' b'']
tf.ones_like(t_1) # TypeError: Expected string, got 1 of
t_2 = [[True, False, False],
[False, False, True],
[False, True, False]] # 2-d arrays are treated like 2-d tensors
tf.zeros_like(t_2) # 3x3 tensor, all elements are False
tf.ones_like(t_2) # 3x3 tensor, all elements are True
输出:
<tf.Tensor 'ones_like_2:0' shape=(3, 3) dtype=bool>
Numpy与TensorFlow类型联系
1.部分类型一致
tf.int32==np.int32
2.可以传递numpy类型到TensorFlow操作
tf.ones([2,2],np.float32) # [[1.0 1.0], [1.0 1.0]]
3.对于tf.Session.run(fetches)
,如果传递的fetches是一个Tensor,输出将是一个 NumPy n维数组.
# 上述联系中第三个例子
import numpy as np
sess = tf.Session()
a = tf.zeros([2, 3], np.int32)
print(type(a))
a = sess.run(a) #<<<< Avoid doing this. Use a_out = sess.run(a)
print(type(a))
<class 'tensorflow.python.framework.ops.Tensor'>
<class 'numpy.ndarray'>
1.Python原生类型:TensorFlow必须推断Python类型
使用Python类型来指定TensorFlow对象既快速又简单,并且对于原型设计思想非常有用。然而,这样做有一个重要的缺陷。 Python类型缺乏显式声明数据类型的能力,但TensorFlow的数据类型更具体。例如,所有整数都是相同的类型,但TensorFlow具有8位,16位,32位和64位整数。因此,如果您使用Python类型,TensorFlow必须推断您的意思是哪种数据类型。
2.NumPy阵列:NumPy不兼容GPU
将数据传递给TensorFlow时,可以将数据转换为适当的类型,但某些数据类型仍然可能难以正确声明,例如复数。因此,建议将手工定义的Tensor对象创建为NumPy数组。
1.不是不变的
2.常量存储在图形定义中
# 打印出图表def
my_const = tf.constant([1.0, 2.0], name="my_const")
with tf.Session() as sess:
print(sess.graph.as_graph_def())
图形定义存储在protobuf中(协议缓冲区,Google的语言中立,平台中立,可扩展的机制,用于序列化结构化数据 - 想想XML,但更小,更快,更简单。)
上述问题就使得当常量很大时加载图形很昂贵,仅对原始类型使用常量。
使用变量或读取器来获取需要更多内存的更多数据。
1.使用tf.Variable创建变量
s = tf.Variable(2,name='scalar')
m = tf.Variable([[0, 1], [2, 3]], name="matrix")
W = tf.Variable(tf.zeros([784,10]))
2.使用tf.get_variable创建变量
s = tf.get_variable('scalar',initializer=tf.constant(2))
m = tf.get_variable('matrix',initializer=tf.constant([[0,1],[2,3]]))
W = tf.get_variable('big_matrix',shape=(784,10),initializer=tf.zeros_initializer())
tf.constant 是一个操作
tf.Variable 是一个包含多个操作的类
tf.Variable holds several ops:
x = tf.Variable(...)
x.initializer # init op
x.value() # read op
x.assign(...) # write op
x.assign_add(...) # and more
W = tf.get_variable("big_matrix", shape=(784, 10), initializer=tf.zeros_initializer())
with tf.Session() as sess:
print(sess.run(W))
# FailedPreconditionError: Attempting to use uninitialized value Variable
因此你必须初始化你变量,初始化是一个操作,你需要在一个session的上下文中去执行!
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
初始化变量的三种形式:
1.最简单的方式是一次初始化所有变量
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
2.只初始化一个变量子集
with tf.Session() as sess:
sess.run(tf.variables_initializer([a, b]))
3.初始化单个变量
W = tf.Variable(tf.zeros([784,10]))
with tf.Session() as sess:
sess.run(W.initializer)
Eval()
类似于sess.run(W)
# W is a random 700 x 100 variable object
W = tf.Variable(tf.truncated_normal([700, 10]))
with tf.Session() as sess:
sess.run(W.initializer)
print(W)
print(sess.run(W))
输出:
<tf.Variable 'Variable_6:0' shape=(700, 10) dtype=float32_ref>
[[-0.5226208 -0.97566205 -0.42451558 ... -0.4133208 -0.50616413
-0.832415 ]
[-1.2517142 -1.5990031 -1.2126933 ... -0.60406566 -1.0643008
1.8198049 ]
[-0.12700588 1.453523 -0.32724586 ... 0.8391837 -0.21167135
0.5361979 ]
...
[ 1.1430322 0.30788425 -0.2411158 ... -0.1196197 -0.11606342
-0.7137539 ]
[ 1.7774947 -0.5762313 -1.0951449 ... -0.8084044 -0.16739914
0.5140143 ]
[-0.4849846 0.04498202 -0.06566923 ... -0.53475994 -0.5896073
-0.8156376 ]]
# W is a random 700 x 100 variable object
W = tf.Variable(tf.truncated_normal([700, 10]))
with tf.Session() as sess:
sess.run(W.initializer)
print(W)
print(W.eval())
输出:
<tf.Variable 'Variable_7:0' shape=(700, 10) dtype=float32_ref>
[[-0.8490288 0.16360298 -1.2199837 ... 0.68452436 0.41389784
1.0369685 ]
[ 0.83160686 0.23120853 -0.14505246 ... -0.68007827 -0.02268635
-0.6440017 ]
[-0.72545207 0.10048836 0.5861655 ... -0.50299203 -1.2190983
-1.1676756 ]
...
[ 0.12471761 -1.5892761 -0.06928917 ... -1.1436646 -1.3074825
0.23211905]
[-1.1235662 1.0567249 -0.40683436 ... 0.55470574 0.64190876
1.1899426 ]
[-0.02476467 0.05603853 -1.3414879 ... 1.119798 -0.5048172
-0.47068778]]
W = tf.Variable(10)
W.assign(100)
with tf.Session() as sess:
sess.run(W.initializer)
print(W.eval())
10
上述结果为什么是?
因为W.assign(100)创建了一个分配操作,这个操作需要在session中执行才会生效。
W = tf.Variable(10)
assign_op = W.assign(100)
with tf.Session() as sess:
sess.run(W.initializer)
sess.run(assign_op)
print(W.eval())
100
您不需要初始化变量,因为assign_op会为您执行此操作。实际上,初始化op是赋值操作,它将变量的初始值赋给变量本身。
# create a variable whose original value is 2
my_var = tf.Variable(2, name="my_var")
# assign a * 2 to a and call that op a_times_two
my_var_times_two = my_var.assign(2 * my_var)
with tf.Session() as sess:
sess.run(my_var.initializer)
print(my_var.eval()) # 2
sess.run(my_var_times_two) # 4
print(my_var.eval())
sess.run(my_var_times_two) # 8
print(my_var.eval())
sess.run(my_var_times_two) # 16
print(my_var.eval())
输出:
2
4
8
16
my_var = tf.Variable(10)
with tf.Session() as sess:
sess.run(my_var.initializer)
# 增加了10
sess.run(my_var.assign_add(10)) # 20
print(my_var.eval())
# 减少了2
sess.run(my_var.assign_sub(2)) # 18
print(my_var.eval())
输出:
20
18
assign_add()和assign_sub()无法为您初始化变量my_var,因为这些操作需要my_var的原始值
可以看到在两个session内,同一个Variable对象的当前值互不干扰:
W = tf.Variable(10)
sess1 = tf.Session()
sess2 = tf.Session()
sess1.run(W.initializer)
sess2.run(W.initializer)
print(sess1.run(W.assign_add(10))) # >> 20
print(sess2.run(W.assign_sub(2))) # >> 8
print(sess1.run(W.assign_add(100))) # >> 120
print(sess2.run(W.assign_sub(50))) # >> -42
sess1.close()
sess2.close()
输出:
20
8
120
-42
tf.Graph.control_dependencies(control_inputs)
# defines which ops should be run first
# your graph g have 5 ops: a, b, c, d, e
g = tf.get_default_graph()
with g.control_dependencies([a, b, c]):
# 'd' and 'e' will only run after 'a', 'b', and 'c' have executed.
d = ...
e = …
TF程序通常有两个阶段:
1.组装图表
2.使用会话在图中执行操作。
首先组装图形,而不知道计算所需的值
比喻:
在不知道x或y的值的情况下定义函数f(x,y)= 2 * x + y
。
x,y是实际值的占位符。
We, or our clients, can later supply their own data when they need to execute the computation.
我们或我们的客户可以在需要执行计算时提供自己的数据。
占位符原型:
tf.placeholder(dtype, shape=None, name=None)
# 创建一个3个元素,类型为tf.float32的placeholder
a = tf.placeholder(tf.float32, shape=[3])
b = tf.constant([5, 5, 5], tf.float32)
# 像使用常量或变量一样使用占位符
c = a + b # short for tf.add(a, b)
with tf.Session() as sess:
print(sess.run(c)) # >> InvalidArgumentError: a doesn’t an actual value
# 创建一个3个元素,类型为tf.float32的placeholder
a = tf.placeholder(tf.float32, shape=[3])
b = tf.constant([5, 5, 5], tf.float32)
# 像使用常量或变量一样使用占位符
c = a + b # short for tf.add(a, b)
with tf.Session() as sess:
print(sess.run(c, feed_dict={a: [1, 2, 3]})) # 张量a是key,而不是字符串'a'
# >> [6, 7, 8]
输出:
[6. 7. 8.]
怪癖:
shape = None表示任何形状的张量都将被接受为占位符的值。
shape = None很容易构造图形,但是用于调试的噩梦
shape = None也会破坏所有后续形状推断,这使得许多操作不起作用,因为它们期望某些等级。
Variable是变量,可以在graph里不断的被修改,而placeholder不行。
Variable通常是需要学习的权重,而placeholder通常是训练数据。
Variable使用initializer初始化,而placeholder在run的时候通过fee_dict赋值。
换言之,Variable类比于函数内定义的变量,而placeholder相当于函数方法签名上的形参。
# 比如下面的操作,通过一个循环,反复feed不同的数据:
with tf.Session() as sess:
for a_value in list_of_values_for_a:
print(sess.run(c, {a: a_value}))
这种做法不仅正确,而且很常见,机器学习算法中,定义一个训练op,然后不断feed不同的训练数据进行训练。虽然place_holder一直在传入,但里面的参数通过Variable一直在迭代。
事实上,feed_dict不仅可以feed的是placeholder,还可以feed任何可feed的tensor! placeholder只是一种方法表示必须被feed。
# 我们可以通过is_feedable判断是否可以被feed:
tf.Graph.is_feedable(tensor)
# create operations, tensors, etc (using the default graph)
a = tf.add(2, 5)
b = tf.multiply(a, 3)
with tf.Session() as sess:
# compute the value of b given a is 15
sess.run(b, feed_dict={a: 15}) # >> 45
当您有一个大图并且只想测试某些部分时,您可以提供虚拟值,因此TensorFlow不会浪费时间进行不必要的计算。
懒创建/初始化对象直到它需要
1.正常加载例子
x = tf.Variable(10, name='x')
y = tf.Variable(20, name='y')
z = tf.add(x, y) # create the node before executing the graph
writer = tf.summary.FileWriter('./graphs/normal_loading', tf.get_default_graph())
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for _ in range(10):
sess.run(z)
writer.close()
tf.get_default_graph().as_graph_def()
输出:
...
node {
name: "Add"
op: "Add"
input: "x/read"
input: "y/read"
attr {
key: "T"
value {
type: DT_INT32
}
}
}
...
2.懒加载例子
x = tf.Variable(10, name='x')
y = tf.Variable(20, name='y')
writer = tf.summary.FileWriter('./graphs/normal_loading', tf.get_default_graph())
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for _ in range(10):
sess.run(tf.add(x, y)) # someone decides to be clever to save one line of code
writer.close()
tf.get_default_graph().as_graph_def()
输出:
node {
name: "Add_1"
op: "Add"
input: "x_1/read"
input: "y_1/read"
attr {
key: "T"
value {
type: DT_INT32
}
}
}
...
...
node {
name: "Add_10"
op: "Add"
input: "x_1/read"
input: "y_1/read"
attr {
key: "T"
value {
type: DT_INT32
}
}
}
...
两者都给出相同的z值,有什么问题?
第一种方式:节点“添加”添加一次到图表定义 第二种方式:节点“添加”向图表定义添加了10次或者你想要多次计算z
想象一下,你想要计算一个操作 数千甚至数百万!
你的图表变得臃肿,加载缓慢,传递昂贵。
我在GitHub上看到的最常见的TF非bug错误之一
解决办法: