CS231n 学习笔记:KNN 计算测试样本与训练样本之间的距离

  • 问题描述:样本维度为n,测试样本数量为m1,训练样本数量为m2。求出描述测试样本与训练样本之间的距离的矩阵dists,其中dists.shape为(m1, m2)。测试集为矩阵X,维度为(m1, d);训练集为矩阵X_train,维度为(m2, d)。

  • 问题难点:如何不使用循环计算出dists?

    1. 使用两层循环显而易见非常简单,最内层循环直接计算测试样本i与训练样本j之间的距离。
    2. 使用一层训练也不难想。每一次循环计算出测试样本i分别与每个训练样本之间的距离。使用Python的broadcast机制即可完成。
    3. 不使用循环计算出距离矩阵dists就挺难想的了,那么应该如何解决呢?应该利用numpy的矩阵乘法以及(x - y)^2 = x^2 + y^2 - 2*x*y解决。
  • 两层循环
    X[i] - self.X_train[j]计算出X[i]与self.X_train[j]之间的距离,计算结果为一维矩阵(维度为n)。使用np.square或者**2计算出每个维度的平方然后再np.sum一波即可计算出平方和,再使用np.sqrt即可计算出L2距离。代码如下

def compute_distances_one_loop(self, X):
    num_test = X.shape[0]
    num_train = self.X_train.shape[0]
    dists = np.zeros((num_test, num_train))
    for i in range(num_test):
      diff = np.square(X[i] - self.X_train)
      dists[i] = np.sqrt(np.sum(diff, axis=1))
    return dists
  • 一层循环
    一层循环主要利用了Python的broadcast机制。X[i]为一个行向量,X_train为一个二维矩阵,两者直接相减,所得结果的元素(j, k)为X[i,k]-X_train[j, k]。实现代码如下
  def compute_distances_one_loop(self, X):

    num_test = X.shape[0]
    num_train = self.X_train.shape[0]
    dists = np.zeros((num_test, num_train))

    for i in range(num_test):
      diff = np.square(X[i] - self.X_train)
      dists[i] = np.sqrt(np.sum(diff, axis=1))

    return dists
  • 无循环
    首先展示实现代码
    M = np.dot(X, self.X_train.T)计算出了(x - y)^2 = x^2 + y^2 - 2*x*y中的x*y。其中矩阵维度为(m1, m2)。
    te = np.diag(np.dot(X, X.T))取出了X * X.T的对角线,元素为每个测试样本的各个维度平方的和。tr亦然,只是主体为训练样本。te、tr是一个行向量,te维度为m1,tr维度为m2
    te = np.reshape(np.repeat(te, ncol), M.shape)这行代码,首先将te的每个元素重复m2次,并reshape为(m1, m2)。每一行元素都是相等的,第i行的元素为第i个测试样本的平方和。te计算出了x^2。
    tr = np.reshape(np.repeat(tr, nrow), M.T.shape)亦然,只是重复了m1次。tr的shape为(m2, m1)。tr.T的每行元素的第j个元素为第j个训练样本的平方和。tr.T各行内容重复。tr.T计算出了y^2。
    sq = -2 * M + te + tr.T计算出了(x - y)^2 = x^2 + y^2 - 2*x*y
  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)) 

    M = np.dot(X, self.X_train.T)
    nrow = M.shape[0]
    ncol = M.shape[1]
    te = np.diag(np.dot(X, X.T))
    tr = np.diag(np.dot(self.X_train, self.X_train.T))
    te = np.reshape(np.repeat(te, ncol), M.shape)
    tr = np.reshape(np.repeat(tr, nrow), M.T.shape)
    sq = -2 * M + te + tr.T
    dists = np.sqrt(sq)

    return dists
  • 三种计算方式的时间对比
    Two loop version took 23.252080 seconds
    One loop version took 51.822145 seconds
    No loop version took 0.592002 seconds
    显而易见,循环数越少,运行时间越短,利用了numpy向量化计算的快速。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值