SVD理解和其在PCA,LSI的應用

首先確實是覺得這篇文章寫得很好,後面的附錄也是非常值得一看

整體框架轉自

http://www.cnblogs.com/LeftNotEasy/archive/2011/01/19/svd-and-applications.html

中間穿插一些自己的理解的部分

前言:

    PCA的實現一般有兩種,一種是用特徵值分解去實現的,一種是用奇異值分解去實現的。在上篇文章中便是基於特徵值分解的一種解釋(之後進行補充這一種方法)。特徵值和奇異值在大部分人的印象中,往往是停留在純粹的數學計算中。而且線性代數或者矩陣論裏面,也很少講任何跟特徵值與奇異值有關的應用背景。奇異值分解是一個有着很明顯的物理意義的一種方法,它可以將一個比較複雜的矩陣用更小更簡單的幾個子矩陣的相乘來表示,這些小矩陣描述的是矩陣的重要的特性。就像是描述一個人一樣,給別人描述說這個人長得濃眉大眼,方臉,絡腮鬍,而且帶個黑框的眼鏡,這樣寥寥的幾個特徵,就讓別人腦海裏面就有一個較爲清楚的認識,實際上,人臉上的特徵是有着無數種的,之所以能這麼描述,是因爲人天生就有着非常好的抽取重要特徵的能力,讓機器學會抽取重要的特徵,SVD是一個重要的方法。

    在機器學習領域,有相當多的應用與奇異值都可以扯上關係,比如做feature reduction的PCA,做數據壓縮(以圖像壓縮爲代表)的算法,還有做搜索引擎語義層次檢索的LSI(Latent Semantic Indexing)

一、奇異值與特徵值基礎知識:

    特徵值分解和奇異值分解在機器學習領域都是屬於滿地可見的方法。兩者有着很緊密的關係,我在接下來會談到,特徵值分解和奇異值分解的目的都是一樣,就是提取出一個矩陣最重要的特徵。先談談特徵值分解吧:

   1)特徵值:

    如果說一個向量v是方陣A的特徵向量,將一定可以表示成下面的形式:

image

    這時候λ就被稱爲特徵向量v對應的特徵值,一個矩陣的一組特徵向量是一組正交向量。特徵值分解是將一個矩陣分解成下面的形式:

image

    其中Q是這個矩陣A的特徵向量組成的矩陣,Σ是一個對角陣,每一個對角線上的元素就是一個特徵值。我這裏引用了一些參考文獻中的內容來說明一下。首先,要明確的是,一個矩陣其實就是一個線性變換,因爲一個矩陣乘以一個向量後得到的向量,其實就相當於將這個向量進行了線性變換。比如說下面的一個矩陣:

   image    它其實對應的線性變換是下面的形式:

image    因爲這個矩陣M乘以一個向量(x,y)的結果是:

image    上面的矩陣是對稱的,所以這個變換是一個對x,y軸的方向一個拉伸變換(每一個對角線上的元素將會對一個維度進行拉伸變換,當值>1時,是拉長,當值<1時時縮短),當矩陣不是對稱的時候,假如說矩陣是下面的樣子

 

image

    它所描述的變換是下面的樣子:

image

    這其實是在平面上對一個軸進行的拉伸變換(如藍色的箭頭所示),在圖中,藍色的箭頭是一個最主要的變化方向(變化方向可能有不止一個),如果我們想要描述好一個變換,那我們就描述好這個變換主要的變化方向就好了。反過頭來看看之前特徵值分解的式子,分解得到的Σ矩陣是一個對角陣,裏面的特徵值是由大到小排列的,這些特徵值所對應的特徵向量就是描述這個矩陣變化方向(從主要的變化到次要的變化排列)所以特徵分解就是按照特徵值對應的特徵向量的方向進行變換,同時特徵值越大也就說明這個變換很重要

    當矩陣是高維的情況下,那麼這個矩陣就是高維空間下的一個線性變換,這個線性變化可能沒法通過圖片來表示,但是可以想象,這個變換也同樣有很多的變換方向,我們通過特徵值分解得到的前N個特徵向量,那麼就對應了這個矩陣最主要的N個變化方向。我們利用這前N個變化方向,就可以近似這個矩陣(變換)。也就是之前說的:提取這個矩陣最重要的特徵。總結一下,特徵值分解可以得到特徵值與特徵向量,特徵值表示的是這個特徵到底有多重要,而特徵向量表示這個特徵是什麼,可以將每一個特徵向量理解爲一個線性的子空間,我們可以利用這些線性的子空間幹很多的事情。不過,特徵值分解也有很多的侷限,比如說變換的矩陣必須是方陣。

   2)奇異值:

    下面談談奇異值分解。特徵值分解是一個提取矩陣特徵很不錯的方法,但是它只是對方陣而言的,在現實的世界中,我們看到的大部分矩陣都不是方陣,比如說有N個學生,每個學生有M科成績,這樣形成的一個N * M的矩陣就不可能是方陣,我們怎樣才能描述這樣普通的矩陣呢的重要特徵呢?奇異值分解可以用來幹這個事情,奇異值分解是一個能適用於任意的矩陣的一種分解的方法

image    假設A是一個N * M的矩陣,那麼得到的U是一個N * N的方陣(裏面的向量是正交的,U裏面的向量稱爲左奇異向量),Σ是一個N * M的矩陣(除了對角線的元素都是0,對角線上的元素稱爲奇異值),V’(V的轉置)是一個N * N的矩陣,裏面的向量也是正交的,V裏面的向量稱爲右奇異向量),從圖片來反映幾個相乘的矩陣的大小可得下面的圖片

image

    那麼奇異值和特徵值是怎麼對應起來的呢?首先,我們將一個矩陣A的轉置 * A,將會得到一個方陣,我們用這個方陣求特徵值可以得到:image    這裏得到的v,就是我們上面的右奇異向量。此外我們還可以得到:

image    這裏的σ就是上面說的奇異值,u就是上面說的左奇異向量。奇異值σ跟特徵值類似,在矩陣Σ中也是從大到小排列,而且σ的減少特別的快,在很多情況下,前10%甚至1%的奇異值的和就佔了全部的奇異值之和的99%以上了。也就是說,我們也可以用前r大的奇異值來近似描述矩陣,這裏定義一下部分奇異值分解

image

    r是一個遠小於m、n的數,這樣矩陣的乘法看起來像是下面的樣子:

 

 

 

 

image

    右邊的三個矩陣相乘的結果將會是一個接近於A的矩陣,在這兒,r越接近於n,則相乘的結果越接近於A。而這三個矩陣的面積之和(在存儲觀點來說,矩陣面積越小,存儲量就越小)要遠遠小於原始的矩陣A,我們如果想要壓縮空間來表示原矩陣A,我們存下這裏的三個矩陣:U、Σ、V就好了。

這裏進行補充說明一些內容:
其實SVD意義就是利用特徵空間的轉換進行數據的映射,

對這樣一個DATA = U(Z)Vt 

如果舉一個推薦算法的例子,輸入一個每一行是用戶,每一列是物品,矩陣對應的值是用戶對物品的打分情況,這裏的U和V真正的幾何含義  :  書上的含義是U將物品映射到了新的特徵空間, V的轉置  將 用戶映射到了新的特徵空間

下面是代碼實現,同時SVD還可以用於降維,降維的操作就是通過保留值比較的奇異值

<span style="background-color: rgb(255, 255, 255);"># -*- coding: cp936 -*-
'''
Created on Mar 8, 2011

@author: Peter
'''
from numpy import *
from numpy import linalg as la #用到別名

#這裏主要結合推薦系統介紹SVD,所以這裏的數據都可以看成是用戶對物品的一個打分
def loadExData():
    return[[0, 0, 0, 2, 2],
           [0, 0, 0, 3, 3],
           [0, 0, 0, 1, 1],
           [1, 1, 1, 0, 0],
           [2, 2, 2, 0, 0],
           [5, 5, 5, 0, 0],
           [1, 1, 1, 0, 0]]
    
def loadExData2():
    return[[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5],
           [0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3],
           [0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0],
           [3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0],
           [5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0],
           [0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0],
           [4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1],
           [0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4],
           [0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2],
           [0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0],
           [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]]
    
def ecludSim(inA,inB):
    return 1.0/(1.0 + la.norm(inA - inB))  #計算向量的第二範式,相當於直接計算了歐式距離

def pearsSim(inA,inB):
    if len(inA) < 3 : return 1.0
    return 0.5+0.5*corrcoef(inA, inB, rowvar = 0)[0][1] #corrcoef直接計算皮爾遜相關係數

def cosSim(inA,inB):
    num = float(inA.T*inB)
    denom = la.norm(inA)*la.norm(inB)
    return 0.5+0.5*(num/denom)  #計算餘弦相似度

#協同過濾算法
#dataMat 用戶數據 user 用戶 simMeas 相似度計算方式 item 物品
def standEst(dataMat, user, simMeas, item): 
    n = shape(dataMat)[1] #計算列的數量,物品的數量
    simTotal = 0.0; ratSimTotal = 0.0 
    for j in range(n):
        userRating = dataMat[user,j]
        print(dataMat[user,j])
        if userRating == 0: continue  #如果用戶u沒有對物品j進行打分,那麼這個判斷就可以跳過了
        overLap = nonzero(logical_and(dataMat[:,item].A>0, \
                                      dataMat[:,j].A>0))[0]    #找到對物品 j 和item都打過分的用戶
        if len(overLap) == 0: similarity = 0
        else: similarity = simMeas(dataMat[overLap,item], dataMat[overLap,j])     #利用相似度計算兩個物品之間的相似度
                                   
        print 'the %d and %d similarity is: %f' % (item, j, similarity)
        simTotal += similarity
        ratSimTotal += similarity * userRating  #待推薦物品與用戶打過分的物品之間的相似度*用戶對物品的打分
    if simTotal == 0: return 0
    else: return ratSimTotal/simTotal


#利用SVD進行分解,但是這裏是直接用的庫裏面的函數
    #如果自己實現一個SVD分解,我想就是和矩陣論裏面的求解知識是一樣的吧,但是可能在求特徵值的過程中會比較痛苦
def svdEst(dataMat, user, simMeas, item): #針對用戶user推薦物品item
    n = shape(dataMat)[1]
    simTotal = 0.0; ratSimTotal = 0.0
    U,Sigma,VT = la.svd(dataMat) #直接進行分解
    Sig4 = mat(eye(4)*Sigma[:4]) #arrange Sig4 into a diagonal matrix,取前4個特徵進行處理
    xformedItems = dataMat.T * U[:,:4] * Sig4.I  #create transformed items,將數據映射到用戶特徵的特徵空間,得到V矩陣
    for j in range(n):
        userRating = dataMat[user,j]
        if userRating == 0 or j==item: continue
        similarity = simMeas(xformedItems[item,:].T,\
                             xformedItems[j,:].T)
        print 'the %d and %d similarity is: %f' % (item, j, similarity)
        simTotal += similarity
        ratSimTotal += similarity * userRating
    if simTotal == 0: return 0
    else: return ratSimTotal/simTotal

#真正的推薦函數,後面兩個函數就是採用的相似度的計算方法和推薦用的方法
def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst):
    unratedItems = nonzero(dataMat[user,:].A==0)[1]  #find unrated items  nonzero()[1]返回的是非零值所在的行數,返回的是一個元組   if len(unratedItems) == 0: return 'you rated everything'
    itemScores = []
    for item in unratedItems:
        estimatedScore = estMethod(dataMat, user, simMeas, item)
        itemScores.append((item, estimatedScore))
    return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]


#擴展的例子,利用SVD進行圖像的壓縮
#將圖像打印出來
def printMat(inMat, thresh=0.8):
    for i in range(32):
        for k in range(32):
            if float(inMat[i,k]) > thresh:
                print 1,
            else: print 0,
        print ''



