【问题标题】:Calculation/manipulation of numpy arraynumpy数组的计算/操作
【发布时间】:2016-09-19 17:35:25
【问题描述】:

希望尽快进行此计算。我有 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)

【问题讨论】:

  • exp 调用在您的第二个表达式中去了哪里?
  • 感谢您发现我放弃了 exp()。我还添加了一些代码。
  • 这是一个很好的问题。将来,如果你能像现在这样提前写东西,那就太好了。

标签: python performance numpy sum numpy-einsum


【解决方案1】:

这里有一些完全矢量化的方法 -

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+ 加速展示了它的魔力!

【讨论】:

  • 非常有用的答案。感谢所有的解释。
  • 谢谢!然而,一个问题是有时 A 或 B 的值很大,所以我得到一个错误。我对使用 exp(A-B) 的想法是我缩小了很多,可以放入 np.min(A-B, 200) 或类似内容以避免错误。还有其他想法吗?
  • @Kevin 究竟是什么错误?它说什么?
  • exp 中溢出。我刚刚发现了 bigfloat,但需要花时间看看如何用我的代码实现——看看它是否适用于 knitro。
  • @Kevin Hmm 或者我猜你可以在这些情况下坚持使用vectorized_app1
【解决方案2】:

这是减少双循环的第一步:

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]

【讨论】:

  • 很好的答案。非常小的评论:在你最后的括号中,你的意思是“没有太多的试验和错误”。
  • 不,我尝试了许多替代方案。有多种方法可以将X 扩展为 3d。以及可以相加的3个轴。我没有做到系统化。
猜你喜欢
  • 2016-06-06
  • 2014-09-21
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-21
  • 2018-07-25
相关资源
最近更新 更多