马氏距离详解(数学原理、适用场景、应用示例代码)

看了很多关于马氏距离(Mahalanobis Distance)的介绍,但是总感觉有一些地方不太清晰,所以结合数学公式、机器学习中的应用案例,从头梳理一下。

马氏距离实际上是欧氏距离在多变量下的“加强版”,用于测量点(向量)与分布之间的距离。

在具体介绍马氏距离之前,首先需要了解下协方差的概念欧式距离的概念

什么情况下适用马氏距离?

当需要度量点(向量)与多变量分布之间的距离时,如果直接采用欧式距离,衡量的是两点之间的直接距离(点与分布之间的欧式距离,指的是向量x与变量空间中心的距离),而没有考虑数据的分布特性。

而采用马氏距离,在计算中对协方差进行归一化,则可以规避欧式距离对于数据特征方差不同的风险,从而使所谓的“距离”更加符合数据分布特征以及实际意义。1

什么是多变量分布(Multivariate Distributions)呢?
我们知道,当一个分布中随机变量的个数超过两个的时候,我们称之为多变量分布。与单个随机变量相比,多变量分布在实际应用中应用更广。
以二维随机变量为例:若对Ω中的每一个样本点ω都有一对有序实数(X(ω),Y(ω))与其对应,则称(X,Y)为二维随机变量或二维随机向量,称(X,Y)的取值范围为它的值域,记为Ω(X,Y)。
设(X,Y)为二维随机变量,对任意的(x,y)∈R2,称F(x,y)=P(X≤x,Y≤y)为随机变量(X,Y)的分布函数2

下面举一个具体例子,说明欧式距离在多变量距离度量方面的局限性。

当我们想要对多变量数据使用欧式距离,可能会遇到一个常见的问题:如果多变量(比如数据集中的列)之间相互关联(这在实际数据集中通常是这种情况),则点与点中心(分布)之间的欧几里得距离会具有误导性,无法真实反映一个点与分布之间的实际距离。3

如下图所示,这是是两个变量的简单散点图。左图是两个变量之间不相关,Point 1和Point 2与分布中心的距离相等。右图是两个变量之间呈正相关。即随着一个变量(x轴)的值增加,另一变量(y轴)的值也增加。

在这里插入图片描述

从几何上说,Point 1和Point 2两个点与分布中心的距离相等(欧几里得距离)。但是,即两点到分布的欧几里得距离相等,但实际上只有Point 1(蓝色)更接近该分布。

这是因为,欧几里得距离仅是两点之间的距离,它不考虑数据集中的其余点的分布情况。因此,它不能用来真正判断一个点实际上与点的分布有多接近。所以我们需要的是更健壮的距离度量标准,该度量标准可以精确地表示一个点与分布之间的距离。

马氏距离(Mahalanobis Distance)

马氏距离实际上是欧氏距离在多变量下的“加强版”,用于测量点(向量)与分布之间的距离。

计算公式

