作者:金良([email protected]) csdn博客:http://blog.csdn.net/u012176591
优点:不需要像余弦距离方法一样一次次地迭代,所以速度较快。
能够简化数据规模。
缺点:分类结果往往比较粗糙。
应用方式:实际工作中,先进行奇异值分解,得到粗分类结果,再利用计算向量余弦的方法,在粗分类的基础上,进行几次迭代,得到比较精细的结果。
适用数据类型:数值型数据
新闻分类乃至各种分类其实是一个聚类问题,关键是计算两篇新闻的相似程度。为了完成这个过程,我们要将新闻变成代表他们内容的实词,然后再变成对应的一组数,即向量,最后求这两个向量的夹角。当这两个向量的夹角很小时,新闻就相关;当它们垂直或者说正交时,新闻就无关。这种方法需要对所有新闻做两两计算,而且要进行多次迭代,因此耗时特别长,尤其是当新闻的数量很大,同时此表也很大的时候。我们希望有一个办法,依次就能把所有新闻相关性计算出来。这个一步到位的办法是利用矩阵运算中的奇异值分解(Singular Value Decomposition,简称SVD)。
举个例子,有M篇文章,每篇文章都有一个与N个词相对应的向量,所以就组成如下所示的的矩阵:
myMat = [[1,1,1,0,0],[2,2,2,0,0],[1,1,1,0,0],[5,5,5,0,0],[1,1,0,2,2],[0,0,0,3,3],[0,0,0,1,1]]对其进行奇异值分解:
U,Sigma,VT = la.svd(myMat)分解得到的U,Sigma和VT如下:
U: [[ -1.77939726e-01 -1.64228493e-02 1.80501685e-02 9.48215967e-01 -2.43879623e-01 5.88803098e-02 7.53939908e-02] [ -3.55879451e-01 -3.28456986e-02 3.61003369e-02 -2.42667431e-02 -1.30579552e-01 -8.77959841e-01 -2.87253136e-01] [ -1.77939726e-01 -1.64228493e-02 1.80501685e-02 -2.86917521e-01 -9.17843690e-01 1.99699755e-01 5.57067394e-02] [ -8.89698628e-01 -8.21142464e-02 9.02508423e-02 -1.22552992e-01 2.84576484e-01 2.99467924e-01 8.86811085e-02] [ -1.33954753e-01 5.33527340e-01 -8.35107599e-01 4.16333634e-16 -8.60422844e-16 2.37657116e-16 3.05311332e-16] [ -2.15749771e-02 7.97677135e-01 5.13074760e-01 1.71950731e-02 -2.25601962e-03 9.80604897e-02 -3.00138935e-01] [ -7.19165903e-03 2.65892378e-01 1.71024920e-01 -5.15852193e-02 6.76805885e-03 -2.94181469e-01 9.00416804e-01]] Sigma: [ 9.72140007e+00 5.29397912e+00 6.84226362e-01 1.50962387e-15 1.15387192e-31] VT: [[ -5.81200877e-01 -5.81200877e-01 -5.67421508e-01 -3.49564973e-02 -3.49564973e-02] [ 4.61260083e-03 4.61260083e-03 -9.61674228e-02 7.03814349e-01 7.03814349e-01] [ -4.02721076e-01 -4.02721076e-01 8.17792552e-01 5.85098794e-02 5.85098794e-02] [ -7.07106781e-01 7.07106781e-01 -4.44089210e-16 -6.93889390e-18 -6.24500451e-17] [ 0.00000000e+00 2.38253479e-17 -1.69766851e-17 -7.07106781e-01 7.07106781e-01]] [[ 1.00000000e+00 1.00000000e+00 1.00000000e+00 -1.14491749e-16 -1.21430643e-16] [ 2.00000000e+00 2.00000000e+00 2.00000000e+00 2.81675724e-16 2.67797937e-16] [ 1.00000000e+00 1.00000000e+00 1.00000000e+00 3.25802753e-16 3.18863859e-16] [ 5.00000000e+00 5.00000000e+00 5.00000000e+00 -3.59955121e-17 -9.15066634e-17] [ 1.00000000e+00 1.00000000e+00 -2.77555756e-16 2.00000000e+00 2.00000000e+00] [ -5.55111512e-17 1.08246745e-15 -1.22124533e-15 3.00000000e+00 3.00000000e+00] [ -7.63278329e-17 3.05311332e-16 -2.91433544e-16 1.00000000e+00 1.00000000e+00]]
def getdim(Sigma,threshold): a = 0 for i in range(len(Sigma)): a += Sigma[i]**2 b = [] b.append(Sigma[0]**2) for i in range(1,len(Sigma)): b.append(b[i-1]+Sigma[i]**2) b = array(b)/b[len(b)-1] for i in range(len(b)): if b[i] > threshold: break#里外的循环都停止了 return (i+1)上述函数有两个参数,一个就是奇异值分解得到的Sigma,另一个参数threshold是保留的能量信息的比率,可以使0.90、0.95、0.99等,返回值是要保留的奇异值的个数。
def dimReduce(U,Sigma,VT,dim): dim0 = len(Sigma) U = array(U)[:,0:dim] Sigma = array(Sigma)[0:dim] VT = array(VT)[0:dim,:] return U,Sigma,VT从上图可以看出,删除部分行或列元素的三个矩阵仍满足矩阵相乘的条件,并能生成和最初的 Data矩阵相同行列数的矩阵,而且这个新生成的矩阵保留了原来矩阵的绝大部分信息。三个处理后的矩阵相乘的代码如下:
myMat2 = dot(dot(U,diag(Sigma)),VT) print myMat2将新生成的矩阵打印:
[[ 1.00000000e+00 1.00000000e+00 1.00000000e+00 2.17382536e-16 2.04155269e-16] [ 2.00000000e+00 2.00000000e+00 2.00000000e+00 7.95804395e-17 3.92481186e-17] [ 1.00000000e+00 1.00000000e+00 1.00000000e+00 -5.80481843e-16 -6.00648004e-16] [ 5.00000000e+00 5.00000000e+00 5.00000000e+00 1.25333771e-16 1.77809156e-17] [ 1.00000000e+00 1.00000000e+00 -2.22044605e-16 2.00000000e+00 2.00000000e+00] [ 5.55111512e-17 -5.55111512e-17 -1.11022302e-16 3.00000000e+00 3.00000000e+00] [ -2.77555756e-17 -6.24500451e-17 2.77555756e-17 1.00000000e+00 1.00000000e+00]]可以看到该矩阵与最初的矩阵的元素大小相差无几。
[[ 1.00497377e+00 1.00497377e+00 9.89899934e-01 -7.22620478e-04 -7.22620478e-04] [ 2.00994753e+00 2.00994753e+00 1.97979987e+00 -1.44524096e-03 -1.44524096e-03] [ 1.00497377e+00 1.00497377e+00 9.89899934e-01 -7.22620478e-04 -7.22620478e-04] [ 5.02486883e+00 5.02486883e+00 4.94949967e+00 -3.61310239e-03 -3.61310239e-03] [ 7.69884117e-01 7.69884117e-01 4.67288818e-01 2.03343270e+00 2.03343270e+00] [ 1.41378969e-01 1.41378969e-01 -2.87093661e-01 2.97945956e+00 2.97945956e+00] [ 4.71263231e-02 4.71263231e-02 -9.56978871e-02 9.93153188e-01 9.93153188e-01]]可以看到误差更大了些,不过仍然与最初的矩阵相差不大。
#encoding=UTF-8 ''''' Created on 2014��6��21�� @author: jin ''' from pylab import * from numpy import linalg as la import copy import math def getdim(Sigma,threshold): a = 0 for i in range(len(Sigma)): a += Sigma[i]**2 b = [] b.append(Sigma[0]**2) for i in range(1,len(Sigma)): b.append(b[i-1]+Sigma[i]**2) b = array(b)/b[len(b)-1] for i in range(len(b)): if b[i] > threshold: break#里外的循环都停止了 return (i+1) def dimReduce(U,Sigma,VT,dim): dim0 = len(Sigma) U = array(U)[:,0:dim] Sigma = array(Sigma)[0:dim] VT = array(VT)[0:dim,:] return U,Sigma,VT def Main(): threshold = 0.99 myMat = [[1,1,1,0,0],[2,2,2,0,0],[1,1,1,0,0],[5,5,5,0,0],[1,1,0,2,2],[0,0,0,3,3],[0,0,0,1,1]] U,Sigma,VT = la.svd(myMat) dim = getdim(Sigma,threshold) U,Sigma,VT = dimReduce(U,Sigma,VT,dim) myMat2 = dot(dot(U,diag(Sigma)),VT) print myMat2 Main()