简体   繁体   English

使用矩阵乘法用numpy计算L2距离

[英]Compute L2 distance with numpy using matrix multiplication

I'm trying to do it by myself the assignments from Stanford CS231n 2017 CNN course .我正在尝试自己完成斯坦福 CS231n 2017 CNN 课程的作业

I'm trying to compute L2 distance using only matrix multiplication and sum broadcasting with Numpy.我正在尝试仅使用矩阵乘法和 Numpy 广播求和来计算 L2 距离。 L2 distance is: L2距离为:

在此处输入图片说明

And I think I can do it if I use this formula:如果我使用这个公式,我想我可以做到:

在此处输入图片说明

The following code shows three methods to compute L2 distance.以下代码显示了计算 L2 距离的三种方法。 If I compare the output from the method compute_distances_two_loops with the output from method compute_distances_one_loop , both are equals.如果我将方法compute_distances_two_loops的输出与方法compute_distances_one_loop的输出进行compute_distances_one_loop ,则两者相等。 But I compare the output from the method compute_distances_two_loops with the output from the method compute_distances_no_loops , where I have implemented the L2 distance using only matrix multiplication and sum broadcasting, they are different.但是我将方法compute_distances_two_loops的输出与方法compute_distances_two_loops的输出进行了compute_distances_no_loops ,其中我仅使用矩阵乘法和总和广播实现了 L2 距离,它们是不同的。

def compute_distances_two_loops(self, X):
    """
Compute the distance between each test point in X and each training point
in self.X_train using a nested loop over both the training data and the 
test data.

Inputs:
- X: A numpy array of shape (num_test, D) containing test data.

Returns:
- dists: A numpy array of shape (num_test, num_train) where dists[i, j]
  is the Euclidean distance between the ith test point and the jth training
  point.
"""
    num_test = X.shape[0]
    num_train = self.X_train.shape[0]
    dists = np.zeros((num_test, num_train))
    for i in xrange(num_test):
        for j in xrange(num_train):
            #####################################################################
            # TODO:                                                             #
            # Compute the l2 distance between the ith test point and the jth    #
            # training point, and store the result in dists[i, j]. You should   #
            # not use a loop over dimension.                                    #
            #####################################################################
            #dists[i, j] = np.sqrt(np.sum((X[i, :] - self.X_train[j, :]) ** 2))
            dists[i, j] = np.sqrt(np.sum(np.square(X[i, :] - self.X_train[j, :])))
            #####################################################################
            #                       END OF YOUR CODE                            #
            #####################################################################
    return dists

def compute_distances_one_loop(self, X):
    """
Compute the distance between each test point in X and each training point
in self.X_train using a single loop over the test data.

Input / Output: Same as compute_distances_two_loops
"""
    num_test = X.shape[0]
    num_train = self.X_train.shape[0]
    dists = np.zeros((num_test, num_train))
    for i in xrange(num_test):
        #######################################################################
        # TODO:                                                               #
        # Compute the l2 distance between the ith test point and all training #
        # points, and store the result in dists[i, :].                        #
        #######################################################################
        dists[i, :] = np.sqrt(np.sum(np.square(self.X_train - X[i, :]), axis = 1))
        #######################################################################
        #                         END OF YOUR CODE                            #
        #######################################################################
    print(dists.shape)
    return dists

def compute_distances_no_loops(self, X):
    """
Compute the distance between each test point in X and each training point
in self.X_train using no explicit loops.

Input / Output: Same as compute_distances_two_loops
"""
    num_test = X.shape[0]
    num_train = self.X_train.shape[0]
    dists = np.zeros((num_test, num_train))
    #########################################################################
    # TODO:                                                                 #
    # Compute the l2 distance between all test points and all training      #
    # points without using any explicit loops, and store the result in      #
    # dists.                                                                #
    #                                                                       #
    # You should implement this function using only basic array operations; #
    # in particular you should not use functions from scipy.                #
    #                                                                       #
    # HINT: Try to formulate the l2 distance using matrix multiplication    #
    #       and two broadcast sums.                                         #
    #########################################################################
    dists = np.sqrt(-2 * np.dot(X, self.X_train.T) +
                    np.sum(np.square(self.X_train), axis=1) +
                    np.sum(np.square(X), axis=1)[:, np.newaxis])
    print(dists.shape)
    #########################################################################
    #                         END OF YOUR CODE                              #
    #########################################################################
    return dists

You can find a full working testable code here .您可以在此处找到完整的可测试代码。

