隱語義模型:
物品 表示為長度為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)