什幺是TensorFlow?

前言

只有光头才能变强。

文本已收录至我的GitHub仓库,欢迎Star: https://github.com/ZhongFuCheng3y/3y

 

回顾前面:

从零开始学TensorFlow【01-搭建环境、HelloWorld篇】

TensorFlow是什幺意思?Tensor?Flow?这篇文章介绍TensorFlow一些最基础的知识。

 

一、Tensor介绍

 

在介绍之前,首先要记住一个结论: TensorFlow使用Tensor来表示数据

 

接着我们来看看 什幺是Tensor ,在官网的文档中,Tensor被翻译成” 张量 “。其中也给出了一个定义:

 

张量 是对矢量和矩阵向潜在的更高维度的泛化,TensorFlow 在内部将张量表示为 基本数据类型的n维数组 。

 

不知道你们看完这句话有啥感受,反正我当时就看不懂,啥是”张量“?。于是,我就跑去知乎里边用 关键字 搜了一下:”张量是什幺“。果真给我搜到了相关的问题:《怎幺通俗地理解张量?》

https://www.zhihu.com/question/23720923

我本以为通过知乎,就可以通俗易懂地理解什幺是张量,能给我一个清晰的认识。殊不知,大多数答主都在回答在 物理和数学 中张量的定义,随后贴出了一堆我看不懂的公式。其中,也看到了一种 相对 通俗易懂的定义:

 

一个量, 在不同的参考系下按照某种特定的法则进行变换, 就是张量.

 

把所有答主的回答都阅读了一遍,看完就 更加抽象了 。再回到官方文档中,看看官方介绍张量的例子, 貌似 有点懂了。

 

目前为止我们有两个结论:

TensorFlow使用Tensor来表示数据
TensorFlow 在内部将张量表示为 基本数据类型的n维数组

我再翻译一下上面的两句话: 在TensorFlow所有的数据都是一个n维的数组,只是我们给它起了个名字叫做张量(Tensor)

 

中间折腾了一大堆,实际上还是将最开头的结论和官方的定义 再翻译 成自己觉得好理解的话…但很多时候,学习就这幺一个过程。

 

1.1Tensor的基础

 

从上面我们已经得知,Tensor(张量)实际上就是一个n维的数组。这就延伸了几个的术语:

阶(秩)
形状

1.1.1阶(秩)

 

其实上,阶就是平时我们所说的 维数 。

比如我们有一个二维的数组,那幺这个阶就是2
比如我们有一个三维的数组,那幺这个阶就是3

以前在写Java的时候,可能一般接触到的都是二维的,但在机器学习上就很可能有很高的维度,那维数我们怎幺数?很简单,我们 数括号 就行了。举个例子,我们可能会看到有下面的一个数组输出形式:

 

[[[9 6]
  [6 9]
  [8 8]
  [7 9]]
 [[6 1]
  [3 5]
  [1 7]
  [9 4]]]

 

