1967年,史丹福大學的兩位研究員Cover和Hart,以《戰國策》中 「物以類聚,人以群分」的思想,提出了一種實用的機器學習算法。它的基本思想是這樣的:某家餐廳新出了一款漢堡D,不知道如何定價。參考店裡已有的商品發現:漢堡A-18元,漢堡B-22元,漢堡C-38元,可樂A-8元,可樂B-9元,薯條A-12元...
根據常識,店家知道新漢堡應該參考已有的漢堡,而不是可樂、薯條的售價(
相比可樂、薯條,漢堡D與同類漢堡A/B/C的特徵更接近 )。於是,他們決定暫時把新漢堡的價格定為漢堡A/B/C的平均價,26元。
這就是被稱為數據挖掘10大算法之一的
K近鄰算法 (KNN),利用數據集對特徵向量空間進行劃分,並作為其分類的模型。
簡單的說,假設現在有100種食品(訓練集),
,當一種新食品加入時,找到與該食品最相似的20個食品(12種是漢堡,5種是雞肉卷,3種是三明治),這20個實例的多數屬於某個類,就把該實例分為這個類 (漢堡)。下面我們簡單介紹KNN的三要素,重點關注它的實現方法 。 01 距離度量當我們通過特徵工程或神經網絡獲得了數據集和新樣本的特徵後,首先需要確定一種距離度量策略,來衡量距離的遠近。 機器學習領域常用的距離度量方法有歐式距離、餘弦距離、曼哈頓距離、dot內積等等。其中,n維特徵的a、b向量的歐式距離體現數值上的絕對差異,餘弦距離體現方向上的相對差異 。對向量經過歸一化 處理後,兩者的效果基本是等價的。 更多距離度量方法,可以參考:https://zhuanlan.zhihu.com/p/33694613102 K值和決策規則有了距離度量方法後,我們需要確定參數k和最終的決策規則。如果選擇較小的k值,相當於只取少量的樣本進行預測,預測結果會對近鄰的樣本點非常敏感。換句話說,k值減小意味著整體模型變得複雜,容易發生過擬合。 如果選擇較大的k值,預測的樣本會更多,與實例不太相似的樣本也會參與預測,意味著整體的模型變得簡單。 極端情況下, , 無論輸入樣本是什麼,都粗暴地預測它屬於訓練樣本中最多的類,模型過於簡單,很明顯並不可取。所以實際應用中,我們一般先設一個較小的k值,通過交叉驗證 選取最優的k。 至於決策規則,對於分類 而言,一般採用多數表決規則,少數服從多數 。對於回歸 任務,可以採用平均法則 。如果不打算將該算法實際應用的讀者,讀到這兒就可以啦。相信今後看到KNN這個詞,一定不會陌生。想將該算法予以實踐的讀者,請瀏覽到最後哦! 03 KNN實現KNN的原理非常簡單,難點在於如何實現KNN算法 。受並發、時延等條件限制,高效性 往往是我們實際項目中「算法落地」要考慮的重要因素。 實際上,K近鄰的實現方法多達數十種,下面將拋磚引玉,介紹幾種常用的方法。 線性掃描即暴力法。將新樣本和訓練樣本逐一比對,最終挑選距離最接近的k個樣本,時間複雜度O(n)。對於樣本數量比較少的情況,這種方法簡單穩定,已經能有不錯的效果。但是數據規模較大時,時間開銷線性上升,無法接受。KD樹KD樹是二叉樹,核心思想是對 k 維特徵空間不斷切分(假設特徵維度是100,對於0-99中的每一個維度,以中值遞歸切分 )構造的樹,每一個節點是一個超矩形,小於結點的樣本劃分到左子樹,大於結點的樣本劃分到右子樹。構造完畢後,檢索時(1)從根結點出發,遞歸向下訪問KD樹。若目標點 x 當前維的坐標小於切分點的坐標,移動到左子樹,否則移動到右子樹,直到葉結點;(2)以此葉結點為「最近點」,遞歸地向上回退,查找該結點的兄弟結點中是否存在更近的點,若存在,則更新「最近點」,否則回退;(3)未到達根結點時繼續執行(2);(4)回退到根結點時,搜索結束。KD樹在特徵維度小於20時效率最高 ,一般適用於訓練樣本數遠大於空間維數時的k近鄰搜索。當空間維數接近訓練實例數時,效率會迅速下降,幾乎接近線形掃描。AnnoyAnnoy,全稱「A pproximate N earest N eighbors O h Y eah」,是一種適合實際應用的快速相似查找算法。Annoy 同樣通過建立一個二叉樹,使得每個點查找時間複雜度是O(log n)。和KD樹不同的是,Annoy沒有對k維特徵進行切分。Annoy的每一次空間劃分,可以看作聚類數為2的KMeans過程 。收斂後在產生的兩個聚類中心連線之間建立一條垂線(圖中的黑線),把數據空間劃分為兩部分。在劃分的子空間內不停的迭代劃分,直到每個子空間最多只剩下k個數據節點,劃分結束。最終生成的二叉樹具有類似如下結構,二叉樹底層是葉子節點,記錄原始數據;中間節點記錄的是分割超平面的信息。查詢過程和KD樹類似,先從根向葉子結點遞歸查找,再向上回溯即可。HNSW和前幾種算法不同,HNSW(H ierarchcal N avigable S mall W orld graphs)是基於圖存儲 的數據結構。
假設我們現在有13個2維數據向量,我們把這些向量放在了一個平面直角坐標系內,隱去坐標系刻度,它們的位置關係如上圖所示。
樸素查找法:把某些點和點之間連上線,構成一個查找圖,存儲備用;當我想查找與粉色點最近的一點時,我從任意一個黑色點出發,計算它和粉色點的距離,與這個任意黑色點有連接關係的點我們稱之為「友點」(直譯),然後我要計算這個黑色點的所有「友點」與粉色點的距離,從所有「友點」中選出與粉色點最近的一個點,把這個點作為下一個進入點,繼續按照上面的步驟查找下去。
如果當前黑色點對粉色點的距離比所有「友點」都近,終止查找,這個黑色點就是我們要找的離粉色點最近的點。
https://blog.csdn.net/u011233351/article/details/85116719為了達到快速搜索的目標,HNSW在構建圖時還至少要滿足如下要求:1)圖中每個點都有「友點」;2)相近的點都互為「友點」;3)圖中所有連線的數量最少;4)配有高速公路機制的構圖法 。HNSW低配版——NSW論文中配了這樣一張圖,黑線是近鄰點連線,紅線是「高速公路」 ,如此可以大幅減少平均搜索的路徑長度。在NSW基礎之上,HNSW加入跳表結構 做了進一步優化。最底層是所有數據點,每一個點都有50%概率進入上一層的有序鍊表。這樣可以保證表層是「高速通道」,底層是精細查找 。通過層狀結構,將邊按特徵半徑進行分層,使每個頂點在所有層中平均度數變為常數,從而將NSW的計算複雜度由多重對數複雜度降到了對數複雜度 。關於HNSW的詳細介紹可以參考原論文[2]和博客HNSW算法理論的來龍去脈[3]。04 實驗實驗在CLUE發布的今日頭條短文本分類數據集 上進行,訓練集規模:53,360條短文本。實驗環境 :Ubuntu 16.04.6,CPU:126G/20核,python 3.6requirement :scikit-learn、annoy、hnswlib、bert-as-service 實驗中使用了肖涵博士開源的bert-as-service服務預先將文本編碼為768維度的特徵向量,作為輸入特徵。距離度量統一採用歐式距離 。為了方便測試,我從驗證集中隨機抽取了200條 樣本,使用不同的KNN實現方法統計top1-top3的查找準確率和時間開銷。具體如下:
我們發現,Annoy算法的Top準確率相差不大,200條樣本的批量查找速度卻從之前的15秒(75ms/條)降到了0.08秒(0.4ms/條),提升了100倍以上 。HNSW算法預測200條樣本耗時0.05秒(0.25ms/條) ,略優於Annoy。 此外,同樣的53,360條特徵向量(768維度),保存為靜態索引文件 後,ann索引的大小是227MB,hnsw索引是171MB,從這一點看hnsw也略勝一籌,可以節約部分內存。實驗部分代碼可以參考:https://zhuanlan.zhihu.com/p/152522906/05 總結本文介紹了K近鄰算法,重點關注其實現方式。通過理論介紹和實驗證明,Annoy和HNSW 是可以在實際業務中落地的算法。其中Bert/Sentence-Bert+HNSW 的組合會有不錯的(粗排)召回效果。K近鄰算法的實現方法其實還有很多,感興趣的同學可以閱讀相關論文進一步研究。在NLP情報局公眾號後臺回復「三件套」,即可獲取深度學習三件套: 《PyTorch深度學習》,《Hands-on Machine Learning》,《Python深度學習》 歡 迎 關 注 👇由於微信平臺算法改版,訂閱號內容將不再以時間排序展示,如果大家想第一時間看到我們的推送,強烈建議星標 我們幫我們點【在看 】。星標具體步驟: (2)點擊右上角的小點點 ,在彈出頁面點擊「設為星標 」,就可以啦參 考 文 獻[1] K近鄰算法哪家強?KDTree、Annoy、HNSW原理和使用方法介紹:https://zhuanlan.zhihu.com/p/152522906/edit[2] Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs[3] 一文看懂HNSW算法理論的來龍去脈:
https://blog.csdn.net/u011233351/article/details/85116719
[4] 快速計算距離Annoy算法原理及Python使用:
https://blog.csdn.net/m0_37850187/article/details/92712490
原創不易,有收穫的話請幫忙點擊分享、 點讚 、 在看 吧🙏