机器学习实战(12) 利用SVD简化数据 基于python3

SVD初探

SVD是一种最常见的矩阵分解技术,SVD将原始的数据Data分解为U Sigma和 V T V^T ,U为m行m列,Sigma为m行n列, V T V^T 为n行n列。Sigma矩阵只有对角元素,其他元素均为0,Sigma的对角元素是从大到小排列的,这些对角元素称为奇异值。这里的奇异值是矩阵Data*Data.T特征值的平方根。
这里我们直接调用numpy的linalg模块来实现矩阵的SVD处理

svddata = np.array([[1,1],[7,7]])
U,Sigma,VT = np.linalg.svd(svddata)
print(U,'\n',Sigma,'\n',VT)
output:
[[-0.14142136 -0.98994949]
 [-0.98994949  0.14142136]] 
 [10.  0.] 
 [[-0.70710678 -0.70710678]
 [-0.70710678  0.70710678]]

奇异值分解是一件很神奇的事情,我们接下来来看下

def loaddata():
    return [[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]]
data = loaddata()
U,Sigma,VT = np.linalg.svd(data)
Sig3 = np.array([[Sigma[0],0,0],[0,Sigma[1],0],[0,0,Sigma[2]]])
np.dot(np.dot(U[:,:3],Sig3),VT[:3,:])#我们取前三个奇异值合成运算
#可以看到返回的数据几乎与原数据一样
output:
array([[ 1.00000000e+00,  1.00000000e+00,  1.00000000e+00,
        -3.06102944e-16, -3.24716158e-16],
       [ 2.00000000e+00,  2.00000000e+00,  2.00000000e+00,
         1.56507791e-16,  1.19281363e-16],
       [ 1.00000000e+00,  1.00000000e+00,  1.00000000e+00,
         1.59285357e-15,  1.57424036e-15],
       [ 5.00000000e+00,  5.00000000e+00,  5.00000000e+00,
        -1.16268486e-17, -1.04909757e-16],
       [ 1.00000000e+00,  1.00000000e+00, -1.08845596e-15,
         2.00000000e+00,  2.00000000e+00],
       [ 2.54588945e-16,  1.60102837e-15, -1.68123482e-15,
         3.00000000e+00,  3.00000000e+00],
       [ 1.30086946e-16,  5.78900087e-16, -6.70962949e-16,
         1.00000000e+00,  1.00000000e+00]])

基于协同过滤的推荐引擎

首先我们考虑相似度的概念,相似度的计算方式有欧氏距离、皮尔逊相关系数和余弦相似度。

def eclud(ina,inb):
    return 1.0/(1.0+np.linalg.norm(ina-inb))#np.linalg.norm 求范数
def pears(ina,inb):
    if len(ina)<3: return 1.0
    return 0.5+0.5*np.corrcoef(ina,inb,rowvar=0)[0][1]#第0组数组和第一组数组的相关系数
def coss(ina,inb):
    num = np.dot(ina.T,inb).astype(float)
    denom = np.linalg.norm(ina)*np.linalg.norm(inb)
    return 0.5+0.5*(num/denom)

基于物品相似度的推荐引擎

第一个standest函数是给定数据以及用户和物品编号,计算基于相似度的估计,通过计算每一个物品与该物品的相似度乘上其值,这里相似度类似于权重,相似度越高,权重越大。获取的和值除以权重的和值得到最终的估计。
recommend推荐函数获取某一用户未评价的物品也即矩阵的0项,对于每一未评价的项计算基于相似度的估计值,以tuple的形式保存到列表中。

def standest(data,user,simeas,item):#user给定
    n = data.shape[1]
    simtal = 0.0
    ratsimtal = 0.0
    for j in range(n):
        userrating = data[user,j]
        if userrating == 0: continue
        overlap = np.nonzero(np.logical_and(data[:,item],data[:,j]))[0]#获取两个物品都非0的索引
        if len(overlap)==0:
            similar = 0
        else: similar = simeas(data[overlap,item],data[overlap,j])#计算j和item的物品都非0的相似度(默认余弦相似度)
        print('the %d and %d similarity is: %f' %(item,j,similar))
        simtal += similar
        ratsimtal += similar*userrating#每一个特征的值乘相关系数
    if simtal ==0: return 0
    else: return ratsimtal/simtal
