1. 程式人生 > 其它 >超碼、主碼、候選碼的概念與區分

超碼、主碼、候選碼的概念與區分

前言

在機器學習中,經常需要使用距離和相似性計算的公式,在做分類時,常常需要計算不同樣本之間的相似性度量(Similarity Measurement),計算這個度量,我們通常採用的方法是計算樣本之間的“距離(Distance)”。比如利用k-means進行聚類時,判斷個體所屬的類別,就需要使用距離計算公式得到樣本距離簇心的距離,利用kNN進行分類時,也是計算個體與已知類別之間的相似性,從而判斷個體的所屬類別。

本文對常用的相似性度量進行了一個總結

  1. 歐氏距離
  2. 曼哈頓距離
  3. 切比雪夫距離
  4. 閔可夫斯基距離
  5. 馬氏距離
  6. 夾角餘弦
  7. 漢明距離
  8. 傑卡德距離 & 傑卡德相似係數
  9. 相關係數 & 相關距離
  10. 資訊熵

1. 歐式距離(Euclidean Distance)

歐式距離是最易於理解的一種距離計算方法,也稱歐幾里得距離,源自歐式空間中兩點的距離公式,是指在m維空間兩點之間的真實距離,歐式距離在機器學習中使用的範圍比較廣,也比較通用,如利用k-means對二維空間內的點進行聚類。

1.1. 二維空間的歐式距離

二維空間的兩點a(x_1, y_1)與b(x_2, y_2)之間的歐氏距離

d_{12}=sqrt{(x_1-x_2)^2+(y_1-y_2)^2}

Python實現:

def euclidean2(a, b):
    distance = sqrt( (a[0]-b[0])**2 + (a[1]-b[1])**2 )
    return distance
print ('a, b兩點之間的歐式距離為: ', euclidean2((1,1),(2,2)))

1.2. 三維空間的歐氏距離

三維空間的兩點a(x_1, y_1, z_1)與b(x_2, y_2, z_2)之間的歐氏距離

d_{1, 2} = sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_i-z_2)^2}

Python實現:

def euclidean3(a, b):
    distance = sqrt( (a[0]-b[0])**2 + (a[1]-b[1])**2 + (a[2]-b[2])**2 )
    return distance
print ('a, b兩點之間的歐式距離為: ', euclidean3((1,1,1),(2,2,2))))

1.3. 多維空間的歐氏距離

多維空間的兩點a(x_{1,1} cdots,x_{1n})與b(x_{2,1}, cdots, x_{2n})之間的歐氏距離

d_{1, 2} = sqrt{sum^{n}{k=1}(x{1k}-x_{2k})^2}

Python實現:

def euclideann(a, b):
    sum = 0
    for i in range(len(a)):
        sum += (a[i]-b[i])** 2
    distance = np.sqrt(sum)
    return distance
print ('n 維空間a, b兩點之間的歐式距離為: ', euclideann((1,1,2,2),(2,2,4,4)))

def euclideann2(a, b):
    """
    不使用迴圈
    """
    A = np.array(a)
    B = np.array(b)
    c = (A - B) ** 2
    distance = np.sqrt(sum(c))
    return distance
print ('n 維空間a, b兩點之間的歐式距離為: ', euclideann2((1,1,2,2),(2,2,4,4)))

這裡可以傳入任意緯度空間的點

1.4. 標準化歐式距離(Standardized Euclidean Distance )

在長方體區域進行聚類的時候,普通的距離計算公式無法滿足需求,按照普通距離計算後進行聚類出的大多數是圓形區域,這時候需要採用標準化歐氏距離計算公式。

標準歐氏距離的定義

  標準化歐氏距離是針對簡單歐氏距離的缺點而作的一種改進方案。

標準歐氏距離的思路:既然資料各維分量的分佈不一樣,好吧!那我先將各個分量都“標準化”到均值、方差相等吧。

均值和方差標準化到多少呢?

這裡先複習點統計學知識吧,假設樣本集X的均值(mean)為m,標準差(standard deviation)為s,那麼X的“標準化變數”表示為:

X^*=frac{X-m}{s}

