计算测试集与训练集之间欧氏距离的代码理解

书中有一段代码,它的主要目的是计算测试样本集 X 与训练样本集 X_train 之间的欧氏距离,并将结果存储在一个距离矩阵 dists 中。

下面,我们逐行解释代码。


代码解释

def compute_distances(X, X_train):
    '''
    输入:
    X:测试样本实例矩阵(形状为 (num_test, D))
    X_train:训练样本实例矩阵(形状为 (num_train, D))
    输出:
    dists:欧氏距离矩阵(形状为 (num_test, num_train))
    '''
    # 获取测试和训练样本的数量
    num_test = X.shape[0]
    num_train = X_train.shape[0]
    
    # 初始化距离矩阵,大小为 (num_test, num_train)
    dists = np.zeros((num_test, num_train))
    
    # 计算测试样本与训练样本的内积矩阵,形状为 (num_test, num_train)
    M = np.dot(X, X_train.T)
    
    # 计算测试样本的每个样本的平方和,结果是一个形状为 (num_test,) 的一维数组
    te = np.square(X).sum(axis=1)
    
    # 计算训练样本的每个样本的平方和,结果是一个形状为 (num_train,) 的一维数组
    tr = np.square(X_train).sum(axis=1)
    
    # 利用向量化公式计算欧氏距离矩阵
    dists = np.sqrt(-2 * M + tr + np.matrix(te).T)
    
    return dists

详细说明

  1. 获取样本数量

    num_test = X.shape[0]
    num_train = X_train.shape[0]
    
    • X.shape[0] 返回测试样本的数量,即行数。
    • X_train.shape[0] 返回训练样本的数量。
  2. 初始化距离矩阵

    dists = np.zeros((num_test, num_train))
    
    • 创建一个大小为 (num_test, num_train) 的零矩阵,用于存储每个测试样本与每个训练样本之间的距离。
  3. 计算内积矩阵

    M = np.dot(X, X_train.T)
    
    • np.dot 计算矩阵乘法。
    • X 的形状为 (num_test, D)X_train.T 的形状为 (D, num_train)
    • 结果 M 的形状为 (num_test, num_train),其中 M[i][j] 是第 i 个测试样本与第 j 个训练样本的内积。
  4. 计算测试样本的平方和

    te = np.square(X).sum(axis=1)
    
    • np.square(X) 计算每个元素的平方。
    • .sum(axis=1) 对每一行(即每个样本)的平方值求和。
    • te 是一个长度为 num_test 的数组,te[i] 是第 i 个测试样本特征的平方和。
  5. 计算训练样本的平方和

    tr = np.square(X_train).sum(axis=1)
    
    • 类似地,计算训练样本的每个样本特征的平方和。
    • tr 是一个长度为 num_train 的数组,tr[j] 是第 j 个训练样本特征的平方和。
  6. 计算欧氏距离矩阵

    dists = np.sqrt(-2 * M + tr + np.matrix(te).T)
    
    • 解释公式:

      欧氏距离的平方可以表示为:

      dist ( X i , X j ) = ∑ k = 1 D ( X i k − X j k ) 2 \text{dist}(X_i, X_j) = \sqrt{\sum_{k=1}^{D} (X_{ik} - X_{jk})^2} dist(Xi,Xj)=k=1D(XikXjk)2

      展开后:

      dist 2 ( X i , X j ) = ∑ k X i k 2 − 2 ∑ k X i k X j k + ∑ k X j k 2 \text{dist}^2(X_i, X_j) = \sum_{k} X_{ik}^2 - 2 \sum_{k} X_{ik} X_{jk} + \sum_{k} X_{jk}^2 dist2(Xi,Xj)=kXik22kXikXjk+kXjk2

      用矩阵形式表示:

      dists 2 = te − 2 ⋅ M + tr \text{dists}^2 = \text{te} - 2 \cdot M + \text{tr} dists2=te2M+tr

    • 代码实现:

      • -2 * M 对应公式中的 − 2 ∑ k X i k X j k -2 \sum_{k} X_{ik} X_{jk} 2kXikXjk
      • tr 是训练样本平方和,需广播(broadcasting)到每一行。
      • np.matrix(te).Tte 转置为列向量,以便在矩阵加法中正确广播。
      • np.sqrt() 计算平方根,得到最终的欧氏距离矩阵。
  7. 返回距离矩阵

    • 最终返回的 dists 是一个大小为 (num_test, num_train) 的矩阵,其中每个元素 dists[i][j] 表示第 i 个测试样本与第 j 个训练样本之间的欧氏距离。

总结

  • 高效计算

    • 通过利用矩阵运算和广播机制,避免了使用嵌套循环,大大提高了计算效率。
  • 向量化操作

    • 使用 numpy 的向量化操作,使代码更加简洁、清晰,同时提升了性能。
  • 数学原理

    • 理解欧氏距离公式的矩阵化表示,对理解这段代码至关重要。

补充说明

  • 广播机制(Broadcasting)

    • 当执行数组运算时,numpy 会自动扩展数组的维度,以匹配操作数的形状。
    • dists = np.sqrt(-2 * M + tr + np.matrix(te).T) 中,trnp.matrix(te).T 会根据需要自动扩展,以进行矩阵加法。
  • 为什么使用 np.matrix(te).T

    • te 是一维数组,形状为 (num_test,)
    • np.matrix(te).T 将其转置为列向量,形状为 (num_test, 1),以便在加法中与 -2 * M + tr 的形状兼容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值