TensorFlow基础

​ 其是一个面向于深度学习算法的科学计算库,内部数据保存在张量(Tensor)对象上,所有操作也都是基于张量对象进行。

1.数据类型

  • 数值型 ——其是TensorFlow的主要数据载体
  • 字符串型
  • 布尔型

1.1数值类型

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

标量(Scalar) 单个的实数,维度数(Dimension,也叫秩)为0,shape=[]

向量(Vector) n个实数的有序集合,通过中括号包裹,如[1,2],维度数位1,长度不定,shape=[n]

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

**张量(Tensor) ** 所有维度数dim > 2的数组统称为张量,张量的每个维度也做轴(Axis), 比如Shape =[2,32,32,3]的张量共有 4 维

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

In :
aa = tf.constant(1.2) # 创建标量
type(aa)
Out:
(float, tensorflow.python.framework.ops.EagerTensor, True)
In :
x = tf.constant([1,2.,3.3])  #创建向量
x,x.shape
Out: 
(<tf.Tensor: id=165, shape=(3,), dtype=float32, numpy=array([1. , 2. , 3.3], dtype=float32),TensorShape([3])>)
#其中 id 是 TensorFlow 中内部索引对象的编号,shape 表示张量的形状,dtype 表示张量的数值精度,张量 numpy()方法可以返回 Numpy.array 类型的数据。
In :
a = tf.constant([[1,2],[3,4]])  #创建矩阵
a, a.shape
Out:
(<tf.Tensor: id=13, shape=(2, 2), dtype=int32, numpy= 
 array([[1, 2],[3, 4]])>, TensorShape([2, 2])) 

1.2字符串类型

​ TensorFlow 还支持字符串(String)类型的数据,例如在表示图 片数据时,可以先记录图片的路径,再通过预处理函数根据路径读取图片张量。

In : 
a = tf.constant('Hello, Deep Learning.') 
Out: 
<tf.Tensor: id=17, shape=(), dtype=string, numpy=b'Hello, Deep Learning.'> 

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

1.3布尔类型

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

In : 
a = tf.constant(True) 
Out: 
<tf.Tensor: id=22, shape=(), dtype=bool, numpy=True> 

TensorFlow 的布尔类型和 Python 语言的布尔类型并不对等,不能通用

2.数值精度

​ Bit 位越长,精度越高,同时占用的内存空间也就越大。常用的精度类型有 tf.int16, tf.int32, tf.int64, tf.float16, tf.float32, tf.float64,其中 tf.float64 即为 tf.double。

In : 
tf.constant(123456789, dtype=tf.int16) 
tf.constant(123456789, dtype=tf.int32) 
Out: 
<tf.Tensor: id=33, shape=(), dtype=int16, numpy=-13035> 
<tf.Tensor: id=35, shape=(), dtype=int32, numpy=123456789> 

​ 保存精度过低时,数据 123456789 发生了溢出,得到了错误的结果,一般使用 tf.int32, tf.int64 精度。对于浮点数,高精度的张量可以表示更精准的数据。

​ 对于大部分深度学习算法,一般使用 tf.int32, tf.float32 可满足运算精度要求,部分对 精度要求较高的算法,如强化学习,可以选择使用 tf.int64, tf.float64 精度保存张量。

2.1读取精度

访问张量的 dtype 成员属性

In : 
print('before:',a.dtype) 
if a.dtype != tf.float32:
    a = tf.cast(a,tf.float32) # 转换精度 
print('after :',a.dtype)
Out: 
before: <dtype: 'float16'> 
after : <dtype: 'float32'> 

2.2类型转换

通过 tf.cast 函数进行转换

In : 
a = tf.constant(np.pi, dtype=tf.float16) 
tf.cast(a, tf.double) 
Out[16]: 
<tf.Tensor: id=44, shape=(), dtype=float64, numpy=3.140625>

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

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

3.待优化张量

