1. 张量
张量(Tensor)是 Tensorflow 的基本操作对象,可以看做是包含单一数据类型元素的多维矩阵。图像的实质是多维张量,对张量的认识和使用是我们开始深度学习的第一步。
2. 张量操作示例
2.1 索引与切片
2.1.1 索引
索引操作比较简单,直接通过维度编号进行索引。
import tensorflow as tf
# 创建2维张量
t = tf.constant([[1,2,3,4,5],[6,7,8,9,10]])
# 取0维度上的第一组数据
a = t[0]
# 取x[0]中的第二组数据
b = t[0][1]
print(a)
print('_'*50)
print(b)
tf.Tensor([1 2 3 4 5], shape=(5,), dtype=int32)
__________________________________________________
tf.Tensor(2, shape=(), dtype=int32)
2.1.2 切片
切片采用 [start🔚step] 的方式。start:end 为含有不含尾的切片操作,表示从 start 位开始,一直到 end-1 位结束;加入步长 step 后,从 start 到 end-1 间隔 step 取数。
# 先取0维度第二组数据:[6,7,8,9,10],再从[6 7 8 9] 中间隔2取数据
t[1,0:4:2]
<tf.Tensor: shape=(2,), dtype=int32, numpy=array([6, 8])>
2.2 维度变换
基本的维度变换包含改变视图的 tf.reshape,插入新维度 tf.expand_dims,删除维度 tf.squeeze,交换维度 tf.transpose 等
2.2.1 tf.reshape
tf.reshape(tensor, shape, name=None) 第一个参数为被调整维度的张量,第二个参数为要调整为的形状,返回一个 shape 形状的新 rensor。shape 里最多有一个维度的值可以为 -1,表示自动计算此维度。
# 生成一个从 0 开始的 24 个元素的数组
t = tf.range(24)
# 生成 2 二维数组,每个二维数组为 4 行 3 列
t1 = tf.reshape(t, [2, 4, 3])
print(t)
print('_'*50)
print(t1)
tf.Tensor([ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23], shape=(24,), dtype=int32)
__________________________________________________
tf.Tensor(
[[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
[[12 13 14]
[15 16 17]
[18 19 20]
[21 22 23]]], shape=(2, 4, 3), dtype=int32)
2.2.2 tf.expand_dims
通过 tf.expand_dims(input, axis) 可在指定的 axis 轴前插入一个新的维度。axis 为正时,表示在当前维度之前插入一个新的维度,为负时,表示在当前维度之后插入一个新的维度。
# shape [2,3]
t = [[1,2,3],[4,5,6]]
# shape [1,2,3]
t1 = tf.expand_dims(t, axis=0)
# shape [2,1,3]
t2 = tf.expand_dims(t, axis=1)
# shape [2,3,1]
t3 = tf.expand_dims(t, axis=2)
# shape [2,3,1]
t4 = tf.expand_dims(t, axis=-1)
print(t)
print('-'*50)
print(t1)
print('-'*50)
print(t2)
print('-'*50)
print(t3)
print('-'*50)
print(t4)
[[1, 2, 3], [4, 5, 6]]
--------------------------------------------------
tf.Tensor(
[[[1 2 3]
[4 5 6]]], shape=(1, 2, 3), dtype=int32)
--------------------------------------------------
tf.Tensor(
[[[1 2 3]]
[[4 5 6]]], shape=(2, 1, 3), dtype=int32)
--------------------------------------------------
tf.Tensor(
[[[1]
[2]
[3]]
[[4]
[5]
[6]]], shape=(2, 3, 1), dtype=int32)
--------------------------------------------------
tf.Tensor(
[[[1]
[2]
[3]]
[[4]
[5]
[6]]], shape=(2, 3, 1), dtype=int32)
2.2.3 tf.squeeze
通过tf.squeeze(input, axis)可以删除长度为1的维度,axis参数为待删除维度的索引号,删除维度只能删除长度为1的维度,如果不指定维度参数axis,即tf.sequeeze(input),则其会默认删除所有长度为1的维度。
# [0 1 2 3 4 5]
t = tf.range(6)
t1 = tf.reshape(t, [1,2,1,3,1])
t2 = tf.squeeze(t1)
print(t1)
print('-'*50)
print(t2)
tf.Tensor(
[[[[[0]
[1]
[2]]]
[[[3]
[4]
[5]]]]], shape=(1, 2, 1, 3, 1), dtype=int32)
--------------------------------------------------
tf.Tensor(
[[0 1 2]
[3 4 5]], shape=(2, 3), dtype=int32)
2.2.4 tf.transpose
交换维度操作是十分常见的,可以通过tf.transpose(x, perm)函数完成维度交换操作,其中perm表示新维度的顺序List。考虑图片张量shape为[2, 5, 5, 3],图片数量、行、列、通道数的维度索引为[0, 1, 2, 3],将其交换为[b, c, h, w]格式,则新维度的索引号为[0, 3, 1, 2]。
#sahpe [2, 5, 5, 3]
t = tf.random.normal([2, 5, 5, 3])
t1 = tf.transpose(t, perm = [0, 3, 1, 2])
print(t1.shape)
(2, 3, 5, 5)
2.3 合并与分割
2.3.1 合并
合并是指将多个张量沿某个维度合并为一个张量。按照是否产生新的维度,张量合并分为张量拼接(concatenate)和张量堆叠(stack)。 张量拼接在Tensorflow中通过tf.concat(tensors,axis)实现,其中tensors为list,每个元素为一个需要合并的张量,axis为需要合并的维度。
a = tf.constant([[1, 2, 3], [4, 5, 6]])
b = tf.constant([[7, 8, 9], [10, 11, 12]])
c1 = tf.concat([a, b], 0)
c2 = tf.concat([a, b], 1)
print(c1) # 未产生新维度
print('-'*50)
print(c2) # 未产生新维度
tf.Tensor(
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]], shape=(4, 3), dtype=int32)
--------------------------------------------------
tf.Tensor(
[[ 1 2 3 7 8 9]
[ 4 5 6 10 11 12]], shape=(2, 6), dtype=int32)
需要注意的是:张量合并在非合并维度的长度必须一致。
张量堆叠会产生新的维度,通过tf.stack(tensors, axis) 实现,tensors为可以合并的多个张量,axis 指定插入新维度的位 置。当axis ≥ 0时,在 axis 之前插入;当axis < 0时, 在 axis 之后插入新维度。
a = tf.constant([[1, 2, 3], [4, 5, 6]])
b = tf.constant([[7, 8, 9], [10, 11, 12]])
tf.stack([a, b]) # axis默认为0
<tf.Tensor: shape=(2, 2, 3), dtype=int32, numpy=
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])>
需要注意的是:需要合并的张量 shape 完全一致才可堆叠,否则会出现错误。
2.3.2 分割
tf.unstack
tf.unstack(tensor, num, axis),tf.unstack()将tensor根据axis分解成num个张量,返回的值是list类型,如果没有指定num则根据axis推断出。
a = tf.constant([[3,2,4,5,6],[1,6,7,8,0]])
c = tf.unstack(a,axis=0)
d = tf.unstack(a,axis=1)
print(c)
print('-'*50)
print(d)
[<tf.Tensor: shape=(5,), dtype=int32, numpy=array([3, 2, 4, 5, 6])>, <tf.Tensor: shape=(5,), dtype=int32, numpy=array([1, 6, 7, 8, 0])>]
--------------------------------------------------
[<tf.Tensor: shape=(2,), dtype=int32, numpy=array([3, 1])>, <tf.Tensor: shape=(2,), dtype=int32, numpy=array([2, 6])>, <tf.Tensor: shape=(2,), dtype=int32, numpy=array([4, 7])>, <tf.Tensor: shape=(2,), dtype=int32, numpy=array([5, 8])>, <tf.Tensor: shape=(2,), dtype=int32, numpy=array([6, 0])>]
tf.split
tf.split(tensor, num_or_size_splits, axis),tensor为待分割张量,num_or_size_splits控制分割后对应维度上元素的个数,axis为指定分割的维度索引号。
x = [[1,2,3],[4,5,6]]
tf.split(x, 3, 1)
[<tf.Tensor: shape=(2, 1), dtype=int32, numpy=
array([[1],
[4]])>,
<tf.Tensor: shape=(2, 1), dtype=int32, numpy=
array([[2],
[5]])>,
<tf.Tensor: shape=(2, 1), dtype=int32, numpy=
array([[3],
[6]])>]
2.4 统计
2.4.1 范数
范数(Vector norm)是表征向量“长度”的一种度量方法,在神经网络中,常用来 表示张量的权值大小,梯度大小等。常用的范数有:L1 范数、L2 范数、∞ −范数。在 TensorFlow 中,可以通过 tf.norm(x, ord)求解张量的 L1, L2, ∞等范数,其中参数 ord 指定为 1,2 时计算 L1, L2 范数,指定为 np.inf 时计算∞ −范数:
import numpy as np
x=tf.ones([3,3])
print(x)
# L1 范数
L1=tf.norm(x,ord=1)
print("L1 norm is: {}".format(L1))
# L2范数
L2=tf.norm(x,ord=2)
print("L2 norm is: {}".format(L2))
# ∞范数
L_inf=tf.norm(x,ord=np.inf)
print("∞ norm is: {}".format(L_inf))
tf.Tensor(
[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]], shape=(3, 3), dtype=float32)
L1 norm is: 9.0
L2 norm is: 3.0
∞ norm is: 1.0
2.4.2 最值、均值
通过 tf.reduce_max, tf.reduce_min, tf.reduce_mean可以求取指定维度上的最大、最小和均值,也可求取全部数组中的最大、最小和均值。
#创建张量
x = tf.random.normal([3,3])
print(x)
#统计最大值
max=tf.reduce_max(x,axis=1)
print("max is: {}".format(max))
#统计最小值
min=tf.reduce_min(x,axis=1)
print("min is: {}".format(min))
# 统计均值
mean=tf.reduce_mean(x,axis=1)
print("mean is: {}".format(mean))
tf.Tensor(
[[ 0.7039088 -1.3010858 -0.26340982]
[-0.9290817 -1.6276046 0.22816275]
[ 0.30339584 0.2684896 0.96004856]], shape=(3, 3), dtype=float32)
max is: [0.7039088 0.22816275 0.96004856]
min is: [-1.3010858 -1.6276046 0.2684896]
mean is: [-0.28686228 -0.77617455 0.5106447 ]
当不指定 axis 参数时,tf.reduce_*函数会求解出全局元素的最大、最小、均值。
#创建张量
x = tf.random.normal([3,3])
#统计最大值
max=tf.reduce_max(x)
print("max is: {}".format(max))
#统计最小值
min=tf.reduce_min(x)
print("min is: {}".format(min))
# 统计均值
mean=tf.reduce_mean(x)
print("mean is: {}".format(mean))
max is: 1.9886242151260376
min is: -1.1376621723175049
mean is: 0.13465966284275055
2.5 张量比较
在深度学习中,通常需要将预测结果与真实标签进行比较,从而计算准确率。常用的张量比较函数有以下几种:
以下以tf.equal(a,b)为例说明其用法。 tf.equal()函数返回布尔型的张量比较结果,通过统计张量中True元素的个数即可获得预测正确的数量。一般情况下,我们先将布尔型转换为整型张量0和1,再求和即可得到1的个数,也就是True元素的数量。
pre_out=tf.convert_to_tensor(np.array([[1,2,3,4,2,3,5,6,7,8,9,2,3,4,5,4]]))
label=tf.convert_to_tensor(np.array([[1,7,3,4,2,3,4,6,7,3,9,2,7,4,2,4]]))
# 预测值与真实值比较
acc=tf.equal(pre_out,label)
print("acc is: {}".format(acc))
# 布尔型转 int 型
acc = tf.cast(acc, dtype=tf.float32)
# 统计 True 的个数
correct = tf.reduce_sum(acc)
print("correct is: {}".format(correct))
acc is: [[ True False True True True True False True True False True True
False True False True]]
correct is: 11.0
其它的比较运算函数请读者自行测试。
2.6 填充和复制
2.6.1 填充
tf.pad(tensor,paddings,mode=“CONSTANT”,constant_values=0)主要是用来对tensor的大小进行扩展,包括水平、垂直、深度等,tensor为输入的张量,paddings为设置填充的大小,mode为填充方式(可选择CONSTANT、REFLECT和SYMMETRIC,默认是CONSTANT),constant_values为CONSTANT填充方式的填充值,默认为0,当填充方式为REFLECT和SYMMETRIC时,constant_values无效,使用tensor中的值进行填充。
a = tf.fill([3,3],2)
# 第一维度,前面补一维度,后面补一维度;第二维度,前面补两维度,后面补两维度。
tf.pad(a,[[1,1],[2,2]])
<tf.Tensor: shape=(5, 7), dtype=int32, numpy=
array([[0, 0, 0, 0, 0, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 0, 0]])>
2.6.2 复制
可以通过tf.tile(tensor, multiples)函数完成数据在指定维度上的复制操作,multiples分别指定了每个维度上的复制倍数,对应位置为1表明不复制,为2表明新长度为原来长度的2倍,即数据复制一份,以此类推。 如下,通过tf.tile(x, multiples=[2, 1])可以在axis = 0维度复制一次,在axis=1维度不复制。
a = tf.constant([[1,2],[3,4]])
# 在axis=0维度复制一次,在axis=1维度不复制。
tf.tile(a, multiples=[2, 1])
<tf.Tensor: shape=(4, 2), dtype=int32, numpy=
array([[1, 2],
[3, 4],
[1, 2],
[3, 4]])>
2.7 数据限幅操作
通过简单的数据限幅运算可以限制数据的范围,在Tensorflow中,通过tf.maximum(x,a)实现数据的下限幅:x ∈ [𝑎,+∞);通过 tf.minimum(x, a)实现数据的上幅限制:𝑥 ∈(-∞,a] 。
x=tf.range(100)
#下限幅 50
tf.maximum(x,50)
<tf.Tensor: shape=(100,), dtype=int32, numpy=
array([50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99])>
#上限幅80
tf.minimum(x,80)
<tf.Tensor: shape=(100,), 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, 32, 33,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 80, 80, 80, 80,
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80])>
通过组合 tf.maximum(x, a)和 tf.minimum(x, b)可以实现同时对数据的上下边界限幅: 𝑥 ∈ [𝑎,𝑏]:
# 上下限幅为
x = tf.range(10)
tf.minimum(tf.maximum(x,3),8)
<tf.Tensor: shape=(10,), dtype=int32, numpy=array([3, 3, 3, 3, 4, 5, 6, 7, 8, 8])>
数据限幅操作主要用在激活函数中,如relu函数中限制输出的上下限,读者可自行实现一下限幅relu激活函数。