向量 x \bold{x} x到一个均值为 μ \mu μ,协方差为 S S S的样本分布的马氏距离计算如下:
d m a h a l = ( x − μ ) S − 1 ( x − μ ) ′ d_{mahal} = \sqrt{(\bold{x}-\bold{\mu})\bold{S^{-1}}(\bold{x}-\bold{\mu})'} dmahal=(xμ)S1(xμ)

直观解释

( x − μ ) (\bold{x}-\bold{\mu}) (xμ)本质上是向量与平均值的距离。然后,将其除以协方差矩阵(或乘以协方差矩阵的逆数)。

这实际上是多元变量的常规标准化(z =(x – mu)/ sigma)。也就是说,z =(x向量)–(平均向量)/(协方差矩阵)。

如果数据集中的变量高度相关,则协方差将很高。除以较大的协方差将有效缩短距离。

同样,如果X不相关,则协方差也不高,距离也不会减少太多。

因此,它有效地解决了规模问题以及前文中谈到的变量之间的相关性。

马氏距离与欧式距离的区别

从计算上来说:马氏距离与欧几里得距离相比,有以下不同步骤:

  • 首先将列转换为不相关的变量
  • 缩放列以使其方差等于1
  • 最后,计算出加强版欧几里得距离。

如果协方差矩阵为单位矩阵,即样本向量之间独立同分布,就得到欧式距离:
d E u c l i d e a n = ( x − μ ) ( x − μ ) ′ d_{Euclidean} = \sqrt{(\bold{x}-\bold{\mu})(\bold{x}-\bold{\mu})'} dEuclidean=(xμ)(xμ)

马氏距离可以排除变量之间的相关性的干扰。但是在计算马氏距离过程中,要求总体样本数大于样本的维数,否则样本协方差矩阵的逆矩阵不存在。4

代码实现:

直接利用公式计算:

def mahalanobis(x=None, data=None, cov=None):
    """Compute the Mahalanobis Distance between each row of x and the data  
    x    : vector or matrix of data with, say, p columns.
    data : ndarray of the distribution from which Mahalanobis distance of each observation of x is to be computed.
    cov  : covariance matrix (p x p) of the distribution. If None, will be computed from data.
    """
    x_minus_mu = x - np.mean(data)
    if not cov:
        cov = np.cov(data.values.T)
    inv_covmat = np.linalg.inv(cov)
    left_term = np.dot(x_minus_mu, inv_covmat)
    mahal = np.dot(left_term, x_minus_mu.T)
    return mahal.diagonal()

或者调用scipy中的scipy.spatial.distance.mahalanobis(u, v, iv)5,其中u、v都是大小为(N,)的一维向量,iv是协方差矩阵的逆。

from scipy.spatial import distance
iv = [[1, 0.5, 0.5], [0.5, 1, 0.5], [0.5, 0.5, 1]]
distance.mahalanobis([1, 0, 0], [0, 1, 0], iv)
#[out]:1.0
distance.mahalanobis([0, 2, 0], [0, 1, 0], iv)
#[out]:1.0
distance.mahalanobis([2, 0, 0], [0, 1, 0], iv)
#[out]:1.7320508075688772

马氏距离详细教程可以参考:
https://download.csdn.net/download/Bit_Coders/16757248

应用举例

它在多变量异常检测,高度不平衡的数据集分类和一类分类以及更多未开发的用例方面具有出色的应用。3

下面以一个数据集为例,看下如何应用马氏距离进行多变量异常检测(Multivariate outlier detection)。

import pandas as pd
import scipy as sp
import numpy as np

filepath = 'https://raw.githubusercontent.com/selva86/datasets/master/diamonds.csv'
df = pd.read_csv(filepath)
df.head()

数据集每行是一个样本,每列代表不同的变量:

在这里插入图片描述

计算数据集中的样本点距离整体分布的马氏距离:

def mahalanobis(x=None, data=None, cov=None):
    """Compute the Mahalanobis Distance between each row of x and the data  
    x    : vector or matrix of data with, say, p columns.
    data : ndarray of the distribution from which Mahalanobis distance of each observation of x is to be computed.
    cov  : covariance matrix (p x p) of the distribution. If None, will be computed from data.
    """
    x_minus_mu = x - np.mean(data)
    if not cov:
        cov = np.cov(data.values.T)
    inv_covmat = sp.linalg.inv(cov)
    left_term = np.dot(x_minus_mu, inv_covmat)
    mahal = np.dot(left_term, x_minus_mu.T)
    return mahal.diagonal()

df = df.iloc[:, [0,4,6]] # 选取类型为数值的三列
df_x = df[['carat', 'depth', 'price']].head(500)
df_x['mahala'] = mahalanobis(x=df_x, data=df[['carat', 'depth', 'price']])
df_x.head()

在这里插入图片描述
假设统计量服从卡方分布,则在0.01显著性水平和2自由度下的临界值计算如下(scipy.stat.chi2的文档见6):

# Critical values for two degrees of freedom
from scipy.stats import chi2
chi2.ppf((1-0.01), df=2) # 百分比点函数
#> 9.21

如果观测值的马氏距离超过9.21,则可以认为是极端样本。

也可以用P值来确定观察值是否为极值,则可以按以下方式计算P值:

# Compute the P-Values
df_x['p_value'] = 1 - chi2.cdf(df_x['mahala'], 2)

# Extreme values with a significance level of 0.01
outliers = df_x.loc[df_x.p_value < 0.01]
outliers .head(10)

检测出的异常样本:
在这里插入图片描述
用蓝色绘制所有样本点,红色绘制检测出的异常样本点,可以看出这些样本与常规样本相比,确实是比较极端的:
在这里插入图片描述

小结

  • 马氏距离实际上是欧氏距离在多变量下的“加强版”,用于测量点(向量)与分布之间的距离。
  • 在计算马氏距离过程中,要求总体样本数大于样本的维数,否则无法求解。

  1. https://blog.csdn.net/qq_46098574/article/details/115875336 ↩︎

  2. 《概率论与数理统计》 ↩︎

  3. https://www.machinelearningplus.com/statistics/mahalanobis-distance/ ↩︎ ↩︎

  4. https://www.cnblogs.com/denny402/p/7027954.html ↩︎

  5. https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.mahalanobis.html ↩︎

  6. https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.chi2.html ↩︎

猜你喜欢

转载自blog.csdn.net/Bit_Coders/article/details/115859264