【推薦算法】從零開始做推薦(三)——傳統矩陣分解的TopK推薦實戰

前言

  在前兩章,我們已對本系列的數據集、評價指標做了相應的介紹,從本章開始將進行推薦實戰,算法上從最經典的矩陣分解講起。

  如果你對本系列(未寫完,持續更新中)感興趣,可接以下傳送門:
  【推薦算法】從零開始做推薦(一)——認識推薦、認識數據
  【推薦算法】從零開始做推薦(二)——推薦系統的評價指標,計算原理與實現樣例
  【推薦算法】從零開始做推薦(三)——傳統矩陣分解的TopK推薦
  【推薦算法】從零開始做推薦(四)——python Keras框架 利用Embedding實現矩陣分解TopK推薦
  【推薦算法】從零開始做推薦(五)——貝葉斯個性化排序矩陣分解 (BPRMF) 推薦實戰
  【推薦算法】從零開始做推薦(六)——貝葉斯性化排序矩陣分解 (BPRMF) 的Tensorflow版

矩陣分解

  本系列以實戰爲主,算法上講下大概的思想。矩陣分解較全的分析介紹可參照機器學習矩陣分解解析Recommender.Matrix.Factorization。本文實現的是最簡的矩陣分解版本,詳細思想參照矩陣分解在協同過濾推薦算法中的應用,主體算法公式參照梯度下降的矩陣分解公式推導
  什麼是矩陣分解?
  下面進行簡單介紹。矩陣分解,顧名思義是將矩陣進行拆分,線性代數裏兩個矩陣相乘可以得到一個新的矩陣,而矩陣分解的思想就是矩陣乘法的逆運用,將一個矩陣分解成兩個矩陣相乘。注意,這裏的分解是近似的,因爲有可能找不出兩個矩陣的乘積恰好等於原矩陣。而這個近似就給推薦帶來了思路。
  分解前的矩陣是什麼?
  答:用戶與項目的交互矩陣,具體含義可以是評分、簽到、點擊等等。大致分爲兩類,一類是0-1矩陣,即有過交互即爲1,否則爲0;另一類是具體的數值,根據交互的次數、評價等得出。這裏我們用Rating代指分解前的矩陣。
  分解後的兩個矩陣分別是什麼?
  答:分別爲用戶隱因子矩陣、項目隱因子矩陣,中間維度K認爲是特徵的數量。這樣一來就用兩個矩陣分別表示了用戶與項目。假設Rating的維度是M×N的,那麼就可以分解爲M×K的User矩陣,K×N的Item矩陣,滿足矩陣Rating和矩陣[User×Item]中的非零項儘可能相等。
在這裏插入圖片描述
  爲什麼要矩陣分解?
  答:無論Rating是何種矩陣,我們都默認了如果用戶和項目未交互則對應位置填0,但實際上用戶對該項目是可能感興趣的,如何得到這個興趣程度呢?我們讓分解後的矩陣儘量與原矩陣逼近,注意,逼近的只有原矩陣的非零項。因爲是近似的分解,所以得到的新矩陣,原先非零位置就有了數值,於是我們就可以根據新矩陣的數值由大到小進行排序取前K個進行TopK推薦。

核心算法

  自己寫是不可能自己寫的,Github上雖然有很多,但都是面向對象的寫法。面向對象的代碼儘管複用性強,也易於修改,但就是寫得太羅嗦,不能很直觀的理解。
  這裏與理論統一,對網上一個廣泛流傳的版本進行修改,先看理論部分:
在這裏插入圖片描述
  修改完的代碼如下,採用的是最簡單版本的梯度下降,步長alpha和正則lamda(正確打法是lambda,代碼寫lamda是爲了與python中的關鍵字作區分)固定。
  對梯度下降感興趣的可以重點看下這段代碼,關鍵之處在於梯度下降結束的三個條件:
  梯度下降結束條件:
    1.滿足最大迭代次數;
    2.loss過小;
    3.loss之差過小,梯度消失。

