一、問題描述
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,每一個測試點都要重新計算,並且訓練集需要保留。
如有錯誤請指正