Tensorflow2.0基础函数学习笔记
数据类型
包括了数值型、字符串型和布尔型。
数值类型
- 标量
tf.constant(1.2) - 向量
tf.constant([1.,2.,3.3]) - 矩阵
tf.constant([[[1,2],[3,4]],[[5,6],[7,8]]]
字符串类型
tf.constant('Hello, Deep Learning.')
布尔类型
tf.constant(True)
数值精度
tf.constant(123456789, dtype=tf.int16)
tf.constant(123456789, dtype=tf.int32)
tf.constant(np.pi, dtype=tf.float32)
读取精度
print('before:',a.dtype)
类型转换
tf.constant(np.pi, dtype=tf.float16)
tf.cast(a, tf.double)
待优化张量
tf.Variable 类型在普通的张量类 型基础上添加了 name,trainable 等属性来支持计算图的构建。
a = tf.Variable([[1,2],[3,4]])
<tf.Variable 'Variable:0' shape=(2, 2) dtype=int32, numpy=
array([[1, 2], [3, 4]])>
从 Numpy, List 对象创建
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 类型。
创建全 0,全 1 张量
a = tf.ones([2,3])
tf.zeros_like(a)
a = tf.zeros([3,2])
tf.ones_like(a)
创建自定义数值张量
创建元素为-1 的标量:
tf.fill([], -1)
创建所有元素为-1 的向量:
tf.fill([1], -1)
创建所有元素为 99 的矩阵:
tf.fill([2,2], 99)
创建已知分布的张量
创建均值为 0,标准差为 1 的正太分布:
tf.random.normal([2,2])
创建采样自区间[0,1],shape 为[2,2]的矩 阵:
tf.random.uniform([2,2])
tf.random.uniform([2,2],maxval=100,dtype=tf.int32)
创建序列
tf.range(10)
<tf.Tensor: id=180, shape=(10,), dtype=int32, numpy=array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])>
tf.range(10,delta=2)
<tf.Tensor: id=185, shape=(5,), dtype=int32, numpy=array([0, 2, 4, 6, 8])>
tf.range(1,10,delta=2)
<tf.Tensor: id=190, shape=(5,), dtype=int32, numpy=array([1, 3, 5, 7, 9])>
张量的典型应用
标量
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)
向量
z = tf.random.normal([4,2])
b = tf.zeros([2]) # 模拟偏置向量
z = z + b # 累加偏置
通过高层接口类 Dense()方式创建的网络层,张量 W 和𝒃存储在类的内部,由类自动创 建并管理。可以通过全连接层的 bias 成员变量查看偏置变量𝒃,例如创建输入节点数为 4, 输出节点数为 3 的线性层网络,那么它的偏置向量 b 的长度应为 3:
fc = layers.Dense(3) # 创建一层
Wx+b,输出节点为 3 # 通过 build 函数创建 W,b 张量,输入节点为 4
fc.build(input_shape=(2,4))
fc.bias # 查看偏置
矩阵
矩阵也是非常常见的张量类型,比如全连接层的批量输入𝑋 = [𝑏,𝑑𝑖𝑛],其中𝑏表示输入 样本的个数,即 batch size,𝑑𝑖𝑛表示输入特征的长度。比如特征长度为 4,一共包含 2 个样 本的输入可以表示为矩阵:
w = tf.ones([4,3]) # 定义 W 张量
b = tf.zeros([3]) # 定义 b 张量
o = x@w+b # X@W+b 运算
其中 X,W 张量均是矩阵。x@w+b 网络层称为线性层,在 TensorFlow 中可以通过 Dense 类直接实现,Dense 层也称为全连接层。我们通过 Dense 类创建输入 4 个节点,输出 3 个 节点的网络层,可以通过全连接层的 kernel 成员名查看其权值矩阵 W:
fc = layers.Dense(3) # 定义全连接层的输出节点为 3 fc.build(input_shape=(2,4)) # 定义全连接层的输入节点为 4
fc.kernel
Out[45]:
<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)>
3 维张量
三维的张量一个典型应用是表示序列信号,它的格式是 𝑋 = [𝑏,𝑠𝑒𝑞𝑢𝑒𝑛𝑐𝑒 𝑙𝑒𝑛,𝑓𝑒𝑎𝑡𝑢𝑟𝑒 𝑙𝑒𝑛] 其中𝑏表示序列信号的数量,sequence len 表示序列信号在时间维度上的采样点数,feature len 表示每个点的特征长度。
4 维张量
4 维张量在卷积神经网络中应用的非常广泛,它用于保存特征图(Feature maps)数据, 格式一般定义为 [𝑏,ℎ, ,𝑐]
其中𝑏表示输入的数量,h/w分布表示特征图的高宽,𝑐表示特征图的通道数,部分深度学 习框架也会使用[𝑏,𝑐,ℎ, ]格式的特征图张量,例如 PyTorch。图片数据是特征图的一种, 对于含有 RGB 3 个通道的彩色图片,每张图片包含了 h 行 w 列像素点,每个点需要 3 个数 值表示 RGB 通道的颜色强度,因此一张图片可以表示为[h,w,3]。
索引与切片
索引
通过索引与切片操作可以提取张量的部分数据,使用频率非常高。
x = tf.random.normal([4,32,32,3])
接下来我们使用索引方式读取张量的部分数据。
取第 1 张图片的数据
x[0]
取第 1 张图片的第 2 行:
x[0][1]
取第 1 张图片,第 2 行,第 3 列的像素:
x[0][1][2]
取第 3 张图片,第 2 行,第 1 列的像素,B 通道(第 2 个通道)颜色强度值:
x[2][1][0][1]
当张量的维度数较高时,使用[𝑖][𝑗]…[𝑘]的方式书写不方便,可以采用[𝑖,𝑗,…,𝑘]的方 式索引,它们是等价的。
x[1,9,2]
切片
通过𝑠𝑡𝑎𝑟𝑡:𝑒𝑛𝑑:𝑠𝑡𝑒𝑝切片方式可以方便地提取一段数据,其中 start 为开始读取位置的 索引,end 为结束读取位置的索引(不包含 end 位),step 为读取步长。
以 shape 为[4,32,32,3]的图片张量为例:
读取第 2,3 张图片:
In [56]: x[1:3]
Out[56]:
<tf.Tensor: id=441, shape=(2, 32, 32, 3), dtype=float32, numpy=
array([[[[ 0.6920027 , 0.18658352, 0.0568333 ],
[ 0.31422952, 0.75933754, 0.26853144],
[ 2.7898 , -0.4284912 , -0.26247284],...
start:end:step切片方式有很多简写方式,其中 start、end、step 3 个参数可以根据需要 选择性地省略,全部省略时即::,表示从最开始读取到最末尾,步长为 1,即不跳过任何元素。
如 x[0,::]表示读取第 1 张图片的所有行,其中::表示在行维度上读取所有行,它等于 x[0]的写法:
In [57]: x[0,::]
Out[57]:
<tf.Tensor: id=446, shape=(32, 32, 3), dtype=float32, numpy=
array([[[ 1.3005302 , 1.5301839 , -0.32005513],
[-1.3020388 , 1.7837263 , -1.0747638 ],
[-1.1230233 , -0.35004002, 0.01514002],
...
为了更加简洁,::可以简写为单个冒号:,如
In [58]: x[:,0:28:2,0:28:2,:]
Out[58]:
<tf.Tensor: id=451, shape=(4, 14, 14, 3), dtype=float32, numpy=
array([[[[ 1.3005302 , 1.5301839 , -0.32005513],
[-1.1230233 , -0.35004002, 0.01514002],
[ 1.3474811 , 0.639334 , -1.0826371 ],
...
切片方式 | 意义 |
---|---|
start:end :step | 从 start 开始读取到 end(不包含 end),步长为 step |
start:end : | 从 start 开始读取到 end(不包含 end),步长为 1 |
start: | 从 start 开始读取完后续所有元素,步长为 1 |
start::step | 从 start 开始读取完后续所有元素,步长为 step |
:end:step | 从 0 开始读取到 end(不包含 end),步长为 step |
:end | 从 0 开始读取到 end(不包含 end),步长为 1 |
::step | 每隔 step-1 个元素采样所有 |
:: | 读取所有元素 |
: | 读取所有元素 |
特别地,step 可以为负数,考虑最特殊的一种例子,step = −1时,start:end :−1表示 从 start 开始,逆序读取至 end 结束(不包含 end),索引号𝑒𝑛𝑑 ≤ 𝑠𝑡𝑎𝑟𝑡。考虑一 0~9 简单序 列,逆序取到第 1 号元素,不包含第 1号
In [59]: x = tf.range(9)
x[8:0:-1]
Out[59]:
<tf.Tensor: id=466, shape=(8,), dtype=int32, numpy=array([8, 7, 6, 5, 4, 3,
2, 1])>
当张量的维度数量较多时,不需要采样的维度一般用单冒号:表示采样所有元素,此时 有可能出现大量的:出现。继续考虑[4,32,32,3]的图片张量,当需要读取 G 通道上的数据 时,前面所有维度全部提取,此时需要写为
In [63]: x[:,:,:,1]
Out[63]:
<tf.Tensor: id=492, shape=(4, 32, 32), dtype=float32, numpy=
array([[[ 0.575703 , 0.11028383, -0.9950867 , ..., 0.38083118,
-0.11705163, -0.13746642],
...
为了避免出现像𝑥[:,:,:,1]这样出现过多冒号的情况,可以使用⋯符号表示取多个维度 上所有的数据,其中维度的数量需根据规则自动推断:当切片方式出现⋯符号时,⋯符号 左边的维度将自动对齐到最左边,⋯符号右边的维度将自动对齐到最右边,此时系统再自 动推断⋯符号代表的维度数量,它的切片方式总结如表格
切片方式 | 意义 |
---|---|
a,⋯,b | a 维度对齐到最左边,b 维度对齐到最右边,中间的维度全部读取, 其他维度按 a/b 的方式读取 |
a,⋯ | a维度对齐到最左边,a 维度后的所有维度全部读取,a 维度按 a 方式 读取。这种情况等同于 a 索引/切片方式 |
⋯,b | b 维度对齐到最右边,b 之前的所有维度全部读取,b 维度按 b 方式 读取 |
⋯ | 读取张量所有数据 |
读取第 1-2 张图片的 G/B 通道数据
In [64]: x[0:2,...,1:]
Out[64]:
<tf.Tensor: id=497, shape=(2, 32, 32, 2), dtype=float32, numpy=
array([[[[ 0.575703 , 0.8872789 ],
[ 0.11028383, -0.27128693],
[-0.9950867 , -1.7737272 ],
读取 R/G 通道数据
In [66]: x[...,:2]
Out[66]:
<tf.Tensor: id=507, shape=(4, 32, 32, 2), dtype=float32, numpy=
array([[[[-1.26881 , 0.575703 ],
[ 0.98697686, 0.11028383],
[-0.66420585, -0.9950867 ],
...
小结
张量的索引与切片方式多种多样,尤其是切片操作,初学者容易犯迷糊。但其实本质 上切片操作只有𝑠𝑡𝑎𝑟𝑡:𝑒𝑛𝑑:𝑠𝑡𝑒𝑝这一种基本形式,通过这种基本形式有目的地省略掉默认 参数,从而衍生出多种简写方法,这也是很好理解的。它衍生的简写形式熟练后一看就能 推测出省略掉的信息,书写起来也更方便快捷。由于深度学习一般处理的维度数在 4 维以 内,⋯操作符完全可以用:符号代替,因此理解了这些就会发现张量切片操作并不复杂。