1. 程式人生 > >【機器學習】實驗三 Parzen窗 KNN

【機器學習】實驗三 Parzen窗 KNN

一、實驗內容

  • 考慮上面表格中點的parzen window估計和分類器。設視窗函式為球面高斯函式:
    在這裡插入圖片描述

    • a) 編寫一個程式,根據Parzen視窗對任意測試點x進行分類估計。使用三維資料訓練你的分類器。設h = 1,分類樣本點為:(0.50;1.0;0.0)t(0.31;1.51;0.50)t(0.3;0.44;0.1)t(0.50;1.0;0.0)^t (0.31;1.51;-0.50)^t和(-0.3;0.44;-0.1)^t
    • b) h = 0.1, 重複a)
  • 考慮在不同維數下的k-最近鄰密度估計

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

    • (b)編寫一個程式,對於二維的情況,當有n個數據樣本點時,進行k-緊鄰概率密度估計。對錶格中的類別w2,中的特徵(x1x2)T(x1,x2)^T,用程式畫出當k = 1,3,5時的概率密度估計結果。

    • ( c )對上表中三個類別的三維資料編寫一個k-近鄰分類器。當k=1,3,5時,對下面點的概率密度進行估計。(0.41,0.82,0.88)t,(0.14,0.72,4.1)tand(0.81,0.61,0,38)t.(-0.41, 0.82, 0.88)^t , (0.14, 0.72, 4.1)^t and (-0.81, 0.61, -0,38)^t.

      )t,(0.14,0.72,4.1)tand(0.81,0.61,0,38)t.

  • 使用的資料:
    在這裡插入圖片描述

二、實驗環境

  • linux系統
  • g++編譯器6.3.0及以上版本
  • matlab 2016a
  • 可在windows下使用同樣或更高版本的g++編譯器編譯,但需要修改Makefile檔案,刪除命令rm改為del, 可執行檔案main改為main.exe

三、理論知識

  • 非引數估計:不假定其分佈符合哪種分佈,而是直接用樣本和待判定向量計算其條件概率。

  • 關鍵公式:pn(x)=(kn/n)/Vnp_n(x)=( k_n/n)/V_n, Parzen方法是固定體積VnV_n

    KnK_n.而KNN方法是固定要包含的樣本個數,求幾何體體積。

  • 估計概率密度:估計概率密度的時候,我們最終的輸出結果應該是一個概率值。

    • Parzen方法:以輸入的x為中心,輸入的h為半徑(邊長),做一個球體(超幾何體),然後看這個類的樣本中有多少個樣本在這個球體內部,這個數量就是上面公式中的knk_n,將其代入公式中求出pn(x)p_n(x),這個值就是我們想要的值。
    • KNN方法:在這個類別的樣本中找到距離x (輸入資料)最近的K (是輸入資料)個樣本,然後再在找到的這K個樣本中找到離他最遠的那個樣本,以他們之間的距離為半徑,以輸入的x為中心,做一個球體,這個球體的體積就是VnV_n.將其代入上述公式,求出pn(x)p_n(x)即可。
  • 分類

    • 將上面的pn(x)p_n(x)帶入到貝葉斯公式,最後貝葉斯公式變成了 p(wix)=ki/knp(w_i|x) = k_i/k_n

    • parzen方法:kik_i是在以待判定向量x為中心以h為邊長(半徑)的超幾何體所包含的樣本中wiw_i樣本的數量。knk_n是上述超幾何體所包含的全部樣本數量。由於在判定後驗概率大小的時候,knk_n對於所有類別都是相同的。所以我們在計算的時候只需要計算kik_iki=i=1nϕ((xxi)/h)k_i=\sum_{i = 1}^n\phi((x-x_i)/h)。最後比較各個類別中kik_i的大小,那個大則屬於哪一類。

    • KNN方法:就是找到距離輸入向量x最近的K個向量,然後看找到的這些樣本中,哪一類的最多則判為哪一類。

四、實驗過程

1、parzen窗

  • 由於題目中要求進行分類,所以我們沒有必要直接求出pn(x)p_n(x),只要求出kik_i,然後哪個大,就屬於那類。關鍵程式碼如下:
int NPE::judgePar(string vec_str_in, float h)
{
	this->storage.reset();
	Matrix vec1 = this->userInputProcess(vec_str_in);
	float g = - FLT_MAX, temp = 0;//將g賦值為最小浮點數
	int victory = -1;
	for (int counter1 = 1; counter1 <= this->classSize; counter1++)
	{
		temp = 0;
		for (int counter2 = 0; counter2 < this->sampleSize; counter2++)
		{
			Matrix vec2 = vec1 - this->storage.readData();
			temp += exp(-(vec2.trans()*vec2).matrixToFloat()/(2*pow(h,2)));
		}
		if (temp > g)
		{
			g = temp;
			victory = counter1;
		}
	}
	return victory;
}
  • 結果
    在這裡插入圖片描述

2、kNN

新加函式及其正確性說明

  • 這次比上次在Matrix類中增加了友元函式eDist來計算歐氏距離,下面驗證其正確性,這個函式會在C小題中用到。
  • 程式碼:
    就是計算[1.55,2.66]和[2,3]之間的歐式距離
cout<<eDist(npe.userInputProcess("[1.55,2.66]"), npe.userInputProcess("[2,3]"));
  • 結果:
    在這裡插入圖片描述
  • 結果驗證:
    在這裡插入圖片描述

a小題

  • 找到最近的k個點然後V = 2*dis, dis是x到第k個最近點之間的歐式距離。
    x取了[0,3]之間的數,每隔0.01取一個值。
  • 結果:
    k = 1,3,5
    在這裡插入圖片描述

b小題

  • 基本和a小題一樣,不同之處就在於,這裡是二維,所以V應該是面積,以x為圓心,x到離它最近的第k個點的歐式距離為半徑,求面積。畫圖時,x軸取[-3,3],每隔0.1取一個值,y軸取[-3,4],每隔0.1取一個值。
  • 結果:
    在這裡插入圖片描述

c小題

在這裡插入圖片描述

五、遇到的問題

1、思路和演算法都正確,結果不正確

  • 在編寫parzen方法時思路就像上面說的,是正確的,結果就是不正確。所以我在思考是我的程式碼有問題,還是抄資料的時候出了錯誤。所以,就想百度一個已有的程式碼,然後用我的樣本資料去執行這個程式碼。如果結果和網上的一樣說明我的資料是沒有問題的,是程式碼實現時出錯了;反之,就是資料有問題。百度了一個已經寫好的程式碼1(python語言),看了下實現思路,和我的不一樣,是直接求概率密度,這樣就更好了,兩種方法驗證,我copy下來,使用我的資料檔案執行,發現結果正確,這就說明我的資料是沒有問題的,然後就debug。把所有變數一一輸出。。。。。。。一大堆操作。最後發現,原來是判決第一個向量後讀取檔案指標到了檔案末尾,然後沒有把他移到開頭,就開始判斷下一個向量了,導致出錯。
  • matlab不常用三維畫圖不會,弄了好久好不容易弄出上面的圖,雖然很醜。。。。