3 张量操作
3.1 索引
Tensorflow中,支持基本的[i][j]…的标准索引方式,也支持通过逗号分隔索引号的索引方式[i, j, k]。假设如下为图片数据
In [3]: x = tf.random.normal([4, 32, 32, 3])
- 取第一张图片数据
In [4]: x[0]
Out[4]:
<tf.Tensor: id=15, shape=(32, 32, 3), dtype=float32, numpy=
- 取第一张图片的第二行
In [5]: x[0][1]
Out[5]:
<tf.Tensor: id=23, shape=(32, 3), dtype=float32, numpy=
- 取第1张图片,第2行,第3列的像素
In [6]: x[0][1][2]
Out[6]: <tf.Tensor: id=35, shape=(3,), dtype=float32, numpy=array([-0.11009463, 0.0398486 , 1.8434385 ], dtype=float32)>
- 取第三张图片,第2行,第1列元素,第2个通道的颜色强度值
In [7]: x[2][1][0][1]
Out[7]: <tf.Tensor: id=51, shape=(), dtype=float32, numpy=0.96755>
- 取第2张图片,第10行,第3列
In [8]: x[1, 9, 2]
Out[8]: <tf.Tensor: id=55, shape=(3,), dtype=float32, numpy=array([0.7964836, 1.7832114, 0.4268306], dtype=float32)>
3.2 切片
通过 切片方式可以方便地截取一段数据,start为开始读取位置的索引,end为结束读取位置的索引,不包含end,step为读取步长。
读取第2,3张图片
In [9]: x[1:3]
切片方式简写方式:start、end、step3个参数均可以选择行省略,全部省略时即::,表示从最开始读取到最末尾,步长为1。
x[0,::]表示读取第1张图片的所有行,其中::表示在行维度上读取所有行。
为了简洁,::可以简写为单个冒号:,
In [10]: x[:, 0:28:2, 0:28:2, :]
表示取所有图片,隔行采样,隔列采样,所有通道信息,相当将图片高宽缩放至50%。
|
切片方式 |
意义 |
|
|
从start开始读取至end,步长为step |
|
|
从start开始读取至end,步长为1 |
|
|
从start开始读取晚后续所有元素,步长为1 |
|
|
从start开始读取晚后续所有元素,步长为step |
|
|
从0开始读取到end,步长为step |
|
|
从0开始读取到end,步长为1 |
|
|
每隔step-1个元素采样所有 |
|
|
读取所有元素 |
|
|
读取所有元素 |
特别地,step可以为负数,表示逆序读取。当张量的维度数较多时,不需要采样的维度一般用单毛号:表示采样所有元素。
3.3 维度变换
在神经网络运算中,维度变换时最核心的张量操作,通过维度变换可以将数据任意的切换形式,满足不同场合的运算需求。
基本的维度变换包含改变视图的reshape,插入新维度expand_dims,删除维度squeeze,交换维度transpose,复制数据tile等。
- Reshape
tf.reshape(tensor, shape, name=None)
第一个参数为被调整维度的张量,第二个参数为要调整为的形状,返回一个shape形状的新rensor。注意shape里最多有一个维度的值可以为-1,表示自动计算此维度。
In [3]: x = tf.range(24)
In [4]: x = tf.reshape(x, [2, 4, 3])
在存储数据时,内存并不支持维度层级概念,只能以平铺的形式写入内存,因此这种层级关系需要人为管理,每个张量的存储顺序需要人为跟踪。
- 增删维度
通过tf.expand_dims(x, axis)可在指定的axis轴前插入一个新的维度。
考虑一张[28, 28]的图片,即shape为[28, 28],在末尾给张量增加一新维度,定义为通道维度数,此时张量shape变为[28, 28, 1]
In [6]: tf.expand_dims(x, axis = 2)
Out[6]: <tf.Tensor: id=15, shape=(28, 28, 1), dtype=int32, numpy=
注意:aixs为正时,表示在当前维度之前插入一个新的维度,为负时,表示在当前维度之后插入一个新的维度。
通过tf.squeeze(x, axis)可以删除长度为1的维度,axis参数为待删除维度的索引号,删除维度只能删除长度为1的维度,也不会改变张量的存储。
In [8]: x = tf.random.uniform([28, 28, 1], maxval = 10, dtype = tf.int32)
In [9]: tf.squeeze(x, axis = 2)
Out[9]: <tf.Tensor: id=20, shape=(28, 28), dtype=int32, numpy=
如果不指定维度参数axis,即tf.sequeeze(x),则其会默认删除所有长度为1的维度。
- 交换维度
交换维度操作式十分常见的,比如在Tensorflow中,图片张量的默认存储格式事通道后行的格式:[b, h, w, c],但是部分库(pytorch)中图片格式是通道先行:[b, c, h, w],因此需要完成[b, h, w, c]到[b, c, h, w]的维度交换。
可以通过tf.transpose(x, perm)函数完成维度交换操作,其中perm表示新维度的顺序List。考虑图片张量shape为[2, 32, 32, 3],图片数量、行、列、通道数的维度索引为[0, 1, 2, 3],将其交换为[b, c, h, w]格式,则新维度的索引号为[0, 3, 1, 2]。
In [14]: x = tf.random.normal([2, 28, 28, 3])
In [15]: tf.transpose(x, perm = [0, 3, 1, 2])
Out[15]: <tf.Tensor: id=41, shape=(2, 3, 28, 28), dtype=float32, numpy=
注意的是,通过tf.transpose完成维度交换后,张量的存储顺序已经改变,视图也随之改变,后续的所有操作必须基于新的存储顺序进行。
- 数据复制
可以通过tf.tile(x, multiples)函数完成数据在指定维度上的复制操作,multiples分别指定了每个维度上的复制倍数,对应位置为1表明不复制,为2表明新长度为原来长度的2倍,即数据复制一份,以此类推。
如下,通过tf.tile(x, multiples=[2, 1])可以在axis = 0维度复制一次,在axis=1维度不复制。
In [17]: tf.tile(x, multiples=[2, 1])
Out[17]:
<tf.Tensor: id=44, shape=(2, 2), dtype=int32, numpy=
array([[1, 2],
[1, 2]], dtype=int32)>
来源:CSDN
作者:biubiu先生
链接:https://blog.csdn.net/u013752048/article/details/103246488