Hausdorff Distance 和 Euclidean Distance Mean欧氏距离

arwen-xu / 2024-08-31 / 原文

import torch
import torch.nn as nn


class HausdorffDistanceLoss(nn.Module):
    def __init__(self):
        super(HausdorffDistanceLoss, self).__init__()

    def forward(self, pred, target):
        # 扩展为 (B, N, 1, D) 和 (B, 1, M, D)
        pred = pred.unsqueeze(1)  # (N, D) -> (N, 1, D)
        target = target.unsqueeze(0)  # (M, D) -> (1, M, D)

        # 计算两组点之间的 pairwise 距离
        distances = torch.norm(pred - target, dim=2)  # (N, M)

        # Hausdorff 距离的两个方向
        h_a_to_b = distances.min(dim=1)[0].max()  # a -> b 距离
        h_b_to_a = distances.min(dim=0)[0].max()  # b -> a 距离

        return (h_a_to_b + h_b_to_a) / 2  # 返回 Hausdorff 距离


class OptimizedHausdorffDistanceLoss(nn.Module):
    def __init__(self):
        super(OptimizedHausdorffDistanceLoss, self).__init__()

    def forward(self, pred, target):
        """
        pred: (B, N, D) - 预测点集
        target: (B, M, D) - 目标点集
        B: 批量大小
        N: 每个预测点集的点数
        M: 每个目标点集的点数
        D: 点的维度
        """
        # 确保两个张量的数据类型一致
        if pred.dtype != target.dtype:
            target = target.to(pred.dtype)

        # 使用torch.cdist计算距离矩阵
        distances = torch.cdist(pred, target)  # (B, N, M)

        # 从pred到target的最大最小距离
        h_a_to_b = distances.min(dim=1)[0].max(dim=0)[0]  # (B,)
        # 从target到pred的最大最小距离
        h_b_to_a = distances.min(dim=1)[0].max(dim=0)[0]  # (B,)

        # 返回Hausdorff距离作为损失值
        return ((h_a_to_b + h_b_to_a) / 2).mean()  # 返回批量的平均损失


def euclidean_distance_mean(pred, target):
    """
    计算两个点集之间的欧氏距离,并返回平均值。

    参数:
    pred: Tensor (N, D) - 预测点集
    target: Tensor (M, D) - 目标点集

    返回:
    mean_distance: float - 欧氏距离的平均值
    """
    # 计算两个点集之间的 pairwise 欧氏距离
    distances = torch.cdist(pred, target, p=2)  # 使用 p=2 计算欧氏距离

    # 计算 pairwise 距离的平均值
    mean_distance = distances.mean()

    return mean_distance

# 测试部分
if __name__ == "__main__":
    # 创建两个点集,每个点集只有两个点
    pred = torch.tensor([[1.0, 2.0, 3.0], [3.0, 4.0, 5.0]])
    target = torch.tensor([[2.0, 3.0, 3.0], [3.0, 4.0, 5.0]])

    # 初始化损失函数
    loss_fn = HausdorffDistanceLoss()
    loss_fn2 = OptimizedHausdorffDistanceLoss()

    # 计算损失值
    loss = loss_fn(pred, target)
    loss2 = loss_fn2(pred, target)

    print("Hausdorff Distance Loss:", loss.item())
    print("Optimized Hausdorff Distance Loss:", loss2.item())

    # 计算欧氏距离的平均值
    mean_distance = euclidean_distance_mean(pred, target)

    print("Euclidean Distance Mean:", mean_distance.item())
D:\ProgramData\Anaconda3\envs\torch38\python.exe D:\Desktop\Aidyna\test2.py 
Hausdorff Distance Loss: 1.4142135381698608
Optimized Hausdorff Distance Loss: 1.4142135381698608
Euclidean Distance Mean: 1.8319511413574219

进程已结束,退出代码为 0

区别解释:

  1. Hausdorff Distance:

    • Hausdorff 距离用于衡量两个点集之间最远的最近点的距离。
    • 它首先计算从一个点集到另一个点集的最短距离,然后在这些最短距离中选择最大的那个值。这通常用于比较两个几何形状的相似性。
    • h_a_to_b 计算的是 predtarget 的最大最小距离,h_b_to_a 计算的是 targetpred 的最大最小距离,最终 Hausdorff 距离是这两个距离的平均值。

    由于 Hausdorff 距离只关心两个点集中最远的最近点,因此它的值通常较小,尤其是在两个点集较为相似的情况下。

  2. Euclidean Distance Mean:

    • 欧氏距离的均值则是两个点集中所有点之间的 pairwise 距离的均值。
    • 它计算所有点对之间的欧氏距离,然后取这些距离的平均值。

    因为它考虑了所有点对的距离,因此它会产生一个相对较大的值,尤其是当点集中的所有点距离都不近时。

为什么结果不同?

  • Hausdorff 距离 专注于找到两个点集中最不相似的部分,因此它的值反映的是最远的最近距离,这在你例子中是 1.4142135381698608(即 sqrt(2),与点 (1, 2, 3)(2, 3, 3) 的距离一致)。
  • 欧氏距离均值 则计算了所有点对的距离,包括一些距离较大的点对(如 (1, 2, 3)(3, 4, 5)),因此它得出了较大的平均值 1.8319511413574219

这就是为什么 Hausdorff 距离和欧氏距离的平均值会不同,它们度量的东西不同:

  • Hausdorff 距离:更关注最坏情况的点对。
  • 欧氏距离均值:关注所有点对的整体距离。