1、TensorFlow是什么?
TensorFlow支持各种异构平台,支持多CPU/GPU、服务器、移动设备,具有良好的跨平台性;TensorFlow架构灵活,能够支持各种网络模型,具有良好的通用性。
2、TensorFlow设计理念
(1)将图定义和图运算完全分开。TensorFlow被认为是一个“符号主义”的库。编程模式通常分为命令式编程和符号式编程。命令式编程就是编写我们理解的通常意义上的程序,很容易理解和调试,按照原有逻辑执行。符号式编程涉及很多的嵌入和优化,不容易理解和调试,但运行速度相对有所提升。现有的深度学习框架中,Torch是典型的命令式的,Caffe、MANet采用了两种编程模式混合的方法,而TnesorFlow完全采用符号式编程。
符号计算一般是先定义各种变量,然后建立一个数据流图,在数据流图中规定各变量间的计算关系,最后需要对数据流图进行编译,但此时的数据流图还是一个空壳,里面没有任何实际数据,只有把需要运算的输入放进去后,才能在整个模型中形成数据流,从而形成输出值。
例如:
t = 8 + 9
print(t)
在传统的程序操作中,定义了 t 的运算,在运行时就执行了,并输出 17。而在 TensorFlow中,数据流图中的节点,实际上对应的是 TensorFlow API 中的一个操作,并没有真正去运行:
import tensorflow as tf
t = tf.add(8,9)
print(t)
#输出 Tensor{"Add_1:0",shape={},dtype=int32}
(2)TensorFlow 中涉及的运算都要放在图中,而图的运行只发生在会话(session)中。开启会话后,就可以用数据去填充节点,进行运算;关闭会话后,就不能进行计算了。因此,会话提供了操作运行和 Tensor 求值的环境。
例如:
import tensorflow as tf
#创建图
a = tf.constant([4.0,5.0])
b = tf.constant([6.0,7.0])
c = a * b
#创建会话
sess = tf.Session()
#计算c
print(sess.run(c)) #进行矩阵乘法,输出[24.,35.]
sess.close()
3、TensorFlow系统架构
整个系统从底层到上层可分为七层:
设备层:硬件计算资源,支持CPU、GPU
网络层:支持两种通信协议
数值计算层:提供最基础的计算,有线性计算、卷积计算
高维计算层:数据的计算都是以数组的形式参与计算
计算图层:用来设计神经网络的结构
工作流层:提供轻量级的框架调用
构造层:最后构造的深度学习网络可以通过TensorBoard服务端可视化
4、TensorFlow编程模型
TensorFlow的编程模型:让向量数据在计算图里流动。那么在编程时至少有这几个过程:1.构建图,2.启动图,3.给图输入数据并获取结果。
(1)构建图
TensorFlow的图的类型是tf.Graph,它包含着计算节点和tensor的集合。
这里引用了两个新概念:tensor和计算节点。我们先介绍tensor,一开始我们就介绍了,我们需要把数据输入给启动的图才能获取计算结果。那么问题来了,在构建图时用什么表示中间计算结果?这个时候tensor的概念就需要引入了。
类型是tf.Tensor,代表某个计算节点的输出,一定要看清楚是“代表”。它主要有两个作用:
1.构建不同计算节点之间的数据流
2.在启动图时,可以设置某些tensor的值,然后获取指定tensor的值。这样就完成了计算的输入输出功能。
如下代码所示:
inImage = tf.placeholder(tf.float32,[32,32,3],"inputImage")
processedImage = tf.image.per_image_standardization(inImage,"processedImage")
这里inputImage和processedImage都是tensor类型。它们代表着计算节点输出的数据,数据的值具体是多少在启动图的时候才知道。上面两个方法调用都传递了一个字符串,它是计算节点的名字,最好给节点命名,这样我们可以在图上调用get_tensor_by_name(name)获取对应的tensor对象,十分方便。(tensor名字为“<计算节点名字>:
创建tensor时,需要指定类型和shape。对不同tensor进行计算时要求类型相同,可以使用 tf.cast 进行类型转换。同时也要求 shape (向量维度)满足运算的条件,我们可以使用 tf.reshape 改变shape。
现在了解计算节点的概念,其功能是对tensor进行计算、创建tensor或进行其他操作,类型是tf.Operation。获取节点对象的方法为get_operation_by_name(name)。
构建图,如下代码:
g=tf.Graph()
with g.as_default():
input_data=tf.placeholder(tf.float32,[None,2],"input_data")
input_label=tf.placeholder(tf.float32,[None,2],"input_label")
W1=tf.Variable(tf.truncated_normal([2,2]),name="W1")
B1=tf.Variable(tf.zeros([2]),name="B1")
output=tf.add(tf.matmul(input_data,W1),B1,name="output")
cross_entropy=tf.nn.softmax_cross_entropy_with_logits(logits=output,labels=input_label)
train_step=tf.train.AdamOptimizer().minimize(cross_entropy,name="train_step")
initer=tf.global_variables_initializer()
上面的代码中我们创建了一个图,并在上面添加了很多节点。我们可以通过调用get_default_graph()获取默认的图。
Input_data,input_label,W1,B1,output,cross_entropy都是tensor类型,train_step,initer,是节点类型。
有几类tensor或节点比较重要,下面介绍一下:
1.placeholder
Tensorflow,顾名思义, tensor代表张量数据,flow代表流,其最初的设计理念就是构建一张静态的数据流图。图是有各个计算节点连接而成,计算节点之间流动的便是中间的张量数据。要想让张量数据在我们构建的静态计算图中流动起来,就必须有最初的输入数据流。而placeholder,翻译过来叫做占位符,顾名思义,是给我们的输入数据提供一个接口,也就是说我们的一切输入数据,例如训练样本数据,超参数数据等都可以通过占位符接口输送到数据流图之中。使用实例如下代码:
import tensorflow as tf
x = tf.placeholder(dtype=tf.float32,shape=[],name='x')
y = tf.placeholder(dtpe=tf.float32,shape=[],nmae='y')
z = x*y
with tf.Session() as sess:
prod = sess.run(z,feed_dict={x:1.,y:5.2})
print(prod)
[out]:5.2
2.variable
无论是传统的机器学习算法,例如线性支持向量机(Support Vector Machine, SVM),其数学模型为y =
import tensorflow as tf
a = tf.Variable(2.)
b = tf.Variable(3.)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer()) #变量初始化
print(sess.run(a*b))
[out]:6.
3.initializer
由于tensorflow构建的是静态的计算流图,在开启会话之前,所有的操作都不会被执行。因此为了执行在计算图中所构建的赋值初始化计算节点,需要在开启会话之后,在会话环境下运行初始化。如果计算图中定义了变量,而会话环境下未执行初始化命令,则程序报错,代码如下:
import tensorflow as tf
a = tf.Variable(2.)
b = tf.Variable(3.)
with tf.Session() as sess:
#sess.run(tf.global_variables_initializer()) #注释掉初始化命令
print(sess.run(a*b))
[Error]: Attempting to use uninitialized value Variable
(2)启动图
先了解session的概念,然后才能更好的理解图的启动。图的每个运行实例都必须在一个session里,session为图的运行提供环境。Session的类型是tf.Session,在实例化session对象时我们需要给它传递一个图对象,如果不显示给出将使用默认的图。Session有一个graph属性,我们可以通过它获取session对应的图。
代码如下:
numOfBatch=5
datas=np.zeros([numOfBatch,2],np.float32)
labels=np.zeros([numOfBatch,2],np.float32)
sess=tf.Session(graph=g)
graph=sess.graph
sess.run([graph.get_operation_by_name("initer")])
dataHolder=graph.get_tensor_by_name("input_data:0")
labelHolder=graph.get_tensor_by_name("input_label:0")
train=graph.get_operation_by_name("train_step")
out=graph.get_tensor_by_name("output:0")
for i inrange(200):
result=sess.run([out,train],feed_dict={dataHolder:datas,labelHolder:labels})
if i%100==0:
saver.save(sess,"./moules")
sess.close()
要注意2点:1.别忘记运行初始化节点,2.别忘记close掉session对象以释放资源。
(3)绘图输入数据并获取结果
代码:
for i inrange(200):
result=sess.run([out,train],feed_dict={dataHolder:datas,labelHolder:labels})
这里主要用到了session对象的run方法,它用来运行某个节点或tensor并获取对应的值。我们一般会一次传递一小部分数据进行mini-batch梯度下降来优化模型。
我们需要把我们需要运行的节点或tensor放入一个列表,然后作为第一个参数(不考虑self)传递给run方法,run方法会返回一个计算结果的列表,与我们传递的参数一一对应。
如果我们运行的节点依赖某个placeholder,那我们必须给这个placeholder指定值,怎么指定代码里面很清楚,给关键字参数feed_dict传递一个字典即可,字典里的元素的key是placeholder对象,value是我们指定的值。值的数据的类型必须和placeholder一致,包括shape。值本身的类型是numpy数组。
这里再解释一个细节,在定义placeholder时代码如下:
input_data=tf.placeholder(tf.float32,[None,2],"input_data")
input_label=tf.placeholder(tf.float32,[None,2],"input_label")
shape为[None,2],说明数据第一个维度是不确定的,然后TensorFlow会根据我们传递的数据动态推断第一个维度,这样我们就可以在运行时改变batch的大小。比如一个数据是2维,一次传递10个数据对应的tensor的shape就是[10,2]。可不可以把多个维度指定为None?理论上不可以!
1、 Pytorch
Pytorch是torch的python版本,是由Facebook开源的神经网络框架,专门针对 GPU 加速的深度神经网络(DNN)编程。Torch 是一个经典的对多维矩阵数据进行操作的张量(tensor )库,在机器学习和其他数学密集型应用有广泛应用。与Tensorflow的静态计算图不同,pytorch的计算图是动态的,可以根据计算需要实时改变计算图。但由于Torch语言采用 Lua,导致在国内一直很小众,并逐渐被支持 Python 的 Tensorflow 抢走用户。作为经典机器学习库 Torch 的端口,PyTorch 为 Python 语言使用者提供了舒适的写代码选择。
2、Pytorch特性
1.简洁:
PyTorch的设计追求最少的封装,尽量避免重复造轮子。不像 TensorFlow 中充斥着session、graph、operation、name_scope、variable、tensor、layer等全新的概念,PyTorch 的设计遵循tensor→variable(autograd)→nn.Module 三个由低到高的抽象层次,分别代表高维数组(张量)、自动求导(变量)和神经网络(层/模块),而且这三个抽象之间联系紧密,可以同时进行修改和操作。 简洁的设计带来的另外一个好处就是代码易于理解。PyTorch的源码只有TensorFlow的十分之一左右,更少的抽象、更直观的设计使得PyTorch的源码十分易于阅读。
2.速度:
PyTorch 的灵活性不以速度为代价,在许多评测中,PyTorch 的速度表现胜过 TensorFlow和Keras 等框架。框架的运行速度和程序员的编码水平有极大关系,但同样的算法,使用PyTorch实现的那个更有可能快过用其他框架实现的。
3.易用:
PyTorch 是所有的框架中面向对象设计的最优雅的一个。PyTorch的面向对象的接口设计来源于Torch,而Torch的接口设计以灵活易用而著称,Keras作者最初就是受Torch的启发才开发了Keras。PyTorch继承了Torch的衣钵,尤其是API的设计和模块的接口都与Torch高度一致。PyTorch的设计最符合人们的思维,它让用户尽可能地专注于实现自己的想法,即所思即所得,不需要考虑太多关于框架本身的束缚。
4.活跃的社区:
PyTorch 提供了完整的文档,循序渐进的指南,作者亲自维护的论坛 供用户交流和求教问题。Facebook 人工智能研究院对 PyTorch 提供了强力支持,作为当今排名前三的深度学习研究机构,FAIR的支持足以确保PyTorch获得持续的开发更新,不至于像许多由个人开发的框架那样昙花一现。
3、 PyTorch 的架构
PyTorch(Caffe2) 通过混合前端,分布式训练以及工具和库生态系统实现快速,灵活的实验和高效生产。PyTorch 和 TensorFlow 具有不同计算图实现形式,TensorFlow 采用静态图机制(预定义后再使用),PyTorch采用动态图机制(运行时动态定义)。PyTorch 具有以下高级特征:
混合前端:新的混合前端在急切模式下提供易用性和灵活性,同时无缝转换到图形模式,以便在C ++运行时环境中实现速度,优化和功能。 分布式训练:通过利用本地支持集合操作的异步执行和可从Python和C ++访问的对等通信,优化了性能。 Python优先: PyTorch为了深入集成到Python中而构建的,因此它可以与流行的库和Cython和Numba等软件包一起使用。 丰富的工具和库:活跃的研究人员和开发人员社区建立了丰富的工具和库生态系统,用于扩展PyTorch并支持从计算机视觉到强化学习等领域的开发。 本机ONNX支持:以标准ONNX(开放式神经网络交换)格式导出模型,以便直接访问与ONNX兼容的平台,运行时,可视化工具等。 C++前端:C++前端是PyTorch的纯C++接口,它遵循已建立的Python前端的设计和体系结构。它旨在实现高性能,低延迟和裸机C++应用程序的研究。 使用GPU和CPU优化的深度学习张量库。
4、 Pytorch 与 tensorflow 之间的差异
上面也将了PyTorch 最大优势是建立的神经网络是动态的, 对比静态的 Tensorflow, 它能更有效地处理一些问题, 比如说 RNN 变化时间长度的输出。各有各的优势和劣势。两者都是大公司发布的, Tensorflow(Google)宣称在分布式训练上下了很大的功夫, 那就默认 Tensorflow 在分布式训练上要超出 Pytorch(Facebook),还有tensorboard可视化工具, 但是 Tensorflow 的静态计算图使得在 RNN 上有一点点被动 (虽然它用其他途径解决了), 不过用 PyTorch 的时候, 会对这种动态的 RNN 有更好的理解。而且 Tensorflow 的高度工业化, 它的底层代码很难看懂, Pytorch 好那么一点点, 如果深入 PytorchAPI, 至少能比看 Tensorflow 多看懂一点点 Pytorch 的底层在干啥。
5、Pytorch有哪些常用工具包?
torch :类似 NumPy 的张量库,强 GPU 支持 ; torch.autograd :基于 tape 的自动区别库,支持 torch 之中的所有可区分张量运行; torch.nn :为最大化灵活性未涉及、与 autograd 深度整合的神经网络库; torch.optim:与 torch.nn 一起使用的优化包,包含 SGD、RMSProp、LBFGS、Adam 等标准优化方式; torch.multiprocessing: python 多进程并发,进程之间 torch Tensors 的内存共享; torch.utils:数据载入器。具有训练器和其他便利功能; torch.legacy(.nn/.optim) :处于向后兼容性考虑,从 Torch 移植来的 legacy 代码;