Tensor (Tensorflow)与ndarray (Numpy)的概念用法比较与总结,包含轴(axis)、形状(shape)和图解【详尽】

本文围绕ndarray和tensor展开,介绍了ndarray存储/读取逻辑,对比了二者轴和形状的异同。阐述了ndarray轴的概念、应用及形状理解方法,分析了tensor的图解、维度、轴方向及维度描述用途,指出其维度排列利于神经网络张量运算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于使用Tensorflow的朋友们,想必都熟悉张量(tensor)这一概念。此外,numpy库中还存在一个类似的概念ndarray。本文记录和整理了ndarray和tensor之间,关于轴(axis)和形状(shape)之间的一些异同比较。考虑到大多数读者对数组的图形解释比较熟悉和习惯,(例如C/C++和python的数组),本文结合《图解深度学习与神经网络:从张量到TensorFlow实现》一书中的tensor图解,对数组与tensor进行较为详细的比较和总结。

ndarray的存储/读取逻辑

首先我们来讲一下ndarray对于数组的存储/读取的逻辑,这一点对实际使用并无影响。计算机怎么存就会怎么读,编程人员获取的结果都是一样的,但有助于理解后续的维度和轴。
ndarray对于数组的存储方式有两类,分别是C/C++语言模式(即行优先)和Fortran语言模式(即列优先),分别用order属性的’C’ 和’F’表示,默认以C语言模式存储。
下面以C模式为例:
如果我们想要创建一个多维数组,一般可以采用嵌套的数组表示,如[[[1, 2],[3,5],[2,4]],[[2,4],[4,6],[2,9]],[[3,5],[1,3],[34,12]]],这是一个三维数组。根据行优先的存储模式,可以知道,计算机内存存储时的实际顺序便是上方数组的书写顺序。三维数组包含列、行、页三个维度。计算机存储三维数组的过程便是先存列,再存行,最后存页。等等,不是说好的行优先吗,为什么说先存列。其实行优先只是便于理解的一种描述,即存完一行再存下一行,而不是存完一列再存下一列。实际上从数组角度来看,存放时先放的是第0行第0列,然后是第0行第1列,第0行第2列,第0行存放完才轮到第1行第0列,以此类推。可以发现先发生改变的或者说先出现维度概念的是列,之后才是行。

当你熟悉了ndarray对象的使用后,你就可以完全摒弃到行列这种容易混淆理解的说法,而是以轴(axis)来进行理解和表达,因为对于高维数组是不存在真正的行和列的,只有轴(axis)是始终有意义的表达方式。当然为了便于初学者理解,下文仍使用行、列的描述,以展现它们和轴的关系。

ndarray和tensor

1. ndarray

对于numpy中的多维数组,即ndarray,遵循上文的默认存储/读取顺序,随着维度的增高,数组将逐一产生列、行、页等维度。需要指出,列、行和页只是方便理解的通俗表述,严谨来说应该表述为第几维度。
为了方便理解和比较,对于不同维度,选择了各不相同的长度。
下面先给出不同维数数组的shape、axis和维度情况:
一维数组,例如5列的一维数组:

   [15  6 19  1 18]

该数组的shape=(5, ),分别对应列维度,也对应于axis=0。
注意向量陷阱,严格来说一维数组并非向量,向量必须为二维数组。因为一维数组缺少行维度,在部分操作时会产生错误的结果,例如:

   a = np.random.randn(5)	#shape=(5,)
   np.dot(a.T,a)	# 得到常数
   b = np.random.randn(1,5) #shape=(1,5)
   np.dot(b.T,b)	# 得到5行5列的二维数组

二维数组,例如4行5列的二维数组:

  [[15  6 19  1 18]
   [18  0 16  6  7]
   [ 2 17  5 15 19]
   [15 17  4 18  3]]

shape=(4,5),分别对应行和列维度,也对应于axis=0,1。
三维数组
三维数组可以看作由多个二维数组构成,例如3页4行5列的三维数组:

 [[[15  6 19  1 18]
   [18  0 16  6  7]
   [ 2 17  5 15 19]
   [15 17  4 18  3]]
  [[12 19  9 10  5]
   [14 16  1 12 17]
   [ 3  0 18 15 18]
   [15  3  4  8  4]]
  [[14 12 11 10  7]
   [11 11  1  5 12]
   [10 13 19  1 17]
   [ 7 10 11  0  8]]]