# -*- coding: utf-8 -*-
"""
Created on Fri Apr 10 14:42:17 2020

@author: Yang Lechuan
"""
import numpy as np
import time 
def matrix_factorization(R,P,Q,d,steps,alpha=0.05,lamda=0.002):
    Q=Q.T
    sum_st = 0 #總時長
    e_old = 0 #上一次的loss
    flag = 1
    for step in range(steps): #梯度下降結束條件:1.滿足最大迭代次數,跳出
        st = time.time()
        e_new = 0
        for u in range(len(R)):
            for i in range(len(R[u])):
                if R[u][i]>0:
                    eui=R[u][i]-np.dot(P[u,:],Q[:,i])
                    for k in range(d):
                        P[u][k] = P[u][k] + alpha*eui * Q[k][i]- lamda *P[u][k]
                        Q[k][i] = Q[k][i] + alpha*eui * P[u][k]- lamda *Q[k][i]
        cnt = 0
        for u in range(len(R)):
            for i in range(len(R[u])):
                if R[u][i]>0:
                    cnt = cnt + 1
                    e_new = e_new + pow(R[u][i]-np.dot(P[u,:],Q[:,i]),2)
        et = time.time()
        e_new = e_new / cnt
        if step == 0: #第一次不算loss之差
            e_old = e_new
            continue
        sum_st = sum_st + (et-st)
        if e_new<1e-3:#梯度下降結束條件:2.loss過小,跳出
            flag = 2
            break
        if e_old - e_new<1e-10:#梯度下降結束條件:3.loss之差過小,梯度消失,跳出
            flag = 3
            break
        else:
            e_old = e_new
    print('---------Summary----------\n',
      'Type of jump out:',flag,'\n',
      'Total steps:',step + 1,'\n',
      'Total time:',sum_st,'\n',
      'Average time:',sum_st/(step+1.0),'\n',
      "The e is:",e_new)        
    return P,Q.T

R=[
   [5,2,0,3,1],
   [0,2,1,4,5],
   [1,1,0,2,4],
   [2,2,0,5,0]
   ]
d = 3
steps = 5000
N = len(R)
M = len(R[0])
P = np.random.normal(loc=0,scale=0.01,size=(N,d)) #正態分佈隨機初始化
Q = np.random.normal(loc=0,scale=0.01,size=(M,d))
nP,nQ = matrix_factorization(R,P,Q,d,steps)
print('-----原矩陣R:------')
print(R)
print('-----近似矩陣nR:------')
print(np.dot(nP,nQ.T))

  這塊代碼的目的是爲了驗證,我們的矩陣分解算法是否真的做到了分解。來看看結果。
在這裏插入圖片描述
  簡單來講,行就是用戶,列就是項目,數值可看成評分。可以看出,原先爲0的位置也有了評分,我們依靠新矩陣的評分進行推薦。
  注意,這裏的e是指損失函數,用的是MSE,計算公式如下:
在這裏插入圖片描述

ML100K實現完整的矩陣分解TopK推薦

  數據集介紹及訓練集測試集劃分請看【推薦算法】從零開始做推薦(一)——認識推薦、認識數據
評價指標請看【推薦算法】從零開始做推薦(二)——推薦系統的評價指標,計算原理與實現樣例
  簡單的數據集介紹:
在這裏插入圖片描述

構造矩陣

  要將矩陣分解的核心算法進行應用到數據集上,首先要根據數據集得到矩陣R,數據集直接由評分這列,非常簡單就可以實現。

def getUI(dsname,dformat): #獲取全部用戶和項目
    st = time.time()
    train = pd.read_csv(dsname+'_train.txt',header = None,names = dformat)
    test = pd.read_csv(dsname+'_test.txt',header = None,names = dformat)
    data = pd.concat([train,test])
    all_user = np.unique(data['user'])
    all_item = np.unique(data['item'])
    train.sort_values(by=['user','item'],axis=0,inplace=True) #先按時間、再按用戶排序
    num_user = max(all_user)+1
    num_item = max(all_item)+1
    rating = np.zeros([num_user,num_item])
    for i in range(0,len(train)):
        user = train.iloc[i]['user']
        item = train.iloc[i]['item']
        score = train.iloc[i]['rating']
#        score = 1
        rating[user][item] = score
    if os.path.exists('./Basic MF'):
        pass
    else:
        os.mkdir('./Basic MF')
    train.to_csv('./Basic MF/train.txt',index = False,header=0)
    test.to_csv('./Basic MF/test.txt',index = False,header=0)
    np.savetxt('./Basic MF/rating.txt',rating,delimiter=',',newline='\n')
    et = time.time()
    print("get UI complete! cost time:",et-st)

  爲了在多次實驗裏節省時間,我們將構建好的數據存在磁盤中,省得每次都要運行佔內存。於是我們還需要一個從本地讀數據的函數。

