目录
TensorFlow简介
什么是tensorflow
这是一个来自Google的深度学习框架,热度逐年上升,在深度学习领域的生态环境很好。后来facebook的AI实验室不甘示弱,开源了pytorch和caffe2,pytorch侧重于科研,caffe2侧重于工业生产。pytorch借鉴了torch,torch本身是一个很好的框架,但不支持最普及的python语言,而是在lua上;

第一个DL框架Caffe的依赖过多,不易安装及个性化搭建自己的实验模型;pytorch书写简便,更加符合python哲学,但在分布式GPU上没有tensorflow高效;
- tensorflow兼容于Android,Windows,iOS,Linux,而且有优秀的可视化工具tensorboard;
- Checkpoints用于实验状态的保存和恢复;
- Autograd自动梯度计算,在caffe中,如果要自定义一个层,则需要开发者自己去实现梯度的计算,tensorflow解决了caffe的局限性;
- tensorflow目前已合并了高层框架keras,这使tensorflow更加简单易用;
注意
pytorch与tensorflow的autograd并不是自动计算梯度;
在pytorch中,通过计算图,调用方法backward才会依次对计算图中的各个张量计算出梯度;
在tensorflow中,则是用tensorflow.gradient()计算梯度;
tensorflow的核心
tensorflow的核心为graph(计算图)和session(会话),计算图是一个有向无环图;深度学习实验的流程总是为:
1.定义计算图;
2.在session中执行运算;

基于这样的理念,实现了定义与执行的分离。张量tensor在图中通过操作符 operator(简写为op)进行传递和变换(流动),由此得名tensorflow;
事实上,一个单独的operator其实也是一个非常小的计算图;所谓多GPU并行计算,其实就是人为地把原计算图中并行的一些operator组合(子计算图)设置到不同GPU上计算,再将结果汇合进行后续计算;
补充一点,operator,variable,constant都是计算图,variable,constant可以理解为一种特殊的operator,tensor只存在于计算图的执行中;
Tensor即张量:
- 0-dim张量:标量或数值;
- 1-dim张量:向量;
- 2-dim张量:矩阵;
… - n-dim张量:n维矩阵;
Numpy与TensorFlow
回忆pytorch笔记本,相似地,ndarray在CPU上计算,tensor可以在GPU上计算,且包括自动计算梯度;

Tensorflow基本操作
tensorflow不同版本间,其使用方式差异较大,本篇的操作基于tensorflow1.x;
如果要安装支持GPU计算的tensorflow,需要指明安装tensorflow-gpu;
初步认识tensorflow与tensorboard
首先,导入numpy与tensorflow:
import tensorflow as tf
import numpy as np
import tensorflow.keras as keras
tf.__version__ # 1.13.1
keras.__version__ # 2.2.4-tf
# 检查是否可用GPU
tf.test.is_gpu_available()
创建两个矩阵:
matrix_1=np.zeros((5,3))
matrix_1
"""
array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
"""
matrix_2=tf.zeros((5,3))
# 发现是取不到值的,只是初步定义了一个对象
# 本质上matrix_2是一个张量也是一个计算图
matrix_2
"""
<tf.Tensor 'zeros:0' shape=(5, 3) dtype=float32>
"""
# 对张量施加op,其实是对op再次叠加op
tf.reshape(matrix_2,(1,15))
"""
<tf.Tensor 'Reshape:0' shape=(1, 15) dtype=float32>
"""
通过上面实例可以知道,在会话执行前,所有op都是在定义计算图,下面再定义一个简单的计算图:
a=tf.add(3,5,name="Add")
# a不仅是一个张量,其实已经是一个计算图
print(a)
"""
Tensor("Add:0", shape=(), dtype=int32)
"""
经过tensorboard可视化后有:

在定义计算图时,使用了tf.add,并赋予名称"Add",该op依赖于另外两个op:x和y,即常量3和5,在定义时,tensorflow已经自动使用constant初始化,并为其命名为x和y;
常量constant也叫做源op,源op不需要输入计算图作为依赖:
sourceop = tf.constant(3,name="sop")
a与matrix_2一样,取不到结果,因为还没有执行计算图;执行计算图使用tf.Session:
sess=tf.Session()
# 可以简单理解为:sess可以把当前张量(计算图)依赖的张量(子图)逐步找到并执行
print(sess.run(a))
print(sess.run(matrix_2))
sess.close()
# 考虑书写规范
with tf.Session() as sess:
print(sess.run(a))
使用writer可以将感兴趣的对象写入日志:
a=tf.constant(2)
b=tf.constant(3)
x=tf.add(a,b)
with tf.Session() as sess:
# 将感兴趣的对象(比如计算图)写入日志,需在运行sess前初始化writer
writer=tf.summary.FileWriter("./graphs/const_add",sess.graph)
print(sess.run(x))
#关闭writer
writer.close()
sess.graph就是当前会话中的所有计算图:
print(sess.graph)
"""
<tensorflow.python.framework.ops.Graph object at 0x7f3f540cc710>
"""
日志可视化需要tensorboard,使用命令行解析日志,在浏览器端可视化,用法举例如下(注意输入的是绝对路径):
tensorboard --logdir=/home/baijingyi/python37/MASK/graphs/const_add
另外,必须用谷歌浏览器才能访问tensorboard开启的进程;
为了便于管理计算图,可以为op赋予名称name:
a=tf.constant([2,3],name='a') #[2,]
b=tf.constant([2,3],name='b') # [2,]
# 注意tf.multiply是张量逐个元素相乘,tf.matmul才是矩阵乘法
x=tf.multiply(a,b,name='dot_product') #[2,]
另外,我可以将要执行的计算图加入列表,即变成fetch,再执行:
x=tf.constant(2)
y=tf.constant(3)
add_op=tf.add(x,y)
mul_op=tf.multiply(x,y)
useless=tf.multiply(x,add_op)
pow_op=tf.pow(add_op,mul_op)
with tf.Session() as sess:
z,n=sess.run(
[pow_op,useless]
)
计算图为:

额外补充,计算图中,非源op是没有箭头的(在低版本的tensorflow中没有箭头,高版本的tensorflow计算图中,每个op都有箭头指向);
前面曾经提到,可以将计算图分解到不同的设备上并行计算,只需要为目标op在定义时指明设备,在会话中写入配置tf.ConfigProto(log_device_placement=True):
#指定设备
#定义图
with tf.device('/gpu:2'):
a=tf.constant([i for i in range(6)],dtype=tf.float32,name='a')
b=tf.constant([i for i in range(6)],dtype=tf.float32,name='b')
c=tf.multiply(a,b)
#构建session
with tf.Session(
#在sess中写入配置,让任务必须在指定gpu上完成
config=tf.ConfigProto(log_device_placement=True)
) as sess:
sess.run(c)
constant
constant是tensorflow中构造常量的op,属于源op;
tf.constant(value,dtype=None,shape=None,name='Const',verify_shape=False)
其中,参数verify_shape默认为False,如果修改为True表示检查value的形状与shape是否相符,如果不符会报错;
常量分三种,一种是人为定义,一种是随机采样,另一种是zeros,ones一类的常量;
1.人为定义的常量
a = tf.constant([2, 2], name="a")
b = tf.constant([[0, 1], [2, 3]], name="b")
2.从随机分布中采样
tensorflow已实现的常用随机分布常量有:
tf.random_normal(shape,mean=0.0,stddev=1.0,dtype=tf.float32,seed=None,name=None)
tf.truncated_normal(shape,mean=0.0,stddev=1.0,dtype=tf.float32,seed=None,name=None)
tf.random_uniform(shape,minval=0,maxval=None,dtype=tf.float32, seed=None,name=None)
tf.random_shuffle(value,seed=None,name=None)
tf.random_crop(value,size,seed=None,name=None)
tf.multinomial(logits,num_samples,seed=None,name=None)
tf.random_gamma(shape,alpha,beta=None,dtype=tf.float32,seed=None, name=None)
实例如下:
x=tf.random_normal([5,3],mean=0.0,stddev=1.0)
with tf.Session() as sess:
print(sess.run(x))
"""
[[-0.544223 0.3129832 -1.1097211 ]
[-0.96931726 -0.97435105 2.201063 ]
[-1.8638048 0.17048389 0.6574646 ]
[ 0.2907467 -0.95708215 -0.63632256]
[-0.4043712 -0.3576785 -0.70155025]]
"""
3.zeros与ones
tf.zeros和tf.ones实质上也是常量的范畴:
x=tf.zeros([5,3])
Variable
变量是一种更特殊的op,根据前面内容已经知道常量是一个op,而变量是一个类,它可以用源op或者其他常规op作为参数;
在使用变量前必须进行初始化,否则不会为变量分配计算资源。初始化方式一般有三种,每种初始化都需加入会话执行才生效;
对于以下变量:
# 使用变量前必须初始化,只有初始化变量后,计算机才会为变量分配资源
a=tf.Variable(6,name="a")
b=tf.Variable(8,name="b")
- 最简单的方法,把全部变量都进行初始化
init=tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
- 构造Variable的fetch,初始化变量子集
init_ab=tf.variables_initializer([a,b],name="init_ab")
with tf.Session() as sess:
sess.run(init_ab)
- 初始化单个变量
W=tf.Variable(tf.zeros([5,3]))
with tf.Session() as sess:
sess.run(W.initializer)
对于完成了初始化的变量,可以使用sess.run获取其值,但是每次都sess.run比较麻烦,且耗费资源,可以通过实例方法eval()获取到值(eval适用于所有op,它本质上是sess.run的简化写法):
W=tf.Variable(tf.zeros([5,3]))
with tf.Session() as sess:
sess.run(W.initializer)
print(W.eval())
"""
[[0. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]
"""
以上实例都是用源op作为Variable的参数,也可以用常规op输入:
a=tf.add(3,5)
x=tf.Variable(a)
init=tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
print(x.eval()) # 8
placeholder
placeholder是一个占位符,用于存放训练数据:
tf.placeholder(dtype,shape=None,name=None)
placeholder通常与sess.run中的feed_dict配合使用,placeholder用于提前声明,feed_dict用于执行传入实际的数据,feed_dict传入python格式的数据,被placeholder自动转换为tensorflow的张量格式:
a=tf.placeholder(tf.float32, shape=[3])
b=tf.constant([1,2,3],dtype=tf.float32)
c=a+b # 等价于tf.add(a,b)
with tf.Session() as sess:
print(
sess.run(
c,
# feed_dict用字典形式保存传入的数据
feed_dict={a:[6,7,8]}
)
)
一般来说,待学习的参数用Variable定义,训练数据用placeholder定义
assign,gridents,cast
tf.assign
assign(ref,value,validate_shape=None,name=None)
同样是一个op,此操作用value覆盖了输入张量ref的值,validate_shape用于检查两者的shape是否一致;
tf.gridents
回顾pytorch笔记本,必须在执行backward后,计算图才会被反向计算,得到张量的梯度,tensorflow也一样,如果不调用gridents,计算图是不会反向传播的,也就不存在参数的更新;
对于tf.gradient:
tf.gradients(
ys,
xs,
name='gradients',
stop_gradients=None
# 还有其他很多参数,简单起见就只讨论以上参数
)
ys:目标函数,需要被微分的计算图
xs:需要计算梯度的对象
stop_gradients:终止计算梯度的对象
实例如下:
a = tf.constant(0.)
b = 2 * a
g = tf.gradients(a + b, [a, b]) # 目标函数是3*a
with tf.Session() as sess:
print(sess.run(g))
# 结果:[3.0, 1.0]
a = tf.constant(0.)
b = 2 * a
g = tf.gradients(a + b, [a, b], stop_gradients=[a])
with tf.Session() as sess:
print(sess.run(g))
# 结果:[3.0, 1.0]
a = tf.constant(0.)
b = 2 * a
g = tf.gradients(a + b, [a, b], stop_gradients=[b])
with tf.Session() as sess:
print(sess.run(g))
# 结果:[1.0, 1.0]
对于第2个例子,设置a终止梯度计算,即计算图寻找完依赖子图a后就不再计算梯度,因此,计算图递进到a+2*a,所以a的梯度为3,b的梯度为1;
对于第3个例子,设置b终止了梯度计算,即计算图寻找完依赖子图b后就不再计算梯度,因此计算图停留在a+b,a的梯度只有1,b也只有自己的梯度1;
tf.cast
cast(x, dtype, name=None)
x:待转换的张量
dtype:目标数据类型
name:定义操作的名称
882

被折叠的 条评论
为什么被折叠?



