Calculation/manipulation 个 numpy 数组
Calculation/manipulation of numpy array
希望尽快进行此计算。我有 X 作为 n x m numpy 数组。我想将 Y 定义为以下内容:
Y_11 = 1 / (exp(X_11-X_11) + exp(X_11-X_12) + ... exp(X_11 - X_1N) ).
或 Y_00
1/np.sum(np.exp(X[0,0]-X[0,:]))
所以基本上,Y 也是 n x m,其中 i,j 元素是 1 / sum_j' exp(X_ij - X_ij')
任何提示都会很棒!谢谢
请求的示例代码:
np.random.seed(111)
J,K = 110,120
X = np.random.rand(J,K)
Y = np.zeros((J,K))
for j in range(J):
for k in range(K):
Y[j,k] = 1/np.sum(np.exp(X[j,k]-X[j,:]))
# note each row will sum to 1 under this operation
np.sum(Y,axis=1)
这是减少双循环的第一步:
def foo2(X):
Y = np.zeros_like(X)
for k in range(X.shape[1]):
Y[:,k]=1/np.exp(X[:,[k]]-X[:,:]).sum(axis=1)
return Y
我想我也可以删除 k
循环,但我必须花更多时间弄清楚它在做什么。 X[:,[k]]-X[:,:]
并不明显(在大图中)。
另一个步骤:
Z = np.stack([X[:,[k]]-X for k in range(X.shape[1])],2)
Y = 1/np.exp(Z).sum(axis=1)
可以使用
进一步完善(经过反复试验)
Z = X[:,None,:]-X[:,:,None]
这里有一些完全矢量化的方法 -
def vectorized_app1(X):
return 1/np.exp(X[:,None] - X[...,None]).sum(1)
def vectorized_app2(X):
exp_vals = np.exp(X)
return 1/(exp_vals[:,None]/exp_vals[...,None]).sum(1)
def vectorized_app3(X):
exp_vals = np.exp(X)
return 1/np.einsum('ij,ik->ij',exp_vals,1/exp_vals)
使用的技巧和吸取的教训
使用 None/np.newaxis
扩展维度以引入广播并以矢量化方式执行所有操作。
np.exp
是一个昂贵的操作。因此,在广播的巨大阵列上使用它会很昂贵。所以,使用的技巧是:exp(A-B) = exp(A)/exp(B)
。因此,我们预先执行np.exp(X)
,然后执行广播划分。
可以使用 np.einsum
实现这些总和减少。这带来了内存效率,因为我们不必创建巨大的广播数组,这会极大地提升性能。
运行时测试 -
In [111]: # Setup input, X
...: np.random.seed(111)
...: J,K = 110,120
...: X = np.random.rand(J,K)
...:
In [112]: %timeit original_approach(X)
1 loop, best of 3: 322 ms per loop
In [113]: %timeit vectorized_app1(X)
10 loops, best of 3: 124 ms per loop
In [114]: %timeit vectorized_app2(X)
100 loops, best of 3: 14.6 ms per loop
In [115]: %timeit vectorized_app3(X)
100 loops, best of 3: 3.01 ms per loop
看起来 einsum
再次展示了它的魔力 100x+
加速!
希望尽快进行此计算。我有 X 作为 n x m numpy 数组。我想将 Y 定义为以下内容:
Y_11 = 1 / (exp(X_11-X_11) + exp(X_11-X_12) + ... exp(X_11 - X_1N) ).
或 Y_00
1/np.sum(np.exp(X[0,0]-X[0,:]))
所以基本上,Y 也是 n x m,其中 i,j 元素是 1 / sum_j' exp(X_ij - X_ij')
任何提示都会很棒!谢谢
请求的示例代码:
np.random.seed(111)
J,K = 110,120
X = np.random.rand(J,K)
Y = np.zeros((J,K))
for j in range(J):
for k in range(K):
Y[j,k] = 1/np.sum(np.exp(X[j,k]-X[j,:]))
# note each row will sum to 1 under this operation
np.sum(Y,axis=1)
这是减少双循环的第一步:
def foo2(X):
Y = np.zeros_like(X)
for k in range(X.shape[1]):
Y[:,k]=1/np.exp(X[:,[k]]-X[:,:]).sum(axis=1)
return Y
我想我也可以删除 k
循环,但我必须花更多时间弄清楚它在做什么。 X[:,[k]]-X[:,:]
并不明显(在大图中)。
另一个步骤:
Z = np.stack([X[:,[k]]-X for k in range(X.shape[1])],2)
Y = 1/np.exp(Z).sum(axis=1)
可以使用
进一步完善(经过反复试验)Z = X[:,None,:]-X[:,:,None]
这里有一些完全矢量化的方法 -
def vectorized_app1(X):
return 1/np.exp(X[:,None] - X[...,None]).sum(1)
def vectorized_app2(X):
exp_vals = np.exp(X)
return 1/(exp_vals[:,None]/exp_vals[...,None]).sum(1)
def vectorized_app3(X):
exp_vals = np.exp(X)
return 1/np.einsum('ij,ik->ij',exp_vals,1/exp_vals)
使用的技巧和吸取的教训
使用
None/np.newaxis
扩展维度以引入广播并以矢量化方式执行所有操作。np.exp
是一个昂贵的操作。因此,在广播的巨大阵列上使用它会很昂贵。所以,使用的技巧是:exp(A-B) = exp(A)/exp(B)
。因此,我们预先执行np.exp(X)
,然后执行广播划分。可以使用
np.einsum
实现这些总和减少。这带来了内存效率,因为我们不必创建巨大的广播数组,这会极大地提升性能。
运行时测试 -
In [111]: # Setup input, X
...: np.random.seed(111)
...: J,K = 110,120
...: X = np.random.rand(J,K)
...:
In [112]: %timeit original_approach(X)
1 loop, best of 3: 322 ms per loop
In [113]: %timeit vectorized_app1(X)
10 loops, best of 3: 124 ms per loop
In [114]: %timeit vectorized_app2(X)
100 loops, best of 3: 14.6 ms per loop
In [115]: %timeit vectorized_app3(X)
100 loops, best of 3: 3.01 ms per loop
看起来 einsum
再次展示了它的魔力 100x+
加速!