​ 为了区分需要计算梯度信息的张量不需要计算梯度信息的张量,TensorFlow 增加了 一种专门的数据类型来支持梯度信息的记录:tf.Variable。对于不需要的优化的张量,如神经网络的输入 X, 不需要通过 tf.Variable 封装;相反,对于需要计算梯度并优化的张量,如神经网络层的W 和𝒃,需要通过 tf.Variable 包裹以便 TensorFlow 跟踪相关梯度信息。

In : 
a = tf.constant([-1, 0, 1, 2]) 
aa = tf.Variable(a) 
aa.name, aa.trainable 
Out: 
 ('Variable:0', True) 

​ 其中张量的 nametrainable 属性是 Variable 特有的属性,name 属性用于命名计算图中的变量,这套命名体系是 TensorFlow 内部维护的,一般不需要用户关注 name 属性;trainable 表征当前张量是否需要被优化创建 Variable 对象是默认启用优化标志,可以设置 trainable=False 来设置张量不需要优化

​ 待优化张量可看做普通张量的特殊类型,普通张量也可以通过 GradientTape.watch()方法临时加入跟踪梯度信息的列表

4.创建张量

4.1 从 Numpy, List 对象创建

​ 通过 tf.convert_to_tensor 可以创建新 Tensor,并将保存在 Python List 对象或者 Numpy Array 对象中的数据导入到新 Tensor 中:

tf.convert_to_tensor([1,2.]) 
tf.convert_to_tensor(np.array([[1,2.],[3,4]])) 

​ Numpy 中浮点数数组默认使用 64-Bit 精度保存数据,转换到 Tensor 类型时 精度为 tf.float64,可以在需要的时候转换为 tf.float32 类型。

​ **tf.constant()**和 **tf.convert_to_tensor()**都能够自动的把 Numpy 数组或者 Python List 数据类型转化为 Tensor 类型,使用其一即可,常使用tf.constant()

4.2 创建全 0,全 1 张量

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

 tf.zeros([]),tf.ones([]) #创建全0或全1的标量
 tf.zeros([1]),tf.ones([1]) #创建全0或全1的向量
 tf.zeros([2,2])  #创建全0的矩阵

​ 通过 tf.zeros_like, tf.ones_like 可以方便地新建一个与某个张量 shape 一致,内容全 0 或全 1 的张量

In : 
a = tf.ones([2,3]) 
tf.zeros_like(a) 
Out: 
<tf.Tensor: id=113, shape=(2, 3), dtype=float32, numpy= 
array([[0., 0., 0.], 
       [0., 0., 0.]], dtype=float32)> 

tf.*_like 是一个便捷函数,可以通过 **tf.zeros(a.shape)**等方式实现。

4.3 创建自定义数值张量

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

In :
tf.fill([2,2], 99) 
Out: 
<tf.Tensor: id=136, shape=(2, 2), dtype=int32, numpy= 
array([[99, 99], 
       [99, 99]])> 

4.4 创建已知分布的张量

​ **正态分布(Normal Distribution,或 Gaussian Distribution)和均匀分布(Uniform Distribution)**是最常见的分布之一,创建采样自这 2 种分布的张量非常有用,比如在卷积神经网络中,卷积核张量 W 初始化为正态分布有利于网络的训练;在对抗生成网络中,隐藏变量z一般采样自均匀分布。

​ 通过 tf.random.normal(shape, mean=0.0, stddev=1.0)可以创建形状为 shape,均值为 mean,标准差为 stddev 的正态分布𝒩(𝑚𝑒𝑎𝑛,𝑠𝑡𝑑𝑑𝑒𝑣2)

In : 
tf.random.normal([2,2], mean=1,stddev=2) 
Out: 
<tf.Tensor: id=150, shape=(2, 2), dtype=float32, numpy= 
array([[-2.2687864, -0.7248812], 
       [ 1.2752185,  2.8625617]], dtype=float32)> 

​ 通过 **tf.random.uniform(shape, minval=0, maxval=None, dtype=tf.float32)**可以创建采样自 [𝑚𝑖𝑛𝑣𝑎𝑙,𝑚𝑎𝑥𝑣𝑎𝑙]区间的均匀分布的张量

