Tensorflow入门学习(1)

Tensorflow简单介绍

Tensorflow是谷歌在2015年正式开源的计算框架,它可以很好地支持深度学习的各种算法,但它的应用不限于深度学习。这个框架是干嘛的呢?举个例子,如果你想用Python实现一个加法的运算,那简单的写成print(a+b)即可,但是如果我想实现更加复杂的运算,比如训练一个神经网络(又比如二维卷积),我们或许也可以这样写:1.输入数据2.前向传播,定义一个矩阵的乘法运算,输出结果3.计算估计值和实际值的偏差,利用反向传播算法更新权值4.不断迭代。如果是一层网络还好,但是如果是很深的网络,光反向传播的模块就要手写很多吧,而且还要考虑不同的优化策略,随机梯度下降还是更优的优化策略?这些我们都要自己写,如果没有tensorflow这个框架。而有了这个框架以后,这些模块已经封装好了,可以随时让我们调用。这是专门为训练网络(深度学习)而生的一款框架(当然它的实际应用不止这些)。当然了还有很多其他类似的框架,比如MXnet、Caffe等,而我主要介绍这一款。

它的工作流程非常简单:先用Python来定义一个计算图,然后tensorFlow底层用优化的高效C++代码来计算。什么是计算图呢?

我们可以把tensorflow的计算图理解为一个DAG(有向无环图),节点之间的有向边代表了操作和操作之间、数据和数据之间、数据和操作之间的依赖关系。一开始我学习tensorflow的时候总感觉怪怪的,但是后来逐渐意识到它是以图结构来支持数据的流动并实现计算过程的一种模式:某种运算就是一个节点,数据流动到某节点实现运算过程,运算后得到的数据流又开始流动,继续流动到下一个操作节点。比如在实现a+b的时候,a和b都是对应计算图中的一个节点点,而+操作也对应一个节点,如下图所示:

《Tensorflow入门学习(1)》
const、const_1代表a和b,Add代表+

当然这个图比较简单,还有更加复杂的,而对于更加复杂的图,这种依赖关系就需要更加明确,而用这种拓扑结构可以做到这一点。比如下面的这个图:

《Tensorflow入门学习(1)》
《Tensorflow入门学习(1)》

当我们在实现某一种运算或者某一种功能的时候,一般分为两个操作(写tensorflow程序):第一部分是构建计算图,即构造节点和节点之间的拓扑关系;第二部分是运行图,这个过程实现数据的运算,通过构建一个会话(Session)得到最终的结果。第一部分很像安装水管,但是这个时候并不通水,只有水管都安装好了之后才会执行第二部分:通水。通水之后,每一个节点的水量就可以容易到。而获得水量的方法叫做Session,这个可能比较抽象,一会具体的解释。

上面的讲的这些是一个对于tensorflow结构和功能 抽象层面的认知,接下来会具体讲一些基本的操作细节并用代码实现。

Tensorflow计算模型—–计算图

Tensorflow程序一般分为 两个阶段:定义计算图的 各种运算和执行运算

import tensorflow as tf
a = tf.constant([1.0,2.0], name = "a") 
b = tf.constant([2.0,3.0], name = "b")
result  = a + b

为了整洁方便,直接用tf代替tensorflow,tensorflow会自动将定义的运算转化为计算图上的节点,而这个程序中会自动维护一个默认的图,通过tf.get_default_graph函数可以获得当前的图,并且可以通过tf.Graph函数来生成新的计算图。同时,tensorflow可以把计算图分成几块,然后并行的在多个GPU/CPU上跑。TensorFlow还支持分布式计算,这样,它可以训练百万级别参数的神经网络,利用十亿级的样本和百万级的特征。因此我们可以用tf.Graph.device来指定运行计算的设备:

g = tf.Graph()
with g.device('/gpu:0'):
    result = a + b
with g.device('/gpu:1'):
    result = a * b

Tensorflow数据模型—–张量

什么是张量?我们以前听说过标量、向量,张量其实是一个多维数组的总称。零阶张量表示标量(scalar),也就是一个数;一阶张量为向量,是一个一维数组,n阶张量可以理解为一个n维数组。这里面的张量和numpy中的数组不同,它不是一个特定的数字,而是一种运算结果的引用,从下面的代码可以看出:

import tensorflow as tf
a = tf.constant([1.0, 2.0], name = "a")
b  =tf.constant([2.0, 3.0], name = "b")
result = tf.add(a, b, name = "c")
print(result)
#output: Tensor("c:0", shape=(2,), dtype=float32)

输出是一个Tensor类型的数据引用,主要包含了以下的三个属性:名字(name)、维度(shape)和 类型(type)。那为什么会是这个结果呢?为什么不是一个具体的数字呢?还是最开始说的那样,这里我们只是构建了计算图,却没有运行计算图,运行图需要对所有的变量初始化值。这里我们只是输出了结果result所对应节点的一个引用,可以理解为一个节点信息。

再看我们的下面的例子:

import tensorflow as tf
a = tf.constant([1.0, 2.0], name = "a")
b  =tf.constant([2, 3], name = "b")
result = tf.add(a, b, name = "c")
print(result)
#TypeError: Input 'y' of 'Add' Op has type int32 that does not match type float32 of argument 'x'.

这里报错了,原因是因为不同的数据类型无法匹配做运算,一般在神经网络中我们的权值数据类型使用float32即可。Tensorflow支持了14中数据类型:tf.float32, tf.float64, tf.int8, tf.int16, tf.int32, tf.int64, tf.unit8, tf.bool, tf.complex64, tf.complex128等。

Tensorflow运行模型—–会话

前面主要讲了Tensorflow如何组织数据的 和运算的,这里我们主要讲解如何使用会话来执行定义好的运算。会话是啥意思呢?通俗点说,就是我的水管安装好了,水管和水管之间的拓扑关系我们已经知道了,这时候我们开始通水了。若我们想知道某个节点的结果(即水流量),我们需要执行下面的操作即可:

import tensorflow as tf
a = tf.constant([1.0, 2.0], name = "a")
b  =tf.constant([2.0, 3.0], name = "b")
result = tf.add(a, b, name = "c")
sess = tf.Session()
print(sess.run(result))
sess.close()

此时我们就知道result的取值了(即流量)。

上面这种执行方式有个不好的地方:sess = tf.Session()和 sess.close()要成对存在,怪麻烦的。如果不close会话的话,可能会无法释放资源从而导致资源泄露。

为了解决这个问题并且让程序变得更加方便,我们直接采用下面的这种形式:

import tensorflow as tf
a = tf.constant([1.0, 2.0], name = "a")
b  =tf.constant([2, 3], name = "b")
result = tf.add(a, b, name = "c")
with tf.Session() as sess:
    print(sess.run(result))

这个with内部我怎么来都可以,这个上下文管理器可以自动帮忙实现资源的释放。另外,当会话生成之后,可以通过设定默认会话来计算张量的取值:

import tensorflow as tf
a = tf.constant([1.0, 2.0], name = "a")
b  =tf.constant([2, 3], name = "b")
result = tf.add(a, b, name = "c")
sess = tf.Session()
with sess.as_default():
    print(result.eval())

好了,这次就这些内容,讲的都是一些简单、基本的概念,在以后的更加复杂的工程中,这些基本的知识都要熟记于心,才能快速写出代码。首先最重要的是要对tensorflow这种以图作为计算结构的框架有深刻的理解,知道数据和操作之间的关系是关键。下一次我主要给大家详细讲解用tensorflow实现线性回归模型 和全连接神经网络模型(手动数学推导+代码实现+详解)。第一次写专栏,希望大家多多支持,第一次可能会有很多语句不通顺、逻辑的问题,这些问题我会在不断的讲故事中继续完善和总结。

点个赞再走呗!

    原文作者:不抱怨的世界
    原文地址: https://zhuanlan.zhihu.com/p/68403354
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