shape=(3,4,5),分别对应页、行和列三个维度,也对应于axis=0,1,2。
高维数组
从四维数组开始,新产生的维度没有统一的简洁称呼,一般称为第几维度。或者直接采用轴(axis)进行描述。同理,四维数组可看作由多个三维数组组成。例如,由2个3页4行5列三维数组构成的四维数组:

[[[[15  6 19  1 18]
   [18  0 16  6  7]
   [ 2 17  5 15 19]
   [15 17  4 18  3]]
  [[12 19  9 10  5]
   [14 16  1 12 17]
   [ 3  0 18 15 18]
   [15  3  4  8  4]]
  [[14 12 11 10  7]
   [11 11  1  5 12]
   [10 13 19  1 17]
   [ 7 10 11  0  8]]]
 [[[ 6  2 10  7 17]
   [19  8 13  7 14]
   [12 18 17  6  9]
   [10 14 13  3  5]]
  [[10 18  6  5 13]
   [19  7 12  8  6]
   [13  0 12  5  3]
   [ 0 17  6  6 13]]
  [[ 7 19  1 14  9]
   [17 10 12  8 13]
   [ 0  8 12 13 16]
   [ 0  1 18 15 10]]]]

shape=(2,3,4,5),分别对应第四维度、页、行和列,也对应于axis=0,1,2,3。

★需要指出,官方关于axis和维度的对应关系与上文关于维度的表述是相反的,对于一个n维数组,其定义为:
axis = 0,表示第一个维度;axis = 1,表示第二个维度,以此类推。
本文的表述是按维度产生的前后顺序排列的,即行, 列, 页, 第四维度, 第五维度, …, 第n维度,对应着axis=n, n-1, n-2, …, 1。
官方的定义没有纳入行、列、页的概念,为了包含行、列、页的概念,方便大家理解,本文的表述没有遵循官方的标准。

总结,在ndarray中,
注意此处没有采用官方的维度与轴对应关系
①新的维度总是出现在shape的最左侧
②轴(axis)与维度之间没有固定的对应关系,而是与shape相对应
(在二维数组中axis=0对应行,而三维数组中axis=0则对应页)
注:纳入行列页概念后的弊端,如采用官方的定义则不存在这一问题,轴(axis)、维度和shape始终保持对应关系。
③ndarray的图解与数组的打印格式完全一致,在对数组进行处理时这将提供很大的便利
④需要指出,即便采用Fortran语言模式(即列优先)存储,也不会改变上述轴和形状的顺序和定义

1.1轴(axis)

在实际应用中,numpy内置的许多函数能够对数组进行复杂而高效的操作,这些函数中都包含参数axis。因此,理解axis这一概念是必不可少的。
关于轴(axis)这一概念,可以类比数学中的坐标轴,下图给出了二维数组的轴、轴坐标和各元素的索引。
一般来说,我们认为对于二维数组,行(的)方向为横向,列(的)方向为纵向。在图中,0号轴(axis=0)和1号轴(axis=1)分别沿着列方向和行方向。这似乎与此前二维数组中维度与轴的对应关系相反(axis=0对应行维度、axis=1对应列维度)。
这是由于,前面说的axis=0对应行维度,实际上是对应着行维度的长度,即m行n列数组中的m,因此其必须沿行号变化的方向(即所谓的列方向)。换言之,0号轴上的坐标反映了不同的行号,反过来也对应了行维度,如下图所示。
因此,建议按照 0号轴对应行维度⇄0号轴沿行号变化的方向来理解和记忆,这样不会引起混淆。1号轴(axis=1)同理。
在这里插入图片描述
如果你理解了上述表述,那么高维数组都是一样的。例如三维数组,如下图所示。
按照之前的表述,在三维数组中,axis=0对应页维度,axis=1对应行维度,axis=2对应列维度,因此0号轴(axis=0)沿页号变化的方向,1号轴(axis=1)沿行号变化的方向,2号轴(axis=2)沿列号变化的方向。与下图完全对应,非常简单易懂。在这里插入图片描述
总结
n号轴对应第n维度⇄n号轴沿第n维度编号变化的方向

1.2 轴(axis)的应用

知道轴的含义和方向后,现在实战一些numpy的内置函数

求和:sum(axis)
用法:np.sum(A,axis) 或 A.sum(axis)
以下面的3页4行5列三维数组为例,axis的取值有None、0、1和2:

当参数为None时,把数组中的所有元素相加,得到一个常数;
当参数为0时,沿页号变化的方向(0号轴方向)累加,得到一个4行5列的二维数组(页维度被压缩,其他维度不变);
当参数为1时,沿行号变化的方向(1号轴方向)累加,得到一个3行5列的二维数组(行维度被压缩,页维度变为行维度)
当参数为2时,沿列号变化的方向(2号轴方向)累加,得到一个3行4列的二维数组(列维度被压缩,页维度变为行维度,行维度变为列维度)

 #三维数组A
 [[[15  6 19  1 18]
   [18  0 16  6  7]
   [ 2 17  5 15 19]
   [15 17  4 18  3]]
  [[12 19  9 10  5]
   [14 16  1 12 17]
   [ 3  0 18 15 18]
   [15  3  4  8  4]]
  [[14 12 11 10  7]
   [11 11  1  5 12]
   [10 13 19  1 17]
   [ 7 10 11  0  8]]]
  A.sum() = 614
  A.sum(axis=0) = 
  [[41 37 39 21 30]
   [43 27 18 23 36]
   [15 30 42 31 54]
   [37 30 19 26 15]]
  A.sum(axis=1) =
  [[50 40 44 40 47]
   [44 38 32 45 44]
   [42 46 42 16 44]]
  A.sum(axis=2) =
  [[59 47 58 57]
   [55 60 54 34]
   [54 40 60 36]]

求积:prod(axis)
该函数用来计算数组元素的乘积,对于多维数组可以指定轴

同样以上面的3页4行5列三维数组为例,
当参数为None时,求数组的所有元素乘积,得到一个常数;
当参数为0时,沿页号变化的方向(0号轴方向)累乘,得到一个4行5列的二维数组(页维度被压缩,其他维度不变);
当参数为1时,沿行号变化的方向(1号轴方向)累乘,得到一个3行5列的二维数组(行维度被压缩,页维度变为行维度)
当参数为2时,沿列号变化的方向(2号轴方向)累乘,得到一个3行4列的二维数组(列维度被压缩,页维度变为行维度,行维度变为列维度);

 #三维数组A
A.prod() = 0
A.prod(axis=0) =
[[2520 1368 1881  100  630]
 [2772    0   16  360 1428]
 [  60    0 1710  225 5814]
 [1575  510  176    0   96]]
A.prod(axis=1) =
[[ 8100     0  6080  1620  7182]
 [ 7560     0   648 14400  6120]
 [10780 17160  2299     0 11424]]
A.prod(axis=2) =
[[ 30780      0  48450  55080]
 [102600  45696      0   5760]
 [129360   7260  41990      0]]

统计量
mean(axis):计算元素的均值
var(axis):计算元素的方差
std(axis) :计算元素标准差
max(axis):计算元素的最大值
min(axis):计算元素的最小值
ptp(axis):计算元素的取值范围,即最大值和最小值的差值
median(axis):计算元素的中位数

以max(axis)函数为例,结果如下

 #三维数组A
A.max() = 19
A.max(axis=0) =
[[15 19 19 10 18]
 [18 16 16 12 17]
 [10 17 19 15 19]
 [15 17 11 18  8]]
A.max(axis=1) =
[[18 17 19 18 19]
 [15 19 18 15 18]
 [14 13 19 10 17]]
A.max(axis=2) =
[[19 18 19 18]
 [19 17 18 15]
 [14 12 19 11]]

总结:
从效果上来说,axis参数可理解为“将要被消除或折叠的维度或轴”。
从计算上来说,axis参数可理解为“数组沿轴的方向进行运算”。

1.3 形状(shape)

shape的概念实际上非常简单,这里简单说一下高维数组的shape

[[[[15  6 19  1 18]
   [18  0 16  6  7]
   [ 2 17  5 15 19]
   [15 17  4 18  3]]
  [[12 19  9 10  5]
   [14 16  1 12 17]
   [ 3  0 18 15 18]
   [15  3  4  8  4]]
  [[14 12 11 10  7]
   [11 11  1  5 12]
   [10 13 19  1 17]
   [ 7 10 11  0  8]]]
 [[[ 6  2 10  7 17]
   [19  8 13  7 14]
   [12 18 17  6  9]
   [10 14 13  3  5]]
  [[10 18  6  5 13]
   [19  7 12  8  6]
   [13  0 12  5  3]
   [ 0 17  6  6 13]]
  [[ 7 19  1 14  9]
   [17 10 12  8 13]
   [ 0  8 12 13 16]
   [ 0  1 18 15 10]]]]