def getData(dformat):
    rating = np.loadtxt('./Basic MF/rating.txt',delimiter=',',dtype=float)
    train = pd.read_csv('./Basic MF/train.txt',header = None,names = dformat)
    test = pd.read_csv('./Basic MF/test.txt',header = None,names = dformat)
    data = pd.concat([train,test])
    all_user = np.unique(data['user'])
    all_item = np.unique(data['item'])
    return rating,train,test,all_user,all_item

訓練

  接下來就可以訓練了,訓練時包裝成函數,方便調用。同樣,爲了多次實驗方便,我們將訓練完成的數據存在本地,在測試的時候調用。

def train(rating,d,steps):
    R = rating
    N=len(R) #用戶數
    M=len(R[0]) #項目數
    P = np.random.normal(loc=0,scale=0.001,size=(N,d))
    Q = np.random.normal(loc=0,scale=0.001,size=(M,d))
    nP,nQ = matrix_factorization(R,P,Q,d,steps)
#    nR=np.dot(nP,nQ.T)
    np.savetxt('./Basic MF/nP.txt',nP,delimiter=',',newline='\n')
    np.savetxt('./Basic MF/nQ.txt',nQ,delimiter=',',newline='\n')

測試

  在測試之前還需要幾個步驟,首先是評價指標,這裏選用本系列(二)中的PRE,REC,MAP,MRR。代碼就不單獨展示了,後文有全部代碼。
  除此之外,我們還需要一個TopK排序的算法。這也是TopK推薦的特色,我們需要返回評分最高的前K個項目,根據評分而得出項目,這裏存在評分和項目的對應關係,因此用字典來實現。實現方法就每次找最高的,直到找滿K個爲止。

def topk(dic,k):
    keys = []
    values = []
    for i in range(0,k):
        key,value = max(dic.items(),key=lambda x: x[1])
        keys.append(key)
        values.append(value)
        dic.pop(key)
    return keys,values

  接下來就是對測試的思考,首先明確,測試的對象是誰?是測試集裏的用戶! 而非全部用戶。
  其次,這裏提出幾點限制:
  限制1. 必須推薦給用戶其未訪問過的項目。 從直覺上來講,用戶訪問過的項目用戶本身自己知道,如果用戶已知一個項目還去交互的話,他帶着非常強的主觀意願,此時你推薦或不推薦都不影響他的決策。因此,推薦用戶已知的項目,意義是不大的。推薦系統應該更關注用戶的深層偏好,通俗來講就是AI找到你自己都不知道的愛好,給人一種驚喜感。
  限制2. 單次推薦中不能給用戶重複推薦項目。 這很好理解,實際上這一步是怕用戶真實訪問記錄裏有相同的,而TopK推薦只可能推薦一個項目一次,因此若不去重,則評價指標會偏低。
  這個限制轉化成代碼分爲三步:
  1)去掉測試集中某目標用戶已訪問過的項目。
  2)將測試集中某目標用戶的真實訪問記錄去重。
  3)若經過1)2)後測試集中某目標用戶的真實訪問記錄變爲空集,則跳過且不計數。
  經過上述討論,我們得到如下代碼:

def test(train_data,test_data,all_item,dsname,k):
    nP = np.loadtxt('./Basic MF/nP.txt',delimiter=',',dtype=float)
    nQ = np.loadtxt('./Basic MF/nQ.txt',delimiter=',',dtype=float)
    PRE = 0
    REC = 0
    MAP = 0
    MRR = 0
    
    AP = 0
    HITS = 0
    sum_R = 0
    sum_T = 0
    valid_cnt = 0
    stime = time.time()
    test_user = np.unique(test_data['user'])
    for user in test_user:
#        user = 0
        visited_item = list(train_data[train_data['user']==user]['item'])
#        print('訪問過的item:',visited_item)
        if len(visited_item)==0: #沒有訓練數據,跳過
            continue
        per_st = time.time()
        testlist = list(test_data[test_data['user']==user]['item'].drop_duplicates()) #去重保留第一個
        testlist = list(set(testlist)-set(testlist).intersection(set(visited_item))) #去掉訪問過的item
        if len(testlist)==0: #過濾後爲空,跳過
            continue
