機器學習:Parzen窗、k-nn

一、問題描述

1、考慮對於表格中的數據進行Parzen窗估計和設計分類器。窗函數爲一個球形的高斯函數,如下所示:

(a)編寫程序,使用Parzen窗估計方法對一個任意的測試樣本點x進行分類。對分類器的訓練則使用表格中的三維數據。同時令h=1,分類樣本點爲

(b)現在我們令h=0.1,重複(a)。

2、考慮不同維數的空間中,使用k-近鄰概率密度估計方法的效果。

(a)編寫程序,對於一維的情況,當有個數據樣本點時,進行k-近鄰概率密度估計。對錶格中的類別中的特徵,用程序畫出當k=1,3,5時的概率密度估計結果。

(b)編寫程序,對於二維的情況,當有個數據樣本點時,進行k-近鄰概率密度估計。對錶格中的類別中的特徵,用程序畫出當k=1,3,5時的概率密度估計結果。

(c)對錶格中的3個類別的三維特徵,使用k-鄰近概率密度估計方法。並且對下列點處的概率密度進行估計:

二、算法核心思想分析

1、非參數化概率密度估計

對於未知概率密度函數的估計方法,核心思想是:一個向量落在區域中的概率爲

其中,是概率密度函數的平滑了的版本,因此,我們可以通過計算概率來估計概率密度函數。假設個樣本都是根據概率密度函數獨立同分布的抽取得到的。顯然,其中個樣本落在區域中的概率服從二項式定理:

的期望值爲

的二項式形式的分佈在均值附近有非常顯著的波峯,因此,我們可以用比值來估計概率,且當樣本個數非常大的時候將非常準確。假設是連續的,並且區域足夠小,以至於在這個區間中幾乎沒有變化,那麼有:

其中爲一個點,則是區域所包含的體積,概率密度函數的估計爲:

如下圖所示:

爲了估計點處的概率密度函數,構造一系列包含點的區域:。第一個區域使用1個樣本,第二個區域使用2個樣本,以此類推。記爲區域的體積,爲落在區域中的樣本個數,而表示對的第次估計:

如果要求能夠收斂到,需滿足以下三個條件:

有兩種經常採用的獲得這種區域序列的途徑,如下圖所示。其中“Parzen窗方法”是根據某一個確定的體積函數,比如,來逐漸收縮一個給定的初始區間,這就要求隨機變量能夠保證能夠收斂到。第二種“k-近鄰法”則是確定的某個函數,比如,這樣,體積就必須逐漸增長,直到能包含進個相鄰點。這兩種方法最終都能夠收斂,但是卻很難預測它們在有限樣本情況下的效果。

2、Parzen窗方法

假設區域是一個以爲中心的維的超立方體,令表示超立方體的邊長,那麼體積爲:

以二維情況爲例,如下圖所示:

通過定義如下窗函數,表示是否落入超立方體中:

因此,超立方體中樣本個數爲:

代入得:

本題是三維數據,且假設窗函數爲一個球形的高斯函數:

最終得到判別的核心函數,即概率密度函數爲:

Notice:

① 數據使用說明:

表格中給出了3類數據,每類數據中含有10個三維數據,對於每類數據都需要將10個三維數據代入上述公式中,求出該類的概率密度函數,通過30組測試數據得出每類的概率密度函數,然後將測試數據代入到三個概率密度函數中,概率密度函數最大的那個類便是該數據所在的類。

② 窗的寬度h說明:

H越小,越小,統計結果的穩定性不夠

H越大,越大,統計結果的分別率太低

所以窗寬度的選擇對於訓練模型有影響,同時窗函數的選擇也十分重要。

3、k-近鄰估計

在Parzen窗方法中,窗函數的選擇往往是個需要權衡的問題,而k-近鄰算法提供了一種解決辦法,是一種經典的非參數估計方法。基本思路是:已知訓練數據,估計,以點爲中心,不斷擴大體積,直到區域內包含個樣本點爲止,這些樣本被稱爲點個最近鄰點。