对于上面这个四维数组,其shape=(2,3,4,5)。
有两种理解方法:
①按照行列页的维度去理解,即由2个 3页4行5列的三维数组构成;
②从外向内依次去括号,即最外侧(第1个)括号内包含2个“元素”:

 #元素1
 [[[15  6 19  1 18]
   [18  0 16  6  7]
   [ 2 17  5 15 19]
   [15 17  4 18  3]]
  [[12 19  9 10  5]
   [14 16  1 12 17]
   [ 3  0 18 15 18]
   [15  3  4  8  4]]
  [[14 12 11 10  7]
   [11 11  1  5 12]
   [10 13 19  1 17]
   [ 7 10 11  0  8]]]
  #元素2
 [[[ 6  2 10  7 17]
   [19  8 13  7 14]
   [12 18 17  6  9]
   [10 14 13  3  5]]
  [[10 18  6  5 13]
   [19  7 12  8  6]
   [13  0 12  5  3]
   [ 0 17  6  6 13]]
  [[ 7 19  1 14  9]
   [17 10 12  8 13]
   [ 0  8 12 13 16]
   [ 0  1 18 15 10]]]

次外侧(第2个)括号内包含3个“元素”:

 #元素1
  [[15  6 19  1 18]
   [18  0 16  6  7]
   [ 2 17  5 15 19]
   [15 17  4 18  3]]
 #元素2
  [[12 19  9 10  5]
   [14 16  1 12 17]
   [ 3  0 18 15 18]
   [15  3  4  8  4]]
 #元素3
  [[14 12 11 10  7]
   [11 11  1  5 12]
   [10 13 19  1 17]
   [ 7 10 11  0  8]]]

同理,第3个括号内包含4个“元素”,第4个括号内包含4个“元素”。
因此,shape=(2,3,4,5)

2. tensor

说了这么多,下面讲一讲Tensorflow中的张量(tensor),其是一种具有统一类型的多维数组。因此,tensor与ndarray具有很大的相似性。
但如果你看过或者了解过《图解深度学习与神经网络:从张量到TensorFlow实现》一书(简称《图解张量》),或看过笔者的Tensorflow中CNN的基础概念解析,则会发现张量的图解和维度与此前的ndarray存在很大的区别。
因此,先简单介绍一下tensor的图解和维度,重点解读一下二者的异同和二种表述的优劣。

2.1 tensor的图解与维度

后续以《图解张量》中图解的张量为例进行解读

一维张量
即向量,注意其没有行/列向量的区别。
实际上其仍然遵循ndarray中一维数组的shape规则,即

   tensor = tf.constant([1,-2,2,1], dtype=tf.double) #shape=(4,)
   tf.transpose(tensor) #转置,shape不变—— shape=(4,)

   ndarray=tensor.numpy() #tensor转换为ndarray,shape=(4,)
   #ndarray=[1,-2,2,1]

如下图所示
在这里插入图片描述
尽管一维张量没有行列的概念,但为了方便理解,姑且认为对应的维度为行维度

二维张量
即矩阵,例如

   tensor = tf.constant([[-1,1,8,5]
   						 [ 2,3,1,9]
   						 [ 7,2,6,4]], dtype=tf.double) #shape=(3,4)
   ndarray=tensor.numpy() #shape=(3,4)

其图解如下图所示,即3行4列的二维张量(矩阵),与ndarray中的二维数组基本一致。
在这里插入图片描述
三维张量
可以类比ndarray中的三维数组,例如

   tensor = tf.constant([[[1,-11],[1,11],[8,18],[5,15]],
   						 [[2, 12],[3,13],[1,11],[9,19]],
   						 [[7, 17],[2,12],[6,16],[4,14]]], 
   						dtype=tf.double) #shape=(3,4,2)
   ndarray=tensor.numpy() #shape=(3,4,2)

如下图所示,即3行4列2深度的三维张量。此时,其shape与ndarray的shape一致,但维度描述和图解有很大的区别。在ndarray中,其是3页4行2列的三维数组。
在这里插入图片描述
换言之,在tensor中,对于三维张量,按照shape的顺序,维度依次为行、列和深度,而不是ndarray中的页、行和列。这一顺序也是维度的产生先后顺序。

