深入理解Tensor的广播机制
鹿鼎记肯定 发布于2020-10-28 19:30 浏览:438 回复:11
4
收藏

广播(Boradcasting)机制最开始出现在Numpy中,目的是解决不同维度数据的计算问题,这里主要谈谈广播是怎么“广播”的?

 

在读2.0beta版本的Paddle文档时,遇到这么个问题(假设大家都懂一点广播~)

import paddle
import numpy as np

paddle.disable_static()

x = paddle.to_tensor(np.ones((2, 3, 4), np.float32))
y = paddle.to_tensor(np.ones((2, 3, 4), np.float32))
# 两个张量 形状一致,可以广播

x = paddle.to_tensor(np.ones((2, 3, 1, 5), np.float32))
y = paddle.to_tensor(np.ones((3, 4, 1), np.float32))
# 从后向前依次比较:
# 第一次:y的维度大小是1
# 第二次:x的维度大小是1
# 第三次:x和y的维度大小相等
# 第四次:y的维度不存在
# 所以 x和y是可以广播的

# 相反
x = paddle.to_tensor(np.ones((2, 3, 4), np.float32))
y = paddle.to_tensor(np.ones((2, 3, 6), np.float32))
# 此时x和y是不可广播的,因为第一次比较 4不等于6

第一个例子好理解,第二个例子的第一次、第二次...第四次到底在说啥?。。。我怎么愣是没懂。然后我又查阅了Numpy官网对广播的解释:

When operating on two arrays, NumPy compares their shapes element-wise. It starts with the trailing dimensions and works its way forward. Two dimensions are compatible when

  1. they are equal, or
  2. one of them is 1

大意就是在操作两个数组时,NumPy会按元素比较它们的形状。它从后面的维度开始,然后继续前进。如果符合下面两个条件,那他们是兼容的(可广播的),要么两个维度相等,要么有一个维度是1。

我又懵了!

后来看了很多文章才反应过来,这里用的词是dimensions,而不是shape。什么意思呢?假设一个Tensor的shape为[2, 3, 4, 5],它有四个维度,2、3、4和5,我们是逐个比较维度,而不是比较shape。现在知道了逐个比较维度这个概念,就很容易明白这个广播机制了。举个栗子:

A.shape = [2, 3, 1, 5]
B.shape = [3, 4, 1]

我们从后往前比较,按照上面的规则:他们要么相等,要么一个维度是1。维度小的左边补1。

A      (4d array):  2 x 3 x 1 x 5
B      (3d array): (1 x)3 x 4 x 1

Result (4d array):  2 x 3 x 4 x 5

这样子一比较,就能发现这个规则是这么用的。那这时候有人问,“鹿老师,我悟了,是不是shape为[2, 3, 4, 5]和[4]的就无法比较了!”我们分析一下

A.shape = [2, 3, 4, 5]
B.shape = [4]

我们从后往前比较,按照上面的规则:他们要么相等,要么一个维度是1。维度小的左边补1。

A      (4d array):  2 x 3 x 1 x 5
B      (3d array): (1 x 1 x 1 x)4

Result (4d array):  2 x 3 x 1 x ?

确实没法比较,看来这位同学果然学会了Tensor的广播机制精髓!

可是,Paddle又说了:

什么意思呢?就是我们还能让原来没法广播的数据再一次符合广播的条件。还是上面的例子

# 伪代码
> a.shape = [2, 3, 4, 5]
> b.shape = [4]
>
> paddle.disable_static()
> a = paddle.to_tensor(a)
> b = paddle.to_tensor(b)
> a+b
InvalidArgumentError: Broadcast dimension mismatch. Operands could not be broadcast together with the shape of X = [2, 3, 4, 5] and the shape of Y = [4]. Received [5] in X is not equal to [4] in Y at i:3.

# 使用axis参数
> paddle.elementwise_add(a, b, axis=2)
Tensor: eager_tmp_5
  - place: CPUPlace
  - shape: [2, 3, 4, 5]
  - layout: NCHW
  - dtype: int
  - data: [0 1 2 3 4 6 7 8 9 10 12 13 14 15 16 18 19 20 21 22 20 21 22 23 24 26 27 28 29 30 32 33 34 35 36 38 39 40 41 42 40 41 42 43 44 46 47 48 49 50 52 53 54 55 56 58 59 60 61 62 60 61 62 63 64 66 67 68 69 70 72 73 74 75 76 78 79 80 81 82 80 81 82 83 84 86 87 88 89 90 92 93 94 95 96 98 99 100 101 102 100 101 102 103 104 106 107 108 109 110 112 113 114 115 116 118 119 120 121 122]

这里要说到axis这个概念。我先前写过一篇关于axis的理解 https://ai.baidu.com/forum/topic/show/965956 。

前面说到从后往前比,而为什么是从后往前比呢?这就涉及到一个比较顺序的计算。对于a.shape=[2, 3, 4, 5]和b.shape=[4]两个Tensor,他们的rank(维度)差为3,因此从i=3这个下标开始比较

A.shape = [2, 3, 4, 5]
B.shape = [4]


             下标:  0   1   2   3
A      (4d array):  2 x 3 x 1 x 5
B      (3d array):              4

可以看到最右边是不满足“要么相等要么有一个维度是1”规则,因此是无法广播的。而指定axis=2后,会无视rank=3这一要求,直接从i=2这个下标开始比较

A.shape = [2, 3, 4, 5]
B.shape = [4]


             下标:  0   1   2   3
A      (4d array):  2 x 3 x 1 x 5
B      (3d array):          4

所以就成功了~

这时候又有同学要问了,“鹿老师,那如果两个Tensor的shape相等并且没有维度为1的值该怎么办呢?”

鹿老师:(猛地跳起)那还广播个锤子啊!这节课白学了?!

收藏
点赞
4
个赞
共11条回复 最后由鹿鼎记肯定回复于2020-11-03 11:25
#12鹿鼎记肯定回复于2020-11-03 11:25:50
#8 fiyen123_回复
这个广播机制和Numpy的相同不?

“基本一样”

如果熟悉Numpy直接当成Numpy用就行了

0
#11鹿鼎记肯定回复于2020-11-03 11:22:48

补充一下,2.0rc版本默认动态图模式,所以不需要指定

paddle.disable_static()
0
#10鹿鼎记肯定回复于2020-11-03 11:20:58

太爱你们了~~~一下子四个赞!

0
#9189******30回复于2020-11-01 20:31:28
#8 fiyen123_回复
这个广播机制和Numpy的相同不?

应该差不多

0
#8fiyen123_回复于2020-11-01 15:27:09

这个广播机制和Numpy的相同不?

0
#7fxbnl123回复于2020-10-31 18:56:27
#6 鹿鼎记肯定回复
别光顾着扣6  点个赞呗~

安排

0
#6鹿鼎记肯定回复于2020-10-30 21:20:03
#5 fxbnl123回复
666

别光顾着扣6  点个赞呗~

0
#5fxbnl123回复于2020-10-29 22:20:57

666

0
#4189******30回复于2020-10-29 21:28:12

开始时投入点时间,弄清楚了能省好大事呢~~

0
#3鹿鼎记肯定回复于2020-10-28 22:49:19
#2 189******30回复
偷懒竟然都要这么多规矩~~

不要偷懒,跟我一起深入理解~

0
#2189******30回复于2020-10-28 21:53:06

偷懒竟然都要这么多规矩~~

0
TOP
切换版块