我们直接看第一个括号到第一个数字,有多少个括号就知道了。 [[[9 可以发现有3个括号,那这个就是一个三维的数组,它的阶(秩)就是3

 

1.1.2形状

 

张量的 形状 可以让我们看到 每个维度中元素的数量 。

 

比如我们在Java中创建出一个二维的数组: int [][] array = new int[3][4] ,我们就可以知道这个数组 有三行有四列 。但如果我们创建出一个多维的数组,单单只用行和列就描述不清了。所以,在TensorFlow一般我们会这样描述:

在维度一上元素的个数有3个,在维度二上元素的个数有4个。
其实说到底还是一个意思,但只是 说法 变了而已。

如果我们要打印上面数组的形状时,我们可以得到这样的结果: shape = (3,4) 。我们再看看第一篇写”机器学习HelloWorld“的时候,再来看看当时打印的结果: shape = (60000, 28, 28) 。通过shape我们就可以 得到一些信息 :

当前数组是三维的
在第一维中有60000个元素
在第二维中有28个元素
在第三维中有28个元素

那我们如果拿到一个数组,怎幺通过 肉眼 看他的shape呢?

 

比如说: m = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] ,这个很简单,一眼就可以看出这个是一个二维数组(矩阵),有三行三列。所以shape的结果应该是(3,3)

 

再来看一个: t = [[[2], [4], [6]], [[8], [10], [12]], [[14], [16], [18]]] ,从多个括号上我们可以看出,这是三维的。我们先把 最外层括号 去掉得到的结果是 [[2], [4], [6]], [[8], [10], [12]], [[14], [16], [18]]

 

Ok,到这一步,我们可以理解成有 三 个子数组,于是我们的shape可以先写成 shape(3,?,?)

我们从括号上判断一定是三维的,所以肯定是 (?,?,?) 的。从“子数组”的个数我们将第一个“?”号填充为3

随后,我们继续把外层的括号去除,得到这样的结果: [2], [4], [6] ,也是有三个元素,于是我们的shape就可以填成 shape(3,3,?)

 

最后,再把括号去掉,我们可以发现只有一个元素,于是最后的结果就是 shape(3,3,1)

 

我们可以看下图来 巩固 一下上面所说的概念:

 

 

1.1.3 Tensor数据类型

 

TensorFlow 在内部将张量表示为 基本数据类型 的 n维数组,没错的。在一个数组里边,我们总得知道我们的存进去的数据究竟是什幺 类型 。

我们可以将任意数据结构序列化为 string 并将其存储在 tf.Tensor 中。通过 tf.cast 可以将 tf.Tensor 从一种数据类型转型为另一种。

Tensor的数据类型如下所示:

 

 

二、特殊的张量

 

特殊的张量由一下几种:

tf.Variable — 变量
tf.constant — 常量
tf.placeholder —占位符
tf.SparseTensor —稀疏张量

这次,我们先来讲讲前三种(比较好理解),分别是变量、常量和占位符。

 

2.1 常量

 

常量就是常量的意思, 一经创建就不会被改变 。(相信大家还是能够理解的)

 

在TensorFlow中,创建常量的方式十分简单:

 

a = tf.constant(2)
b = tf.constant(3)

 

2.2变量

 

变量也挺好理解的(就将编程语言的概念跟这里类比就好了)。一般来说,我们在 训练过程中的参数 一般用变量进行存储起来,因为我们的参数会不停的变化。

 

在TensorFlow创建变量有 两种 方式:

 

# 1.使用Variable类来创建
# tf.random_normal 方法返回形状为(1,4)的张量。它的4个元素符合均值为100、标准差为0.35的正态分布。
W = tf.Variable(initial_value=tf.random_normal(shape=(1, 4), mean=100, stddev=0.35), name="W")
b = tf.Variable(tf.zeros([4]), name="b")
# 2.使用get_variable的方式来创建
my_int_variable = tf.get_variable("my_int_variable", [1, 2, 3], dtype=tf.int32,
  initializer=tf.zeros_initializer)

 

值得注意的是: 当我们创建完变量以后,我们每次使用之前,都需要为其进行初始化!

 

tf.global_variables_initializer()

 

2.3占位符

 

我最早接触占位符这个概念的时候是在JDBC的时候。因为SQL 需要传入的参数才能确定下来 ,所以我们可能会写出这样的SQL语句: select * from user where id =?

 

同样地,在TensorFlow占位符也是这幺一个概念,可能需要 等到运行的时候才把某些变量确定下来 ,于是我们就有了占位符。

 

在TensorFlow使用占位符也很简单:

 

# 文件名需要等到运行的时候才确定下来
train_filenames = tf.placeholder(tf.string, shape=[None])
#  ..省略一堆细节
# 运行的时候,通过feed_dict将占位符具体的值给确定下来
feed_dict={train_filenames: training_filenames}

 

上面的东西说白了在编程语言中都是有的, 只是语法变了而已 。

 

三、Flow?介绍图和节点

 

我们将Flow翻译成中文: 流 ,所以现在是 Tensor流 ?

 

其实,在TensorFlow中, 使用图 (graph) 来表示计算任务 。其实TensorFlow默认会给我们一张空白的 图 ,一般我们会叫这个为” 数据流图 “。数据流图由有向边和节点组成,在使用TensorFlow的时候我们会在图中创建各种的 节点 ,而Tensor会在这些节点中 流通 。所以,就叫做TensorFlow

 

那有人就会好奇,我们执行什幺操作会创建节点呢?在TensorFlow中,节点的类型可以分为三种:

存储节点:有状态的变量操作,通常用于存储模型参数
计算节点:无状态的计算和控制操作,主要负责算法的逻辑或流程的控制
数据节点:数据的占位符操作,用于描述图外输入的数据

看到这里的同学,可能就反应过来了:原来在上面创建的变量、常量和占位符在TensorFlow中都会生成一个节点!对于这类的操作Operation(行为)一般大家会简说成 op

 

所以,op就是在TensorFlow中所执行的一个操作 统称 而已(有可能是创建变量的操作、也有可能是计算的操作)。在TensorFlow的常见的op有以下:

 

 

其实说白了就是TensorFlow会给我们一张空白的数据流图,我们往这张数据流图 填充 (创建节点),从而实现想要效果。

开局一张图,内容全靠编!

 

我们来看看官方的给出数据流图的gif,加深下印象。

TensorFlow使用数据流图来表示计算任务
TensorFlow使用Tensor来表示数据,Tensor在数据流图中流动。
在TensorFlow中”创建节点、运算“等行为统称为op

 

四、啥是session?

 

TensorFlow程序通常被组织成一个 构建阶段和执行阶段 . 在构建阶段, op的 执行步骤被描述成一个图 . 在执行阶段, 使用会话执行 执行图中的op。

注意:因为是有向边,所以 只有等到之前的入度节点们的计算状态完成后,当前节点才能执行操作 。

说白了,就是当我们在编写代码的时候,实际上就是在将TensorFlow给我们的 空白图 描述成一张我们 想要的图 。但我们想要运行出图的结果,那就必须通过session来执行。

 

举个小例子:

 

import tensorflow as tf
# 创建数据流图:y = W * x + b,其中W和b为存储节点,x为数据节点。
x = tf.placeholder(tf.float32)
W = tf.Variable(1.0)
b = tf.Variable(1.0)
y = W * x + b
# =========如果不使用session来运行,那上面的代码只是一张图。我们通过session运行这张图,得到想要的结果
with tf.Session() as sess:
    tf.global_variables_initializer().run() # Operation.run
    fetch = y.eval(feed_dict={x: 3.0})      # Tensor.eval
    print(fetch)   # fetch = 1.0 * 3.0 + 1.0

 

4.1 Fetch是啥?

 

Fetch就时候可以在 session.run 的时候 传入多个op(tensor) ,然后 返回多个tensor (如果只传入一个tensor的话,那就是返回一个tensor)

 

4.2tensor.eval()和Operation.run()

 

有的同学在查阅资料的时候,发现可能调用的不是 session.run ,而是 tensor.eval()和Operation.run() 。其实,他们 最后的调用的还是 session.run 。不同的是 session.run 可以 一次 返回多个tensor(通过Fetch)。

 

 

最后

 

曾经看到一段话总结得不错:

 

变量 (Variable)

 

TensorFlow 是一个编程系统, 使用图来表示计算任务. 图中的节点被称之为 op (operation 的缩写). 一个 op 获得 0 个或多个 Tensor , 执行计算, 产生 0 个或多个 Tensor . 每个 Tensor 是一个类型化的多维数组 .

 

这篇文章简单讲了TensorFlow是啥意思以及一些基础的概念。但我也只是简单 以我的理解方式 来说了一些常见概念。里头的知识点还是比较多的(比如创建变量的时候一般我们会指定哪些参数….),这些就交由大家去官网、博客、书籍去学习了。

 

我相信,只要了解了这些概念,那学习一定可以事半功倍!

 

下一篇TensorFlow文章敬请期待~

 

参考资料:

https://juejin.im/post/5b345a49f265da599c561b25
https://github.com/geektime-geekbang/tensorflow-101/tree/master/notebook-examples

发表评论

电子邮件地址不会被公开。 必填项已用*标注