更改多行数组的numpy矢量化方式(行可以重复)
numpy vectorized way to change multiple rows of array(rows can be repeated)
我 运行 在为 cs231n assignment1 实现矢量化 svm 梯度时遇到了这个问题。
这是一个例子:
ary = np.array([[1,-9,0],
[1,2,3],
[0,0,0]])
ary[[0,1]] += np.ones((2,2),dtype='int')
它输出:
array([[ 2, -8, 1],
[ 2, 3, 4],
[ 0, 0, 0]])
一切都很好,直到行不是唯一的:
ary[[0,1,1]] += np.ones((3,3),dtype='int')
虽然没有报错,但是输出的结果真的很奇怪:
array([[ 2, -8, 1],
[ 2, 3, 4],
[ 0, 0, 0]])
我希望第二行应该是 [3,4,5] 而不是 [2,3,4],
我用来解决这个问题的天真的方法是使用这样的 for 循环:
ary = np.array([[ 2, -8, 1],
[ 2, 3, 4],
[ 0, 0, 0]])
# the rows I want to change
rows = [0,1,2,1,0,1]
# the change matrix
change = np.random.randn((6,3))
for i,row in enumerate(rows):
ary[row] += change[i]
所以我真的不知道如何向量化这个 for 循环,在 NumPy 中有更好的方法吗?
为什么这样做是错误的?:
ary[rows] += change
如果有人好奇我为什么要这样做,这是我对 svm_loss_vectorized 函数的实现,我需要根据标签 y:
计算权重的梯度
def svm_loss_vectorized(W, X, y, reg):
"""
Structured SVM loss function, vectorized implementation.
Inputs and outputs are the same as svm_loss_naive.
"""
loss = 0.0
dW = np.zeros(W.shape) # initialize the gradient as zero
# transpose X and W
# D means input dimensions, N means number of train example
# C means number of classes
# X.shape will be (D,N)
# W.shape will be (C,D)
X = X.T
W = W.T
dW = dW.T
num_train = X.shape[1]
# transpose W_y shape to (D,N)
W_y = W[y].T
S_y = np.sum(W_y*X ,axis=0)
margins = np.dot(W,X) + 1 - S_y
mask = np.array(margins>0)
# get the impact of num_train examples made on W's gradient
# that is,only when the mask is positive
# the train example has impact on W's gradient
dW_j = np.dot(mask, X.T)
dW += dW_j
mul_mask = np.sum(mask, axis=0, keepdims=True).T
# dW[y] -= mul_mask * X.T
dW_y = mul_mask * X.T
for i,label in enumerate(y):
dW[label] -= dW_y[i]
loss = np.sum(margins*mask) - num_train
loss /= num_train
dW /= num_train
# add regularization term
loss += reg * np.sum(W*W)
dW += reg * 2 * W
dW = dW.T
return loss, dW
使用内置 np.add.at
此类任务的内置是np.add.at
,即
np.add.at(ary, rows, change)
但是,由于我们使用的是 2D
数组,这可能不是性能最高的数组。
杠杆速度快matrix-multiplication
事实证明,对于这种情况,我们也可以利用非常高效的 matrix-multplication
,并且给定足够数量的重复行进行求和,这可能非常好。下面是我们如何使用它 -
mask = rows == np.arange(len(ary))[:,None]
ary += mask.dot(change)
基准测试
让我们用 np.add.at
方法来对抗基于 matrix-multiplication
的更大数组的方法 -
In [681]: ary = np.random.rand(1000,1000)
In [682]: rows = np.random.randint(0,len(ary),(10000))
In [683]: change = np.random.rand(10000,1000)
In [684]: %timeit np.add.at(ary, rows, change)
1 loop, best of 3: 604 ms per loop
In [687]: def matmul_addat(ary, rows, change):
...: mask = rows == np.arange(len(ary))[:,None]
...: ary += mask.dot(change)
In [688]: %timeit matmul_addat(ary, rows, change)
10 loops, best of 3: 158 ms per loop
我 运行 在为 cs231n assignment1 实现矢量化 svm 梯度时遇到了这个问题。 这是一个例子:
ary = np.array([[1,-9,0],
[1,2,3],
[0,0,0]])
ary[[0,1]] += np.ones((2,2),dtype='int')
它输出:
array([[ 2, -8, 1],
[ 2, 3, 4],
[ 0, 0, 0]])
一切都很好,直到行不是唯一的:
ary[[0,1,1]] += np.ones((3,3),dtype='int')
虽然没有报错,但是输出的结果真的很奇怪:
array([[ 2, -8, 1],
[ 2, 3, 4],
[ 0, 0, 0]])
我希望第二行应该是 [3,4,5] 而不是 [2,3,4], 我用来解决这个问题的天真的方法是使用这样的 for 循环:
ary = np.array([[ 2, -8, 1],
[ 2, 3, 4],
[ 0, 0, 0]])
# the rows I want to change
rows = [0,1,2,1,0,1]
# the change matrix
change = np.random.randn((6,3))
for i,row in enumerate(rows):
ary[row] += change[i]
所以我真的不知道如何向量化这个 for 循环,在 NumPy 中有更好的方法吗? 为什么这样做是错误的?:
ary[rows] += change
如果有人好奇我为什么要这样做,这是我对 svm_loss_vectorized 函数的实现,我需要根据标签 y:
计算权重的梯度def svm_loss_vectorized(W, X, y, reg):
"""
Structured SVM loss function, vectorized implementation.
Inputs and outputs are the same as svm_loss_naive.
"""
loss = 0.0
dW = np.zeros(W.shape) # initialize the gradient as zero
# transpose X and W
# D means input dimensions, N means number of train example
# C means number of classes
# X.shape will be (D,N)
# W.shape will be (C,D)
X = X.T
W = W.T
dW = dW.T
num_train = X.shape[1]
# transpose W_y shape to (D,N)
W_y = W[y].T
S_y = np.sum(W_y*X ,axis=0)
margins = np.dot(W,X) + 1 - S_y
mask = np.array(margins>0)
# get the impact of num_train examples made on W's gradient
# that is,only when the mask is positive
# the train example has impact on W's gradient
dW_j = np.dot(mask, X.T)
dW += dW_j
mul_mask = np.sum(mask, axis=0, keepdims=True).T
# dW[y] -= mul_mask * X.T
dW_y = mul_mask * X.T
for i,label in enumerate(y):
dW[label] -= dW_y[i]
loss = np.sum(margins*mask) - num_train
loss /= num_train
dW /= num_train
# add regularization term
loss += reg * np.sum(W*W)
dW += reg * 2 * W
dW = dW.T
return loss, dW
使用内置 np.add.at
此类任务的内置是np.add.at
,即
np.add.at(ary, rows, change)
但是,由于我们使用的是 2D
数组,这可能不是性能最高的数组。
杠杆速度快matrix-multiplication
事实证明,对于这种情况,我们也可以利用非常高效的 matrix-multplication
,并且给定足够数量的重复行进行求和,这可能非常好。下面是我们如何使用它 -
mask = rows == np.arange(len(ary))[:,None]
ary += mask.dot(change)
基准测试
让我们用 np.add.at
方法来对抗基于 matrix-multiplication
的更大数组的方法 -
In [681]: ary = np.random.rand(1000,1000)
In [682]: rows = np.random.randint(0,len(ary),(10000))
In [683]: change = np.random.rand(10000,1000)
In [684]: %timeit np.add.at(ary, rows, change)
1 loop, best of 3: 604 ms per loop
In [687]: def matmul_addat(ary, rows, change):
...: mask = rows == np.arange(len(ary))[:,None]
...: ary += mask.dot(change)
In [688]: %timeit matmul_addat(ary, rows, change)
10 loops, best of 3: 158 ms per loop