當涉及到鄰點時,我們通常選用歐幾里得距離來度量。

① 一維和二維情況:求出自變量與每個訓練數據的直線距離,通過能包含個點的最小距離計算,進而得到概率密度值,畫出圖像。

② 三維數據情況:規定值,找出距離測試樣本點個最近的點,哪一類的點多就判定爲該類。

三、代碼及運行結果

1、Parzen窗

import xlrd
import numpy as np


# 讀取數據
def read_data():
    x = []
    data = xlrd.open_workbook("lab3_data.xlsx")
    table = data.sheets()[0]
    rows = table.nrows
    for i in range(1, rows):
        row_value = table.row_values(i)
        x.append(row_value)
    return x


def get_phi(x, xi, h):
    x = np.mat(x)
    xi = np.mat(xi)
    phi = np.exp(-(x - xi) * (x - xi).T / (2 * h * h))
    return phi


def get_px(x, xi, h):
    phi = 0
    n = len(xi)
    for i in range(n):
        # print("xi[i]", xi[i])
        phi += get_phi(x, xi[i], h)
    px = phi * 3 / (4 * np.pi * n * np.power(h, 3))
    return px


def parzen(h, test, data):
    px = [0, 0, 0]
    print("h =", h)
    for j in range(len(test)):
        print("x =", test[j])
        for i in range(len(px)):
            xi = [x[:3] for x in filter(lambda x: x[3] == i + 1, data)]
            # print("xi", xi)
            # print("len xi", len(xi))
            px[i] = get_px(test[j], xi, h)
            print("px", i+1, "=", px[i])

        if px[0] > px[1] and px[0] > px[2]:
            print("belong to w1")
        if px[1] > px[0] and px[1] > px[2]:
            print("belong to w2")
        if px[2] > px[0] and px[2] > px[1]:
            print("belong to w3")


def main():
    data = read_data()
    # print(np.mat(data))
    test = [[0.5, 1.0, 0.0], [0.31, 1.51, -0.50], [-0.3, 0.44, -0.1]]
    h1 = 1
    h2 = 0.1

    parzen(h1, test, data)
    parzen(h2, test, data)


if __name__ == '__main__':
    main()

(a) h=1時,測試樣本點分類結果:

(b) h=0.1時,測試樣本點分類結果:

2、k-nn

(a)一維特徵,k=1,3,5時的概率密度估計結果:

import xlrd
import numpy as np
import matplotlib.pyplot as plt


# 讀取數據
def read_data():
    x = []
    data = xlrd.open_workbook("lab3_data.xlsx")
    table = data.sheets()[0]
    rows = table.nrows
    for i in range(1, rows):
        row_value = table.row_values(i)
        x.append(row_value)
    return x


def get_distance(k, x, xi):
    dist = []
    for i in range(len(xi)):
        dist.append(np.sqrt(np.sum(np.square(x - xi[i]))))
    dist.sort()
    return dist[k]


def k_nn(k, xi):
    num = 1000
    x = np.linspace(0, 3, num)
    px = []
    for i in range(num):
        h = 2 * get_distance(k, x[i], xi)
        px.append(k / (len(xi) * h))
    plt.subplot(1, 5, k)
    plt.plot(x, px)
    plt.xlabel("x")
    plt.ylabel("p(x)")
    plt.title("k=" + str(k))


def main():
    data = read_data()
    xi = [x[0] for x in filter(lambda x: x[3] == 3, data)]
    print(np.mat(xi).T)
    k1 = 1
    k2 = 3
    k3 = 5

    k_nn(k1, xi)
    k_nn(k2, xi)
    k_nn(k3, xi)

    plt.show()


if __name__ == '__main__':
    main()

(b)二維特徵,k=1,3,5時的概率密度估計結果:

import xlrd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D


