TensorFlow 张量
张量的概念
在数学里,张量是一种几何实体,广义上表示任意形式的数据。
张量可以理解为0阶标量、1阶向量和2阶矩阵在高维空间上的推广,张量的阶描述它表示数据的最大维度。
TensorFlow 中的张量
TensorFlow 中的张量表示:某种相同数据类型的多维数组
因此,张量有两个重要属性:
- 数据类型(如浮点型、整型、字符串)
- 数组形状(各个维度的大小)
当我们去定义一个张量的时候,它里面的所有的元素都应该是相同的数据类型,并且还要明确的描述出这个数组的形状。
TensorFlow 中的张量的作用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQIgAF7Y-1663421914899)(/Volumes/猪猪博雅/1.All 下载文件/Safari浏览器下载/2019-01-28-tensorflow-introduction-02-01.gif)]
这张图里边里面流动的就是数据(张量),边就是张量。每一个节点都是操作,在执行操作的时候需要输入数据的 ,例如:y=a+b,a和b就是我们的输入数据,需要张量流入到这个操作节点才能够进行运算,y又会作为下一个操作的一个输入数据。所以y=a+b这个操作的输出的数据也是由张量来表示的。
张量的类别
在 TensorFlow 中,有几类比较特别的张量,由以下操作产生: tf.constant
- 常量,用常量操作创建出来张量之后,张量的值是不可改变的
tf.placeholder
- 占位符,可以简单理解为一个描述了数据的壳,本身是要通过数据流图外面的数据填充进来才可以得到数据的这么一种特殊的操作。甚至可以在填充数据的时候,再去定义深度到底是多少,就给的模型训练的数据填充留下足够灵活的接口。
tf.Variable
- 变量,可以在整个数据流图的过程当中维护我的状态,达到保存值的一个效果。使得值在整个数据流图运行结束之后也不会被释放,会常驻在内存里面,使得的值能够保存下来。
如何创建一个张量
张量的创建并不是张量的类的构造方法去创建,而是直接通过执行一个操作来创建,开始有提到的形状属性很重要,很多时候说必须要定义形状,是指在实际的运行时, 必须要把形状定义下来。
比例:两个矩阵相乘,如果不清楚矩阵的形状,无法去做相乘。
在写代码的时候,其实是不一定能确定形状,因为跟输入数据有关。可以把形状具体某一部分缺审,然后在实际运行的时候通过形状推断(运算到这一层的时候,当前的张量或者说数组到底是什么形状)
mammal = tf.Variable('Elephant',tf.string)
ignition = tf.Variable(451,tf.int64)
floating = tf.Variable(3.1415926,tf.float64)
its_complicated = tf.Variable(12.3 - 4.85)
[mammal,ignition,floating,its_complicated]
输出: [<tf.Variable ‘Variable:0’ shape=() dtype=string, numpy=b’Elephant’>, <tf.Variable ‘Variable:0’ shape=() dtype=int32, numpy=451>, <tf.Variable ‘Variable:0’ shape=() dtype=float32, numpy=3.1415925>, <tf.Variable ‘Variable:0’ shape=() dtype=float32, numpy=7.45>]
mystr = tf.Variable(['Hello','World'],tf.string)
cool_numbers = tf.Variable([3.14159,2.71828],tf.float32)
first_primes = tf.Variable([2,3,4,7,11],tf.int32)
its_very_complicated = tf.Variable([12.3-4.85j,7.5-6.23j],tf.complex64)
mymat = tf.Variable([[7],[11]],tf.int64)
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)
my_image = tf.zeros([10,299,299,3])
TensorFlow 变量
变量的作用
变量一种特殊的张量,变量的主要作用:维护特定节点的状态 比如:深度学习或者机器学习当中的模型参数,w跟b就是变量。模型参数反复更新之后,仍然不会在内存当中释放,而是会常驻在内存里面。
变量与张量的异同
通过 tf.Variable 方法创建的变量,可以作为操作的输入和输出(与张量一样)。
不同之处在于: 张量的生命周期通常随依赖的计算完成而结束,内存也随即释放。 变量则常驻内存,维护他的状态。在每一步训练时不断更新其值,以实现模型参数的更新(减少不必要的开销:重载模型参数、重新初始化)。
w = tf.Variable(<initial-value>,name=<optional-name>)
y = tf.matmul(w,...another variable or tensor...)
z = tf.sigmoid(w + y)
w.assign(w + 1.0)
w.assign_ass(1.0)
整个创建变量的过程,实际可以理解为定义了一个规则(包括初始值、数据类型、形状等等),但在开始会话之前(开始整个图运算之前),其实是没有任何真正的计算执行。都只是一个抽象的图的定义,没有执行。那如果真正要开始执行的时候,其实需要去加载数据流图到会话里,然后通过初始化我们的变量(运行创建变量时定义的规则,可以理解为 C 语言语法,按照这个规则去初始化定义了一个变量)才能开始运算。所以真正开始执行一张数据流图的时,所有的变量都是需要初始化,定义跟初始化是完全分开的两个步骤?
大家可以认为创建变量操作是描述清楚了这个函数、变量长什么样子,他的初始值、类型等是什么,但是真正开始计算的时候,需要在会话里面去计算,需要去申请对应的资源。
当你放置到设备上之后,开始执行计算的时候,还需要去执行这个初始化的这么一个操作。
TensorFlow提供了一个给全局所有的变量做初始化的一个操作,可以把一张图里面的所有的变量按照定义或者创建的初始值进行一遍操作,把值给他填充进去(背后的原理:本身是在内部调用assign)
变量的典型使用流程
tf.Variable 整个TensorFlow变量的一个典型的使用流程,其实可以总结下来分为5块
-
当我们创建一个变量的时候,我们可以通过初始值,给Variable去直接显示的去给他赋一个值 -
当继续训练,从这个模型文件里面去加载这个模型参数的时候,也可以从checkpoint文件里去读取出上一次训练的变量的值,用checkpoint文件里的变量值去恢复当前数据,而不再是从头开始的初始值。 -
每一步更新,其实都是在这个每一步训练完成之后才会做的事,那训练完成之后,更新完的模型参数,也需要不断的存储到模型文件里面。
tf.Variable 可完成
-
创建(操作) -
初始化(操作) 把数据流图加载到会话中,然后通过一个全局初始化的操作,真正的把值赋给我们的变量 那这个是在会话当中进行的 -
更新(操作) assign
tf.train.Saver 引入一个新的类(tf.train.Saver ),跟真正的模型训练相关的一个很重要的类。 很多时候训练一个很大的模型不太可能会一次性就训练成功,可能你需要训练到中途的时候停下来,调整一些东西(超参数、训练的设备等)。这个时候你需要把你的模型参数持久化或者说保存到一个本地的文件里面或者远端的一个文件系统里。 这个时候需要用tf.train.Saver ,把所有的变量或者说把需要训练的这个模型参数对应的变量保存下来,保存的文件就叫做checkpoint 文件。保存下来之后,还可以通过tf.train.Saver 去恢复这个文件。
TensorFlow 操作
操作就是数据流途当中的节点,数据流图是一种声明式的编程范式,更加偏向于函数以及算法模型的抽象表达。所以说通常用数据流图来表示算法模型,而操作作为节点才是真正模型功能的一个实际的载体。
操作的类别
按照功能的不同分为3种
- 存储节点:有状态的变量操作,通常用来存储模型参数;
- 计算节点:无状态的计算或控制操作,主要负责算法逻辑表达或流程控制;
- 数据节点:数据的占位符操作,用于描述图外输入数据的属性。(一个数据流图他的输入数据,其实就是通过数据节点,又是我们的占位符从图外输入)
操作的输入和输出可以是张量或操作(一个操作得到的结果用作下一个操作的输入)(函数式编程)
TensorFlow典型的计算和控制操作
TensorFlow占位符操作
占位符操作表示图外输入的数据(比如训练、测试数据),TensorFlow数据流图描述了算法模型的计算拓扑,其中的各个操作(节点)都是抽象的函数映射或数学表达式。 换句话说,数据流图本身是一个具有计算拓扑和内部结构的“壳”。在用户向数据流图(边和点:张量和操作)填充数据前,图中并没有真正执行任何计算。
所以说很多时候第一步开始执行,就是给数据流图填充数据。那填充数据是一个什么样的过程:利用了python的字典(feed_dict={x:2,y:3} )
x = tf.placeholder(tf.int16,shape=(),name='x')
y = tf.placeholder(tf.int16,shape=(),name='y')
with tf.Session() as sess:
print(sess.run(add,feed_dict={x:2,y:3}))
print(sess.run(mul,feed_dict={x:2,y:3}))
要从数据流图里面取数据一定要通过session这种方式去执行,那如果要往这个数据流图里面填充数据,通常是通过这个占位符操作往里填充。
数据流图没有在会话中正式执行之前,整个数据流图都只是一个壳(没有真实的数据在里面流动), 也就是没有图里面的流动的线条,因为我们的这个Tensor里面什么都没有。
TensorFlow 的 Python API 和 Python完全区分开来
Python是一个语言,但TensorFlow的Python API并不是Python,而是一种领域特定语言,他实现了一种声明式的编程范式,所以说如果用Python的逻辑,包括这个循环判断去理解这个数据流图的话, 有时可能会出现一些问题。并不能够在这个编译时刻或者说在编写代码的时刻去预测实际运行时的状况,也是尽量不要去使用TensorFlow的逻辑控制和调试的操作的一个原因。
|