#        print("對用戶",user)
        valid_cnt = valid_cnt + 1 #有效測試數
        poss = {}        
        for item in all_item:
            if item in visited_item:
                continue
            else:
                poss[item] = np.dot(nP[user],nQ[item])
#        print(poss)
        rankedlist,test_score = topk(poss,k) 
#        print("Topk推薦:",rankedlist)
#        print("實際訪問:",testlist)
#        print("單條推薦耗時:",time.time() - per_st)
        AP_i,len_R,len_T,MRR_i,HITS_i= cal_indicators(rankedlist, testlist)
        AP += AP_i
        sum_R += len_R
        sum_T += len_T
        MRR += MRR_i
        HITS += HITS_i
#        print(test_score)
#        print('--------')
#        break
    etime = time.time()
    PRE = HITS/(sum_R*1.0)
    REC = HITS/(sum_T*1.0)
    MAP = AP/(valid_cnt*1.0)
    MRR = MRR/(valid_cnt*1.0)
    p_time = (etime-stime)/valid_cnt
    print('評價指標如下:')
    print('PRE@',k,':',PRE)
    print('REC@',k,':',REC)
    print('MAP@',k,':',MAP)
    print('MRR@',k,':',MRR)
    print('平均每條推薦耗時:',p_time)
    with open('./Basic MF/result_'+dsname+'.txt','w') as f:
        f.write('評價指標如下:\n')
        f.write('PRE@'+str(k)+':'+str(PRE)+'\n')
        f.write('REC@'+str(k)+':'+str(REC)+'\n')
        f.write('MAP@'+str(k)+':'+str(MAP)+'\n')
        f.write('MRR@'+str(k)+':'+str(MRR)+'\n') 
        f.write('平均每條推薦耗時@:'+str(k)+':'+str(p_time)+'\n') 

結果分析

  這裏的迭代次數就不像自己構造的小矩陣那樣設那麼高了,因爲時間成本比較高,且也並非迭代越多越好。
  這裏擺一個多次運行TopK@10的結果,注意這裏的d=40α=0.05λ=0.002d=40,α=0.05,λ=0.002,別問,問就是試出來的。可以看出隨step的迭代次數,指標先增後減。
在這裏插入圖片描述

進階!靈魂拷問

  拷問1. 矩陣分解的效果與什麼因素有關?
  1) 與潛在因子矩陣P,Q的初始值有關。一開始看網上說直接(0,1)之間,但實際效果很差,後來調小了才變好,猜測是過大的話如果跑偏了(即本來不喜歡結果初值很高),梯度一直下降也救不回來,還有就是正態分佈可以有負數,這在潛在特徵裏是允許的,而(0,1)要變爲負數更加困難,都是正數變相地增加了分解地難度。
  2) 與學習率α與正則參數λ有關。這個有兩種解決方式,歐皇之間隨便調,玄學調參。老實人可以選擇更好的梯度下降策略,SGD,ADAM等等。
  3) 與潛在矩陣的維度d有關。這個可以有限範圍內的調試,如20~100,20一增。如果是打比賽的話還是看臉。
  3) 與迭代次數steps有關。歐皇笑了,非酋哭了。
  拷問2. 爲什麼steps越高,效果有可能反而不好?
  在本實驗中,steps越高,損失e確實是在下降,但效果反而不好,用機器學習的理論來講,這就是出現了過擬合。其次,如果是0-1矩陣,從主觀上來分析,交互過和喜歡並非是絕對關係,因此越貼合原矩陣,並非就越喜歡。
  拷問3. 評分矩陣和0-1矩陣,哪個效果好?
  這個應該沒有絕對的比較,從本次實驗來講,下圖爲0-1矩陣分解的結果,是評分的更好。理論上看似如果評分信息是真是代表用戶偏好,那麼評分的會更好,實則用戶的偏好是在一直變化的,0-1矩陣更提供了泛化的可能,而評分限制的較死。更真實的情況在於用戶評分數據會過於稀疏,真實環境裏很難有效,而0-1交互數據更容易收集,也就更稠密。因此我個人是站0-1這邊的。
在這裏插入圖片描述
  拷問4. 換個大點的數據集(ML1M),效果怎麼樣?
  下圖爲參數不動,直接拿來用的結果。