In : 
tf.random.uniform([2,2],maxval=10) 
Out: 
<tf.Tensor: id=166, shape=(2, 2), dtype=float32, numpy= 
array([[4.541913  , 0.26521802], 
       [2.578913  , 5.126876  ]], dtype=float32)> 

4.5 创建序列

tf.range(limit, delta=1)可以创建[0,𝑙𝑖𝑚𝑖𝑡)之间,步长为 delta 的整形序 列,不包含 limit 本身。

通过 tf.range(start, limit, delta=1)可以创建[𝑠𝑡𝑎𝑟𝑡,𝑙𝑖𝑚𝑖𝑡),步长为 delta 的序列,不包含 limit 本身。

In : 
tf.range(1,10,delta=2) 
Out: 
<tf.Tensor: id=190, shape=(5,), dtype=int32, numpy=array([1, 3, 5, 7, 9])> 

5.张量的典型应用

5.1 标量

​ 标量最容易理解,它就是一个简单的数字,维度数为 0,shape 为 []。标量的典型用途之一是误差值的表示、各种测量指标的表示,比如准确度(Accuracy, acc)精度(Precision)召回率(Recall)。

经过 tf.keras.losses.mse(或 tf.keras.losses.MSE)返回每个样本 上的误差值,最后取误差的均值作为当前 batch 的误差,它是一个标量

In :  
out = tf.random.uniform([4,10]) #随机模拟网络输出 
y = tf.constant([2,3,2,0]) # 随机构造样本真实标签 
y = tf.one_hot(y, depth=10) # one-hot 编码 
loss = tf.keras.losses.mse(y, out) # 计算每个样本的 MSE  
loss = tf.reduce_mean(loss) # 平均 MSE 
print(loss) 
Out: 
tf.Tensor(0.19950335, shape=(), dtype=float32) 

5.2 向量

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

​ 每个全连接层的输出节点都添加了一个偏置值,把所有输出节点的偏置表示成向量形式:𝒃 = [𝑏1,𝑏2]𝑇。

​ 通过高层接口类 **Dense()**方式创建的网络层,张量 W 和𝒃存储在类的内部,由类自动创 建并管理。可以通过全连接层的 bias 成员变量查看偏置变量𝒃。

​ 创建输入节点数为 4, 输出节点数为 3 的线性层网络,那么它的偏置向量 b 的长度应为 3。

In :  
fc = layers.Dense(3) # 创建一层 Wx+b,输出节点为 3 
# 通过 build 函数创建 W,b 张量,输入节点为 4 
fc.build(input_shape=(2,4))  
fc.bias # 查看偏置 
Out: 
<tf.Variable 'bias:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], 
dtype=float32)>  #可以看到,类的偏置成员 bias 初始化为全 0,这也是偏置𝒃的默认初始化方案。 

5.3 矩阵

​ 矩阵也是非常常见的张量类型,比如全连接层的批量输入𝑋 = [𝑏,𝑑𝑖𝑛],其中𝑏表示输入 样本的个数,即 batch size𝑑𝑖𝑛表示输入特征的长度。比如特征长度为 4,一共包含 2 个样 本的输入可以表示为矩阵:x = tf.random.normal([2,4])

In :  
w = tf.ones([4,3]) # 定义 W 张量 
b = tf.zeros([3]) # 定义 b 张量 
o = x@w+b # X@W+b 运算 
Out: 
<tf.Tensor: id=291, shape=(2, 3), dtype=float32, numpy= 
array([[ 2.3506963,  2.3506963,  2.3506963], 
       [-1.1724043, -1.1724043, -1.1724043]], dtype=float32)>

​ 其中 X,W 张量均是矩阵x@w+b 网络层称为线性层,在 TensorFlow 中可以通过 Dense 类直接实现,**Dense 层也称为全连接层。**我们通过 Dense 类创建输入 4 个节点,输出 3 个 节点的网络层,可以通过全连接层的 kernel 成员名查看其权值矩阵 W:

In :  
fc = layers.Dense(3) # 定义全连接层的输出节点为 3 fc.build(input_shape=(2,4)) # 定义全连接层的输入节点为 4 
fc.kernel 
Out: 
<tf.Variable 'kernel:0' shape=(4, 3) dtype=float32, numpy= 
array([[ 0.06468129, -0.5146048 , -0.12036425], 
       [ 0.71618867, -0.01442951, -0.5891943 ], 
       [-0.03011459,  0.578704  ,  0.7245046 ], 
       [ 0.73894167, -0.21171576,  0.4820758 ]], dtype=float32)> 

5.4 3维张量

三维的张量一个典型应用是表示序列信号,它的格式是 𝑋 = [𝑏,𝑠𝑒𝑞𝑢𝑒𝑛𝑐𝑒 𝑙𝑒𝑛,𝑓𝑒𝑎𝑡𝑢𝑟𝑒 𝑙𝑒𝑛] 其中𝑏表示序列信号的数量,sequence len 表示序列信号在时间维度上的采样点数,feature len 表示每个点的特征长度。

​ 为了能够方便字符串被神经网络处理,一般将单词通过嵌入层(Embedding Layer)编码为固定长度的向量,比如“a”编码为某个长度 3 的向量,那么 2 个 等长(单词数为 5)的句子序列可以表示为 shape 为[2,5,3]的 3 维张量,其中 2 表示句子个 数,5 表示单词数量,3 表示单词向量的长度:

In :  # 自动加载 IMDB 电影评价数据集 
(x_train,y_train),(x_test,y_test)=keras.datasets.imdb.load_data(num_words=10 000) 
# 将句子填充、截断为等长 80 个单词的句子 
x_train = keras.preprocessing.sequence.pad_sequences(x_train,maxlen=80) 
x_train.shape 
Out [46]: 
(25000, 80)  # 25000 表示句子个数,80 表示每个句子 共 80 个单词,每个单词使用数字编码方式

​ 通过 layers.Embedding 层将数字编码的单词转换为长度为 100 个词向量

In [47]: # 创建词向量 Embedding 层类 
embedding=layers.Embedding(10000, 100) 
# 将数字编码的单词转换为词向量 
out = embedding(x_train) 
out.shape 
Out[47]: TensorShape([25000, 80, 100]) # 100表示每个单词编码为长度 100 的向量。

​ 对于特征长度为 1 的序列信号,比如商品价格在 60 天内的变化曲线,只需要一个标量即可表示商品的价格,因此 2 件商品的价格变化趋势可以使用 shape 为[2,60]的张量表示。为了方便统一格式,也将价格变化趋势表达为 shape 为 [2,60,1]的张量,其中的 1 表示特征长度为 1。

5.5 4 维张量

​ 4维张量在卷积神经网络中应用的非常广泛,它用于保存**特征图(Feature maps)**数据,格式一般定义为 [b,h,w,c],其中𝑏表示输入的数量,h/w分布表示特征图的高宽,𝑐表示特征图的通道数。一张图片可以表示为[h,w,3]。

In : 
# 创建 32x32 的彩色图片输入,个数为 4 
x = tf.random.normal([4,32,32,3]) 
# 创建卷积神经网络 
layer = layers.Conv2D(16,kernel_size=3) 
out = layer(x) # 前向计算 
out.shape # 输出大小 
Out: 
TensorShape([4, 30, 30, 16]) 
其中卷积核张量也是 4 维张量,可以通过 kernel 成员变量访问: 
In : 
layer.kernel.shape 
Out: 
TensorShape([3, 3, 3, 16]) 
 

6.索引与切片

6.1 索引

​ 在 TensorFlow 中,支持基本的[𝑖][𝑗]…标准索引方式,也支持通过逗号分隔索引号的索引方式。输入 X 为 4 张 32x32 大小的彩色图片,shape 为[4,32,32,3]。

In :
x = tf.random.normal([4,32,32,3]) 
x[0]  #取第一张图片数据
Out:
<tf.Tensor: id=379, shape=(32, 32, 3), dtype=float32, numpy= 
array([[[ 1.3005302 ,  1.5301839 , -0.32005513], 
        [-1.3020388 ,  1.7837263 , -1.0747638 ], ... 
        [-1.1092019 , -1.045254  , -0.4980363 ], 
        [-0.9099222 ,  0.3947732 , -0.10433522]]],dtype=float32)> 
        

x[0][1]        #取第 1 张图片的第2行
x[0][1][2]     #取第 1 张图片,第 2 行,第 3 列的像素
x[2][1][0][1]  #取第 3 张图片,第 2 行,第 1 列的像素,B 通道(第 2 个通道)颜色强度值

当张量的维度数较高时,使用 [i] [j]…[𝑘]的方式书写不方便,可以采用[𝑖,𝑗,…,𝑘]的方 式索引,它们是等价的

x[1,9,2]  #取第 2 张图片,第 10 行,第 3 列

6.2 切片

#以shape为[4,32,32,3]的图片张量为例
x[1:3]  #读取第2,3张图片

start:end:step切片方式有很多简写方式,其中 start、end、step 3 个参数可以根据需要 选择性地省略,全部省略时即::,表示从最开始读取到最末尾,步长为 1,即不跳过任何元 素。如 x[0,::]表示读取第 1 张图片的所有行,其中::表示在行维度上读取所有行,它等于 x[0]的写法。

x[0,::]
#为了更加简洁,::可以简写为单个冒号:
x[:,0:28:2,0:28:2,:] 
#表示取所有图片,隔行采样,隔列采样,所有通道信息,相当于在图片的高宽各缩放至原 来的 50%。 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2dqbrVed-1583337541398)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210175710410.png)]

特别地,step 可以为负数,考虑最特殊的一种例子,step = −1时,start:end:−1表示 从 start 开始,逆序读取至 end 结束(不包含 end),索引号𝑒𝑛𝑑 ≤ 𝑠𝑡𝑎𝑟𝑡。

In : 
x = tf.range(9) 
x[8:0:-1] 
Out: 
<tf.Tensor: id=466, shape=(8,), dtype=int32, numpy=array([8, 7, 6, 5, 4, 3, 2, 1])> 

​ 为了避免出现像𝑥[:,:,:,1]这样出现过多冒号的情况,可以使用⋯符号表示取多个维度 上所有的数据,其中维度的数量需根据规则自动推断:当切片方式出现⋯符号时,⋯符号左边的维度将自动对齐到最左边,⋯符号右边的维度将自动对齐到最右边,此时系统再自 动推断⋯符号代表的维度数量。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dX0VApA3-1583337541399)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210180215157.png)]

x[0:2,...,1:] #读取第1-2张图片的G/B通道数据
x[2:,...]    #读取最后 2 张图片
x[...,:2]    #读取 R/G 通道数据

7.维度变换

​ 算法的每个模块对于数据张量的格式有不同的逻辑要求,当现有的数据格式不满足算 法要求时,需要通过维度变换将数据调整为正确的格式。这就是维度变换的功能。

​ 基本的维度变换包含了改变视图 reshape插入新维度 expand_dims删除维度 squeeze交换维度 transpose复制数据 tile

7.1 Reshape

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

In : x=tf.range(96) 
x=tf.reshape(x,[2,4,4,3])  
Out: 
<tf.Tensor: id=11, shape=(2, 4, 4, 3), dtype=int32, numpy= 
array([[[[ 0,  1,  2], 
         [ 3,  4,  5], 
         [ 6,  7,  8], 
         [ 9, 10, 11]],… 

​ 内存并不支持这个维度层级概念,只能以平铺方式按序写入内存。

​ 张量 shape 中相对靠左侧的维度叫做大维度,shape 中相对靠右侧的维度叫做小维度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LEBtfg7I-1583337541399)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210185911907.png)]

​ 如果定义新视图为[𝑏,w ,ℎ,𝑐],[𝑏,𝑐,ℎ ∗ w] 或者[𝑏,𝑐,ℎ, w]等时,与张量的存储顺序相悖,如果不同步更新张量的存储顺序,那么恢复 出的数据将与新视图不一致,从而导致数据错乱。

​ 新视图的维度顺 序不能与存储顺序相悖,否则需要通过交换维度操作将存储顺序同步过来。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ySjatZB0-1583337541400)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210190204071.png)]

通过张量的 ndim 和 shape 成员属性获得张量的维度数和形状

In : x.ndim,x.shape 
Out:(4, TensorShape([2, 4, 4, 3]))
In : 
tf.reshape(x,[2,-1]) 
Out:
<tf.Tensor: id=520, shape=(2, 48), dtype=int32, numpy= 
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,… 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95]])> 
#其中的参数-1 表示当前轴上长度需要根据视图总元素不变的法则自动推导,从而方便用户书写。比如,上面的-1可以推导为48

7.2 增删维度

增加维度 增加一个长度为1的维度相当于给原有的数据增加一个新维度的概念,维度长度为1,故数据并不需要改变,仅仅是改变数据的理解方式

通过 tf.expand_dims(x, axis)可在指定的 axis 轴前可以插入一个新的维度

In :
x = tf.random.uniform([28,28],maxval=10,dtype=tf.int32)
x = tf.expand_dims(x,axis=2)
Out: 
<tf.Tensor: id=555, shape=(28, 28, 1), dtype=int32, numpy= 
array([[[4], 
        [5], 
        [7], 
        [6], 
        [3],… 

tf.expand_dimsaxis 为正时,表示在当前维度之前插入一个新维度为负时,表示当前维度之后插入一个新的维度。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W3biXqB1-1583337541400)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210191408876.png)]

删除维度 是增加维度的逆操作,与增加维度一样,删除维度只能删除长度为 1 的维 度,也不会改变张量的存储。

​ 可以通过 **tf.squeeze(x, axis)**函数,axis 参数为待删除的维度的索引号, 图片数量的维度轴 axis=0

In : 
x = tf.squeeze(x, axis=2) 
Out: 
<tf.Tensor: id=588, shape=(28, 28), dtype=int32, numpy= 
array([[8, 2, 2, 0, 7, 0, 1, 4, 9, 1, 7, 4, 8, 2, 7, 4, 8, 2, 9, 8, 8, 0,9, 9, 7, 5, 9, 7],[3, 4, 9, 9, 0, 6, 5, 7, 1, 9, 9, 1, 2, 7, 2, 7, 5, 3, 3, 7, 2, 4,5, 2, 7, 3, 8, 0],…

如果不指定维度参数 axis,即 tf.squeeze(x),那么他会默认删除所有长度为 1 的维度

7.3 交换维度

​ 有时需要直接调整的存储顺序,即交换维度(Transpose)。通过交换维度,改变了张量的存储顺序,同时也改变了张量的视图。

使用 tf.transpose(x, perm)函数完成维度交换操作,其中 perm 表示新维度的顺序 List。

​ 图片张量 shape 为[2,32,32,3], 图片数量、行、列、通道数 的维度索引分别为 0,1,2,3,如果需 要交换为[𝑏,𝑐,ℎ,w ]格式,则新维度的排序为 图片数量、通道数、行、列 ,对应的索引号为 [0,3,1,2]

In : x = tf.random.normal([2,32,32,3]) 
tf.transpose(x,perm=[0,3,1,2]) 
Out: 
<tf.Tensor: id=603, shape=(2, 3, 32, 32), dtype=float32, numpy= 
array([[[[-1.93072677e+00, -4.80163872e-01, -8.85614634e-01, ..., 
           1.49124235e-01,  1.16427064e+00, -1.47740364e+00], 
         [-1.94761145e+00,  7.26879001e-01, -4.41877693e-01, ... 

通过 tf.transpose 完成维度交换后,张量的存储顺序已经改变,视图也随之 改变,后续的所有操作必须基于新的存续顺序进行。

7.4 数据复制

​ 以通过 **tf.tile(x, multiples)**函数完成数据在指定维度上的复制操作,multiples 分别指定了每个维度上 面的复制倍数,对应位置为 1 表明不复制,为 2 表明新长度为原来的长度的 2 倍,即数据 复制一份。

In :
b = tf.constant([1,2]) 
b = tf.expand_dims(b, axis=0)
b = tf.tile(b, multiples=[2,1])  #在 batch 维度上复制数据 1 份
Out: 
<tf.Tensor: id=648, shape=(2, 2), dtype=int32, numpy= 
array([[1, 2],[1, 2]])> 
#输入一个2行2列的矩阵
In : 
x = tf.range(4) 
x=tf.reshape(x,[2,2])
x = tf.tile(x,multiples=[1,2])  #在列维度复制1份数据
x = tf.tile(x,multiples=[2,1])  #在行维度复制1份数据
Out: 
<tf.Tensor: id=672, shape=(4, 4), dtype=int32, numpy= 
array([[0, 1, 0, 1], 
       [2, 3, 2, 3], 
       [0, 1, 0, 1], 
       [2, 3, 2, 3]])> 

tf.tile 会创建一个新的张量来保存复制后的张量,由于复制操作涉及到 大量数据的读写 IO 运算,计算代价相对较高。则又出现一个Broadcasting操作

8 Broadcasting

Broadcasting 也叫广播机制(自动扩展也许更合适),它是一种轻量级张量复制的手段, 在逻辑上扩展张量数据的形状,但是只要在需要时才会执行实际存储复制操作。对于大部 分场景,Broadcasting 机制都能通过优化手段避免实际复制数据而完成逻辑运算,从而相对 于 tf.tile 函数,减少了大量计算代价。

​ Broadcasting 并不会立即复制数据,它会逻辑上改变张量的形状,使得视图上变 成了复制后的形状。Broadcasting 和 tf.tile 复制的最 终效果是一样的,操作对用户透明,但是 Broadcasting 机制节省了大量计算资源,建议在 运算过程中尽可能地利用 Broadcasting 提高计算效率。

x = tf.random.normal([2,4]) 
w = tf.random.normal([4,3]) 
b = tf.random.normal([3]) 
y = x@w+b 
#上述加法并没有发生逻辑错误,那么它是怎么实现的呢?这是因为它自动调用Broadcasting 函数 tf.broadcast_to(x, new_shape),将 2 者 shape 扩张为相同的[2,3],即上式可以等效为: 
y = x@w + tf.broadcast_to(b,[2,3])

​ 通过自动调用 **tf.broadcast_to(b, [2,3])**的 Broadcasting 机制,既实现了 增加维度、复制数据的目的,又避免实际复制数据的昂贵计算代价,同时书写更加简洁高 效。

​ **Broadcasting 机制的核心思想是普适性,即同一份数据能普遍适合于其他位置在验证 普适性之前,需要将张量 shape 靠右对齐,然后进行普适性判断:对于长度为 1 的维度, 默认这个数据普遍适合于当前维度的其他位置;对于不存在的维度,则在增加新维度后默 认当前数据也是普适性于新维度的,从而可以扩展为更多维度数、其他长度的张量形状。 **

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ruMZVh4E-1583337541401)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210193200936.png)]

​ 首先将 2 个 shape 靠右对齐,对于通道维度 c,张量的现长度为 1,则默认此数据同样适合 当前维度的其他位置,将数据逻辑上复制𝑐 − 1份,长度变为 c;对于不存在的 b 和 h 维 度,则自动插入新维度,新维度长度为 1,同时默认当前的数据普适于新维度的其他位 置,即对于其它的图片、其他的行来说,与当前的这一行的数据完全一致。这样将数据 b,h 维度的长度自动扩展为 b,h。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8pdvnWNJ-1583337541401)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210193310081.png)]

通过 tf.broadcast_to(x, new_shape)可以显式将现有 shape 扩张为 new_shape

In : 
A = tf.random.normal([32,1])  
tf.broadcast_to(A, [2,32,32,3]) 
Out: 
<tf.Tensor: id=13, shape=(2, 32, 32, 3), dtype=float32, numpy= 
array([[[[-1.7571245 , -1.7571245 , -1.7571245 ], 
         [ 1.580159  ,  1.580159  ,  1.580159  ], 
         [-1.5324328 , -1.5324328 , -1.5324328 ],... 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K5qOL6Vu-1583337541402)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210193438360.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sjhe0NRf-1583337541402)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210193521225.png)]

9 数学运算

9.1 加减乘除

通过 tf.add, tf.subtract, tf.multiply, tf.divide 函数实 现,TensorFlow 已经重载了+ −∗/运算符,一般推荐直接使用运算符来完成加减乘除运 算。

整除和余除也是常见的运算之一,分别通过//和%运算符实现。

In : 
a = tf.range(5) 
b = tf.constant(2) 
a//b 
Out: 
<tf.Tensor: id=115, shape=(5,), dtype=int32, numpy=array([0, 0, 1, 1, 2])> 

In :
a%b 
Out:
<tf.Tensor: id=117, shape=(5,), dtype=int32, numpy=array([0, 1, 0, 1, 0])> 

9.2 乘方

通过 tf.pow(x, a)可以方便地完成𝑦 = 𝑥𝑎乘方运算,也可以通过运算符 实现𝑥 ∗∗ 𝑎运 算

tf.pow(x,3)
x**2 
x**(0.5) #设置指数为1/𝑎形式即可实现根号运算:a√𝑥 

常见的平方和平方根运算,可以使用 tf.square(x)和 tf.sqrt(x)

x = tf.square(x) #平方
tf.sqrt(x)  #平方根

9.3 指数、对数

通过 tf.pow(a, x)或者 运算符可以方便实现指数运算𝑎的𝑥次方。

2**x 

特别地,对于自然指数𝑒𝑥,可以通过 tf.exp(x)实现:

 tf.exp(1.) 

自然对数l g𝑒 𝑥可以通过 tf.math.log(x)实现:

tf.math.log(x)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cah2jxgC-1583337541403)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210194510145.png)]

