8.4 广播机制
广播机制是Numpy让两个不同shape的数组能够做一些运算,需要对参与运算的两个数组做一些处理或者说扩展,最终是参与运算的两个数组的shape一样,然后广播计算(对应位置数据进行某运算)得到结果。
广播时需要对两个数组做广播机制处理,不是所有情况下两个数组都能进行广播机制的处理,有要求即两数组需满足广播兼容,需判断两个数组能否进行广播机制处理成同型shape的数组然后广播。
广播机制首先需要判断参与计算的两个数组能否被广播机制处理?即判断是否广播兼容,规则是,比较两个数组的shape,从shape的尾部开始一一比对。
(1). 如果两个数组的维度相同,对应位置上轴的长度相同或其中一个的轴长度为1,广播兼容,可在轴长度为1的轴上进行广播机制处理。
(2). 如果两个数组的维度不同,那么给低维度的数组前扩展提升一维,扩展维的轴长度为1,然后在扩展出的维上进行广播机制处理。
举例说明:
from numpy import *
import numpy as np
a = np.arange(1, 16).reshape([3, 5])
print a
print a.shape, a.ndim
b = array([2, 3, 4, 5, 6])
print b
print b.shape, b.ndim
print a + b
这里a数组的维度是2即二维数组(3, 5),b是一维数组(5,),判断一下是否广播兼容?b的最后一维的轴长度为5、a的最后一维的轴长度5,满足从后向前开始对应轴的轴长度相同规则。两个数组的维度不同一个是2维一个是1维,那么对b前扩展增加维度变为(1, 5)成为2维数组,此时a和b都是二维数组,维度相同。增维后的b的倒数第二维的长度是1,a倒数第二维轴长度为3,满足维度相同某轴长度为1的兼容规则,继续广播机制,将b倒数第二维轴长度继续加1直至变为和a数组倒数第二轴轴长度相同,这是b的shape经过不断的调整(广播机制)变为(3, 5),和a的shape相同,就可以广播计算了。每次对b倒数第二轴轴长度加1实际是拷贝增加一行。
8.4.1 数组b广播机制变化过程
1).原始b数组(5,)
[2 3 4 5 6]
2). b增加一维(1, 5)变为2维数组
[[2 3 4 5 6]]
3).b扩展变为(2, 5)
[[2 3 4 5 6],
[2 3 4 5 6]]
4).b扩展变为(3, 5),这是b的shape和a的shape一致。
[[2 3 4 5 6],
[2 3 4 5 6],
[2 3 4 5 6]]
从过程可以看出,广播机制在将两数组变成维度和shape相同时,对实施广播机制的数组的处理是拷贝赋值未广播处理的轴的数据。
8.4.2 语句2 * a的广播机制过程
程序如下:
from numpy import *
import numpy as np
a = np.arange(0,10)
print a
print a.shape, a.ndim
a = a.reshape([2, 5])
print a
print a.shape, a.ndim
print 2 * a
这里a的shape是(2, 5),而语句2 * a
参与计算的一个是数一个是数组,数可以看出(0,)数组,那么语句的广播机制处理过程如下:
1). 数2先变成数组,shape为(1,)
[2]
2). [2]
和a数组的广播兼容判断,[2]
的shape是(1,),a的shape是(2, 5),维度不同一个是一维的数组,一个是二维的数组。
3). [2]
变化成[2 2 2 2 2]
先保证两个数组最后一维上的长度相等,那么[2]
变化为[2 2 2 2 2]
,这时的shape为(5,),好了此时参与运算的两数组最后一轴上的长度相同了。
4). 倒数第二轴判断是否广播兼容?[2 2 2 2 2]
的shape是(5,)而a的shape是(2, 5),两个数组维度不同,好对[2 2 2 2 2]
进行前扩展增维变为(1, 5)即
[[2 2 2 2 2]]
[[2 2 2 2 2]]
的shape是(1, 5)而a的shape是(2, 5),此时对[[2 2 2 2 2]]
广播扩展变为:
[[2 2 2 2 2],[2 2 2 2 2]]
[[2 2 2 2 2],[2 2 2 2 2]]
的shape变为(2, 5)和a同型的的数组,就可以进行广播计算了,得到结果。
[[ 0 2 4 6 8]
[10 12 14 16 18]] # 2 * a
8.5 较复杂的广播示例
之前的广播机制处理示例较为简单,只在一个数组的一个维度上进行了广播机制处理,下面看一个例子,两个数组都被广播机制处理了。
from numpy import *
import numpy as np
a = np.arange(1, 5)
b = np.arange(4, 7).reshape((3, 1))
print a
print b
print a + b
程序执行结果:
[1 2 3 4]
[[4]
[5]
[6]]
[[ 5 6 7 8]
[ 6 7 8 9]
[ 7 8 9 10]]
a是[1 2 3 4]
被广播成了
[[1 2 3 4],
[1 2 3 4],
[1 2 3 4]]
b原始为[[4] [ 5] [6]]
被广播成
[[4 4 4 4],
[5 5 5 5],
[6 6 6 6]]
8.5.1 b的变化过程
b变化,首先要和a做广播机制兼容判断,a的shape为(4,)b的shape为(3, 1),从最后向前逐一比对各个轴长度是否相对或为1。a和b是不同维度,a的最后一维的轴长度为4,而b最后一维轴长度为1。那么b最后一维轴长度需要从1变成4。
1). b原始(3, 1)
[[4] [ 5] [6]]
2). b变为(3, 4)记作b‘
[[4 4 4 4],
[5 5 5 5],
[6 6 6 6]]
广播机制处理后的b的shape为(3, 4),其最后一维的轴长度4和a的最后一维的轴长度4相等,那就要比对a和b倒数第二维的轴长度是否相对或者为1,缺失前扩展增维。
8.5.2 a的变化过程
此时a还是(4,)a是一维数组,和变化后的b倒数最后一维的轴长度相等,b数组是二维,a是一维,倒数第一维已经满足广播机制兼容条件,可以对a进行前扩展增维变为(1, 4),然后不断的广播机制处理a看最后a能否和b实现广播计算。
1). a原始的shape(4,)
[1 2 3 4]
2). 前增维扩展变为(1, 4)
[[1 2 3 4]]
此时的a倒数第一维的轴长度和变化后b即(3, 4)最后一维的轴长度均是4相等。倒数第二维轴长度a的是1,b的是3,满足兼容规则,那么把变化后的a的倒数第二维进行广播机制处理使其倒数第二维的轴长度变化成和变化后b倒数第二维的轴长度相等。
3). a倒数第二维轴长度继续变化成(3, 4),记作a'
[[1 2 3 4],
[1 2 3 4],
[1 2 3 4]]
这是的a的shape为(3, 4)和变化后的b的shape一致,那么此时便可对变化后的a'和b'做加法广播计算了,得到结果:
[[4 4 4 4] + [[1 2 3 4] = [[5 6 7 8]
[5 5 5 5] [1 2 3 4] [6 7 8 9]
[6 6 6 6]] [1 2 3 4]] [7 8 9 10]]
8.6 广播不兼容示例
上边的示例都是广播能够兼容的能对两数组进行广播机制处理周后进行广播计算,现实程序里会出现两数组不能进行广播机制处理即不能广播计算的情况,下面来分析一下不兼容的情况案例,从而更好的理解广播机制。
from numpy import *
import numpy as np
a = np.arange(1, 9).reshape((2, 4))
x = np.arange(4, 6).reshape((1, 2))
b = x.reshape((2, 1))
print a, "# a"
print b, "# b"
print a + b, "# a + b"
c = x.reshape((1, 2))
print c, "# c"
print a + c, "# a + c"
这个程序有错误,在最后一行,程序执行结果如下:
[[1 2 3 4]
[5 6 7 8]] # a
[[4]
[5]] # b
[[ 5 6 7 8]
[10 11 12 13]] # a + b
[[4 5]] # c
Traceback (most recent call last):
File "t8b.py", line 11, in <module>
print a + c, "# a + c"
ValueError: operands could not be broadcast together with shapes (2,4) (1,2)
从程序执行结果可以看出a + b被执行了广播机制处理,a和b形状不同但能进行加法求和,而a + c报错异常ValueError: operands could not be broadcast together with shapes (2,4) (1,2)
信息提示不能执行广播机制处理。
8.6.1 a、b满足广播兼容
这里a是二维数组,shape为(2, 4),b是二维数组,shape是(2, 1),a和b维度都是2,需要从后向前一次比较各轴的轴长度,如果相同或某轴长度为1,广播兼容可以进行广播机制处理后进行广播计算得到结果。a的最后一维的轴长度是4,而b最后一维的轴长度是1,满足兼容规则,需要将b最后一轴的轴长度从1变化到和a最后一轴的轴长度相同即b变为(2, 4),那么b变化后为:
[[4 4 4 4],
[5 5 5 5]]
这是的b和a完全同型即可广播计算求得a+b的值。
[[ 5 6 7 8]
[10 11 12 13]] # a + b
8.6.2 a、c不满足广播兼容
b和c都是从x变化而来,为何a + c
不行呢?这里c的shape是(1, 2),a的shape是(2, 4),在进行a + c
运算前需要判断两个数组是否可广播?如果广播机制兼容规则是从前向后那么a的最高维长度为4、c的最高维轴长度为1满足兼容判断规则应该可以,从程序执行结果看不行,显然这个例子证明了兼容判定规则是从最后向前一次比对轴长度判断的说法。那么好按兼容判定规则说的从后向前一次比对是否相等或有一个轴长度为1呢?c的最后一维轴长度为2而a轴后一维轴长度为4不相等,那么可以断定c和a两个数组不满足广播兼容规则,故a和c不能直接相加。
8.7 思考与寄语
一个三维数组的shape是(3, 4, 5),您能写出多少个不同shape的二维数组能和这个三维数组做广播计算?
NumPy的广播机制和重要,在机器学习、深度学习里的tensorflow和keras里使用了大量的广播计算,需深入理解广播机制。