二元操作中的broadcast操作
Posted deepllz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二元操作中的broadcast操作相关的知识,希望对你有一定的参考价值。
1.tensorflow有一种kernels名为BinaryOp(二元操作),像加法、减法、哈达玛积这种都属于二元操作(都是简单的数学操作),所有的二元操作逻辑都是相同的,唯独中间的运算符不同,在实现上将这个操作符作为模板参数传入,本质也是调用的Eigen的API(但是像矩阵乘这样的感觉也是二元操作,但是实现上却不同与二元操作,主要是调用了不同的Eigen API).
二元操作要求两个参数有相同的维度,比如矩阵相加、相减. 但是深度学习中有一种典型的应用:梯度更新的时候,以SGD为例,w-grad_w*lr,其中w一般都是多维的,而lr都是常数,相当于一个常数乘以一个多维张量. 这个操作也属于二元操作,看似很简单,但是却需要二元操作有足够的支持,这种维数不匹配的解决方法称为broadcast,简单理解就是维数扩展.
tensorflow在实现broadcast的时候,借鉴了numpy的broadcast方法,也不是借鉴,就是直接拿过来用了. 其broadcast的实现文件在core/util/bcast.h中.
broadcast的原理很简单,就是维数不匹配的时候,将两个张量的其中之一或者两个都进行维数的扩展,举个例子:
a = [2 3 4] b = 2 c = a*b等价于c = [2 3 4]*[2 2 2]
也就是说看似a是和2相乘,实际上是和[2 2 2]相乘的,也就是说要事先对b进行维数扩展,扩展成和a相同维数才能进行哈达玛积的操作 .
注:在具体实现扩展的时候,绝不是将2变为[2 2 2],这样浪费内存效率差,实际在内存中还是只有一份2存在,只不过我像上面那样讲可以方便理解. 像tensorflow在实现broadcast的时候调用了Eigen的broadcast方法,其内部实现用到了线程池或者cuda实现并发.
a = [[0 1 2]] b = a‘ c = a*a‘ 0 1 2 0 0 0 0 0 0 等价于 c = 0 1 2 * 1 1 1 = 0 1 2 0 1 2 2 2 2 0 2 4
正常来说a的维数是[1,3],b的维数是[3,1],二者是不能做*操作的. 但是由于broadcast的存在,先将两个输入都变成了[3,3]维度的,二者就可以运算了.
具体的broadcast规则可以参考https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html.
其核心主要有两点:
①后缀匹配:对两个输入的维数从后向前匹配,二者若兼容,就对其中一个输入的该维度进行broadcast,不兼容就表示二者不能做二元操作.
②什么情况算作维数兼容:维数相等或者其中一个是1.并且如果其中一个是1,那么最终的输出在该维度上的值就是另一个输入的维度.
以下是官方的例子:
A (2d array): 5 x 4 B (1d array): 1 Result (2d array): 5 x 4 A (2d array): 5 x 4 B (1d array): 4 Result (2d array): 5 x 4 A (3d array): 15 x 3 x 5 B (3d array): 15 x 1 x 5 Result (3d array): 15 x 3 x 5 A (3d array): 15 x 3 x 5 B (2d array): 3 x 5 Result (3d array): 15 x 3 x 5 A (3d array): 15 x 3 x 5 B (2d array): 3 x 1 Result (3d array): 15 x 3 x 5
不兼容的例子:
A (1d array): 3 B (1d array): 4 # trailing dimensions do not match A (2d array): 2 x 1 B (3d array): 8 x 4 x 3 # second from last dimensions mismatched
以上是关于二元操作中的broadcast操作的主要内容,如果未能解决你的问题,请参考以下文章
NSError 代码检查:二元运算符“==”不能应用于两个 Int 操作数
二元运算符“==”不能应用于“字符串?”类型的操作数和 Swift 3 中的“布尔”