在這裏插入圖片描述
  1)參數需要重新調整。
  2)時間開銷猛增,傳統矩陣分解本身就是一個時間開銷較大的算法,這與矩陣的構建方式、行列數量有關。

PS:求點贊、關注、打賞,畢竟這些我都沒啥-。-

完整代碼

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 19 12:37:04 2019

@author: YLC
"""
import os
import numpy as np
import pandas as pd
import time
import math

def getUI(dsname,dformat): #獲取全部用戶和項目
    st = time.time()
    train = pd.read_csv(dsname+'_train.txt',header = None,names = dformat)
    test = pd.read_csv(dsname+'_test.txt',header = None,names = dformat)
    data = pd.concat([train,test])
    all_user = np.unique(data['user'])
    all_item = np.unique(data['item'])
    train.sort_values(by=['user','item'],axis=0,inplace=True) #先按時間、再按用戶排序
    num_user = max(all_user)+1
    num_item = max(all_item)+1
    rating = np.zeros([num_user,num_item])
    for i in range(0,len(train)):
        user = train.iloc[i]['user']
        item = train.iloc[i]['item']
        score = train.iloc[i]['rating']
#        score = 1
        rating[user][item] = score
    if os.path.exists('./Basic MF'):
        pass
    else:
        os.mkdir('./Basic MF')
    train.to_csv('./Basic MF/train.txt',index = False,header=0)
    test.to_csv('./Basic MF/test.txt',index = False,header=0)
    np.savetxt('./Basic MF/rating.txt',rating,delimiter=',',newline='\n')
    et = time.time()
    print("get UI complete! cost time:",et-st)
    
def getData(dformat):
    rating = np.loadtxt('./Basic MF/rating.txt',delimiter=',',dtype=float)
    train = pd.read_csv('./Basic MF/train.txt',header = None,names = dformat)
    test = pd.read_csv('./Basic MF/test.txt',header = None,names = dformat)
    data = pd.concat([train,test])
    all_user = np.unique(data['user'])
    all_item = np.unique(data['item'])
    return rating,train,test,all_user,all_item

def topk(dic,k):
    keys = []
    values = []
    for i in range(0,k):
        key,value = max(dic.items(),key=lambda x: x[1])
        keys.append(key)
        values.append(value)
        dic.pop(key)
    return keys,values

def cal_indicators(rankedlist, testlist):
    HITS_i = 0
    sum_precs = 0
    AP_i = 0 
    len_R = 0 
    len_T = 0
    MRR_i = 0 

    ranked_score = []
    for n in range(len(rankedlist)):
        if rankedlist[n] in testlist:
            HITS_i += 1
            sum_precs += HITS_i / (n + 1.0)
            if MRR_i == 0:
                MRR_i = 1.0/(rankedlist.index(rankedlist[n])+1)
                
        else:
            ranked_score.append(0)
    if HITS_i > 0:
        AP_i = sum_precs/len(testlist)
    len_R = len(rankedlist)
    len_T = len(testlist)
    return AP_i,len_R,len_T,MRR_i,HITS_i

def matrix_factorization(R,P,Q,d,steps,alpha=0.002,lamda=2e-6):
    Q=Q.T
    sum_st = 0 #總時長
    e_old = 0 #上一次的loss
    flag = 1
    for step in range(steps): #梯度下降結束條件:1.滿足最大迭代次數,跳出
        st = time.time()
        e_new = 0
        for u in range(len(R)):
            for i in range(len(R[u])):
                if R[u][i]>0:
                    eui=R[u][i]-np.dot(P[u,:],Q[:,i])
                    for k in range(d):
                        P[u][k] = P[u][k] + alpha*eui * Q[k][i]- lamda *P[u][k]
                        Q[k][i] = Q[k][i] + alpha*eui * P[u][k]- lamda *Q[k][i]
        cnt = 0
        for u in range(len(R)):
            for i in range(len(R[u])):
                if R[u][i]>0:
                    cnt = cnt + 1
                    e_new = e_new + pow(R[u][i]-np.dot(P[u,:],Q[:,i]),2)
        et = time.time()
        print('step',step+1,'cost time:',et-st)
        e_new = e_new / cnt
        if step == 0: #第一次不算loss之差
            e_old = e_new
            continue
        sum_st = sum_st + (et-st)
        if e_new<1e-3:#梯度下降結束條件:2.loss過小,跳出
            flag = 2
            break
        if e_old - e_new<1e-10:#梯度下降結束條件:3.loss之差過小,梯度消失,跳出
            flag = 3
            break
        else:
            e_old = e_new
    print('---------Summary----------\n',
      'Type of jump out:',flag,'\n',
      'Total steps:',step + 1,'\n',
      'Total time:',sum_st,'\n',
      'Average time:',sum_st/(step+1.0),'\n',
      "The e is:",e_new)        
    return P,Q.T


def train(rating,d,steps):
    R = rating
    N=len(R) #用戶數
    M=len(R[0]) #項目數
    P = np.random.normal(loc=0,scale=0.01,size=(N,d))
    Q = np.random.normal(loc=0,scale=0.01,size=(M,d))
    nP,nQ = matrix_factorization(R,P,Q,d,steps)
#    nR=np.dot(nP,nQ.T)
    np.savetxt('./Basic MF/nP.txt',nP,delimiter=',',newline='\n')
    np.savetxt('./Basic MF/nQ.txt',nQ,delimiter=',',newline='\n')
    
def test(train_data,test_data,all_item,dsname,k):
    nP = np.loadtxt('./Basic MF/nP.txt',delimiter=',',dtype=float)
    nQ = np.loadtxt('./Basic MF/nQ.txt',delimiter=',',dtype=float)
    PRE = 0
    REC = 0
    MAP = 0
    MRR = 0
    
    AP = 0
    HITS = 0
    sum_R = 0
    sum_T = 0
    valid_cnt = 0
    stime = time.time()
    test_user = np.unique(test_data['user'])
    for user in test_user:
#        user = 0
        visited_item = list(train_data[train_data['user']==user]['item'])
#        print('訪問過的item:',visited_item)
        if len(visited_item)==0: #沒有訓練數據,跳過
            continue
        per_st = time.time()
        testlist = list(test_data[test_data['user']==user]['item'].drop_duplicates()) #去重保留第一個
        testlist = list(set(testlist)-set(testlist).intersection(set(visited_item))) #去掉訪問過的item
        if len(testlist)==0: #過濾後爲空,跳過
            continue
#        print("對用戶",user)
        valid_cnt = valid_cnt + 1 #有效測試數
        poss = {}        
        for item in all_item:
            if item in visited_item:
                continue
            else:
                poss[item] = np.dot(nP[user],nQ[item])
#        print(poss)
        rankedlist,test_score = topk(poss,k) 
#        print("Topk推薦:",rankedlist)
#        print("實際訪問:",testlist)
#        print("單條推薦耗時:",time.time() - per_st)
        AP_i,len_R,len_T,MRR_i,HITS_i= cal_indicators(rankedlist, testlist)
        AP += AP_i
        sum_R += len_R
        sum_T += len_T
        MRR += MRR_i
        HITS += HITS_i
#        print(test_score)
#        print('--------')
#        break
    etime = time.time()
    PRE = HITS/(sum_R*1.0)
    REC = HITS/(sum_T*1.0)
    MAP = AP/(valid_cnt*1.0)
    MRR = MRR/(valid_cnt*1.0)
    p_time = (etime-stime)/valid_cnt
    print('評價指標如下:')
    print('PRE@',k,':',PRE)
    print('REC@',k,':',REC)
    print('MAP@',k,':',MAP)
    print('MRR@',k,':',MRR)
    print('平均每條推薦耗時:',p_time)
    with open('./Basic MF/result_'+dsname+'.txt','w') as f:
        f.write('評價指標如下:\n')
        f.write('PRE@'+str(k)+':'+str(PRE)+'\n')
        f.write('REC@'+str(k)+':'+str(REC)+'\n')
        f.write('MAP@'+str(k)+':'+str(MAP)+'\n')
        f.write('MRR@'+str(k)+':'+str(MRR)+'\n') 
        f.write('平均每條推薦耗時@:'+str(k)+':'+str(p_time)+'\n') 
        
if __name__ == '__main__':
    dsname = 'ML100K'
    dformat = ['user','item','rating','time']
    getUI(dsname,dformat) #第一次使用後可註釋
    rating,train_data,test_data,all_user,all_item = getData(dformat)
    d = 40 #隱因子維度
    steps = 10
    k = 10
    train(rating,d,steps)
    test(train_data,test_data,all_item,dsname,k)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章