tf.math.log(x)/tf.math.log(10.)

9.4 矩阵相乘

​ **了通过@运算符可以方 便的实现矩阵相乘,还可以通过 tf.matmul(a, b)实现 **

​ 根据矩阵相乘的定义,a 和 b 能够矩阵相乘的条件是,**a 的倒数第一个维度长度(列)和 b 的倒数第二个维度长度(行)必须相等。**比如张量 a shape:[4,3,28,32]可以与张量 b shape:[4,3,32,2]进行矩阵相乘:

In : 
a = tf.random.normal([4,3,28,32]) 
b = tf.random.normal([4,3,32,2]) 
a@b  #tf.matmul(a,b) 
Out: 
<tf.Tensor: id=236, shape=(4, 3, 28, 2), dtype=float32, numpy= 
array([[[[-1.66706240e+00, -8.32602978e+00], 
         [ 9.83304405e+00,  8.15909767e+00], 
         [ 6.31014729e+00,  9.26124632e-01],… 

矩阵相乘函数支持自动 Broadcasting 机制

对数l g𝑒 𝑥可以通过 tf.math.log(x)实现:**

tf.math.log(x)

[外链图片转存中…(img-Cah2jxgC-1583337541403)]

tf.math.log(x)/tf.math.log(10.)

9.4 矩阵相乘

​ **了通过@运算符可以方 便的实现矩阵相乘,还可以通过 tf.matmul(a, b)实现 **

​ 根据矩阵相乘的定义,a 和 b 能够矩阵相乘的条件是,**a 的倒数第一个维度长度(列)和 b 的倒数第二个维度长度(行)必须相等。**比如张量 a shape:[4,3,28,32]可以与张量 b shape:[4,3,32,2]进行矩阵相乘:

In : 
a = tf.random.normal([4,3,28,32]) 
b = tf.random.normal([4,3,32,2]) 
a@b  #tf.matmul(a,b) 
Out: 
<tf.Tensor: id=236, shape=(4, 3, 28, 2), dtype=float32, numpy= 
array([[[[-1.66706240e+00, -8.32602978e+00], 
         [ 9.83304405e+00,  8.15909767e+00], 
         [ 6.31014729e+00,  9.26124632e-01],… 

矩阵相乘函数支持自动 Broadcasting 机制

参考文献:tensorflow2.0深度学习 龙龙老师著

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