#最後發現重構出來的數據圖是差不多的
def imgCompress(numSV=3, thresh=0.8):
    myl = []
    for line in open('0_5.txt').readlines():
        newRow = []
        for i in range(32):
            newRow.append(int(line[i]))
        myl.append(newRow)
    myMat = mat(myl)   #將數據讀入了myMat當中
    
    print "****original matrix******"
    printMat(myMat, thresh)
    U,Sigma,VT = la.svd(myMat)
    SigRecon = mat(zeros((numSV, numSV)))   #構建一個3*3的空矩陣
    for k in range(numSV):#construct diagonal matrix from vector
        SigRecon[k,k] = Sigma[k]
    reconMat = U[:,:numSV]*SigRecon*VT[:numSV,:]
    print "****reconstructed matrix using %d singular values******" % numSV
    printMat(reconMat, thresh)
</span>

利用特徵矩陣最後可以發現數據圖基本是一樣的

M = UZVt

這個時候要對用戶Alice進行推薦,那麼首先找到alice在特徵空間中的位置: Alice = Alice*U*Z(-1)表示Z的逆

如果數據分解出來,取二維的情況,會發現用戶和商品都可以映射到對應的二維圖中,這時候兩種預測方法,一種是利用餘弦相似度計算相似的用戶,進行推薦。

還有一種就是根據中用戶和物品之間的關係進行推薦

二、奇異值的計算:

    奇異值的計算是一個難題,是一個O(N^3)的算法。在單機的情況下當然是沒問題的,matlab在一秒鐘內就可以算出1000 * 1000的矩陣的所有奇異值,但是當矩陣的規模增長的時候,計算的複雜度呈3次方增長,就需要並行計算參與了。Google的吳軍老師在數學之美系列談到SVD的時候,說起Google實現了SVD的並行化算法,說這是對人類的一個貢獻,但是也沒有給出具體的計算規模,也沒有給出太多有價值的信息。

    其實SVD還是可以用並行的方式去實現的,在解大規模的矩陣的時候,一般使用迭代的方法,當矩陣的規模很大(比如說上億)的時候,迭代的次數也可能會上億次,如果使用Map-Reduce框架去解,則每次Map-Reduce完成的時候,都會涉及到寫文件、讀文件的操作。個人猜測Google雲計算體系中除了Map-Reduce以外應該還有類似於MPI的計算模型,也就是節點之間是保持通信,數據是常駐在內存中的,這種計算模型比Map-Reduce在解決迭代次數非常多的時候,要快了很多倍。

    Lanczos迭代就是一種解對稱方陣部分特徵值的方法(之前談到了,解A’* A得到的對稱方陣的特徵值就是解A的右奇異向量),是將一個對稱的方程化爲一個三對角矩陣再進行求解。按網上的一些文獻來看,Google應該是用這種方法去做的奇異值分解的。請見Wikipedia上面的一些引用的論文,如果理解了那些論文,也“幾乎”可以做出一個SVD了。

    由於奇異值的計算是一個很枯燥,純數學的過程,而且前人的研究成果(論文中)幾乎已經把整個程序的流程圖給出來了。更多的關於奇異值計算的部分,將在後面的參考文獻中給出,這裏不再深入,我還是focus在奇異值的應用中去。

 

三、奇異值與主成分分析(PCA):

     主成分分析在上一節裏面也講了一些,這裏主要談談如何用SVD去解PCA的問題。PCA的問題其實是一個基的變換,使得變換後的數據有着最大的方差。方差的大小描述的是一個變量的信息量,我們在講一個東西的穩定性的時候,往往說要減小方差,如果一個模型的方差很大,那就說明模型不穩定了。但是對於我們用於機器學習的數據(主要是訓練數據),方差大才有意義,不然輸入的數據都是同一個點,那方差就爲0了,這樣輸入的多個數據就等同於一個數據了。以下面這張圖爲例子:

image     這個假設是一個攝像機採集一個物體運動得到的圖片,上面的點表示物體運動的位置,假如我們想要用一條直線去擬合這些點,那我們會選擇什麼方向的線呢?當然是圖上標有signal的那條線。如果我們把這些點單純的投影到x軸或者y軸上,最後在x軸與y軸上得到的方差是相似的(因爲這些點的趨勢是在45度左右的方向,所以投影到x軸或者y軸上都是類似的),如果我們使用原來的xy座標系去看這些點,容易看不出來這些點真正的方向是什麼。但是如果我們進行座標系的變化,橫軸變成了signal的方向,縱軸變成了noise的方向,則就很容易發現什麼方向的方差大,什麼方向的方差小了。

    一般來說,方差大的方向是信號的方向,方差小的方向是噪聲的方向,我們在數據挖掘中或者數字信號處理中,往往要提高信號與噪聲的比例,也就是信噪比。對上圖來說,如果我們只保留signal方向的數據,也可以對原數據進行不錯的近似了。

    PCA的全部工作簡單點說,就是對原始的空間中順序地找一組相互正交的座標軸,第一個軸是使得方差最大的,第二個軸是在與第一個軸正交的平面中使得方差最大的,第三個軸是在與第1、2個軸正交的平面中方差最大的,這樣假設在N維空間中,我們可以找到N個這樣的座標軸,我們取前r個去近似這個空間,這樣就從一個N維的空間壓縮到r維的空間了,但是我們選擇的r個座標軸能夠使得空間的壓縮使得數據的損失最小

    還是假設我們矩陣每一行表示一個樣本,每一列表示一個feature,用矩陣的語言來表示,將一個m * n的矩陣A的進行座標軸的變化,P就是一個變換的矩陣從一個N維的空間變換到另一個N維的空間,在空間中就會進行一些類似於旋轉、拉伸的變化。

image

    而將一個m * n的矩陣A變換成一個m * r的矩陣,這樣就會使得本來有n個feature的,變成了有r個feature了(r < n),這r個其實就是對n個feature的一種提煉,我們就把這個稱爲feature的壓縮。用數學語言表示就是:

image    但是這個怎麼和SVD扯上關係呢?之前談到,SVD得出的奇異向量也是從奇異值由大到小排列的,按PCA的觀點來看,就是方差最大的座標軸就是第一個奇異向量,方差次大的座標軸就是第二個奇異向量…我們回憶一下之前得到的SVD式子:

image     在矩陣的兩邊同時乘上一個矩陣V,由於V是一個正交的矩陣,所以V轉置乘以V得到單位陣I,所以可以化成後面的式子

image     將後面的式子與A * P那個m * n的矩陣變換爲m * r的矩陣的式子對照看看,在這裏,其實V就是P,也就是一個變化的向量。這裏是將一個m * n 的矩陣壓縮到一個m * r的矩陣,也就是對列進行壓縮,如果我們想對行進行壓縮(在PCA的觀點下,對行進行壓縮可以理解爲,將一些相似的sample合併在一起,或者將一些沒有太大價值的sample去掉)怎麼辦呢?同樣我們寫出一個通用的行壓縮例子:

image    這樣就從一個m行的矩陣壓縮到一個r行的矩陣了,對SVD來說也是一樣的,我們對SVD分解的式子兩邊乘以U的轉置U'

image    這樣我們就得到了對行進行壓縮的式子。可以看出,其實PCA幾乎可以說是對SVD的一個包裝,如果我們實現了SVD,那也就實現了PCA了,而且更好的地方是,有了SVD,我們就可以得到兩個方向的PCA,如果我們對A’A進行特徵值的分解,只能得到一個方向的PCA。

 補充說一下SVD和PCA之間的區別:
PCA的實現除了利用SVD的奇異值分解之外,還有利用最大化方差,最小化損失函數最後利用特徵值進行求解

SVD一般是用來診斷兩個場的相關關係的,而PCA是用來提取一個場的主要信息的(即主分量)。兩者在具體的實現方法上也有不同,SVD是通過矩陣奇異值分解的方法分解兩個場的協方差矩陣的(兩個場的維數不同,不對稱),而PCA是通過Jacobi方法分解一個場的協方差矩陣(T'*T).


四、奇異值與潛在語義索引LSI:

     潛在語義索引(Latent Semantic Indexing)與PCA不太一樣,至少不是實現了SVD就可以直接用的,不過LSI也是一個嚴重依賴於SVD的算法,之前吳軍老師在矩陣計算與文本處理中的分類問題中談到:

    “三個矩陣有非常清楚的物理含義。第一個矩陣X中的每一行表示意思相關的一類詞,其中的每個非零元素表示這類詞中每個詞的重要性(或者說相關性),數值越大越相關。最後一個矩陣Y中的每一列表示同一主題一類文章,其中每個元素表示這類文章中每篇文章的相關性。中間的矩陣則表示類詞和文章雷之間的相關性。因此,我們只要對關聯矩陣A進行一次奇異值分解,w 我們就可以同時完成了近義詞分類和文章的分類。(同時得到每類文章和每類詞的相關性)。”

     上面這段話可能不太容易理解,不過這就是LSI的精髓內容,我下面舉一個例子來說明一下,下面的例子來自LSA tutorial,具體的網址我將在最後的引用中給出:

image      這就是一個矩陣,不過不太一樣的是,這裏的一行表示一個詞在哪些title中出現了(一行就是之前說的一維feature),一列表示一個title中有哪些詞,(這個矩陣其實是我們之前說的那種一行是一個sample的形式的一種轉置,這個會使得我們的左右奇異向量的意義產生變化,但是不會影響我們計算的過程)。比如說T1這個title中就有guide、investing、market、stock四個詞,各出現了一次,我們將這個矩陣進行SVD,得到下面的矩陣:

image      左奇異向量表示詞的一些特性,右奇異向量表示文檔的一些特性,中間的奇異值矩陣表示左奇異向量的一行與右奇異向量的一列的重要程序,數字越大越重要。

      繼續看這個矩陣還可以發現一些有意思的東西,首先,左奇異向量的第一列表示每一個詞的出現頻繁程度,雖然不是線性的,但是可以認爲是一個大概的描述,比如book是0.15對應文檔中出現的2次,investing是0.74對應了文檔中出現了9次,rich是0.36對應文檔中出現了3次;

      其次,右奇異向量中一的第一行表示每一篇文檔中的出現詞的個數的近似,比如說,T6是0.49,出現了5個詞,T2是0.22,出現了2個詞。

      然後我們反過頭來看,我們可以將左奇異向量和右奇異向量都取後2維(之前是3維的矩陣),投影到一個平面上,可以得到:

image     在圖上,每一個紅色的點,都表示一個詞,每一個藍色的點,都表示一篇文檔,這樣我們可以對這些詞和文檔進行聚類,比如說stock 和 market可以放在一類,因爲他們老是出現在一起,real和estate可以放在一類,dads,guide這種詞就看起來有點孤立了,我們就不對他們進行合併了。按這樣聚類出現的效果,可以提取文檔集合中的近義詞,這樣當用戶檢索文檔的時候,是用語義級別(近義詞集合)去檢索了,而不是之前的詞的級別。這樣一減少我們的檢索、存儲量,因爲這樣壓縮的文檔集合和PCA是異曲同工的,二可以提高我們的用戶體驗,用戶輸入一個詞,我們可以在這個詞的近義詞的集合中去找,這是傳統的索引無法做到的。

    

參考資料:

1)A Tutorial on Principal Component Analysis, Jonathon Shlens 
     這是我關於用SVD去做PCA的主要參考資料 
2)http://www.ams.org/samplings/feature-column/fcarc-svd 
     關於svd的一篇概念好文
3)http://www.puffinwarellc.com/index.php/news-and-articles/articles/30-singular-value-decomposition-tutorial.html 
     另一篇關於svd的入門好文 
4)http://www.puffinwarellc.com/index.php/news-and-articles/articles/33-latent-semantic-analysis-tutorial.html 
     svd與LSI的好文,我後面LSI中例子就是來自此 
5)http://www.miislita.com/information-retrieval-tutorial/svd-lsi-tutorial-1-understanding.html 
     另一篇svd與LSI的文章,也還是不錯,深一點,也比較長 
6)Singular Value Decomposition and Principal Component Analysis, Rasmus Elsborg Madsen, Lars Kai Hansen and Ole Winther, 2004 
     跟1)裏面的文章比較類似

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章