而且標準化變數的數學期望為0,方差為1。

因此樣本集的標準化過程(standardization)用公式描述就是:

標準化後的值 = ( 標準化前的值 - 分量的均值 ) /分量的標準差

經過簡單的推導就可以得到兩個n維向量a(x_{11},x_{12},cdots,x_{1n})與b(x_{21},x_{22},cdots,x_{2n})間的標準化歐氏距離的公式:

d_{12}=sqrt{sum^{n}{k=1}(frac{x{1k}-x_{2k}}{S_k})^2}

其中S_k是分量的標準差

標準差公式: S=sqrt{frac{sum^{n}_{i=1}(s_i-overline{s})^2}{n}}

如果將方差的倒數看成是一個權重,這個公式可以看成是一種加權歐氏距離(Weighted Euclidean distance)。

Python實現:

def euclideans(a, b):
    """
    標準化歐氏距離
    """
    sumnum = 0
    for i in range(len(a)):
        # 計算si 分量標準差
        avg = (a[i] - b[i]) / 2
        si = np.sqrt( (a[i]-avg)**2 + (b[i]-avg)**2 )
        sumnum += ((a[i]-b[i]) / si) **2
    distance = np.sqrt(sumnum)
    return distance
print ('a, b兩點的標準化歐氏距離為: ', euclideann2((1,2,1,2),(3,3,3,4)))

2. 曼哈頓距離(Manhattan Distance)

從名字就可以猜出這種距離的計算方法了。想象你在曼哈頓要從一個十字路口開車到另外一個十字路口,駕駛距離是兩點間的直線距離嗎?顯然不是,除非你能穿越大樓。實際駕駛距離就是這個“曼哈頓距離”。而這也是曼哈頓距離名稱的來源, 曼哈頓距離也稱為城市街區距離(City Block distance)。

2.1. 二維空間的曼哈頓距離

二維空間的兩點a(x_1, y_1)與b(x_2, y_2)之間的曼哈頓距離

d_{12}=left|{x_1-x_2}right|+left|y_1-y_2right|

Python實現:

def manhattan2(a, b):
    """
    二維空間曼哈頓距離
    """
    distance = np.abs(a[0] - b[0]) + np.abs(a[1] - b[1])
    return distance
print ('二維空間a, b兩點之間的曼哈頓距離為: ', manhattan((1,1),(2,2)))

2.2. 多維空間的曼哈頓距離

多維空間的兩點a(x_{1,1} cdots,x_{1n})與b(x_{2,1}, cdots, x_{2n})之間的歐氏距離

d_{12}=sum^{n}{k=1}left|x{1k}-x_{2k}right|

Python實現:

def manhattann(a, b):
    """
    n維空間曼哈頓距離
    """
    distance = 0 
    for i in range(len(a)):
        distance += np.abs(a[i]-b[i])
    return distance
print ('n維空間a, b兩點之間的曼哈頓距離為: ', manhattann((1,1,2,2),(2,2,4,4)))

def manhattann2(a, b):
    """
    n維空間曼哈頓距離, 不使用迴圈
    """
    A = np.array(a)
    B = np.array(b)
    distance = sum(np.abs(A-B))
    return distance
print ('n維空間a, b兩點之間的曼哈頓距離為: ', manhattann2((1,1,2,2),(2,2,4,4)))

由於維距離計算是比較靈活的,所以也同樣適合二維和三維。

3. 切比雪夫距離( Chebyshev Distance )

玩過國際象棋的都知道,國王走一步能夠移動到相鄰的8個方格中的任意一個。那麼國王從格子(x_1, y_1)走到格子(x_2, y_2)最少需要多少步?你會發現最少步數總是max(left|x_2-x_1right|, left|y_2-y_1right|)步 。有一種類似的一種距離度量方法叫切比雪夫距離。這篇文章中曼哈頓距離,歐式距離,明式距離,切比雪夫距離的區別 給了一個很形象的解釋如下:

比如,有同樣兩個人,在紐約準備到北京參拜天安門,同一個地點出發的話,按照歐式距離來計算,是完全一樣的。

但是按照切比雪夫距離,這是完全不同的概念了。