四维张量
可以类比ndarray中的三维数组,例如

   tensor = tf.constant([[[[1,-11],[1,11],[8,18],[5,15]],
   						  [[2, 12],[3,13],[1,11],[9,19]],
   						  [[7, 17],[2,12],[6,16],[4,14]]], 
   						 [[[5,-21],[1,21],[6,18],[2,25]],
   						  [[7, 22],[5,23],[7,21],[9,19]],
   						  [[6, 27],[3,22],[4,26],[2,14]]]],
   						dtype=tf.double) #shape=(2,3,4,2)
   ndarray=tensor.numpy() #shape=(2,3,4,2)

如下图所示,即由2个3行4列深度为2的三维张量构成的四维张量(描述顺序与shape的分量顺序一致)。其shape与ndarray的shape一致,但维度描述和图解同样存在很大区别。在ndarray中,其是由2个3页4行2列的三维数组构成的四维数组。
从第四维度开始,新维度出现在shape的最左侧,而不是最右侧。这也是tensor图解和维度描述最容易搞错的地方。
在这里插入图片描述
总结
在tensor中,
①对于三维张量,按照shape的顺序,维度依次为行、列和深度,而不是ndarray中的页、行和列
②随着张量维度的增加,依次出现的维度为行、列、深度…
③从第四维度开始,新维度出现在shape的最左侧,而不是最右侧,与ndarray一致
④shape与ndarray的shape是一致的,shape与axis的对应关系也与ndarray一致,从左至右依次为axis=0,1,2,3…

2.2 tensor的轴(axis)

正如前面所述,tensor的shape和axis都与ndarray中保持一致,因此关于轴参数的含义、运算结果都是一致的。只是由于二者图解和维度存在的区别,轴的方向也会产生差别。
以上面的三维张量为例,其各个轴的方向如下方第一张图所示
在这里插入图片描述
在这里插入图片描述

上方第二张图是上述三维张量转化为ndarray中三维数组后,其轴方向的示意图。尽管从图解上看,轴的方向完全不同,但张量的轴方向仍然可以按照ndarray中的轴进行分析,即n号轴对应第n维度⇄n号轴沿第n维度编号变化的方向
需要注意,二者的轴方向虽然看似不一致,但实际都沿着同一个方向。例如,tensor中axis=0沿行号变化方向,ndarray中沿页号变化方向,实际上都是沿着-1→ 2→ 7的方向。
因此,tensor的运算中axis参数的作用和效果与ndarray是完全一致的,不用担心二者图解和维度描述的差异(只是人为描述的差别),axis始终与shape保持一致。

2.3 tensor图解和维度描述的用途

相信有读者疑惑,既然二者关于axis的操作完全一致,而且计算机运算时不涉及到维度的概念,为何tensor要特立独行,采用行、列和深度这种顺序的维度描述方法。
这一疑问实际上代入tensor的应用场景中,便可以得到解答。tensorflow的一个主要的应用场景就是图片和视频数据。
对于一张彩色图片,其张量的shape=(H,W,C),其中W和H是图片的宽和高,C是颜色的通道数(如RGB图像是3通道,即C=3)。
而对于视频,其张量的shape=(F,H,W,C),其中F是frames(帧数),视频的每一帧都是一个彩色图片。如果是多个视频组成的5D张量,其shape=(S,F,H,W,C),其中S是samples(视频数量)。
从视频张量每个维度的含义可以发现,其与tensor的图解、维度的概念和顺序是一致的,分别对应着第5维度、第4维度、行、列和深度。
此外,在CNN中,还存在一个多通道卷积的概念,这里通道可以指图片的颜色通道。在多通道卷积时,卷积核也是多通道的,即输入图的各个颜色通道具有不同的卷积核参数,如下图。如果你不熟悉tensorflow中CNN的概念,可以去简单了解一下笔者的Tensorflow中CNN的基础概念解析
可见,这种维度排列顺序可以极大便利神经网络中的张量运算(部分张量运算对张量首尾维度的长度有要求)。
在这里插入图片描述
换言之,在ndarray中,一个二维数组就是一个完整的数据了,三维数组和更高维的数组看作为多个/组的二维数组。而在tensorflow中,一个三维张量才能构成一个完整的数据(宽度、高度、通道),因此从四维张量开始才看作是多个/组的三维张量(即,从第四维度开始,新维度出现在shape的最左侧)。

至此,本文的内容就算全部讲完了,内容比较多,笔者已经尽可能地进行整理和归纳。如果有不理解和不正确的地方,欢迎大家提问和指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值