TensorFlow基础

tensorflow简介

TensorFlow 是一个面向于深度学习算法的科学计算库,内部数据保存在张量(Tensor)对

象上,所有的运算操作(Operation, OP)也都是基于张量对象进行。

核心概念

TensorFlow 中的计 算可以表示为一个有向图( directed graph ) ,或称计

(computation graph) 每一个运算操作 operation )将作为一个节点( node ),节点与

节点之间的连接称为边 (edge) 。这个计算图描述了数据的计算流程,它也负责维护个更新状态,用户可以对计算图的分支进行条件控制或循环操作。计算图中的每一个节点可以有任意输入和任意多个输出,每一个节点描述了一种运算操作。节点可以算是运算操作的实例化(instance)。

在计算图的边中流动(flow)的数据被称为张量(tensorflow),故得名TensorFlow。

 

问题来了,什么是张量,什么是流

张量这个概念在数学或者物理学中可以有不同的解释,但在这里并不强调它本身

的含义,在tensorflow中张量可以被简单的理解为多维数组。

流代表了张量之间通过计算相互转化的过程。

 

使用 TensorFlow, 你必须明白 TensorFlow:

  • 使用图 (graph) 来表示计算任务.
  • 在被称之为 会话 (Session) 的上下文 (context) 中执行图.
  • 使用 tensor 表示数据.
  • 通过 变量 (Variable) 维护状态.
  • 使用 feed 和 fetch 可以为任意的操作(arbitrary operation) 赋值或者从其中获取数据

 

tensorflow中的数据类型

TensorFlow 中的基本数据类型,它包含了数值型、字符串型和布尔型。

数值类型的张量是 TensorFlow 的主要数据载体,分为:

❑ 标量(Scalar) 单个的实数,如 1.2, 3.4 等,维度数(Dimension,也叫秩)为 0,shape 为[]

❑ 向量(Vector) n 个实数的有序集合,通过中括号包裹,如[1.2],[1.2,3.4]等,维度数为

1,长度不定,shape 为[ ]

❑ 矩阵(Matrix) n 行 m 列实数的有序集合,如[[1,2],[3,4]],维度数为2,每个维度上的长度不定,shape为[n,m].

❑ 张量(Tensor) 所有维度数dim > 2的数组统称为张量。张量的每个维度也做轴(Axis),

一般维度代表了具体的物理含义,比如 Shape 为[2,32,32,3]的张量共有 4 维,如果表

示图片数据的话,每个维度/轴代表的含义分别是:图片数量、图片高度、图片宽度、

图片通道数,其中 2 代表了 2 张图片,32 代表了高宽均为 32,3 代表了 RGB 3 个通

道。张量的维度数以及每个维度所代表的具体物理含义需要由用户自行定义

在 TensorFlow 中间,为了表达方便,一般把标量、向量、矩阵也统称为张量,不作区

分,需要根据张量的维度数和形状自行判断。

 

tensorflow是如何创建标量的:

 

必须通过 TensorFlow 规定的方式去创建张量,而不能使用 Python 语言的标准变量创建方

式。

import tensorflow as tf


a = 1
aa = tf.constant(1.2) #创建标量
print(type(a),type(aa),tf.is_tensor(aa))
# 创建向量
# 通过print可以打印出向量x的相关信息
x = tf.constant([1, 2., 3.3])
print(x)
# 创建张量
c = tf.constant([[[1,2],[3,4],[5,6],[7,8]]])

# 运行结果
  True
tf.Tensor([1.  2.  3.3], shape=(3,), dtype=float32)
tf.Tensor(
[[[1 2]
  [3 4]
  [5 6]
  [7 8]]], shape=(1, 4, 2), dtype=int32) (1, 4, 2)

shape 表示张量的形状,dtype 表示张量的数值精度。

 

字符串类型

tensorflow 中除了丰富的数值类型外,tensorflow还支持字符串(String)类型的数据,例如在表示图 片数据时,可以先记录图片的路径,再通过预处理函数根据路径读取图片张量。通过传入 字符串对象即可创建字符串类型的张量:

import tensorflow as tf

a = tf.constant('hello,world')
print(a)


# 结果
tf.Tensor(b'hello,world', shape=(), dtype=string)

在 tf.strings 模块中,提供了常见的字符串型的工具函数,如拼接 join(),长度 length(),切

分 split()等等。

深度学习算法主要还是以数值类型张量运算为主,字符串类型的数据使用频率较低, 我们不做过多阐述。

 

布尔类型

为了方便表达比较运算操作的结果,TensorFlow 还支持布尔类型(Boolean, bool)的张

量。布尔类型的张量只需要传入 Python 语言的布尔类型数据,转换成 TensorFlow 内部布

尔型即可:

b = tf.constant(True)
print(b)

# 结果
tf.Tensor(True, shape=(), dtype=bool)

需要注意的是,TensorFlow 的布尔类型和 Python 语言的布尔类型并不对等,不能通用。

 

数值精度

对于数值类型的张量,可以保持为不同字节长度的精度,如浮点数 3.14 既可以保存为

16-bit 长度,也可以保存为 32-bit 甚至 64-bit 的精度。Bit 位越长,精度越高,同时占用的

内存空间也就越大。常用的精度类型有 tf.int16, tf.int32, tf.int64, tf.float16, tf.float32,

tf.float64,其中 tf.float64 即为 tf.double。

在创建张量时,可以指定张量的保存精度:

c = tf.constant(123456789, dtype=tf.int16)
d = tf.constant(123456789, dtype=tf.int32)

# 结果
tf.Tensor(-13035, shape=(), dtype=int16) tf.Tensor(123456789, shape=(), dtype=int32)

可以看到,保存精度过低时,数据 123456789 发生了溢出,得到了错误的结果,一般使用

tf.int32, tf.int64 精度。

对于大部分深度学习算法,一般使用 tf.int32, tf.float32 可满足运算精度要求,部分对

精度要求较高的算法,如强化学习,可以选择使用 tf.int64, tf.float64 精度保存张量。

读取精度

通过访问张量的 dtype 成员属性可以判断张量的保存精度:

c = tf.constant(123456789, dtype=tf.int16)
d = tf.constant(123456789, dtype=tf.int32)


print(c.dtype,d.dtype)

对于某些只能处理指定精度类型的运算操作,需要提前检验输入张量的精度类型,并

将不符合要求的张量进行类型转换。

 

类型转换

系统的每个模块使用的数据类型、数值精度可能各不相同,对于不符合要求的张量的

类型及精度,需要通过 tf.cast 函数进行转换。

 

进行类型转换时,需要保证转换操作的合法性,例如将高精度的张量转换为低精度的张量

时,可能发生数据溢出隐患:

c = tf.constant(123456789, dtype=tf.int16)
d = tf.constant(123456789, dtype=tf.int32)
e = tf.cast(d,tf.int16)
print(e)

布尔型与整形之间相互转换也是合法的,是比较常见的操作:

a = tf.constant([True,False])
b = tf.cast(a,tf.int32)
print(b)

#运行结果
tf.Tensor([1 0], shape=(2,), dtype=int32)

一般默认 0 表示 False,1 表示 True,在 TensorFlow 中,将非 0 数字都视为 True。

待优化张量

为了区分需要计算梯度信息的张量与不需要计算梯度信息的张量,Tensorflow增加了一种专门的数据类型来支持梯度信息的记录:tf.Variable。tf.Variable类型在普通张量类型的基础上添加了name,trainabled等属性来支持计算图的构建。由于梯度运算会消耗大量的计算资源,而且会自动更新相关参数,对于不需要的优化的张量,如神经网络的输入 X, 不需要通过 tf.Variable 封装;相反,对于需要计算梯度并优化的张量,如神经网络层的W 和 ,需要通过 tf.Variable 包裹以便 TensorFlow 跟踪相关梯度信息。

通过tf.Variable()函数可以将普通的张量转换为待优化张量:

import tensorflow as tf


a = tf.constant([True,False])
b = tf.Variable(a)
print(b)

# 运行结果

除了通过普通张量方式创建Variable之外还可以直接创建:

import tensorflow as tf


a = tf.Variable([[1,2],[3,4]])
print(a)

创建全0,全1的张量

创建全0,全1的张量是非常常见的张量初始化手段。

通过 tf.zeros()和 tf.ones()即可创建任意形 状全 0 或全 1 的张量。

例如,创建为 0 和为 1 的标量张量:

import tensorflow as tf


a = tf.zeros([])
b = tf.ones([])
print(a,b)

例如,创建为 0 和为 1 的标量向量:

import tensorflow as tf


a = tf.zeros([1])
b = tf.ones([1])
print(a,b)

创建全0的矩阵:

import tensorflow as tf


a = tf.zeros([2,2])
b = tf.ones([3,2])
print(a,b)

创建与张量a形状一样的全1的张量:

tf.ones_like(a)

创建自定义数值张量

除了初始化为全 0,或全 1 的张量之外,有时也需要全部初始化为某个自定义数值的

张量,比如将张量的数值全部初始化为-1 等。

通过 tf.fill(shape, value)可以创建全为自定义数值 value 的张量。例如,创建元素为-1

的张量:

import tensorflow as tf


a = tf.fill([3,3],-1)
print(a)

# 结果
tf.Tensor(
[[-1 -1 -1]
 [-1 -1 -1]
 [-1 -1 -1]], shape=(3, 3), dtype=int32)

Process finished with exit code 0

创建已知分布的张量

正态分布(Normal Distribution,或 Gaussian Distribution)和均匀分布(Uniform

Distribution)是最常见的分布之一,创建采样自这 2 种分布的张量非常有用,比如在卷积神

经网络中,卷积核张量 W 初始化为正态分布有利于网络的训练;在对抗生成网络中,隐藏

变量 z 一般采样自均匀分布。

通过 tf.random.normal(shape, mean=0.0, stddev=1.0)可以创建形状为 shape,均值为

mean,标准差为 stddev 的正态分布 ( , 2)。

例如,创建均值为 0,标准差为 1 的正太分布:

import tensorflow as tf


a = tf.random.normal([2,2])
b = tf.random.normal([2,2],mean=0.0,stddev=1.0)
print(a)
print(b)

创建均值为1,标准差为2的正太分布:

c = tf.random.normal([2,2],mean=1,stddev =2)
print(c)

通过 tf.random.uniform(shape, minval=0, maxval=None, dtype=tf.float32)可以创建采样自 [ , ]区间的均匀分布的张量。例如创建采样自区间[0,1],shape 为[2,2]的矩阵:

a = tf.random.uniform([2,2])
print(a)

创建采样区间[0,10],shape为[2,2]的矩阵:

a = tf.random.uniform([2,2],maxval=10)
print(a)

如果需要均匀采样整形类型的数据,必须指定采样区间的最大值 maxval 参数,同时制定数

据类型为 http://tf.int*型:

a = tf.random.uniform([2,2],maxval=10,dtype=tf.int32)
print(a)

#结果
tf.Tensor(
[[3 9]
 [4 5]], shape=(2, 2), dtype=int32)

Process finished with exit code 0

创建序列

在循环计算或者对张量进行索引时,经常需要创建一段连续的整形序列,可以通过tf.range()函数实现。tf.range(limit,delta=1)可以创建[0,limit)之间,步长为 delta 的整形序 列,不包含 limit 本身。例如,创建 0~9,步长为 1 的整形序列:

a = tf.range(10)
print(a)

创建0-9步长为2的整形序列:

a = tf.range(10,delta=2)
print(a)

通过 tf.range(start, limit, delta=1)可以创建[ , ),步长为 delta 的序列,不包含 limit 本身:

a =tf.range(1,10,delta=2)
print(a)

张量的典型应用

介绍完张量的相关属性和创建方式后,我们将介绍每种维度下张量的典型应用,让 读者在看到每种张量时,能够直观地联想到它主要的物理意义和用途,对后续张量的维度 变换等一系列抽象操作的学习打下基础。

标量的典型应用:

在 TensorFlow 中,标量最容易理解,它就是一个简单的数字,维度数为 0,shape 为

[]。标量的典型用途之一是误差值的表示、各种测量指标的表示,比如准确度(Accuracy,

acc),精度(Precision)和召回率(Recall)等。

向量的典型应用:

向量是一种非常常见的数据载体,如在全连接层和卷积神经网络层中,偏置张量 就

使用向量来表示。如图所示,每个全连接层的输出节点都添加了一个偏置值,把所有

