LFM 隱語義模型


隱語義模型:

物品       表示為長度為k的向量q(每個分量都表示  物品具有某個特征的程度)
用戶興趣 表示為長度為k的向量p(每個分量都表示  用戶對某個特征的喜好程度)
用戶u對物品i的興趣可以表示為
  
其損失函數定義為-
      
使用隨機梯度下降,獲得參數p,q
 
負樣本生成:
對於只有正反饋信息(用戶收藏了,關注了xxx)的數據集,需要生成負樣本,原則如下
1.生成的負樣本要和正樣本數量相當
2.物品越熱門(用戶沒有收藏該物品),越有可能是負樣本
 
實現:
# coding=gbk
'''
實現隱語義模型,對隱式數據進行推薦
1.對正樣本生成負樣本
  -負樣本數量相當於正樣本
  -物品越熱門,越有可能成為負樣本
2.使用隨機梯度下降法,更新參數
'''

import numpy as np
import pandas as pd
import random
from sklearn import cross_validation

class LFM():
    
    '''
    初始化隱語義模型
    參數:
    *F  隱特征的個數
    *N  迭代次數
    *data 訓練數據,要求為pandas的dataframe
    *alpha 隨機梯度下降的學習速率
    *r 正則化參數
    *ratio 負樣本/正樣本比例
    '''
    def __init__(self,data,F=100,N=1000,alpha=0.02,r=0.01,ratio=1):
        self.F=F
        self.N=N
        self.alpha=alpha
        self.r=r
        self.data=data
        self.ratio=ratio
    
    '''
    初始化物品池,物品池中物品出現的次數與其流行度成正比
    '''    
    def InitItemPool(self):
        self.itemPool=[]
        groups = self.data.groupby([1])
        for item,group in groups:
            for i in range(group.shape[0]):
                self.itemPool.append(item)
    
    '''
    獲取每個用戶對應的商品(用戶購買過的商品)列表,如
    {用戶1:[商品A,商品B,商品C],
     用戶2:[商品D,商品E,商品F]...}
    ''' 
    def user_item(self,data):
        ui = dict()
        groups = data.groupby([0])
        for item,group in groups:
            ui[item]=set(group.ix[:,1])
        
        return ui
    
    '''
    初始化隱特征對應的參數
    numpy的array存儲參數,使用dict存儲每個用戶(物品)對應的列
    '''
    def initParam(self):
        users=set(self.data.ix[:,0])
        items=set(self.data.ix[:,1])
        
        self.Pdict=dict()
        self.Qdict=dict()
        for user in users:
            self.Pdict[user]=len(self.Pdict)
        
        for item in items:
            self.Qdict[item]=len(self.Qdict)
        
        self.P=np.random.rand(self.F,len(users))/10
        self.Q=np.random.rand(self.F,len(items))/10
    
    '''
    使用隨機梯度下降法,更新參數
    '''
    def stochasticGradientDecent(self):
        alpha=self.alpha
        for i in range(self.N):
            for user,items in self.ui.items():
                ret=self.RandSelectNegativeSamples(items)
                for item,rui in ret.items():
                   p=self.P[:,self.Pdict[user]]
                   q=self.Q[:,self.Qdict[item]]
                   eui=rui-sum(p*q)
                   tmp=p+alpha*(eui*q-self.r*p)
                   self.Q[:,self.Qdict[item]]+=alpha*(eui*p-self.r*q)
                   self.P[:,self.Pdict[user]]=tmp
            alpha*=0.9
            print i
            
    def Train(self):
        self.InitItemPool()
        self.ui = self.user_item(self.data)
        self.initParam()
        self.stochasticGradientDecent()
    
    def Recommend(self,user,k):
        items=self.ui[user]
        p=self.P[:,self.Pdict[user]]
        
        rank = dict()
        for item,id in self.Qdict.items():
            if item in items:
                continue
            q=self.Q[:,id];
            rank[item]=sum(p*q)
        return sorted(rank.items(),lambda x,y:cmp(x[1],y[1]),reverse=True)[0:k-1];
    '''
    生成負樣本
    '''
    def RandSelectNegativeSamples(self,items):
        ret=dict()
        for item in items:
            #所有正樣本評分為1
            ret[item]=1
        #負樣本個數,四舍五入
        negtiveNum = int(round(len(items)*self.ratio))
        
        N = 0
        while N<negtiveNum:
            item = self.itemPool[random.randint(0, len(self.itemPool) - 1)]
            if item in items:
                #如果在用戶已經喜歡的物品列表中,繼續選
                continue
            N+=1
            #負樣本評分為0
            ret[item]=0
        return ret

data=pd.read_csv('../data/ratings.dat',sep='::',nrows=10000,header=None)
data=data.ix[:,0:1]

train,test=cross_validation.train_test_split(data,test_size=0.2)
train = pd.DataFrame(train)
test = pd.DataFrame(test)

lfm = LFM(data=train)
lfm.Train()
lfm.Recommend(1, 10)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM