<jupyter_start><jupyter_text>最常见的一种矩阵分解技术就是SVD。SVD将原始的数据集矩阵 data分解为三个矩阵U,$\sigma$,$V^T$.$$Data_{mxn}=U_{m*m} \sigma_{m*n}V^T_{n*n}$$<jupyter_code>from numpy import *
def loadExData():
    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 = loadExData()
U,Sigma,VT = linalg.svd(Data)
Sigma<jupyter_output><empty_output><jupyter_text>前三个值比其他值大了很多，可以认为是噪声$$Data_{m*n} = U_{m*3}\sigma_{3*3}V^T_{3*n}$$<jupyter_code># 还原矩阵
Sig3 = mat([[Sigma[0],0,0],[0,Sigma[1],0],[0,0,Sigma[2]]])
Sig3
U[:,:3]*Sig3*VT[:3,:]<jupyter_output><empty_output><jupyter_text>推荐应用 本次样本数据是11*11，其中行表示用户，列表示食品，中间数字表示该用户对食品的打分。如果数字为0，表示该用户没有吃过该食品。本次模型的目的就是向用户推荐未吃过的食品。推荐思路：* 首先，寻找用户未评价的食品，即用户-矩阵中的0值；* 再次，对用户未打分的食品，通过相似度计算预计其可能会打多少分数；* 最后，对这些打分的食品根据评分从高到低进行排序，返回前N个食品，这就是推荐结果。<jupyter_code>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]]<jupyter_output><empty_output><jupyter_text>基于协同过滤的推荐引擎  相似度计算<jupyter_code># inA inB都是列向量
from numpy import *
from numpy import linalg as la
def ecludSim(inA,inB):
    #为将距离映射到[0,1]中，相似度=1/(1+欧氏距离)
    return 1.0/(1.0 + la.norm(inA - inB))

def pearsSim(inA,inB):
    #皮尔森系数在[-1,1]之间，为映射到[0,1]之间，相似度=0.5+0.5*corroef
    if len(inA) < 3 : return 1.0
    return 0.5+0.5*corrcoef(inA, inB, rowvar = 0)[0][1]

def cosSim(inA,inB):
    #余弦夹角在[-1,1]之间，为映射到[0,1]之间，相似度=0.5+0.5*cos
    num = float(inA.T*inB)
    denom = la.norm(inA)*la.norm(inB)
    return 0.5+0.5*(num/denom)
mymat = mat(loadExData2())
print(ecludSim(mymat[:,0],mymat[:,4]))
print(pearsSim(mymat[:,0],mymat[:,4]))
print(cosSim(mymat[:,0],mymat[:,4]))<jupyter_output>0.0846263260896
0.300354087029
0.5<jupyter_text>基于物品相似度的推荐引擎<jupyter_code>#该函数用来计算在给定相似度计算方法的条件下，用户对物品的估计评分值。
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]
        if userRating == 0 : continue
        #寻找两个用户都评级的物品    
        overLap  = nonzero(logical_and(dataMat[:,item].A>0,\
                          dataMat[:,j].A>0))[0]  
        if len(overLap) ==0:similarity=0
        else:
            similarity = simMeas(dataMat[overLap,item],
                                dataMat[overLap,j])
        simTotal +=similarity
        ratSimTotal +=similarity*userRating
    if simTotal==0:return 0
    else:
        return ratSimTotal/simTotal<jupyter_output><empty_output><jupyter_text>基于SVD的评分估计<jupyter_code>#该函数对给定用户给定物品构建了一个评分估计值。第三行对数据集进行了SVD分解，利用包含了90%能量值的
#奇异值。然后利用这些奇异值构造出对角矩阵，利用U矩阵将物品转换到低维空间中。
def svdEst(dataMat, user, simMeas, item):
    n = shape(dataMat)[1]
    simTotal = 0.0; ratSimTotal = 0.0
    U,Sigma,VT = la.svd(dataMat)
    Sig4 = mat(eye(4)*Sigma[:4]) #构建对角矩阵
    xformedItems = dataMat.T * U[:,:4] * Sig4.I  #构建转换后的物品
    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 
    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]<jupyter_output><empty_output><jupyter_text>示例：对第三行用户进行推荐<jupyter_code>recommend(mymat,2,estMethod=standEst)<jupyter_output><empty_output><jupyter_text>输出结果为（[(3, 4.0), (5, 4.0), (6, 4.0)]），推荐第4/5/6个食品。<jupyter_code>recommend(mymat,2,estMethod=svdEst)<jupyter_output><empty_output><jupyter_text>输出结果为（[(6, 3.04), (5, 3.009), (10, 3.00)]），推荐第7/6/11个食品。 从结果来看SVD推荐效果比较好,因为他对数据噪音进行了处理。  进一步理解 数据处理过程中得到的三个矩阵也有相关物理含义。但需要自己在业务中去摸索和理解。<jupyter_code>u,s,v = linalg.svd(mymat)<jupyter_output><empty_output><jupyter_text>本次样本数据U的前第1列表示的就是用户吃过的食品的个数.两个值越接近表示越相同，其中第2个和8个用户都是＞ -0.1，表示只吃过2个物品。<jupyter_code>u[:,:2]<jupyter_output><empty_output><jupyter_text>V的第1行表示个食品之间的联系，比如都是日式、印式、美式等主题概念。<jupyter_code>v[:2,]<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.17887667887667888, 'fraction_numerical': 0.06695156695156695, 'mean_word_length': 3.832841691248771, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 46, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '624178', 'license': 'Apache-2.0', 'n_tokens_mistral': 2762, 'n_tokens_neox': 2759, 'n_words': 348, 'path': 'RecommendAlgrithm/SVD/SVD .ipynb', 'repo_name': 'paradise6/MachineLearningBasic', 'size': '4914'}