此功能的完美 numpy 实现
Perfect numpy implementation for this function
这似乎更像是一个直接的问题。我会在最后概括一下。
我正在尝试在 numpy 中使用此功能。我已经成功地使用了嵌套的 for 循环,但我想不出一个 numpy 的方法来做到这一点。
我的实现方式:
bs = 10 # batch_size
nb = 8 # number of bounding boxes
nc = 15 # number of classes
bbox = np.random.random(size=(bs, nb, 4)) # model output bounding boxes
p = np.random.random(size=(bs, nb, nc)) # model output probability
p = softmax(p, axis=-1)
s_rand = np.random.random(size=(nc, nc))
s = (s_rand + s_rand.T)/2 # similarity matrix
pp = np.random.random(size=(bs, nb, nc)) # proposed probability
pp = softmax(pp, axis=-1)
first_term = 0
for b in range(nb):
for b_1 in range(nb):
if b_1 == b:
continue
for l in range(nc):
for l_1 in range(nc):
first_term += (s[l, l_1] * (pp[:, b, l] - pp[:, b_1, l_1])**2)
second_term = 0
for b in range(nb):
for l in range(nc):
second_term += (np.linalg.norm(s[l, :], ord=1) * (pp[:, b, l] - p[:, b, l])**2)
second_term *= nb
epsilon = 0.5
output = ((1 - epsilon) * first_term) + (epsilon * second_term)
我已经努力去除循环并使用 np.tile
和 np.repeat
来代替,以完成任务。但是想不出可行的办法。
我曾尝试搜索 google 以找到类似的练习,这些练习可以帮助我在 numpy 中学习此类转换,但没有成功。
我认为你可以用 einsum
first_term = np.einsum('km, ijklm -> i', s, (pp[..., None, None] - pp[:, None, None, ...])**2 )
second_term = np.einsum('k, ijk -> i', np.linalg.norm(s, axis = 1), (pp - p)**2 )
现在有一个问题:如果 nb
和 nc
变大,first_term
中的 ijklm
张量会变大。您可能应该分发它,以便获得 3 个较小的张量:
first_term = np.einsum('km, ijk, ijk -> i', s, pp, pp) +\
np.einsum('km, ilm, ilm -> i', s, pp, pp) -\
2 * np.einsum('km, ijk, ilm -> i', s, pp, pp)
这利用了 (a-b)**2 = a**2 + b**2 - 2ab
这一事实,允许您将问题分成三个部分,每个部分都可以用点积一步完成
最大优化代码:(删除前两个循环的灵感来自 L.Iridium 的回答)
squared_diff = (pp[:, :, None, :, None] - pp[:, None, :, None, :]) ** 2
weighted_diff = s * squared_diff
b_eq_b_1_removed = b.sum(axis=(3,4)) * (1 - np.eye(nb))
first_term = b_eq_b_1_removed.sum(axis=(1,2))
normalized_s = np.linalg.norm(s, ord=1, axis=1)
squared_diff = (pp - p)**2
second_term = nb * (normalized_s * squared_diff).sum(axis=(1,2))
loss = ((1 - epsilon) * first_term) + (epsilon * second_term)
时间轨道:
512 µs ± 13 µs per loop
问题代码的时间跟踪:
62.5 ms ± 197 µs per loop
这是一个巨大的进步。
P_hat.shape
是(B,L),S.shape
是(L,L),P.shape
是(B,L)。
array_before_sum = S[None,:,None,:]*(P_hat[:,:,None,None]- P_hat[None,None,:,:])**2
array_after_sum = array_before_sum.sum(axis=(1,3))
array_sum_again = (array_after_sum*(1-np.ones((B,B)))).sum()
first_term = (1-epsilon)*array_sum_again
second_term = epsilon*(B*np.abs(S).sum(axis=1)[None,:]*(P_hat - P)**2).sum()
这似乎更像是一个直接的问题。我会在最后概括一下。
我正在尝试在 numpy 中使用此功能。我已经成功地使用了嵌套的 for 循环,但我想不出一个 numpy 的方法来做到这一点。
我的实现方式:
bs = 10 # batch_size
nb = 8 # number of bounding boxes
nc = 15 # number of classes
bbox = np.random.random(size=(bs, nb, 4)) # model output bounding boxes
p = np.random.random(size=(bs, nb, nc)) # model output probability
p = softmax(p, axis=-1)
s_rand = np.random.random(size=(nc, nc))
s = (s_rand + s_rand.T)/2 # similarity matrix
pp = np.random.random(size=(bs, nb, nc)) # proposed probability
pp = softmax(pp, axis=-1)
first_term = 0
for b in range(nb):
for b_1 in range(nb):
if b_1 == b:
continue
for l in range(nc):
for l_1 in range(nc):
first_term += (s[l, l_1] * (pp[:, b, l] - pp[:, b_1, l_1])**2)
second_term = 0
for b in range(nb):
for l in range(nc):
second_term += (np.linalg.norm(s[l, :], ord=1) * (pp[:, b, l] - p[:, b, l])**2)
second_term *= nb
epsilon = 0.5
output = ((1 - epsilon) * first_term) + (epsilon * second_term)
我已经努力去除循环并使用 np.tile
和 np.repeat
来代替,以完成任务。但是想不出可行的办法。
我曾尝试搜索 google 以找到类似的练习,这些练习可以帮助我在 numpy 中学习此类转换,但没有成功。
我认为你可以用 einsum
first_term = np.einsum('km, ijklm -> i', s, (pp[..., None, None] - pp[:, None, None, ...])**2 )
second_term = np.einsum('k, ijk -> i', np.linalg.norm(s, axis = 1), (pp - p)**2 )
现在有一个问题:如果 nb
和 nc
变大,first_term
中的 ijklm
张量会变大。您可能应该分发它,以便获得 3 个较小的张量:
first_term = np.einsum('km, ijk, ijk -> i', s, pp, pp) +\
np.einsum('km, ilm, ilm -> i', s, pp, pp) -\
2 * np.einsum('km, ijk, ilm -> i', s, pp, pp)
这利用了 (a-b)**2 = a**2 + b**2 - 2ab
这一事实,允许您将问题分成三个部分,每个部分都可以用点积一步完成
最大优化代码:(删除前两个循环的灵感来自 L.Iridium 的回答)
squared_diff = (pp[:, :, None, :, None] - pp[:, None, :, None, :]) ** 2
weighted_diff = s * squared_diff
b_eq_b_1_removed = b.sum(axis=(3,4)) * (1 - np.eye(nb))
first_term = b_eq_b_1_removed.sum(axis=(1,2))
normalized_s = np.linalg.norm(s, ord=1, axis=1)
squared_diff = (pp - p)**2
second_term = nb * (normalized_s * squared_diff).sum(axis=(1,2))
loss = ((1 - epsilon) * first_term) + (epsilon * second_term)
时间轨道:
512 µs ± 13 µs per loop
问题代码的时间跟踪:
62.5 ms ± 197 µs per loop
这是一个巨大的进步。
P_hat.shape
是(B,L),S.shape
是(L,L),P.shape
是(B,L)。
array_before_sum = S[None,:,None,:]*(P_hat[:,:,None,None]- P_hat[None,None,:,:])**2
array_after_sum = array_before_sum.sum(axis=(1,3))
array_sum_again = (array_after_sum*(1-np.ones((B,B)))).sum()
first_term = (1-epsilon)*array_sum_again
second_term = epsilon*(B*np.abs(S).sum(axis=1)[None,:]*(P_hat - P)**2).sum()