日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

统计学习笔记(3)——k近邻法与kd树

發(fā)布時(shí)間:2025/3/21 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 统计学习笔记(3)——k近邻法与kd树 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在使用k近鄰法進(jìn)行分類時(shí),對(duì)新的實(shí)例,根據(jù)其k個(gè)最近鄰的訓(xùn)練實(shí)例的類別,通過多數(shù)表決的方式進(jìn)行預(yù)測(cè)。由于k近鄰模型的特征空間一般是n維實(shí)數(shù)向量,所以距離的計(jì)算通常采用的是歐式距離。關(guān)鍵的是k值的選取,如果k值太小就意味著整體模型變得復(fù)雜,容易發(fā)生過擬合,即如果鄰近的實(shí)例點(diǎn)恰巧是噪聲,預(yù)測(cè)就會(huì)出錯(cuò),極端的情況是k=1,稱為最近鄰算法,對(duì)于待預(yù)測(cè)點(diǎn)x,與x最近的點(diǎn)決定了x的類別。k值得增大意味著整體的模型變得簡(jiǎn)單,極端的情況是k=N,那么無論輸入實(shí)例是什么,都簡(jiǎn)單地預(yù)測(cè)它屬于訓(xùn)練集中最多的類,這樣的模型過于簡(jiǎn)單。經(jīng)驗(yàn)是,k值一般去一個(gè)比較小的值,通常采取交叉驗(yàn)證的方法來選取最優(yōu)的k值。

? ? ? ?實(shí)現(xiàn)k近鄰法時(shí),主要考慮的問題是如何對(duì)訓(xùn)練數(shù)據(jù)進(jìn)行快速k近鄰搜索,這點(diǎn)在特征空間的維數(shù)大以及訓(xùn)練數(shù)據(jù)容量大時(shí)尤其重要。k近鄰法的最簡(jiǎn)單實(shí)現(xiàn)是線性掃描,這時(shí)要計(jì)算輸入實(shí)例與每一個(gè)訓(xùn)練實(shí)例的距離,當(dāng)訓(xùn)練集很大時(shí),計(jì)算非常耗時(shí),這種方法是不可行的。為了提高k近鄰搜索的效率,可以考慮使用特殊的結(jié)構(gòu)存儲(chǔ)訓(xùn)練數(shù)據(jù),以減少計(jì)算距離的次數(shù)。具體方法有很多,這里介紹kd樹方法。

1.實(shí)例

? ? ? ?先以一個(gè)簡(jiǎn)單直觀的實(shí)例來介紹k-d樹算法。假設(shè)有6個(gè)二維數(shù)據(jù)點(diǎn){(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},數(shù)據(jù)點(diǎn)位于二維空間內(nèi)(如圖2中黑點(diǎn)所示)。k-d樹算法就是要確定圖2中這些分割空間的分割線(多維空間即為分割平面,一般為超平面)。下面就要通過一步步展示k-d樹是如何確定這些分割線的。


? ? ? ?k-d樹算法可以分為兩大部分,一部分是有關(guān)k-d樹本身這種數(shù)據(jù)結(jié)構(gòu)建立的算法,另一部分是在建立的k-d樹上如何進(jìn)行最鄰近查找的算法。

2.構(gòu)造kd樹

? ? ? ? kd樹是一種對(duì)k維空間中的實(shí)例點(diǎn)進(jìn)行存儲(chǔ)以便對(duì)其進(jìn)行快速搜索的樹形數(shù)據(jù)結(jié)構(gòu)。kd樹是二叉樹,表示對(duì)k維空間的一個(gè)劃分。構(gòu)造kd樹相當(dāng)于不斷地用垂直于坐標(biāo)軸的超平面將k維空間進(jìn)行切分,構(gòu)成一系列的k維超矩形區(qū)域。kd樹的每一個(gè)節(jié)點(diǎn)對(duì)應(yīng)于一個(gè)k維超矩形區(qū)域。k-d樹是一個(gè)二叉樹,每個(gè)節(jié)點(diǎn)表示一個(gè)空間范圍。下表給出的是k-d樹每個(gè)節(jié)點(diǎn)中主要包含的數(shù)據(jù)結(jié)構(gòu)。


? ? ? ? ?從上面對(duì)k-d樹節(jié)點(diǎn)的數(shù)據(jù)類型的描述可以看出構(gòu)建k-d樹是一個(gè)逐級(jí)展開的遞歸過程。下面給出的是構(gòu)建k-d樹的偽碼。

[cpp]?view plaincopy
  • 算法:構(gòu)建k-d樹(createKDTree)??
  • 輸入:數(shù)據(jù)點(diǎn)集Data-set和其所在的空間Range??
  • 輸出:Kd,類型為k-d?tree??
  • 1.If?Data-set為空,則返回空的k-d?tree??
  • 2.調(diào)用節(jié)點(diǎn)生成程序:??
  •   (1)確定split域:對(duì)于所有描述子數(shù)據(jù)(特征矢量),統(tǒng)計(jì)它們?cè)诿總€(gè)維上的數(shù)據(jù)方差。假設(shè)每條數(shù)據(jù)記錄為64維,可計(jì)算64個(gè)方差。挑選出最大值,對(duì)應(yīng)的維就是split域的值。數(shù)據(jù)方差大表明沿該坐標(biāo)軸方向上的數(shù)據(jù)分散得比較開,在這個(gè)方向上進(jìn)行數(shù)據(jù)分割有較好的分辨率;??
  •   (2)確定Node-data域:數(shù)據(jù)點(diǎn)集Data-set按其第split域的值排序。位于正中間的那個(gè)數(shù)據(jù)點(diǎn)被選為Node-data。此時(shí)新的Data-set'?=?Data-set?\?Node-data(除去其中Node-data這一點(diǎn))。??
  • 3.dataleft?=?{d屬于Data-set'?&&?d[split]?≤?Node-data[split]}??
  • ???Left_Range?=?{Range?&&?dataleft}??
  • ??dataright?=?{d屬于Data-set'?&&?d[split]?>?Node-data[split]}??
  • ???Right_Range?=?{Range?&&?dataright}??
  • 4.left?=?由(dataleft,Left_Range)建立的k-d?tree,即遞歸調(diào)用createKDTree(dataleft,Left_Range)。并設(shè)置left的parent域?yàn)镵d;??
  • ???right?=?由(dataright,Right_Range)建立的k-d?tree,即調(diào)用createKDTree(dataleft,Left_Range)。并設(shè)置right的parent域?yàn)镵d。??
  • ? ? ? ?以上述舉的實(shí)例來看,過程如下:

    ? ? ? ? 由于此例簡(jiǎn)單,數(shù)據(jù)維度只有2維,所以可以簡(jiǎn)單地給x,y兩個(gè)方向軸編號(hào)為0,1,也即split={0,1}。

    ? ? ? ? (1)確定split域的首先該取的值。分別計(jì)算x,y方向上數(shù)據(jù)的方差得知x方向上的方差最大,所以split域值首先取0,也就是x軸方向;

    ? ? ? ? ?(2)確定Node-data的域值。根據(jù)x軸方向的值2,5,9,4,8,7排序選出中值為7,所以Node-data = (7,2)。這樣,該節(jié)點(diǎn)的分割超平面就是通過(7,2)并垂直于split = 0(x軸)的直線x = 7;

    ? ? ? ? ?(3)確定左子空間和右子空間。分割超平面x = 7將整個(gè)空間分為兩部分,如下圖所示。x <= 7的部分為左子空間,包含3個(gè)節(jié)點(diǎn){(2,3),(5,4),(4,7)};另一部分為右子空間,包含2個(gè)節(jié)點(diǎn){(9,6),(8,1)}。


    ? ? ? ? ?如算法所述,k-d樹的構(gòu)建是一個(gè)遞歸的過程。然后對(duì)左子空間和右子空間內(nèi)的數(shù)據(jù)重復(fù)根節(jié)點(diǎn)的過程就可以得到下一級(jí)子節(jié)點(diǎn)(5,4)和(9,6)(也就是左右子空間的'根'節(jié)點(diǎn)),同時(shí)將空間和數(shù)據(jù)集進(jìn)一步細(xì)分。如此反復(fù)直到空間中只包含一個(gè)數(shù)據(jù)點(diǎn),如下圖所示。最后生成的k-d樹如下圖所示。

    3.搜索kd樹

    ? ? ? ? 在k-d樹中進(jìn)行數(shù)據(jù)的查找也是特征匹配的重要環(huán)節(jié),其目的是檢索在k-d樹中與查詢點(diǎn)距離最近的數(shù)據(jù)點(diǎn)。這里先以一個(gè)簡(jiǎn)單的實(shí)例來描述最鄰近查找的基本思路。

    ? ? ? ? 星號(hào)表示要查詢的點(diǎn)(2.1,3.1)。通過二叉搜索,順著搜索路徑很快就能找到最鄰近的近似點(diǎn),也就是葉子節(jié)點(diǎn)(2,3)。而找到的葉子節(jié)點(diǎn)并不一定就是最鄰近的,最鄰近肯定距離查詢點(diǎn)更近,應(yīng)該位于以查詢點(diǎn)為圓心且通過葉子節(jié)點(diǎn)的圓域內(nèi)。為了找到真正的最近鄰,還需要進(jìn)行'回溯'操作:算法沿搜索路徑反向查找是否有距離查詢點(diǎn)更近的數(shù)據(jù)點(diǎn)。此例中先從(7,2)點(diǎn)開始進(jìn)行二叉查找,然后到達(dá)(5,4),最后到達(dá)(2,3),此時(shí)搜索路徑中的節(jié)點(diǎn)為小于(7,2)和(5,4),大于(2,3),首先以(2,3)作為當(dāng)前最近鄰點(diǎn),計(jì)算其到查詢點(diǎn)(2.1,3.1)的距離為0.1414,然后回溯到其父節(jié)點(diǎn)(5,4),并判斷在該父節(jié)點(diǎn)的其他子節(jié)點(diǎn)空間中是否有距離查詢點(diǎn)更近的數(shù)據(jù)點(diǎn)。以(2.1,3.1)為圓心,以0.1414為半徑畫圓,如下圖所示。發(fā)現(xiàn)該圓并不和超平面y = 4交割,因此不用進(jìn)入(5,4)節(jié)點(diǎn)右子空間中去搜索。


    ? ? ? ? ?再回溯到(7,2),以(2.1,3.1)為圓心,以0.1414為半徑的圓更不會(huì)與x = 7超平面交割,因此不用進(jìn)入(7,2)右子空間進(jìn)行查找。至此,搜索路徑中的節(jié)點(diǎn)已經(jīng)全部回溯完,結(jié)束整個(gè)搜索,返回最近鄰點(diǎn)(2,3),最近距離為0.1414。

    ? ? ? ? ? 一個(gè)復(fù)雜點(diǎn)了例子如查找點(diǎn)為(2,4.5)。同樣先進(jìn)行二叉查找,先從(7,2)查找到(5,4)節(jié)點(diǎn),在進(jìn)行查找時(shí)是由y = 4為分割超平面的,由于查找點(diǎn)為y值為4.5,因此進(jìn)入右子空間查找到(4,7),形成搜索路徑<(7,2),(5,4),(4,7)>,取(4,7)為當(dāng)前最近鄰點(diǎn),計(jì)算其與目標(biāo)查找點(diǎn)的距離為3.202。然后回溯到(5,4),計(jì)算其與查找點(diǎn)之間的距離為3.041。以(2,4.5)為圓心,以3.041為半徑作圓,如下圖左所示。可見該圓和y = 4超平面交割,所以需要進(jìn)入(5,4)左子空間進(jìn)行查找。此時(shí)需將(2,3)節(jié)點(diǎn)加入搜索路徑中得<(7,2),(2,3)>。回溯至(2,3)葉子節(jié)點(diǎn),(2,3)距離(2,4.5)比(5,4)要近,所以最近鄰點(diǎn)更新為(2,3),最近距離更新為1.5。回溯至(7,2),以(2,4.5)為圓心1.5為半徑作圓,并不和x = 7分割超平面交割,如下圖右所示。至此,搜索路徑回溯完。返回最近鄰點(diǎn)(2,3),最近距離1.5。

    ? ? ? ?k-d樹查詢算法的偽代碼如下所示。

    [cpp]?view plaincopy
  • 算法:k-d樹最鄰近查找??
  • 輸入:Kd,????//k-d?tree類型??
  • ?????target??//查詢數(shù)據(jù)點(diǎn)??
  • 輸出:nearest,?//最鄰近數(shù)據(jù)點(diǎn)??
  • ?????dist??????//最鄰近數(shù)據(jù)點(diǎn)和查詢點(diǎn)間的距離??
  • 1.?If?Kd為NULL,則設(shè)dist為infinite并返回??
  • 2.?//進(jìn)行二叉查找,生成搜索路徑??
  • ???Kd_point?=?&Kd;???????????????????//Kd-point中保存k-d?tree根節(jié)點(diǎn)地址??
  • ???nearest?=?Kd_point?->?Node-data;??//初始化最近鄰點(diǎn)??
  • ???while(Kd_point)??
  • ???  push(Kd_point)到search_path中;?//search_path是一個(gè)堆棧結(jié)構(gòu),存儲(chǔ)著搜索路徑節(jié)點(diǎn)指針??
  • ?/***?If?Dist(nearest,target)?>?Dist(Kd_point?->?Node-data,target)?
  • ???    nearest??=?Kd_point?->?Node-data;????//更新最近鄰點(diǎn)?
  • ???    Max_dist?=?Dist(Kd_point,target);??//更新最近鄰點(diǎn)與查詢點(diǎn)間的距離??***/??
  • ???  s?=?Kd_point?->?split;???????????????????????//確定待分割的方向??
  • ???  If?target[s]?<=?Kd_point?->?Node-data[s]?????//進(jìn)行二叉查找??
  • ???    Kd_point?=?Kd_point?->?left;??
  • ???  else??
  • ???    Kd_point?=?Kd_point?->right;??
  • ???nearest?=?search_path中最后一個(gè)葉子節(jié)點(diǎn);?//注意:二叉搜索時(shí)不比計(jì)算選擇搜索路徑中的最鄰近點(diǎn),這部分已被注釋??
  • ???Max_dist?=?Dist(nearest,target);????//直接取最后葉子節(jié)點(diǎn)作為回溯前的初始最近鄰點(diǎn)??
  • ??
  • 3.?//回溯查找??
  • ???while(search_path?!=?NULL)??
  • ???  back_point?=?從search_path取出一個(gè)節(jié)點(diǎn)指針;???//從search_path堆棧彈棧??
  • ???  s?=?back_point?->?split;???????????????????//確定分割方向??
  • ???  If?Dist(target[s],back_point?->?Node-data[s])?<?Max_dist???//判斷還需進(jìn)入的子空間??
  • ???    If?target[s]?<=?back_point?->?Node-data[s]??
  • ???      Kd_point?=?back_point?->?right;??//如果target位于左子空間,就應(yīng)進(jìn)入右子空間??
  • ???    else??
  • ???      Kd_point?=?back_point?->?left;????//如果target位于右子空間,就應(yīng)進(jìn)入左子空間??
  • ???    將Kd_point壓入search_path堆棧;??
  • ???  If?Dist(nearest,target)?>?Dist(Kd_Point?->?Node-data,target)??
  • ???    nearest??=?Kd_point?->?Node-data;?????????????????//更新最近鄰點(diǎn)??
  • ???    Min_dist?=?Dist(Kd_point?->?Node-data,target);??//更新最近鄰點(diǎn)與查詢點(diǎn)間的距離??
  • ? ? ? ?當(dāng)維數(shù)較大時(shí),直接利用k-d樹快速檢索的性能急劇下降。假設(shè)數(shù)據(jù)集的維數(shù)為D,一般來說要求數(shù)據(jù)的規(guī)模N滿足條件:N遠(yuǎn)大于2的D次方,才能達(dá)到高效的搜索。


    參考:

    http://www.cnblogs.com/eyeszjwang/articles/2429382.html

    總結(jié)

    以上是生活随笔為你收集整理的统计学习笔记(3)——k近邻法与kd树的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。