学习目标
- 张量
- 变量
- 图
- 保存和恢复
基本概念
TensorFlow的核心数据单位是张量,TensorFlow Core程序可以看作是两个相互独立的部分组成:构建计算图,运行计算图。
张量
一个张量由一组阵列(任意维数)的原始值组成。张量的阶是它的维数,而它的的形状是一个整数元组,制定了阵列每个维度的长度。
1 #0阶张量(标量)
[1,2] #1阶张量(向量)
[[1,2],[3,4]] #2阶张量(矩阵)
......
tf.Tensor具有的属性:
- 数据类型(float32,int,string)
- 形状
张量中的每个元素都具有相同的数据类型,且数据类型一定是已知的。形状可能是部分已知。
a = tf.constant(3.0, dtype=tf.float32) #定义一个常量
b = tf.constant(4.0) # 数据类型也是tf.float32
常用的特殊张量:
- tf.Variable
- tf.constant
- tf.placeholder
- tf.SparseTensor
除tf.Variable意外,张量的值不可变。但同一张量在读取随机数等情况下可能返回不同值。
阶
tf.Tensor的阶就是它本身的维数,和数学中矩阵的阶并不是同一个概念。
阶 | 数学实例 |
---|---|
0 | 标量 |
1 | 向量 |
2 | 矩阵 |
3 | 数据立体 |
n | n阶张量(脑补) |
- 0阶
mammal = tf.Variable("Elephant", tf.string)
ignition = tf.Variable(451, tf.int16)
floating = tf.Variable(3.14159265359, tf.float64)
its_complicated = tf.Variable(12.3 - 4.85j, tf.complex64)
- 1阶
mystr = tf.Variable(["Hello"], tf.string)
cool_numbers = tf.Variable([3.14159, 2.71828], tf.float32)
first_primes = tf.Variable([2, 3, 5, 7, 11], tf.int32)
its_very_complicated = tf.Variable([12.3 - 4.85j, 7.5 - 6.23j], tf.complex64)
- 2阶
mymat = tf.Variable([[7],[11]], tf.int16)
myxor = tf.Variable([[False, True],[True, False]], tf.bool)
linear_squares = tf.Variable([[4], [9], [16], [25]], tf.int32)
squarish_squares = tf.Variable([ [4, 9], [16, 25] ], tf.int32)
rank_of_squares = tf.rank(squarish_squares)
mymatC = tf.Variable([[7],[11]], tf.int32)
获取阶
r = tf.rank(my_image) #计算图运行后,r将返回4
切片
my_scalar = my_vector[2] #1阶,返回标量
my_scalar = my_matrix[1, 2] #2阶,返回标量
my_row_vector = my_matrix[2] #2阶,返回行
my_column_vector = my_matrix[:, 3] #2阶,返回列
形状
张量的形状是每个维度中元素的数量
实例 | 阶 | 形状 | 维数 | 说明 |
---|---|---|---|---|
1 | 0 | [] | 0-D | 0维(阶)张量 |
[1,2] | 1 | [2] | 1-D | 形状为[2]的1维(阶)张量 |
[[1,2],[3,4,5]] | 2 | [2,3] | 2-D | 形状为[2,3]的2维(阶)张量 |
[[[1,2],[3,4]],[[5,6,7],[8,9,10]]] | 3 | [2,2,2] | 3-D | 形状为[2,2,2]的3维(阶)张量 |
****** | n | [D0,...,Dn-1] | n-D | 形状为[D0,...,Dn-1]的n维(阶)张量 |
获取形状
c = tf.constant([1,2])
printf(c.shape) #方法1,返回(2,)
tf.shape(c) #方法2,返回TensorShapr目标,<tf.Tensor 'Shape:0' shape=(1,) dtype=int32>
改变形状
张量的元素数量是其所有形状大小的乘积,标量的元素数量永远是1。
rank_three_tensor = tf.ones([3, 4, 5]) #创建一个3维,形状为(3,4,5)的张量
matrix = tf.reshape(rank_three_tensor, [6, 10]) #重构成一个2维,形状为(6,10)的张量
matrixB = tf.reshape(matrix, [3, -1]) #重构成一个2维,形状为(3,20)的张量
matrixAlt = tf.reshape(matrixB, [4, 3, -1]) #重构成一个3维,形状为(4,3,,5)的张量
yet_another = tf.reshape(matrixAlt, [13, 2, -1]) #元素数量不匹配,报错
数据类型
float_tensor = tf.cast(tf.constant([1, 2, 3]), dtype=tf.float32) #将int转换为float32
printf(tf.constant([1, 2, 3]).dtype) #返回数据类型
数据类型 | Python类型 | 描述 |
---|---|---|
DT_FLOAT | tf.float32 | 32 位浮点数. |
DT_DOUBLE | tf.float64 | 64 位浮点数. |
DT_INT64 | tf.int64 | 64 位有符号整型. |
DT_INT32 | tf.int32 | 32 位有符号整型. |
DT_INT16 | tf.int16 | 16 位有符号整型. |
DT_INT8 | tf.int8 | 8 位有符号整型. |
DT_UINT8 | tf.uint8 | 8 位无符号整型. |
DT_STRING | tf.string | 可变长度的字节数组.每一个张量元素都是一个字节数组. |
DT_BOOL | tf.bool | 布尔型. |
DT_COMPLEX64 | tf.complex64 | 由两个32位浮点数组成的复数:实数和虚数. |
DT_QINT32 | tf.qint32 | 用于量化Ops的32位有符号整型. |
DT_QINT8 | tf.qint8 | 用于量化Ops的8位有符号整型. |
DT_QUINT8 | tf.quint8 | 用于量化Ops的8位无符号整型. |
评估张量
t = tf.constant(42.0)
u = tf.constant(37.0)
tu = tf.mul(t, u)
ut = tf.mul(u, t)
with sess.as_default():
tu.eval() # 执行一步
ut.eval() # 执行一步
sess.run([tu, ut]) # 一步执行两个张量
打印张量
调试用
x=tf.constant([2,3,4,5])
x=tf.Print(x,[x,x.shape,'any thing i want'],message='Debug message:',summarize=100)
with tf.Session() as sess:
sess.run(x)
输出:Debug message:[2 3 4 5][4][any thing i want]
变量
创建变量
my_variable = tf.get_variable("my_variable", [1, 2, 3]) #初始值通过tf.glorot_uniform_initializer随机设置
my_int_variable = tf.get_variable("my_int_variable", [1, 2, 3], dtype=tf.int32,initializer=tf.zeros_initializer) #指定类型和初始化器
other_variable = tf.get_variable("other_variable", dtype=tf.int32,initializer=tf.constant([23, 42])) #使用张量的类型
变量集合
Tensorflow提供集合,放置变量。默认情况下,每个 tf.Variable 都放置在以下两个集合中:
- tf.GraphKeys.GLOBAL_VARIABLES - 可以在多个设备共享的变量
- tf.GraphKeys.TRAINABLE_VARIABLES - TensorFlow 将计算其梯度的变量。
my_local = tf.get_variable("my_local", shape=(),collections=[tf.GraphKeys.LOCAL_VARIABLES]) # 添加到 tf.GraphKeys.LOCAL_VARIABLES 集合中
my_non_trainable = tf.get_variable("my_non_trainable",shape=(),trainable=False) # 添加到 tf.GraphKeys.LOCAL_VARIABLES 集合中
tf.add_to_collection("my_collection_name", my_local) #添加到自己命名的my_collection_name集合中
tf.get_collection("my_collection_name") #检索该集合下所有变量
设备放置方式
###将变量放置在第二个GPU设备上
with tf.device("/device:GPU:1"):
v = tf.get_variable("v", [1])
分布式设置
以后再更
初始化变量
### 初始化tf.GraphKeys.GLOBAL_VARIABLES 集合中所有变量
# 创建两个变量
weights = tf.Variable(tf.random_normal([784, 200], stddev=0.35),
name="weights")
biases = tf.Variable(tf.zeros([200]), name="biases")
...
# 添加用于初始化变量的节点
init_op = tf.global_variables_initializer()
# 然后,在加载模型的时候
with tf.Session() as sess:
# 运行初始化操作
sess.run(init_op)
...
# 使用模型
...
### 自行初始化变量
session.run(my_variable.initializer)
print(session.run(tf.report_uninitialized_variables())) #查询哪些变量尚未初始化
默认的 tf.global_variables_initializer 不会指定变量的初始化顺序。因此,如果变量的初始值取决于另一变量的值,那么很有可能会出现错误。
# 使用随机数创建一个变量
weights = tf.Variable(tf.random_normal([784, 200], stddev=0.35),name="weights")
# 创建另一个变量,它与weights拥有相同的初始值
w2 = tf.Variable(weights.initialized_value(), name="w2")
# 创建另一个变量,它的初始值是weights的两倍
w_twice = tf.Variable(weights.initialized_value() * 2.0, name="w_twice")
使用变量
v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer())
w = v + 1
###为变量赋值
v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer())
assignment = v.assign_add(1)
tf.global_variables_initializer().run()
sess.run(assignment) # or assignment.op.run(), or assignment.eval()
v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer())
assignment = v.assign_add(1)
with tf.control_dependencies([assignment]):
w = v.read_value() #w在assign_add操作后反映v的值
共享变量
###创建一个卷积层
def conv_relu(input, kernel_shape, bias_shape):
# Create variable named "weights".
weights = tf.get_variable("weights", kernel_shape,
initializer=tf.random_normal_initializer())
# Create variable named "biases".
biases = tf.get_variable("biases", bias_shape,
initializer=tf.constant_initializer(0.0))
conv = tf.nn.conv2d(input, weights,
strides=[1, 1, 1, 1], padding='SAME')
return tf.nn.relu(conv + biases)
###由于期望的操作不清楚(创建新变量还是重新使用现有变量?),因此 TensorFlow 将会失败。
input1 = tf.random_normal([1,10,10,32])
input2 = tf.random_normal([1,20,20,32])
x = conv_relu(input1, kernel_shape=[5, 5, 32, 32], bias_shape=[32])
x = conv_relu(x, kernel_shape=[5, 5, 32, 32], bias_shape = [32]) # This fails.
用变量域实现共享参数
这里主要包括两个函数接口:`
- tf.get_variable(<name>, <shape>, <initializer>) :根据指定的变量名实例化或返回一个 tensor 对象
- tf.variable_scope(<scope_name>):管理 tf.get_variable() 变量的域名
tf.get_variable() 的机制跟 tf.Variable() 有很大不同,如果指定的变量名已经存在(即先前已经用同一个变量名通过 get_variable() 函数实例化了变量),那么 get_variable()只会返回之前的变量,否则才创造新的变量。
def conv_relu(input, kernel_shape, bias_shape):
# Create variable named "weights".
weights = tf.get_variable("weights", kernel_shape,
initializer=tf.random_normal_initializer())
# Create variable named "biases".
biases = tf.get_variable("biases", bias_shape,
initializer=tf.constant_initializer(0.0))
conv = tf.nn.conv2d(input, weights,
strides=[1, 1, 1, 1], padding='SAME')
return tf.nn.relu(conv + biases)
def my_image_filter(input_images):
with tf.variable_scope("conv1"):
# Variables created here will be named "conv1/weights", "conv1/biases".
relu1 = conv_relu(input_images, [5, 5, 32, 32], [32])
with tf.variable_scope("conv2"):
# Variables created here will be named "conv2/weights", "conv2/biases".
return conv_relu(relu1, [5, 5, 32, 32], [32])
先定义一个 conv_relu() 函数,用 tf.variable_scope() 来分别处理两个卷积层的参数。正如注释中提到的那样,这个函数会在内部的变量名前面再加上一个「scope」前缀,比如:conv1/weights表示第一个卷积层的权值参数。这样一来,我们就可以通过域名来区分各个层之间的参数了。
不过,如果直接这样调用 my_image_filter,是会抛异常的:
result1 = my_image_filter(image1)
result2 = my_image_filter(image2)
# Raises ValueError(... conv1/weights already exists ...)
因为 tf.get_variable()虽然可以共享变量,但默认上它只是检查变量名,防止重复。要开启变量共享,你还必须指定在哪个域名内可以共用变量:
with tf.variable_scope("image_filters") as scope:
result1 = my_image_filter(image1)
scope.reuse_variables()
result2 = my_image_filter(image2)
到这一步,共享变量的工作就完成了。你甚至都不用在函数外定义变量,直接调用同一个函数并传入不同的域名,就可以让 TensorFlow 来帮你管理变量了。
若部分变量共享,部分不共享:
def test(mode):
w = tf.get_variable(name=mode+"w", shape=[1,2])
u = tf.get_variable(name="u", shape=[1,2])
return w, u
with tf.variable_scope("test", reuse=tf.AUTO_REUSE) as scope:
w1, u1 = test("mode1")
w2, u2 = test("mode2")
这里只是加了一个参数 reuse=tf.AUTO_REUSE,但正如名字所示,这是一种自动共享的机制,当系统检测到我们用了一个之前已经定义的变量时,就开启共享,否则就重新创建变量。
变量域的工作机理
首先,TensorFlow 会判断是否要共享变量,也就是判断 tf.get_variable_scope().reuse 的值,如果结果为 False(即你没有在变量域内调用scope.reuse_variables()),那么 TensorFlow 认为你是要初始化一个新的变量,紧接着它会判断这个命名的变量是否存在。如果存在,会抛出 ValueError 异常,否则,就根据 initializer 初始化变量:
with tf.variable_scope("foo"):
v = tf.get_variable("v", [1])
assert v.name == "foo/v:0"
而如果 tf.get_variable_scope().reuse == True,那么 TensorFlow 会执行相反的动作,就是到程序里面寻找变量名为 scope name + name 的变量,如果变量不存在,会抛出 ValueError 异常,否则,就返回找到的变量:
with tf.variable_scope("foo"):
v = tf.get_variable("v", [1])
with tf.variable_scope("foo", reuse=True):
v1 = tf.get_variable("v", [1])
assert v1 is v
变量域的基本使用
- 变量域可以嵌套使用
with tf.variable_scope("foo"):
with tf.variable_scope("bar"):
v = tf.get_variable("v", [1])
assert v.name == "foo/bar/v:0"
我们也可以通过 tf.get_variable_scope() 来获得当前的变量域对象,并通过 reuse_variables() 方法来设置是否共享变量。不过,TensorFlow 并不支持将 reuse 值设为 False,如果你要停止共享变量,可以选择离开当前所在的变量域,或者再进入一个新的变量域(比如,再进入一个 with 语句,然后指定新的域名)。
还需注意的一点是,一旦在一个变量域内将 reuse 设为 True,那么这个变量域的子变量域也会继承这个 reuse 值,自动开启共享变量:
with tf.variable_scope("root"):
# At start, the scope is not reusing.
assert tf.get_variable_scope().reuse == False
with tf.variable_scope("foo"):
# Opened a sub-scope, still not reusing.
assert tf.get_variable_scope().reuse == False
with tf.variable_scope("foo", reuse=True):
# Explicitly opened a reusing scope.
assert tf.get_variable_scope().reuse == True
with tf.variable_scope("bar"):
# Now sub-scope inherits the reuse flag.
assert tf.get_variable_scope().reuse == True
# Exited the reusing scope, back to a non-reusing one.
assert tf.get_variable_scope().reuse == False
捕获变量域对象
如果一直用字符串来区分变量域,写起来容易出错。为此,TensorFlow 提供了一个变量域对象来帮助我们管理代码:
with tf.variable_scope("foo") as foo_scope:
v = tf.get_variable("v", [1])
with tf.variable_scope(foo_scope)
w = tf.get_variable("w", [1])
with tf.variable_scope(foo_scope, reuse=True)
v1 = tf.get_variable("v", [1])
w1 = tf.get_variable("w", [1])
assert v1 is v
assert w1 is w
记住,用这个变量域对象还可以让我们跳出当前所在的变量域区域:
with tf.variable_scope("foo") as foo_scope:
assert foo_scope.name == "foo"
with tf.variable_scope("bar")
with tf.variable_scope("baz") as other_scope:
assert other_scope.name == "bar/baz"
with tf.variable_scope(foo_scope) as foo_scope2:
assert foo_scope2.name == "foo" # Not changed.
在变量域内初始化变量
每次初始化变量时都要传入一个 initializer,这实在是麻烦,而如果使用变量域的话,就可以批量初始化参数了:
with tf.variable_scope("foo", initializer=tf.constant_initializer(0.4)):
v = tf.get_variable("v", [1])
assert v.eval() == 0.4 # Default initializer as set above.
w = tf.get_variable("w", [1], initializer=tf.constant_initializer(0.3)):
assert w.eval() == 0.3 # Specific initializer overrides the default.
with tf.variable_scope("bar"):
v = tf.get_variable("v", [1])
assert v.eval() == 0.4 # Inherited default initializer.
with tf.variable_scope("baz", initializer=tf.constant_initializer(0.2)):
v = tf.get_variable("v", [1])
assert v.eval() == 0.2 # Changed default initializer.
图
TensorFlow 使用数据流图将计算表示为独立的指令之间的依赖关系。这可生成低级别的编程模型,在该模型中,您首先定义数据流图,然后创建 TensorFlow 会话,以便在一组本地和远程设备上运行图的各个部分。
为什么使用数据流图
计算图是排列成一个图的一系列TensorFlow指令。
tf.Graph包含两类相关信息:
- 图结构。由两种类型的对象组成:
- 指令:图的节点。消耗和生成张量的计算。
- 张量:图的边。代表流经图的值。
- 图集合。
构建tf.Graph
tf.Graph 对象为其包含的 tf.Operation对象定义一个命名空间。TensorFlow 会自动为您的图中的每个指令选择一个唯一名称,但您也可以指定描述性名称,使您的程序阅读和调试起来更轻松。TensorFlow API 提供两种方法来改写指令的名称:
- 每个创建新的 tf.Operation 或返回新的 tf.Tensor的 API 函数可以接受可选的 name参数。例如,tf.constant(42.0, name="answer") 创建一个名为 "answer" 的新 tf.Operation并返回一个名为 "answer:0" 的 tf.Tensor。如果默认图已包含名为 "answer"`的指令,则 TensorFlow 会在名称上附加 "_1"、"_2" 等字符,以便让名称具有唯一性。
c_0 = tf.constant(0, name="c") # => operation named "c"
# Already-used names will be "uniquified".
c_1 = tf.constant(2, name="c") # => operation named "c_1"
# Name scopes add a prefix to all operations created in the same context.
with tf.name_scope("outer"):
c_2 = tf.constant(2, name="c") # => operation named "outer/c"
# Name scopes nest like paths in a hierarchical file system.
with tf.name_scope("inner"):
c_3 = tf.constant(3, name="c") # => operation named "outer/inner/c"
# Exiting a name scope context will return to the previous prefix.
c_4 = tf.constant(4, name="c") # => operation named "outer/c_1"
# Already-used name scopes will be "uniquified".
with tf.name_scope("inner"):
c_5 = tf.constant(5, name="c") # => operation named "outer/inner_1/c"
类似于张量的对象
默认情况下,每次您使用同一个类似于张量的对象时,TensorFlow 将创建新的 tf.Tensor。如果类似于张量的对象很大(例如包含一组训练示例的 numpy.ndarray
),且您多次使用该对象,您可能会用光内存。要避免出现此问题,请在类似于张量的对象:
- tf.Tensor
- tf.Variable
- numpy.ndarray
- list
- 标量 Python 类型:bool、float、int、str
tf.Session
# Create a default in-process session.
with tf.Session() as sess:
# ...
# Create a remote session.
with tf.Session("grpc://example.org:2222"):
# ...
由于 tf.Session 拥有物理资源(例如 GPU 和网络连接),它通常用作上下文管理器(在 with代码块中),该管理器可在您退出代码块时自动关闭会话。您也可以在不使用with代码块的情况下创建会话,但应在完成会话时明确调用 tf.Session.close以便释放资源
tf.Session.run
tf.Session.run方法是一种用于运行 tf.Operation 或对 tf.Tensor求值的主要机制。您可以将一个或多个 tf.Operation或 tf.Tensor对象传递到 tf.Session.run,TensorFlow 将执行计算结果所需的指令。
= tf.constant([[37.0, -23.0], [1.0, 4.0]])
w = tf.Variable(tf.random_uniform([2, 2]))
y = tf.matmul(x, w)
output = tf.nn.softmax(y)
init_op = w.initializer
with tf.Session() as sess:
# Run the initializer on `w`.
sess.run(init_op)
# Evaluate `output`. `sess.run(output)` will return a NumPy array containing
# the result of the computation.
print(sess.run(output))
# Evaluate `y` and `output`. Note that `y` will only be computed once, and its
# result used both to return `y_val` and as an input to the `tf.nn.softmax()`
# op. Both `y_val` and `output_val` will be NumPy arrays.
y_val, output_val = sess.run([y, output])
tf.Session.run也可以视情况接受Feed字典
# Define a placeholder that expects a vector of three floating-point values,
# and a computation that depends on it.
x = tf.placeholder(tf.float32, shape=[3])
y = tf.square(x)
with tf.Session() as sess:
# Feeding a value changes the result that is returned when you evaluate `y`.
print(sess.run(y, {x: [1.0, 2.0, 3.0]})) # => "[1.0, 4.0, 9.0]"
print(sess.run(y, {x: [0.0, 0.0, 5.0]})) # => "[0.0, 0.0, 25.0]"
# Raises `tf.errors.InvalidArgumentError`, because you must feed a value for
# a `tf.placeholder()` when evaluating a tensor that depends on it.
sess.run(y)
# Raises `ValueError`, because the shape of `37.0` does not match the shape
# of placeholder `x`.
sess.run(y, {x: 37.0})
使用多个图进行编程
g_1 = tf.Graph()
with g_1.as_default():
# Operations created in this scope will be added to `g_1`.
c = tf.constant("Node in g_1")
# Sessions created in this scope will run operations from `g_1`.
sess_1 = tf.Session()
g_2 = tf.Graph()
with g_2.as_default():
# Operations created in this scope will be added to `g_2`.
d = tf.constant("Node in g_2")
# Alternatively, you can pass a graph when constructing a `tf.Session`:
# `sess_2` will run operations from `g_2`.
sess_2 = tf.Session(graph=g_2)
assert c.graph is g_1
assert sess_1.graph is g_1
# Print all of the operations in the default graph.
g = tf.get_default_graph()
print(g.get_operations())
保存和恢复变量
保存变量
# Create some variables.
v1 = tf.get_variable("v1", shape=[3], initializer = tf.zeros_initializer)
v2 = tf.get_variable("v2", shape=[5], initializer = tf.zeros_initializer)
inc_v1 = v1.assign(v1+1)
dec_v2 = v2.assign(v2-1)
# Add an op to initialize the variables.
init_op = tf.global_variables_initializer()
# Add ops to save and restore all the variables.
saver = tf.train.Saver()
# Later, launch the model, initialize the variables, do some work, and save the
# variables to disk.
with tf.Session() as sess:
sess.run(init_op)
# Do some work with the model.
inc_v1.op.run()
dec_v2.op.run()
# Save the variables to disk.
save_path = saver.save(sess, "/tmp/model.ckpt")
print("Model saved in path: %s" % save_path)
恢复变量
tf.reset_default_graph()
# Create some variables.
v1 = tf.get_variable("v1", shape=[3])
v2 = tf.get_variable("v2", shape=[5])
# Add ops to save and restore all the variables.
saver = tf.train.Saver()
# Later, launch the model, use the saver to restore variables from disk, and
# do some work with the model.
with tf.Session() as sess:
# Restore variables from disk.
saver.restore(sess, "/tmp/model.ckpt")
print("Model restored.")
# Check the values of the variables
print("v1 : %s" % v1.eval())
print("v2 : %s" % v2.eval())
网友评论