譬如,其中一個人是土豪,另一個人是中產階級,第一個人就能當晚直接頭等艙走人,而第二個人可能就要等機票什麼時候打折再去,或者選擇坐船什麼的。

這樣來看的話,距離是不是就不一樣了呢?

或者還是不清楚,我再說的詳細點。

同樣是這兩個人,歐式距離是直接算最短距離的,而切比雪夫距離可能還得加上財力,比如第一個人財富值100,第二個只有30,雖然物理距離一樣,但是所包含的內容卻是不同的。

如圖: 紅藍黃皆為曼哈頓距離,綠色為歐式距離

3.1. 二維切比雪夫距離

二維空間的兩點a(x_1, y_1)與b(x_2, y_2)之間的切比雪夫距離

d_{12}=max(left|{x_1-x_2}right|,left|y_1-y_2right|)

Python實現:

def chebyshev2(a, b):
    """
    二維空間切比雪夫距離
    """
    distance = max(abs(a[0]-b[0]), abs(a[1]-b[1]))
    return distance
print ('二維空間a, b兩點之間的歐式距離為: ', chebyshev2((1,2),(3,4)))

3.2. 多維切比雪夫距離

多維空間的兩點a(x_{1,1} cdots,x_{1n})與b(x_{2,1}, cdots, x_{2n})之間的切比雪夫距離

d_{12}=underset{i}{max}(left|x_{1i}-x_{2i}right|)

該公式等價:

d_{12}=limlimits_{krightarrowinfty}(sum_{i=1}^{n}left|x_{1i}-x_{2i}right|^k)^{frac{1}{k}}(可以用放縮法和夾逼法則來證明)

Python實現:

def chebyshevn(a, b):
    """
    n維空間切比雪夫距離
    """
    distance = 0
    for i in range(len(a)):
        if (abs(a[i]-b[i]) > distance):
            distance = abs(a[i]-b[i])
    return distance
print ('n維空間a, b兩點之間的切比雪夫距離為:' , chebyshevn((1,1,1,1),(3,4,3,4)))

def chebyshevn2(a, b):
    """
    n維空間切比雪夫距離, 不使用迴圈
    """
    distance = 0
    A = np.array(a)
    B = np.array(b)
    distance = max(abs(A-B))
    return distance

print ('n維空間a, b兩點之間的切比雪夫距離為:' , chebyshevn2((1,1,1,1),(3,4,3,4)))

4. 閔可夫斯基距離(Minkowski Distance)

簡稱為閔氏距離

閔氏距離不是一種距離,而是一組距離的定義

4.1. 定義

n維空間的兩點a(x_{1,1} cdots,x_{1n})與b(x_{2,1}, cdots, x_{2n})之間的閔可夫斯基距離定義為:

d_{12}=sqrt[p]{sum^{n}{k=1}left|x{1k}-x_{2k}right|^p}

其中p是一個變引數。

當p=1時,就是曼哈頓距離

當p=2時,就是歐氏距離

當p→∞時,就是切比雪夫距離

根據變引數的不同,閔氏距離可以表示一類的距離。

4.2. 閔可夫斯基距離缺點

閔氏距離,包括曼哈頓距離、歐氏距離和切比雪夫距離都存在明顯的缺點。

舉個例子:二維樣本(身高,體重),其中身高範圍是150 ~ 190,體重範圍是50 ~ 60,有三個樣本:a(180,50),b(190,50),c(180,60)。那麼a與b之間的閔氏距離(無論是曼哈頓距離、歐氏距離或切比雪夫距離)等於a與c之間的閔氏距離,但是身高的10cm真的等價於體重的10kg麼?因此用閔氏距離來衡量這些樣本間的相似度很有問題。

簡單說來,閔氏距離的缺點主要有兩個:(1)將各個分量的量綱(scale),也就是“單位”當作相同的看待了。(2)沒有考慮各個分量的分佈(期望,方差等)可能是不同的。

Python實現:

def minkowski(a, b):
    """
    閔可夫斯基距離
    """
    A = np.array(a)
    B = np.array(b)
    #方法一:根據公式求解
    distance1 = np.sqrt(np.sum(np.square(A-B)))

    #方法二:根據scipy庫求解
    from scipy.spatial.distance import pdist
    X = np.vstack([A,B])
    distance2 = pdist(X)[0]
    return distance1, distance2
print ('二維空間a, b兩點之間的閔可夫斯基距離為:' , minkowski((1,1),(2,2))[0])

5. 馬氏距離(Mahalanobis Distance)

有M個樣本向量X_1~X_m,協方差矩陣記為S,均值記為向量μ,則其中樣本向量X到u的馬氏距離表示為

D(X)=sqrt{(X-mu)^TS^{-1}(X-mu)}

而其中向量X_i與X_j之間的馬氏距離定義為:

D(X_i, X_j)=sqrt{(X_i-X_j)^TS^{-1}(X_i-X_j)}

若協方差矩陣是單位矩陣(各個樣本向量之間獨立同分布),則公式就成了:

D(X_i, X_j)=sqrt{(X_i-X_j)^T(X_i-X_j)}

也就是歐氏距離了。

若協方差矩陣是對角矩陣,公式變成了標準化歐氏距離。

馬氏距離的優缺點:量綱(scale)無關,排除變數之間的相關性的干擾。

Python實現:

def mahalanobis (a, b):
    """
    馬氏距離
    """
    A = np.array(a)
    B = np.array(b)
    #馬氏距離要求樣本數要大於維數,否則無法求協方差矩陣
    #此處進行轉置,表示10個樣本,每個樣本2維
    X = np.vstack([A,B])
    XT = X.T

    #方法一:根據公式求解
    S = np.cov(X)   #兩個維度之間協方差矩陣
    SI = np.linalg.inv(S) #協方差矩陣的逆矩陣
    #馬氏距離計算兩個樣本之間的距離,此處共有10個樣本,兩兩組合,共有45個距離。
    n = XT.shape[0]
    distance1 = []
    for i in range(0, n):
        for j in range(i+1, n):
            delta = XT[i] - XT[j]
            d = np.sqrt(np.dot(np.dot(delta,SI),delta.T))
            distance1.append(d)

    #方法二:根據scipy庫求解
    from scipy.spatial.distance import pdist
    distance2 = pdist(XT,'mahalanobis')
    return  distance1, distance2
print ('(1, 2),(1, 3),(2, 2),(3, 1)兩兩之間的閔可夫斯基距離為:' , mahalanobis((1, 1, 2, 3),(2, 3, 2, 1))[0])

6. 夾角餘弦(Cosine)

幾何中夾角餘弦可用來衡量兩個向量方向的差異,機器學習中借用這一概念來衡量樣本向量之間的差異。

6.1. 二維空間向量的夾角餘弦相似度

在二維空間中向量A(x_1, y_1)與向量B(x_2, y_2)的夾角餘弦公式:

costheta = frac{x_1x_2+y_1y_2}{sqrt{{x_1}^2+{y_1}^2}sqrt{{x_2}^2+{y_2}^2}}

Python實現:

def cos2(a, b):
    cos = (a[0]*b[0] + a[1]*b[1]) / (np.sqrt(a[0]**2 + a[1]**2) * np.sqrt(b[0]**2+b[1]**2))
    return cos
print ('a,b 二維夾角餘弦距離:',cos2((1,1),(2,2)))

6.2. 多維空間向量的夾角餘弦相似度

兩個n維樣本點a(x_{1,1} cdots,x_{1n})與b(x_{2,1}, cdots, x_{2n})之間的夾角餘弦

可以使用類似於夾角餘弦的概念來衡量這兩個樣本點間的相似程度。

costheta=frac{a cdot b}{left|aright|left|bright|}

即:

costheta=frac{sum^n_{k=1}x_{1k}x_{2k}}{sqrt{sum^n_{k=1}x_{1k}^2}sqrt{sum^n_{k=1}x_{2k}^2}}

Python實現:

def cosn(a, b):
    """
    n維夾角餘弦
    """
    sum1 = sum2 = sum3 = 0
    for i in range(len(a)):
        sum1 += a[i] * b[i]
        sum2 += a[i] ** 2
        sum3 += b[i] ** 2
    cos = sum1 / (np.sqrt(sum2) * np.sqrt(sum3))
    return cos
print ('a,b 多維夾角餘弦距離:',cosn((1,1,1,1),(2,2,2,2)))

def cosn2(a, b):
    """
    n維夾角餘弦, 不使用迴圈
    """
    A, B = np.array(a), np.array(b)
    sum1 = sum(A * B)
    sum2 = np.sqrt(np.sum(A**2))
    sum3 = np.sqrt(np.sum(B**2))
    cos = sum1 / (sum2 * sum3)
    return cos
print ('a,b 多維夾角餘弦距離:',cosn2((1,1,1,1),(2,2,2,2)))

夾角餘弦取值範圍為[-1,1]。夾角餘弦越大表示兩個向量的夾角越小,夾角餘弦越小表示兩向量的夾角越大。當兩個向量的方向重合時夾角餘弦取最大值1,當兩個向量的方向完全相反夾角餘弦取最小值-1。

7. 漢明距離(Hamming distance)

7.1. 定義

兩個等長字串s1與s2之間的漢明距離定義為將其中一個變為另外一個所需要作的最小替換次數。例如字串“1111”與“1001”之間的漢明距離為2。

應用:資訊編碼(為了增強容錯性,應使得編碼間的最小漢明距離儘可能大)。

Python實現:

def hamming(a, b):
    """
    漢明距離
    """
    sumnum = 0
    for i in range(len(a)):
        if a[i]!=b[i]:
            sumnum += 1
    return sumnum
print ('a,b 漢明距離:',hamming((1,1,2,3),(2,2,1,3)))

def hamming2(a, b):
    """
    漢明距離, 不使用迴圈
    """
    matV = np.array(a) - np.array(b)
    numsum = len(np.nonzero(matV)[0])
    return numsum
print ('a,b 漢明距離:',hamming2((1,1,2,3),(2,2,1,3)))

8.傑卡德相似係數(Jaccard similarity coefficient) & 傑卡德距離(Jaccard distance)

8.1. 傑卡德相似係數

兩個集合A和B的交集元素在A,B的並集中所佔的比例,稱為兩個集合的傑卡德相似係數,用符號J(A,B)表示。

J(A,B)=frac{left|Acap Bright|}{|A cup B|}

傑卡德相似係數是衡量兩個集合的相似度一種指標。

Python實現:

def jaccard_coefficient(a, b):
    """
    傑卡德相似係數
    """
    set_a = set(a)
    set_b = set(b)
    distance = float(len(set_a & set_b)) / len(set_a | set_b)
    return distance
print ('a,b 傑卡德相似係數:', jaccard_coefficient((1,2,3),(2,3,4)))

8.2. 傑卡德距離

與傑卡德相似係數相反的概念是傑卡德距離(Jaccard distance)。傑卡德距離可用如下公式表示:

J_delta(A,B)=1-J(A,B)=frac{|Acup B|-|Acap B|}{|Acup B|}

傑卡德距離用兩個集合中不同元素佔所有元素的比例來衡量兩個集合的區分度。

Python實現:

def jaccard_distance(a, b):
    """
    傑卡德距離
    """
    set_a = set(a)
    set_b = set(b)
    distance = float(len(set_a | set_b) - len(set_a & set_b)) / len(set_a | set_b)
    return distance
print ('a,b 傑卡德距離:', jaccard_coefficient((1,2,3),(2,3,4)))

8.3. 傑卡德相似係數與傑卡德距離的應用

可將傑卡德相似係數用在衡量樣本的相似度上。

樣本A與樣本B是兩個n維向量,而且所有維度的取值都是0或1。例如:A(0111)和B(1011)。我們將樣本看成是一個集合,1表示集合包含該元素,0表示集合不包含該元素。

p :樣本A與B都是1的維度的個數

q :樣本A是1,樣本B是0的維度的個數

r :樣本A是0,樣本B是1的維度的個數

s :樣本A與B都是0的維度的個數

那麼樣本A與B的傑卡德相似係數可以表示為:

這裡p+q+r可理解為A與B的並集的元素個數,而p是A與B的交集的元素個數。

而樣本A與B的傑卡德距離表示為:

J = frac{p}{p+q+r}

9. 相關係數 ( Correlation coefficient )與相關距離(Correlation distance)

9.1. 相關係數的定義

rho_{XY}=frac{Cov(X,Y)}{sqrt{D(X)}sqrt{D(Y)}}=frac{E((X-EX)(Y-EY))}{sqrt{D(X)}sqrt{D(Y)}}

相關係數是衡量隨機變數X與Y相關程度的一種方法,相關係數的取值範圍是[-1,1]。相關係數的絕對值越大,則表明X與Y相關度越高。當X與Y線性相關時,相關係數取值為1(正線性相關)或-1(負線性相關)。

Python 實現 :

相關係數可以利用numpy庫中的corrcoef函式來計算

例如 對於矩陣a,numpy.corrcoef(a)可計算行與行之間的相關係數,numpy.corrcoef(a,rowvar=0)用於計算各列之間的相關係數,輸出為相關係數矩陣。

def correlation_coefficient():
    """
    相關係數
    """
    a = np.array([[1, 1, 2, 2, 3], [2, 2, 3, 3, 5], [1, 4, 2, 2, 3]])
    print ('a的行之間相關係數為: ', np.corrcoef(a))
    print ('a的列之間相關係數為: ', np.corrcoef(a,rowvar=0))
correlation_coefficient()

9.2. 相關距離的定義

D_{xy}=1-rho_{XY}

Python 實現:(基於相關係數)

同樣針對矩陣a

def correlation_distance():
    """
    相關距離
    """
    a = np.array([[1, 1, 2, 2, 3], [2, 2, 3, 3, 5], [1, 4, 2, 2, 3]])
    print ('a的行之間相關距離為: ', np.ones(np.shape(np.corrcoef(a)),int) - np.corrcoef(a))
    print ('a的列之間相關距離為: ', np.ones(np.shape(np.corrcoef(a,rowvar = 0)),int) - np.corrcoef(a,rowvar = 0))
correlation_distance()

10. 資訊熵(Information Entropy)

資訊熵也成夏農(shanno)熵。資訊熵並不屬於一種相似性度量,是衡量分佈的混亂程度或分散程度的一種度量。分佈越分散(或者說分佈越平均),資訊熵就越大。分佈越有序(或者說分佈越集中),資訊熵就越小。

計算給定的樣本集X的資訊熵的公式:

Entropy(X) = sum^n_{i=1}-p_ilog_2p_i

引數的含義:

n:樣本集X的分類數

pi:X中第i類元素出現的概率

資訊熵越大表明樣本集S分類越分散,資訊熵越小則表明樣本集X分類越集中。當S中n個分類出現的概率一樣大時(都是1/n),資訊熵取最大值log2(n)。當X只有一個分類時,資訊熵取最小值0

Python實現:

def calc_entropy(x):
    """
    計算資訊熵
    """
    x_value_list = set([x[i] for i in range(x.shape[0])])
    ent = 0.0
    for x_value in x_value_list:
        p = float(x[x == x_value].shape[0]) / x.shape[0]
        logp = np.log2(p)
        ent -= p * logp
    return ent

def calc_condition_entropy(x, y):
    """
    計算條件資訊熵
    """

    # calc ent(y|x)
    x_value_list = set([x[i] for i in range(x.shape[0])])
    ent = 0.0
    for x_value in x_value_list:
        sub_y = y[x == x_value]
        temp_ent = calc_entropy(sub_y)
        ent += (float(sub_y.shape[0]) / y.shape[0]) * temp_ent
    return ent

def calc_entropy_grap(x,y):
    """
    計算資訊增益
    """

    base_ent = calc_entropy(y)
    condition_ent = calc_condition_entropy(x, y)
    ent_grap = base_ent - condition_ent
    return ent_grap

參考

http://blog.csdn.net/gamer_gyt/article/details/75165842

http://www.cnblogs.com/heaad/archive/2011/03/08/1977733.html