Do you know what am I doing wrong in compute_distances_no_loops , or wherever?你知道我在compute_distances_no_loops或其他地方做错了什么吗?

UPDATE:更新:

The code that throws the error message is:抛出错误消息的代码是:

dists_two = classifier.compute_distances_no_loops(X_test)

# check that the distance matrix agrees with the one we computed before:
difference = np.linalg.norm(dists - dists_two, ord='fro')
print('Difference was: %f' % (difference, ))
if difference < 0.001:
    print('Good! The distance matrices are the same')
else:
    print('Uh-oh! The distance matrices are different')

And the error message:和错误信息:

Difference was: 372100.327569
Uh-oh! The distance matrices are different

Here is how you can compute pairwise distances between rows of X and Y without creating any 3-dimensional matrices:以下是如何在不创建任何 3 维矩阵的情况下计算 X 和 Y 行之间的成对距离的方法:

def dist(X, Y):
    sx = np.sum(X**2, axis=1, keepdims=True)
    sy = np.sum(Y**2, axis=1, keepdims=True)
    return np.sqrt(-2 * X.dot(Y.T) + sx + sy.T)

It's a late response, but I've solved it in a different way and wanted to post it.这是一个迟到的回复,但我已经以不同的方式解决了它并想发布它。 When I was solving this, I was unaware of the numpy's column-row vector subtractions from the matrix.当我解决这个问题时,我不知道矩阵中 numpy 的列-行向量减法。 As it turns out, we can subtract nx1 or 1xm vector from nxm and when we do it, subtracts from every row-column vector.事实证明,我们可以从 nxm 中减去 nx1 或 1xm 向量,当我们这样做时,从每个行列向量中减去。 If one works with a library that doesn't support this kind of behavior, he/she can use mine.如果有人使用不支持这种行为的库,他/她可以使用我的。 For this situation, I've worked it out the math, and the result is the following one:对于这种情况,我已经算出了数学,结果如下:

sum_x_train=np.sum(self.X_train**2,axis=1, keepdims=True)
sum_x_test=np.sum(X**2,axis=1, keepdims=True)
sum_2x_tr_te=np.dot(self.X_train,X.T)*2
sum_x_train=np.dot(sum_x_train,np.ones((1,X.shape[0])))
sum_x_test=np.dot(sum_x_test,np.ones((1,self.X_train.shape[0])))
dists=np.sqrt(sum_x_test.T+sum_x_train-sum_2x_tr_te).T

The downside of this approach is it uses more memory.这种方法的缺点是它使用更多的内存。

I think that you are looking for the pairwise distance.我认为您正在寻找成对距离。

There is an amazing trick to do that in a single line.有一个惊人的技巧可以在一行中做到这一点。 You have to cleverly play with the boradcasting:你必须巧妙地玩转播:


X_test = np.expand_dims(X, 0) # shape: [1, num_tests, D]
X_train = np.expand_dims(self.X_train, 1) # shape: [num_train, 1, D]
dists = np.square(X_train - X_test) # Thanks to broadcast [num_train, num_tests, D]
dists = np.sqrt(np.sum(dists, axis=-1)) # [num_train, num_tests]

I believe the issue comes from inconsistent array shapes.我相信这个问题来自不一致的数组形状。

 #a^2 matrix (500, 1) alpha = np.sum(np.square(X), axis=1) alpha = alpha.reshape(len(alpha), 1) print(alpha.shape) #b^2 matrix (1, 5000) beta = np.sum(np.square(self.X_train.T), axis=0) beta = beta.reshape(1, len(beta)) print(beta.shape) #ab matrix (500, 5000) alphabeta = np.dot(X, self.X_train.T) print(alphabeta.shape) dists = np.sqrt(-2 * alphabeta + alpha + beta)

This is my solution for function compute_distances_no_loops() that the OP asked for.这是我对 OP 要求的函数compute_distances_no_loops()解决方案。 I don't use the sqrt() function for performance reason:出于性能原因,我不使用sqrt()函数:

def compute_distances_no_loops(self, X):
    num_test = X.shape[0]
    num_train = self.X_train.shape[0]
    dists = np.zeros((num_test, num_train))
    #---------------
    # Get square of X and X_train
    X_sq = np.sum(X**2, axis=1, keepdims=True) 
    Xtrain_sq = np.sum(self.X_train**2, axis=1, keepdims=True)
    # Calculate (squared) dists as (X_train - X)**2 = X_train**2 - 2*X_train*X + X**2
    dists = -2*X.dot(self.X_train.T) + X_sq + Xtrain_sq.T
    #---------------  
    return dists

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM