SVD(奇异值分解)简化数据

SVD优点:简化数据、去除噪声、提高算法的结果

SVD是一个强大的降维工具,我们可以利用SVD来逼近矩阵并从中提取重要特征。通过保留矩阵80%~90%的能量,就可以得到重要的特征并去掉噪声。其中一个重要应用案例就是推荐引擎。

协同过滤的核心式相似度计算方法,有很多相似度计算方法都可以用于计算物品和用户之间的相似度。

3. 原理——矩阵分解

将原始的数据集矩阵data(m*n)分解成三个矩阵U(m*n), Sigma(n*m), VT(m*n):

对于Sigma矩阵:

  •  该矩阵只用对角元素,其他元素均为零
  • 对角元素从大到小排列。这些对角元素称为奇异值,它们对应了原始数据集矩阵的奇异值
  • 这里的奇异值就是矩阵data特征值的平方根。 
  • 在某个奇异值的数目( 1个 )之后,其他的奇异值都置为0。这就意味着数据集中仅有r个重要特征,而其余特征则都是噪声或冗余特征。 
    • 确认r——启发式策略
      • 保留矩阵中90%的能量信息,将奇异值平方和累加加到90%
      • 若有上万奇异值,则保留2-3k

svd分解计算公式在numpy中会有

输入:

import numpy as np
from numpy import linalg as la

data = np.array(
           [[4, 4, 0, 2, 2],
           [4, 0, 0, 3, 3],
           [4, 0, 0, 1, 1],
           [1, 1, 1, 2, 0],
           [2, 2, 2, 0, 0],
           [1, 1, 1, 0, 0],
           [5, 5, 5, 0, 0]])

U, Sigma, VT  = la.svd(data)
print('Sigma:',Sigma)
print(U.shape,Sigma.shape,VT.shape)
Sig = np.mat([
        [Sigma[0],0,0,0,0],
        [0,Sigma[1],0,0,0],
        [0,0,Sigma[2],0,0],
        [0,0,0,Sigma[3],0],
        [0,0,0,0,Sigma[4]]])

arr = U[:,:5] * Sig * VT[:5,:]
print(arr)

输出:

Sigma: [11.90341619  5.99130503  2.59510658  1.92662869  0.98309238]
(7, 7) (5,) (5, 5)
[[ 4.00000000e+00  4.00000000e+00  2.20640467e-15  2.00000000e+00
   2.00000000e+00]
 [ 4.00000000e+00  1.13034751e-16  7.17358258e-16  3.00000000e+00
   3.00000000e+00]
 [ 4.00000000e+00 -8.39303269e-16 -5.24128439e-16  1.00000000e+00
   1.00000000e+00]
 [ 1.00000000e+00  1.00000000e+00  1.00000000e+00  2.00000000e+00
  -3.81525350e-15]
 [ 2.00000000e+00  2.00000000e+00  2.00000000e+00  3.01940761e-16
   8.63819106e-16]
 [ 1.00000000e+00  1.00000000e+00  1.00000000e+00  3.68346319e-16
   3.32088398e-16]
 [ 5.00000000e+00  5.00000000e+00  5.00000000e+00  9.08535912e-16
   1.06916635e-15]]

推荐未尝过的菜肴:

个人觉得 基于物品相似度推荐算法的核心在于

    1:寻找用户未评价菜肴

    2:菜肴对评价菜肴的评分 = (用户吃A菜肴的评分 * A和未评价菜肴的形似度 +....+ 用户吃n菜肴的评分 * n和未评价菜肴的形似度)/ (A和未评价菜肴的形似度+...+n和未评价菜肴的形似度)

    3:根据从大到小依次推荐

输入:

from numpy import *
from numpy import linalg as la
 
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)
 
def loadExData():
    return[[4, 4, 0, 2, 2],
           [4, 0, 0, 3, 3],
           [4, 0, 0, 1, 1],
           [1, 1, 1, 2, 0],
           [2, 2, 2, 0, 0],
           [1, 1, 1, 0, 0],
           [5, 5, 5, 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]]

# 协同过滤算法
# 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] > 0, dataMat[:, j] > 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

def recommand(dataMat, user, N=3, simMeas=pearsSim, estMethod=standEst):
    unratedItem = nonzero(dataMat[user, :]==0)[0]
    print('unretedItem:',unratedItem)
    if len(unratedItem) == 0:
        return  'You rated everything'
    else:
        itemScores=[]
        # 对于未评分的item,对他进行评分
        for item in unratedItem:
            estimatedScore = estMethod(dataMat, user, simMeas, item)
            itemScores.append((item, estimatedScore))
    itemScores = sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]
    print(itemScores)

if __name__ == '__main__':
    data = array(loadExData())
    recommand(data, 2)

输出:

unretedItem: [1 2]
[(2, 2.5), (1, 2.0)]

先对数据集进行svd降维处理,得到较重要的/相似度较高的菜肴,再进行推荐

添加


def svdEst(dataMat,user,simMea,item):
    n = shape(dataMat)[1]
    simTotal = 0.0 
    ratSimTotal = 0.0
 
    u,sigma,vt = la.svd(dataMat) #sigma是行向量
    sig4 = mat(eye(4) * sigma[:4]) #只利用最大的3个奇异值,将其转换为4*4矩阵,非对角元素为0
#    xformedItems = dataMat.T * u[:,:4] * sig4.I #得到n*4
    xformedItems = dot(dot(dataMat.T, u[:, :4]), sig4)
 
    for j in range(n):
        userRate = dataMat[user,j]
        if userRate == 0  or j == item:
            continue
        #得到对菜item和j都评过分的用户id,用来计算物品item和j之间的相似度
        #overlap = nonzero(logical_and(dataMat[:,item].A>0,dataMat[:,j].A>0))[0]
        #if len(overlap) == 0:
        #    similarity = 0
        #else:
            #计算物品item和j之间的相似度
        #    similarity = simMea(dataMat[overlap,item],dataMat[overlap,j]) 
        similarity = simMea(xformedItems[item,:].T,xformedItems[j,:].T)
        simTotal += similarity
        ratSimTotal += similarity * userRate
    if simTotal ==0:
        return 0
    else:
        return ratSimTotal/simTotal  #归一化处理

if __name__ == '__main__':
#     data = array(loadExData())
#     recommand(data, 2)

    data = array(loadExData2())
    
#     通过这个分解就可以判断 该转换到多少维了
#     # SVD 分解
#     U, Sigma, VT = la.svd(data)
#     # 计算取几个奇异值
#     # 计算90%能量:487.83
#     Sig2 = Sigma ** 2
#     s = sum(Sig2) * 0.9
#     print(s)
#     # 计算前两个奇异值包含能量:378(<487.83)
#     print(sum(Sig2[:2]))
#     # 计算前三个奇异值包含能量:500(>487)
#     print(sum(Sig2[:3]))
#     #所以选择前三个就可以了
    
    recommand(data, 1,estMethod = svdEst)

输出:

unretedItem: [0 1 2 4 6 7 8 9]
[(4, 3.3309010344353416), (9, 3.330264957905896), (6, 3.32699627129725)]

构建推荐引擎面临的挑战

1:但是在大数据集中,svd的分解反而会降低程序运行速度,

                解决方法:在大型系统中,SVD每天运行一次或者频率更低,并且要离线运行

2:另一种潜在的计算资源浪费来自于相似度得分

               解决方法:离线计算并保存相似度得分

3:冷启动

        解决方法:基于内容的推荐 如菜肴中的素食、美式BBQ、价格很贵等等都可以作为相似度计算所需要的数据

猜你喜欢

转载自blog.csdn.net/zhuisaozhang1292/article/details/81347471