def recommend(data,user,N=3,simeas=coss,estmethod=standest):
    #print(np.nonzero(data[user,:]==0))
    unrated = np.nonzero(data[user,:]==0)[0]# 获取列索引
    if len(unrated)==0: return 'you rated everything'
    itemscores = []
    for item in unrated:
        estscore = estmethod(data,user,simeas,item)
        itemscores.append((item,estscore))
    return sorted(itemscores,key=lambda jj:jj[1],reverse=True)[:N]

利用SVD提高推荐的效果

通过下面代码可以发现,选取三个奇异值就可以包含90%以上的能量

def loaddata2():
    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]]
data4 = np.array(loaddata2())
U,Sigma,VT = np.linalg.svd(np.array(data4))
print(Sigma)
Sigma2 = Sigma**2
sums = np.sum(Sigma2)
print(np.sum(Sigma2[:3]),0.9*sums)
output:
[15.77075346 11.40670395 11.03044558  4.84639758  3.09292055  2.58097379
  1.00413543  0.72817072  0.43800353  0.22082113  0.07367823]
500.5002891275791 487.7999999999994

接下来我们对之前的评分基于SVD修改
svdEst函数与之前的基于相似度的估计类似,不同点在于这里计算相似度的矩阵是通过奇异值分解后选取4个奇异值合成的矩阵。

#基于SVD评分设计
def svdest(data,user,simeas,item):
    n = data.shape[1]
    simtal = 0.0
    ratsimtal = 0.0
    U,Sigma,VT = np.linalg.svd(data)
    sig4 = np.eye(4)*Sigma[:4]
    formitem = np.dot(np.dot(data.T,U[:,:4]),np.linalg.inv(sig4))
    print(formitem)
    for j in range(n):
        userrating = data[user,j]
        if userrating ==0 or j ==item:
            continue
        similar = simeas(formitem[item,:].T,formitem[j,:].T)
        print('the %d and %d similarity is: %f' %(item,j,similar))
        simtal += similar
        ratsimtal += similar*userrating
    if simtal ==0: return 0
    else: return ratsimtal/simtal
recommend(data4,1,estmethod=svdest,simeas=pears)#获取数据中第一个用户的0物品的估计

基于SVD的图像压缩

下面代码我们通过选择前numsv个奇异值构建的矩阵,可以看到只需要两个奇异值就能相当精确地对图像实现重构。U和V.T都是32x2的矩阵,有两个奇异值,因此总数字数据为64+64+2=130,与原数字相比,几乎压缩了10倍。

#示例:基于SVD的图像压缩
def printmat(inmat,thresh=0.8):
    for i in range(32):
        for j in range(32):
            if inmat[i,j]>thresh:
                print(1,end='')
            else: print(0,end='')
        print('')
def imgcompress(numsv=3,thresh=0.8):
    my1=[]
    
    for line in open(filename).readlines():
        newrow=[]
        for i in range(32):
            newrow.append(int(line[i]))
        my1.append(newrow)
    mymat = np.array(my1)
    print('**********original array*************')
    print(mymat.shape)
    printmat(mymat,thresh)
    U,Sigma,VT = np.linalg.svd(mymat)
    sigrecon = np.zeros((numsv,numsv))
    for k in range(numsv):
        sigrecon[k,k] = Sigma[k]
    reconmat = np.dot(np.dot(U[:,:numsv],sigrecon),VT[:numsv,:])
    print('**********reconstructed array using %d singular values*************'% numsv)
    print(reconmat.shape)
    printmat(reconmat,thresh)

猜你喜欢

转载自blog.csdn.net/weixin_40548136/article/details/86825111