【深度学习】TensorFlow基础知识点总结
TensorFlow基础其是一个面向于深度学习算法的科学计算库,内部数据保存在张量(Tensor)对象上,所有操作也都是基于张量对象进行。1.数据类型数值型——其是TensorFlow的主要数据载体字符串型布尔型1.1数值类型数值类型的张量是TensorFlow的主要数据载体,分为:标量(Scalar) 单个的实数,维度数(D...
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)
其中张量的 name 和 trainable 属性是 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_dims 的 axis 为正时,表示在当前维度之前插入一个新维度;为负时,表示当前维度之后插入一个新的维度。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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)]](https://i-blog.csdnimg.cn/blog_migrate/b21aa869381ae730b90760df3b18e2db.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sjhe0NRf-1583337541402)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210193521225.png)]](https://i-blog.csdnimg.cn/blog_migrate/3e04bd2300d2806a06423ff5a37c9ea9.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深度学习 龙龙老师著
更多推荐



所有评论(0)