输出节点的偏置表示成向量形式: = [ 1, 2] 。

 

TensorFlow基础_第1张图片

2个输出节点的网络层。我们创建长度为2的偏置向量b,并累加在每个输出节点上:

# z=wx,模拟获得激活函数的输入z
z = tf.random.normal([4,2])
# 模拟偏置向量
b = tf.zeros([2])
# 累加偏置向量
z = z + b
print(z)

注意:这里shape为[4,2]的z和shape为[2]的b张量可以直接相加,这是为什么呢?后面在Broadcasting的时候再详解。

通过高层接口类Dense()方式创建的网络层,张量W和b存储在类的内部,由类自动创建并管理,可以通过全连接层的bias成员变量查看偏置变量b,例如创建输入节点数为4,输出节点数为3的线性层网络,那么它的偏置向量b的长度应为3:

 

矩阵的典型应用

矩阵也是非常常见的张量类型,比如全连接层的批量输入 = [ , ],其中 表示输入 样本的个数,即 batch size, 表示输入特征的长度。

比如特征长度为 4,一共包含 2 个样本的输入可以表示为矩阵:

x = tf.random.normal([2,4])

令全连接层的输出节点数为 3,则它的权值张量 W 的 shape 为[4,3]:

w = tf.ones([4,3]) # 定义 W 张量
b = tf.zeros([3]) # 定义 b 张量
o = x@w+b # X@W+b 运算
print(o)

三维张量的典型应用

三维的张量一个典型应用是表示序列信号,它的格式是 = [ , , ]

其中 表示序列信号的数量,sequence len 表示序列信号在时间维度上的采样点数,feature

len 表示每个点的特征长度。

考虑自然语言处理中句子的表示,如评价句子的是否为正面情绪的情感分类任务网 络,如图 4.3 所示。为了能够方便字符串被神经网络处理,一般将单词通过嵌入层测试版(Embedding Layer)编码为固定长度的向量,比如“a”编码为某个长度 3 的向量,那么 2 个 等长(单词数为 5)的句子序列可以表示为 shape 为[2,5,3]的 3 维张量,其中 2 表示句子个数,5 表示单词数量,3 表示单词向量的长度。

四维张量的典型应用

我们这里只讨论 3/4 维张量,大于 4 维的张量一般应用的比较少,如在元学习(meta

learning)中会采用 5 维的张量表示方法,理解方法与 3/4 维张量类似。4 维张量在卷积神经网络中应用的非常广泛,它用于保存特征图(Feature maps)数据,

格式一般定义为[ , ℎ, w, c]其中 表示输入的数量,h/w分布表示特征图的高宽, 表示特征图的通道数,部分深度学

习框架也会使用[ , , ℎ, ]格式的特征图张量,例如 PyTorch。图片数据是特征图的一种,

对于含有 RGB 3 个通道的彩色图片,每张图片包含了 h 行 w 列像素点,每个点需要 3 个数

值表示 RGB 通道的颜色强度,因此一张图片可以表示为[h,w, 3]。如图所示,最上层

的图片表示原图,它包含了下面 3 个通道的强度信息。

TensorFlow基础_第2张图片

神经网络中一般并行计算多个输入以提高计算效率,故 张图片的张量可表示为

[ , ℎ, ,w, 3]。

# 创建32*32的彩色图片输入,个数为4
x = tf.random.normal([4,32,32,3])
# 创建卷积神经网络
layer = tf.layers.Conv2D(16,kernel_size=3)
out = layer(x)
print(out.shape)

索引与切片

在 TensorFlow 中,支持基本的[ ][ ]…标准索引方式,也支持通过逗号分隔索引号的索

引方式。考虑输入 X 为 4 张 32x32 大小的彩色图片(为了方便演示,大部分张量都使用随

即分布模拟产生,后文同),shape 为[4,32,32,3],首先创建张量:

x = tf.random.normal([4,32,32,3])

取第一张图片的数据:

print(x[0])

取第一张图片的第二行:

print(x[0][1])

取第一张图片的第二行第三列的像素:

print(x[0][1][2])

取第三张图片,第二行,第一列的像素,B通道(第二个通道)颜色强度值

print(x[2][1][0][1])

