40

理解张量

 4 years ago
source link: https://yuanxiaosc.github.io/2019/11/28/理解张量/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

神经网络的输入、输出、权重都是张量,神经网络中的各种计算和变换就是对张量操作,张量这种数据结构是神经网络的基石,可以说没有理解张量就没有真正理解神经网络和人工智能。本文由浅入深地详细讲解分析张量,望能给予读者启发——袁宵。

张量的定义

张量(tensor)是一个多维数组(multidimensional arrays),即一种存储数字集合的数据结构,这些数字可通过索引(index)单独访问,并可通过多个索引进行索引。

张量是将向量和矩阵推广到任意维数。如下图所示,一个张量的维数与张量中用来表示标量值的索引的数量一致。

Y3y6jq7.png!web

新张量 = 张量[索引]

张量的视图与存储

点击张量的存储.ipynb 深入学习,下面是该文件的主要内容:

  • 张量,PyTorch中的基本数据结构
  • 索引并在PyTorch张量上进行操作以探索和处理数据
  • 与NumPy多维数组互操作
  • 将计算移至GPU以提高速度

张量的视图与存储的定义

存储(Storage)是一维的数字数据数组,例如包含给定类型的数字(可能是float或int32)的连续内存块。张量是这样一个存储的视图,它能够通过使用偏移量(offset)和每一维度的步长(per-dimension strides)索引(index)到该存储中。存储的布局总是一维的,而与可能涉及到它的任何张量的维数无关。

多个张量可以对相同的存储进行索引,即使它们对数据的索引是不同的。但是,底层内存只分配一次,因此不管存储实例管理的数据有多大,都可以快速地创建数据上的替代张量视图。

ZrIVfy6.png!web

张量视图的多维性意义

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

张量存储的一维性

在存储数据时,内存并不支持这个维度层级概念,只能以平铺方式按序写入内存,因此这 种层级关系需要人为管理,也就是说,每个张量的存储顺序需要人为跟踪。为了方便表达,我们把张量 shape 中相对靠左侧的维度叫做大维度,shape 中相对靠右侧的维度叫做小维度,比如[2,4,4,3]的张量中,图片数量维度与通道数量相比,图片数量叫做大维度,通道 数叫做小维度。在优先写入小维度的设定下,形状(2, 3)张量的内存布局为:

<Tensor: shape=(3, 2), dtype=float32, numpy=
array([[1., 4.],
       [2., 1.],
       [3., 5.]], dtype=float32)>

[1., 4., 2., 1., 3., 5.]

数据在创建时按着初始的维度顺序写入,改变张量的视图仅仅是改变了张量的理解方 式,并不会改变张量的存储顺序,这在一定程度上是从计算效率考虑的,大量数据的写入 操作会消耗较多的计算资源。

张量存储的形状(大小)、存储偏移量和步长

为了索引到存储中,张量依赖于一些信息,这些信息连同它们的存储一起明确地定义了它们:大小、存储偏移量和步长(下图)。

中文 英文 意义 形状 shape 是一个元组,表示张量表示的每个维度上有多少个元素。注意张量的形状(shape)与存储的大小(size)等价。 步长 stride 是一个元组,表示当索引在每个维度上增加1时,必须跳过的存储中的元素数量。 存储偏移量 storage offset 存储中对应于张量中第一个元素的index。

nUBRjmu.png!web

上图例子中,在二维张量中访问元素$(i, j)$的结果是访问存储中的$storage_offset + stride[0] i + stride[1] j$元素。

更加广义的:对于形状为$shape(d1, d2,.., dn)$的张量的视图中的元素$E(e1, e2,…,en)$,如果该张量的存储的步长为 $stride(s1, s2,…,sn)$ 、存储偏移量为 $storage \ offset$,那么元素$E$的存储位置$index$是:

由此我们得出了张量视图的计算式子:

张量视图 = 张量存储 + 张量形状 + 张量步长 + 张量偏移

张量存储对张量操作的影响

这种张量和存储之间的间接性导致了一些操作,比如转置一个张量或者提取一个次张量,这些操作是便宜的,因为它们不会导致内存的重新分配;而是,它们包括分配一个新的张量对象,这个张量对象的形状、存储偏移量或步长有不同的值。

子张量的维数变少,而索引的存储空间仍然和原来的点张量一样。改变子张量会对原张量产生副作用(对子张量的修改会影响原张量)。但是这种效果可能并不总是存在,因为可以把子张量克隆成一个新的张量。

没有分配新的内存:只有通过创建一个新的张量实例来获得转置(transpose),这个张量实例的步长与原来的张量不同。可以通过张量的重新布局函数,比如PyTorch中的contiguous()函数,来强制拷贝一份张量,让它的布局和从新创建的张量一样。

ZryYn2j.png!web

张量的视图与存储的区别与联系

联系

对于形状 shape 为(d1, d2,.., dn)的张量的视图中的元素E(e1, e2,…,en),如果该张量的存储的步长为 stride 为 (s1, s2,…,sn) 、存储偏移量storage offset 为 s_o,那么元素E的存储位置index是:

张量视图 = 张量存储 + 张量形状 + 张量步长 + 张量偏移

区别

  • 相同存储可以有不同的视图:tensor_B.storage() 与 tensor_B_transpose.storage() 相同,但是 tensor_B 与 tensor_B_transpose 不同。
  • 相同的视图可以有不同的存储:tensor_A 与 tensor_B_transpose 相同,但是 tensor_A.storage() 与 tensor_B_transpose.storage() 不同。

总结:张量的视图与存储通过索引来建立关系,它们之间没有必然性,即相同存储可以有不同的视图,相同的视图可以有不同的存储。

张量的操作

点击 TensorFlow张量的常用操作.ipynb 深入学习,下面是该文件的主要内容:

dtype=int32, float32, string, bool
tf.convert_to_tensor, tf.constant, tf.zeros, tf.ones, tf.zeros_like, tf.fill, tf.random.normal, tf.random.uniform, tf.range
A[1][2][1], A[1, 2, 1], A[ :, :, 0:3:2], A[..., 0:3:2]
tf.reshape, tf.expand_dims, tf.squeeze, tf.transpose
tf.tile
+, -, *, /, //, %, **, tf.pow, tf.square, tf.sqrt, tf.math.log, tf.matmul, @
tf.concat, tf.stack, tf.split, tf.unstack
tf.norm, tf.reduce_max min mean sum, tf.argmax, tf.argmin
tf.equal
tf.pad, tf.keras.preprocessing.sequence.pad_sequences, tf.tile
tf.maximum, tf.minimum, tf.clip_by_value
tf.gather, tf.gather_nd
tf.boolean_mask
tf.where
tf.scatter_nd
tf.meshgrid

TODO:增加PyTorch版本张量的操作

张量操作的意义

TODO:丰富内容


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK