大多数面向数组的语言(如APL或J)都有某种形式的广义内积,它可以像标准矩阵乘法一样,但支持任意运算来代替标准运算。例如,在J +/ . *
中是标准乘法 - 然后和,但您也可以例如<./ . +
获取add-then-min操作(比如通过图表逐步更新最短路径的长度)。
在慢速和仅2D的Python中,这将是:
import numpy as np
def general_inner(f, g, x, y):
return np.array([[f(g(x1, y1)) for y1 in y.T] for x1 in x])
x = np.arange(1, 5, dtype="float").reshape((2, 2))
y = np.array([[0.9], [0.1]])
assert(x.dot(y) == general_inner(np.sum, np.multiply, x, y))
numpy
是否提供了直接支持此模式的内容?
答案 0 :(得分:4)
你可以通过切片到达那里。我们可以重新构造这两个参数,以便操作将被广播而不是按元素执行,然后沿着不需要的轴执行缩减操作。
import numpy
a = numpy.array([[1, 2, 3],
[4, 5, 6]])
b = numpy.array([[7, 8],
[9, 10],
[11, 12]])
# Ordinary matrix multiplication
print(a @ b)
# Matrix multiplication using broadcasting
print(numpy.sum(a[:,:,numpy.newaxis] * b[numpy.newaxis,:,:], axis=1))
# Our "generalized" version
print(numpy.min(a[:,:,numpy.newaxis] + b[numpy.newaxis,:,:], axis=1))
我会毫不犹豫地称之为“广义内在产品”,因为内部产品具有这个新版本所缺乏的特定数学结构。
这基本上起作用的方式是任何numpy.newaxis
的长度为1并得到广播,所以:
a[:,:,numpy.newaxis] * b[numpy.newaxis,:,:]
给我们:
result[i,j,k] = a[i,j] * b[j,k]
或者,如果它有助于你理解(我发现有时候广播有点令人困惑),
aa = a[:,:,numpy.newaxis]
bb = b[numpy.newaxis,:,:]
result[i,j,k] = aa[i,j,0] * bb[0,j,k]
答案 1 :(得分:4)
慢速numpy
等效为g(x,y.T)
,利用广播,然后是f(..., axis=1)
。
In [136]: general_inner(np.sum, np.multiply, x, y)
Out[136]:
array([[ 1.1],
[ 3.1]])
In [137]: np.multiply(x,y.T)
Out[137]:
array([[ 0.9, 0.2],
[ 2.7, 0.4]])
In [138]: np.sum(np.multiply(x,y.T),axis=1)
Out[138]: array([ 1.1, 3.1])
类似于最大值:
In [145]: general_inner(np.max, np.add, x, y)
Out[145]:
array([[ 2.1],
[ 4.1]])
In [146]: np.max(np.add(x,y.T), axis=1)
Out[146]: array([ 2.1, 4.1])
np.add
,np.multiply
,np.maximum
ufunc
为np.sum
,np.prod
,np.max
,{ {1}}不是,但请使用axis
参数和keepdims
。 (编辑:np.add.reduce
是ufunc
相当于np.sum
。)
In [152]: np.max(np.add(x,y.T), axis=1, keepdims=True)
Out[152]:
array([[ 2.1],
[ 4.1]])
在np.einsum
中有一个旧的增强请求来实现这种事情。这实现了sum of products
计算,对索引具有高度的控制。因此,从概念上讲,它可以使用相同的索引控件执行max of sums
。但据我所知,没有人试图实施它。
这种普遍的内在产品是APL的一个可爱的特征(几十年前它是我的主要语言)。但显然没那么有用,它从这一系列语言迁移出来。 MATLAB没有类似的东西。
有什么APL&amp; J可以使用这种结构,这可以通过我们演示过的广播在numpy
中完成吗?
使用更常规的x
和y
形状,我需要添加其他答案中给出的newaxis
In [176]: x = np.arange(3*4).reshape(4,3)
In [177]: y = np.arange(3*2).reshape(3,2)
In [178]: np.sum(np.multiply(x[...,None],y[None,...]),axis=1)
Out[178]:
array([[10, 13],
[28, 40],
[46, 67],
[64, 94]])
In [179]: np.max(np.add(x[...,None],y[None,...]),axis=1)
Out[179]:
array([[ 6, 7],
[ 9, 10],
[12, 13],
[15, 16]])
概括为3d,同时使用matmul
的最后一个dim / 2nd last的matmul
想法:
In [195]: x = np.arange(2*4*5).reshape(2,4,5)
In [196]: y = np.arange(2*5*3).reshape(2,5,3)
In [197]: np.einsum('ijk,ikm->ijm', x, y).shape
Out[197]: (2, 4, 3)
In [203]: np.add.reduce(np.multiply(x[...,None], y[...,None,:,:]), axis=-2).shape
Out[203]: (2, 4, 3)
# shapes broadcast: (2,4,5,n) * (2,n,5,3) => (2,4,5,3); sum on the 5
因此虽然numpy
(和MATLAB)没有像APL
这样的特殊语法,但是扩展(outter)操作然后减少的想法已经足够了。
测试其他ufunc
:
In [205]: np.maximum.reduce(np.add(x[...,None], y[...,None,:,:]), axis=-2).shape
Out[205]: (2, 4, 3)
In [208]: np.logical_or.reduce(np.greater(x[...,None], y[...,None,:,:]), axis=-2).shape
Out[208]: (2, 4, 3)