# 讀取數據
def read_data():
    x = []
    data = xlrd.open_workbook("lab3_data.xlsx")
    table = data.sheets()[0]
    rows = table.nrows
    for i in range(1, rows):
        row_value = table.row_values(i)
        x.append(row_value)
    return x


def get_distance(k, x, xi):
    dist = []
    for i in range(len(xi)):
        dist.append(np.sqrt(np.sum(np.square(x - xi[i]))))
    dist.sort()
    return dist[k]


def k_nn(k, xi):
    num = 100
    x = y = np.linspace(-3, 4, num)
    px = []
    X = []
    for i in range(num):
        for j in range(num):
            X.append([x[i], y[j]])
    for i in range(num * num):
        h = 2 * get_distance(k, np.mat(X)[i], xi)
        px.append(k / (len(xi) * np.pi * h * h))

    px2d = []
    for i in range(num):
        list = []
        for j in range(num):
            list.append(px[i*num+j])
        px2d.append(list)
    px2d = np.array(px2d)

    x, y = np.meshgrid(x, y)

    fig = plt.figure()
    ax = Axes3D(fig)
    ax.plot_surface(x, y, px2d, rstride=1, cstride=1, cmap='rainbow')
    ax.set_xlabel("x1", color='r')
    ax.set_ylabel("x2", color='g')
    ax.set_zlabel("p(x)", color='b')
    plt.title("k=" + str(k))


def main():
    data = read_data()
    xi = [x[:2] for x in filter(lambda x: x[3] == 2, data)]
    print(np.mat(xi))
    k1 = 1
    k2 = 3
    k3 = 5

    k_nn(k1, xi)
    k_nn(k2, xi)
    k_nn(k3, xi)

    plt.show()


if __name__ == '__main__':
    main()

(c)三維特徵,k=5,測試點概率密度估計結果:

import xlrd
import numpy as np


# 讀取數據
def read_data():
    x = []
    data = xlrd.open_workbook("lab3_data.xlsx")
    table = data.sheets()[0]
    rows = table.nrows
    for i in range(1, rows):
        row_value = table.row_values(i)
        x.append(row_value)
    return x


def get_distance(test, xi):
    dist = []
    for i in range(len(xi)):
        dist.append(np.sqrt(np.sum(np.square(test - xi[i][:3]))))
    return dist


def k_nn(k, test, data):
    x = [x[:3] for x in filter(lambda x: x, data)]
    dist = get_distance(np.mat(test), np.mat(x))
    index = []
    index = np.argsort(dist)
    w = [0, 0, 0]
    for i in range(k):
        if data[index[i]][3] == 1:
            w[0] += 1
        if data[index[i]][3] == 2:
            w[1] += 1
        if data[index[i]][3] == 3:
            w[2] += 1
    if w[0] > w[1] and w[0] > w[2]:
        return "w1"
    if w[1] > w[0] and w[1] > w[2]:
        return "w2"
    if w[2] > w[0] and w[2] > w[1]:
        return "w3"


def main():
    data = read_data()
    k = 5
    test = [[-0.41, 0.82, 0.88], [0.14, 0.72, 4.1], [-0.81, 0.61, -0.38]]

    for i in range(len(test)):
        print(test[i])
        print("belong to", k_nn(k, test[i], data))


if __name__ == '__main__':
    main()

四、總結

Parzen窗和k-近鄰是非參數估計的兩種經典方法,由  ,n是已知的總的樣本點,k和V未知。Parzen窗方法通過固定V改變k來求概率密度,而k-近鄰通過固定k改變V來估計概率密度。Parzen窗需要先確定條件概率密度後才能對測試點進行分類,而k-近鄰可以直接根據k個近鄰中哪個類別的樣本點最多就將其分爲哪一類。k-近鄰是典型的lazy-learning,每一個測試點都要重新計算,並且訓練集需要保留。

 

如有錯誤請指正

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章