当张量的维度数比较高的的时候,用[i][j].......[k]的方式很不方便说书写,可以采用[i,j,k]的方式索引,它们是等价的。

取第二张图片第十行第三列:

print(x[1,9,2])

切片

通过start:end:step切片方式可以方便地提取一段数据,其中start为开始读取位置的索引,end为结束读取位置的索引(不包含end位),step为读取步长。

以shape为[4,32,32,3]的图片张量为例:

读取第2,3张图片

x[1:3]

start: end: step切片方式有很多简写方式,其中 start、end、step 3 个参数可以根据需要

选择性地省略,全部省略时即::,表示从最开始读取到最末尾,步长为 1,即不跳过任何元

素。如 x[0,::]表示读取第 1 张图片的所有行,其中::表示在行维度上读取所有行,它等于

x[0]的写法。

x[0,::]

为了更加简洁,::可以简写为单个冒号:,如

x[:,0:28:2,0:28:2,:]

表示取所有图片,隔行采样,隔列采样,所有通道信息,相当于在图片的高宽各缩放至原来的百分之五十。

总结一下start:end:step切片的简写方式,其中从第一个元素读取时start可以省略,即start=0是可以省略的,取到最后一个元素时end可以省略,步长为1时step可以省略,简写方式总结如表格所示:

TensorFlow基础_第3张图片

特别地,step可以为负数,考虑最特殊的一种例子,step = -1时,start:end:-1表示从start开始,逆序读取至end结束(不包含end),索引号end<=strat。

总结:

张量的索引与切片方式多种多样,尤其是切片操作,初学者容易犯迷糊。但其实本质 上切片操作只有 : : 这一种基本形式,通过这种基本形式有目的地省略掉默认 参数,从而衍生出多种简写方法,这也是很好理解的。它衍生的简写形式熟练后一看就能 推测出省略掉的信息,书写起来也更方便快捷。由于深度学习一般处理的维度数在 4 维以 内,⋯操作符完全可以用:符号代替,因此理解了这些就会发现张量切片操作并不复杂。

维度变换

在神经网络计算过程中,维度变换是最核心的张量操作,通过维度变换可以将数据任意地切换形式,满足不同的场合的运算需求。

那么为什么需要维度变换呢?考虑线性层的批量形式:Y = X@W + b

其中 X 包含了 2 个样本,每个样本的特征长度为 4,X 的 shape 为[2,4]。线性层的输出为 3

个节点,即 W 的 shape 定义为[4,3],偏置 的 shape 定义为[3]。那么X@W的运算张量

shape 为[2,3],需要叠加上 shape 为[3]的偏置 。不同 shape 的 2 个张量怎么直接相加呢?

 

回到我们设计偏置的初衷,我们给每个层的每个输出节点添加一个偏置,这个偏置数据是对所有的样本都是共享的,换言之,每个样本都应该累加上同样的偏置向量。如图所示:

TensorFlow基础_第4张图片

因此对于2个样本的输入X,我们需要将shape为[3]的偏置b

按样本数量复制1份,变成矩阵形式的 ′

通过与X′ = X@W

相加,此时此时X′与 ′ shape 相同,满足矩阵相加的数学条件:

通过这种方式,既满足了数学上矩阵相加需要 shape 一致的条件,又达到了给每个输入样

本的输出节共享偏置的逻辑。为了实现这种运算方式,我们将 插入一个新的维度,并把

它定义为 batch 维度,然后在 batch 维度将数据复制 1 份,得到变换后的B′,新的 shape 为

[2,3]。

Reshape

在介绍改变视图操作之前,我们先来认识一下张量的存储和视图(View)的概念。张量 的视图就是我们理解张量的方式,比如 shape 为[2,4,4,3]的张量 A,我们从逻辑上可以理解 为 2 张图片,每张图片 4 行 4 列,每个位置有 RGB 3 个通道的数据;张量的存储体现在张 量在内存上保存为一段连续的内存区域,对于同样的存储,我们可以有不同的理解方式, 比如上述 A,我们可以在不改变张量的存储下,将张量 A 理解为 2 个样本,每个样本的特征为长度 48 的向量。这就是存储与视图的关系。

你可能感兴趣的:(#,tensorflow)