机器学习算法与Python实践之(二)k近邻(KNN)
?
機器學習算法與Python實踐之(二)k近鄰(KNN)
(基于稀疏矩陣的k近鄰(KNN)實現)
?
一、概述
? ? ? ?這里我們先來看看當我們的數據是稀疏時,如何用稀疏矩陣的特性為KNN算法加速。KNN算法在之前的博文中有提到,當時寫的測試程序是針對稠密矩陣數據的。但實際上我們也會遇到不少的稀疏數據,而且有很多是有意而為之的,因為稀疏數據具有稠密數據無法媲美的存儲和計算特性,這對工程應用中的內存需求和實時需求是很重要的。所以這里我們也來關注下稀疏矩陣的存儲和其在knn算法中的應用舉例。
? ? ? ?大家都知道,所謂稀疏矩陣,就是很多元素為零的矩陣,或者說矩陣中非零元素的個數遠遠小于矩陣元素的總數,如上圖。如果我們只存儲這些少量的非零元素,就可以大大的降低這個矩陣的存儲空間。例如一個1000x1000的矩陣,里面只有100個非零的元素。如果全部存儲這個矩陣,那么需要1000x1000x4Byte=4MB的空間(假設矩陣元素是float型,占4個字節)。但如果只存儲那100個非零元素,那只需要100x4Byte=0.4KB的空間。這強大的差別對對寸土寸金的內存來說是非常討人喜歡的。哎喲,你雪亮的眼睛可以看出問題了,只占0.4KB的空間就可以描述這個稀疏矩陣了?矩陣每個元素不是具有位置意義的么?那不是還得存儲每個元素在哪一行和哪一列么?嗯,沒錯,我們還需要提供輔助數組來描述非零元素在原矩陣中的位置,一個蘿卜一個坑,得標記好。
? ? ? ?對于矩陣操作而言,不同的稀疏數據組織方式會有不同的特點,所以這就出現了多種不同的稀疏矩陣的存儲格式,但也萬變不離其宗,即存儲矩陣所有的非零元素到一個線性數組中,并提供輔助數組來描述非零元素在原矩陣中的位置。例如著名的scipy庫的稀疏矩陣模塊就提供了以下幾種存儲方式:
? ? ? ?bsr_matrix:?? Block Sparse Row matrix
? ? ? ?coo_matrix:?? A sparse matrix in COOrdinate format.
? ? ? ?csc_matrix:?? Compressed Sparse Column matrix
? ? ? ?csr_matrix:?? Compressed Sparse Row matrix
? ? ? ?dia_matrix:?? Sparse matrix with DIAgonal storage
? ? ? ?dok_matrix:?? Dictionary Of Keys based sparse matrix.
? ? ? ?lil_matrix:?? ?Row-basedlinked list sparse matrix
? ? ? ?關于其中的差別,我們可以看看后面的參考資料和scipy的官方文檔。這里只介紹下主流的CSR格式。
?
二、CSR稀疏矩陣存儲方式
? ? ? ?CSR全名是 CompressedSparse Row Format。它的存儲方式通過下面一張圖可以清晰的看出來:
? ? ? ?可以看到,一個稠密矩陣被存儲到三個數組里面。其中把矩陣的所有非零元素值按照行的順序保存到values數組中。那怎么記錄這些非零元素在原矩陣中的位置呢?矩陣的位置不就是哪一行哪一列嗎?那我們再分別添加一個行數組和一個列數組來輔助。這是很自然的,水到渠成的想法。首先,需要記錄values數組中每個元素(也就是原矩陣的非零元素)所在的行號是多少。這樣我們就新建一個數組叫row indices,它的值是[0 0 1 1 2 2 2 3 3]。還需要新建一個數組column indices來記錄values數組中每個元素的列號是多少,它的值是[0 1 1 2 0 2 3 1 3]。很明顯,這三個數組一一對應起來就可以描述原數組了。例如,values的第三個非零元素是2,它在原矩陣的行是row indices的第三個元素,也就是1,所在的列是column indices中的第三個元素,也是1,所以非零元素在原矩陣的位置是(1,1)。
? ? ? ?到這里我們的目標就達到了,但需要的存儲空間是“非零元素個數x3”。那還能再小點嗎?Smaller than smaller!答案是可以的,CSR就是其中一種方法了。它的思想也很簡單,矩陣中很多非零元素都屬于同一行對不對?例如上面例子中,values數組中的第1和第2個元素都屬于矩陣的第1行,第5,6,7個元素都屬于矩陣的第3行。我們在row indices中也可以看出來,它的值是[0 0 1 1 2 2 2 3 3],可以看到很多元素是連續相同而且基數遞增的,那我們就可以把這些連續相同的數據合并,只標記每行的開始和結尾即可。也可以說只記錄偏移量。這個數組名字就叫row offset,它的大小就是原矩陣的行數+1,它用自己的第i和第i+1位置上的元素值來標記values數組中的offset[i]到offset[i+1]-1位置上的數都是原矩陣第i行的元素。所以上面例子中row offset是[0 2 4 7 9]。([0 2 4 7 9]中的0和2表示value中第0和1(2-1)位置的元素屬于第一行,2和4表示value中第2和3(4-1)位置的元素屬于第二行,4和7表示value中第4.5.6(7-1)位置的元素屬于第三行,7和9表示value中第7和8(9-1)位置的元素屬于第四行)
三、基于csr的knn實現
? ? ? ?基于稀疏矩陣的運算是比較快的,例如矩陣乘法,因為矩陣中很多元素為0,任何數與0相乘都等于0。目前也有很多庫實現了稀疏矩陣的運算。在這里我們不涉及細節的實現過程,我們只運用現有的優化過的庫來搭搭積木。這里我們使用scipy包的csr_matrix模塊,關于它的說明,請參考官方文檔。我們是在python環境下做實驗,建議使用Anaconda這個python發行版本,最新的版本提供了多達195個流行的python包,包含了我們常用的numpy、scipy等等科學計算的包。有了它,媽媽再也不用擔心我焦頭爛額地安裝一個又一個依賴包了。Anaconda在手,輕松我有!下載地址如下:http://www.continuum.io/downloads
? ? ? ?這里我們拿knn來做實驗。因為它涉及到稀疏矩陣的乘法等。對KNN,一般有兩個矩陣,一個是存儲N個訓練樣本的矩陣A,假設矩陣每一行代表一個訓練樣本。還有一個是存儲M個測試樣本的矩陣B。KNN要求我們計算:對每個B中的樣本,計算它與矩陣A中N個樣本的歐式距離(這里采用),然后找出距離最小的K個。我們知道歐式距離可以展開:|a-b|2=a2-2ab+b2.所以在程序中,我們可以先分別計算矩陣A和B每一行的平方和,也就是a2和b2。因為矩陣A中所有樣本和B中所有樣本的內積,所以可以統一到矩陣A和B相乘。這樣就可以把所有的計算都統一到矩陣運算中去了,也就可以借助矢量化運算的神力了。故而得到了以下的程序:
knn_sparse_csr.py
[python]?view plaincopy
? ? ? ?程序輸出如下:
[python]?view plaincopy
? ? ? ?這個代碼在這個toy數據下運行看不到什么加速的。但如果處理龐大的高維數據,而且稀疏度較高,那稀疏的計算加速比還是很驚人的。PS:上述程序在大矩陣中運行過。
?
四、參考資料:
[1]?稀疏矩陣的存儲格式(Sparse Matrix Storage Formats)
[2]?http://www.bu.edu/pasi/files/2011/01/NathanBell1-10-1000.pdf
總結
以上是生活随笔為你收集整理的机器学习算法与Python实践之(二)k近邻(KNN)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 稀疏表示介绍(下)
- 下一篇: 斯坦福大学机器学习第一课“引言(Intr