ビッグ データ - 協調フィルタリング推奨アルゴリズム: 線形回帰アルゴリズム

レコメンデーション システムの協調フィルタリング アルゴリズムは、通常、次の 2 つのカテゴリに分類されます。

  • 行動ベースの協調フィルタリング アルゴリズム (メモリベース CF) は、ユーザー行動データを使用して、ユーザー間の類似性やアイテム間の類似性などの類似性を計算します。
  • 機械学習アルゴリズムを使用してユーザーの好みを予測するモデルベースの協調フィルタリング アルゴリズム (モデルベース CF) は、ユーザー データがまばらな場合に適しています。

この記事では主にモデルベースの協調フィルタリング アルゴリズムを紹介します。

1.モデル協調フィルタリングアルゴリズムに基づくモデルベースCF

ユーザーアイテム評価マトリックスを使用して機械学習モデルをトレーニングし、アイテムに対するユーザーの評価を予測します。評価は主に次のカテゴリに分類できます。

  • 分割、回帰、またはクラスタリング アルゴリズムに基づく
  • 行列分解に基づく推奨アルゴリズム
  • ニューラルネットワークアルゴリズムに基づく
  • グラフィカルモデルベースのアルゴリズム

2. 回帰モデルアルゴリズムに基づく協調フィルタリング

回帰モデルの前提は連続値であるため、スコアを連続値とみなし、以下のBaseline(ベースライン予測)実装戦略を採用します。アイデアは、全員の好みを異なる方法で使用することです。

優しいユーザーで他のユーザーより評価が高い、厳しいユーザーで他のユーザーより評価が低い、人気がある一方で一般的なアイテムより評価が高く、嫌いなアイテムもあるなど、通常のアイテムより評価が低くなります。

ベースラインは、各ユーザーと他のユーザーのバイアス値bu b_uを見つけることです。bあなた、各項目の他の項目に対するバイアス値bi b_ib私は、最終的な目標は、最適なbu b_uを見つけることになります。bあなた b i b_i b私はしたがって、ベースライン アルゴリズムの手順は次のとおりです。

  1. すべての映画の平均評価uuを計算しますあなた;
  2. 各ユーザーの評価と平均評価のバイアス値bu b_uを計算しますbあなた;
  3. 各映画の評価と平均評価のバイアス値bi b_iを計算しますb私は
  4. 预测用户对电影的评分:
    r ^ u i = b u i = u + b u + b i \hat{r}_{ui} = b_{ui} = u+b_u+b_i r^あなたは=bあなたは=あなた+bあなた+b私は

例として、ユーザー A の「Fengshen Part I」の評価を考えてみましょう。

  1. まず、すべての映画の平均評価を計算しますu = 3.5 u=3.5あなた=3.5
  2. ユーザー A はより親切で、一般に平均スコアより 1 ポイント高く、バイアス値bu = 1 b_u=1bあなた=1
  3. 「風神 Part I」は当初悪いレビューが多く、スコアは平均スコアより 0.5 ポイント低く、バイアス値bi = − 0.5 b_i=-0.5b私は=−0.5 _
  4. この場合、ユーザー A の「風神 Part I」の評価は、3.5+1-0.5=4.1 ポイントとなります。

在线性回归问题中,我们用平方差构建损失函数:
C o s t = ∑ u , i ∈ R ( r u i − r ^ u i ) 2 = ∑ u , i ∈ R ( r u i − u − b u − b i ) 2 Cost = \sum_{u,i∈R}(r_{ui}-\hat{r}_{ui})^2 = \sum_{u,i∈R}(r_{ui}-u-b_u-b_i)^2 コスト_ _=u , i R( rあなたはr^あなたは2=u , i R( rあなたはあなたbあなたb私は2
为了防止过拟合,需要加入L2范式,最后的公示如下:
C o s t = ∑ u , i ∈ R ( r u i − u − b u − b i ) 2 + λ ( ∑ u b u 2 + ∑ i b i 2 ) Cost = \sum_{u,i∈R}(r_{ui}-u-b_u-b_i)^2 + \lambda(\sum_u{b_u}^2+\sum_i{b_i}^2) コスト_ _=u , i R( rあなたはあなたbあなたb私は2+l (あなたbあなた2+b私は2 )
損失関数の最小値を取得したいと考えており、一般に確率的勾配降下法または最小二乗法を使用して実現を最適化します。

2.1 ベースラインの確率的勾配降下法アルゴリズム

step1 : 勾配降下法の導出:
J ( θ ) = f ( bu , bi ) J(θ) = f(b_u,b_i)J ( θ )=f ( bあなたb私は)
勾配降下パラメータ更新の元の式:
θ j : = θ j − α ∂ ∂ θ j J ( θ ) \theta_j :=\theta_j-\alpha\frac{∂}{∂\theta_j}J(\theta)j:=jある∂θ _jJ(θ)
对参数求偏导:
∂ ∂ b u J ( θ ) = ∂ ∂ b u f ( b u , b i ) = − 2 ∑ u , i ∈ R ( r u i − u − b u − b i ) + 2 λ b u \frac{∂}{∂b_u}J(\theta) = \frac{∂}{∂b_u}f(b_u,b_i) = -2\sum_{u,i∈R}(r_{ui}-u-b_u-b_i) + 2\lambda b_u ∂b _あなたJ ( θ )=∂b _あなたf ( bあなたb私は=2u , i R( rあなたはあなたbあなたb私は+2 λ bあなた
代入梯度下降参数更新公式:
b u : = b u + α ( ∑ u , i ∈ R ( r u i − u − b u − b i ) − λ b u ) b_u:=b_u+\alpha(\sum_{u,i∈R}(r_{ui}-u-b_u-b_i) -\lambda b_u) bあなた:=bあなた+( _u , i R( rあなたはあなたbあなたb私はλb _あなた)
bi : = bi + α ( ∑ u , i ∈ R ( rui − u − bu − bi ) − λ bi ) b_i:=b_i+\alpha(\sum_{u,i∈R}(r_{ui}-u) -b_u-b_i) -\lambda b_i)b私は:=b私は+( _u , i R( rあなたはあなたbあなたb私はλb _私は)
ステップ 2: 確率的勾配降下
法 確率的勾配降下法では、基本的に損失の合計を毎回計算するのではなく、各サンプルの損失を使用してパラメーターを更新します。
1 サンプル損失値:
error = rui − r ^ ui = rui − u − bu − bi error = r_{ui} - \hat{r}_{ui} = r_{ui} - u-b_u-b_iエラー=rあなたはr^あなたは=rあなたはあなたbあなたb私は
したがって、勾配降下の式は次のように更新できます。
bu : = bu + α ( error − λ bu ) b_u:=b_u+\alpha(error -\lambda b_u)bあなた:=bあなた+α エラーλb _あなた)
bi : = bi + α ( error − λ bi ) b_i:=b_i+\alpha(error -\lambda b_i)b私は:=b私は+α エラーλb _私は)
step3: アルゴリズム実装
モジュールとデータをインポート

# 随机梯度下降算法实现
import pandas as pd
import numpy as np
df = pd.read_csv("ml-latest-small/ratings.csv", usecols=range(3))
df

ベースライン勾配降下法アルゴリズムの実装

class BaselineCFBySGD(object):
    '''max_epochs 梯度下降迭代次数
        alpha 学习率
        reg 过拟合参数
        columns 数据字段名称'''
    def __init__(self,max_epochs, alpha,reg,columns=['uid','mid','rating']):
        self.max_epochs = max_epochs
        self.alpha = alpha
        self.reg = reg
        self.columns = columns
        
    def fit(self,data):
        '''
        :param data:uid,mid,rating
        :return:'''
        self.data = data
        # 用户评分数据
        self.users_rating = data.groupby(self.columns[0]).agg([list])[[self.columns[1], self.columns[2]]]
        # 电影评分数据
        self.items_rating = data.groupby(self.columns[1]).agg([list])[[self.columns[0], self.columns[2]]]
        # 全局平均分
        self.global_mean = self.data[self.columns[2]].mean()
        # 调用随机梯度下降训练模型参数
        self.bu,self.bi = self.sgd()
        
    def sgd(self):
        '''
        随机梯度下降,优化bu和bi值
        :return: bu bi'''
        bu = dict(zip(users_rating.index, np.zeros(len(users_rating))))
        bi = dict(zip(items_rating.index, np.zeros(len(items_rating))))
        
        for i in range(max_epochs):
            # 将dataframe的每一行数据单独读出来,代入梯度下降参数公式
            for uid, mid, real_rating in df.itertuples(index=False):
                error = real_rating - (global_mean+bu[uid]+bi[mid])
                bu[uid] += alpha*(error - reg*bu[uid])
                bi[mid] += alpha*(error - reg*bi[mid])
        return bu,bi
    
    def predict(self,uid,mid):
        '''
        使用评分公式进行预测
        param uid,mid;
        return predict_rating;'''
        predict_rating = self.global_mean+self.bu[uid]+self.bi[mid]
        return predict_rating
    
    def test(self,testset):
        '''
        使用预测函数预测测试集数据
        param testset;
        return yield;'''
        for uid,mid,real_rating in testset.itertuples(index=False):
            try:
                # 使用predict函数进行预测
                pred_rating = self.predict(uid,mid)
            except Exception as e:
                print(e)
            else:
                # 返回生成器对象
                yield uid,mid,real_rating,pred_rating

テストセットとトレーニングセットの分割機能

# 训练集和测试集的划分
def data_split(data_path, x=0.8, random=False):
    ratings = pd.read_csv(data_path, usecols=range(3))
    testset_index = []
    
    for uid in ratings.groupby('userId').any().index:
        user_rating_data = ratings.where(ratings['userId']==uid).dropna()
        if random:
            index = list(user_rating_data.index)
            np.random.shuffle(index)
            _index = round(len(user_rating_data)*x)
            testset_index += list(index[_index:])
        else:
            index = round(len(user_rating_data)*x)
            testset_index += list(user_rating_data.index.values[index:])
            
    testset = ratings.loc[testset_index]
    trainset = ratings.drop(testset_index)
    return trainset,testset

アルゴリズム評価関数

def accuray(predict_reselts, method='all'):
    # 计算均方根误差
    def rmse(predict_reselts):
        length = 0
        _rmse_sum = 0
        for uid,mid, real_rating, pred_rating in predict_reselts.itertuples(index=False):
            length+=1
            _rmse_sum += (pred_rating - real_rating)**2
        return round(np.sqrt(_rmse_sum/length),4)
    
    # 计算绝对值误差
    def mae(predict_reselts):
        length=0
        _mae_sum=0
        for uid,mid,real_rating,pred_rating in predict_reselts.itertuples(index=False):
            length +=1
            _mae_sum += abs(pred_rating-real_rating)
        return round(_mae_sum/length,4)
    
    # 两个都计算
    def rmse_mae(predict_reselts):
        length = 0
        _rmse_sum=0
        _mae_sum=0
        for uid,mid,real_rating,pred_rating in predict_reselts.itertuples(index=False):
            length +=1
            _mae_sum += abs(pred_rating-real_rating)
            _rmse_sum += (pred_rating - real_rating)**2
        return round(np.sqrt(_rmse_sum/length),4),round(_mae_sum/length,4)
    
    # 根据输入的参数放回对应的评估方法
    if method.lower() =='rmse':
        return rmse(predict_reselts)
    elif method.lower() == 'mae':
        return mae(predict_reselts)
    else:
        return rmse_mae(predict_reselts)

データをアルゴリズムと評価関数に代入する

trainset, testset = data_split('ml-latest-small/ratings.csv',random=True)
bcf = BaselineCFBySGD(20,0.1,0.1,['userId','movieId','rating'])
bcf.fit(trainset)
pred_test = bcf.test(testset)
# 生成器对象用list进行转化,然后转化为dataframe格式
df_pred = pd.DataFrame(list(pred_test), columns=[['userId','movieId','rating','pred_rating']])

rmse, mae = accuray(df_pred,'all')
print('rmse:',rmse,';mae:',mae)

実効値: 0.8647 ;前値: 0.6595

2.2 ベースライン交互最小二乗アルゴリズム

step1 : 交互最小二乗法の導出
基本的な考え方: 損失関数の偏導関数を計算し、その偏導関数を 0 とします。
損失関数は次のとおりです。
J ( θ ) = f ( bu , bi ) J(θ) = f(b_u,b_i)J ( θ )=f ( bあなたb私は)
对参数求偏导:
∂ ∂ b u J ( θ ) = ∂ ∂ b u f ( b u , b i ) = − 2 ∑ u , i ∈ R ( r u i − u − b u − b i ) + 2 λ b u \frac{∂}{∂b_u}J(\theta) = \frac{∂}{∂b_u}f(b_u,b_i) = -2\sum_{u,i∈R}(r_{ui}-u-b_u-b_i) + 2\lambda b_u ∂b _あなたJ ( θ )=∂b _あなたf ( bあなたb私は=2u , i R( rあなたはあなたbあなたb私は+2 λ bあなた
偏导为0,则可得:
∑ u , i ∈ R ( r u i − u − b u − b i ) = 2 λ b u \sum_{u,i∈R}(r_{ui}-u-b_u-b_i) = 2\lambda b_u u , i R( rあなたはあなたbあなたb私は=2 λ bあなた
∑ u , i ∈ R ( rui − u − bi ) = ∑ u ∈ R bu + λ bu \sum_{u,i∈R}(r_{ui}-u-b_i) = \sum_{u∈R}b_u+ \lambda b_uu , i R( rあなたはあなたb私は=u Rbあなた+λb _あなた
为了方便计算,令 ∑ u ∈ R b u ≈ ∣ R ( u ) ∣ ∗ b u \sum_{u∈R}b_u≈|R(u)|*b_u u RbあなたR ( u ) bあなた,则可得:
b u : = ∑ u , i ∈ R ( r u i − u − b i ) λ 1 + ∣ R ( u ) ∣ b_u:=\frac{\sum_{u,i∈R}(r_{ui}-u-b_i)}{\lambda_1+|R(u)|} bあなた:=1+R ( u ) u , i R( rあなたはあなたb私は)

∣ R ( u ) ∣ |R(u)|R ( u ) ∣ はユーザー u の評価数を示します

同理可得:
b i : = ∑ u , i ∈ R ( r u i − u − b u ) λ 2 + ∣ R ( i ) ∣ b_i:=\frac{\sum_{u,i∈R}(r_{ui}-u-b_u)}{\lambda_2+|R(i)|} b私は:=2+R ( i ) u , i R( rあなたはあなたbあなた)
ステップ 2 : 交互最小二乗法 (ALS)
それぞれの式を導出しましたが、式には相互に含まれるため、計算には交互最小二乗法を使用します。

  1. まず値の 1 つを修正し、もう 1 つの値を見つけます。
  2. 次に、別の項目の値を修正し、最初の項目の値を検索します。このようにして 2 つの値を繰り返し更新し、最終的に結果を取得します

要求 b u b_u bあなた、最初のセットbi b_ib私はそれは知られていると考えてください; bi b_iを求めてくださいb私は、最初にbu b_uを入れますbあなた知られているとみなされる

step3 : アルゴリズムの実装
コード全体は確率的勾配降下法と似ています

# 最小二乘法算法实现
class BaselineCFByALS(object):
    '''max_epochs 梯度下降迭代次数
        alpha 学习率
        reg 过拟合参数
        columns 数据字段名称'''
    def __init__(self,max_epochs,reg_bu,reg_bi,columns=['userId','movieId','rating']):
        self.max_epochs = max_epochs
        self.reg_bu = reg_bu
        self.reg_bi = reg_bi
        self.columns = columns
        
    def fit(self,data):
        '''
        :param data:uid,mid,rating
        :return:'''
        self.data = data
        # 用户评分数据
        self.users_rating = data.groupby(self.columns[0]).agg([list])[[self.columns[1], self.columns[2]]]
        # 电影评分数据
        self.items_rating = data.groupby(self.columns[1]).agg([list])[[self.columns[0], self.columns[2]]]
        # 全局平均分
        self.global_mean = self.data[self.columns[2]].mean()
        # 调用随机梯度下降训练模型参数
        self.bu,self.bi = self.als()
        
    def als(self):
        '''
        最小二乘法,优化bu和bi值
        :return: bu bi'''
        bu = dict(zip(users_rating.index, np.zeros(len(users_rating))))
        bi = dict(zip(items_rating.index, np.zeros(len(items_rating))))
        
        for i in range(max_epochs):
            # 计算bi
            for mid, uids, real_ratings in items_rating.itertuples(index=True):
                _sum=0
                for uid,rating in zip(uids,real_ratings):
                    _sum += rating - global_mean-bu[uid]
                bi[mid] = _sum/(self.reg_bi+len(uids))
                    
                # 计算bu
            for uid,mids,real_ratings in users_rating.itertuples(index=True):
                _sum=0
                for mid,rating in zip(mids,real_ratings):
                    _sum+= rating -self.global_mean-bi[mid]
                bu[uid] = _sum/(self.reg_bu+len(mids))
        return bu,bi
    
    def predict(self,uid,mid):
        '''
        使用评分公式进行预测
        param uid,mid;
        return predict_rating;'''
        predict_rating = self.global_mean+self.bu[uid]+self.bi[mid]
        return predict_rating
    
    def test(self,testset):
        '''
        使用预测函数预测测试集数据
        param testset;
        return yield;'''
        for uid,mid,real_rating in testset.itertuples(index=False):
            try:
                # 使用predict函数进行预测
                pred_rating = self.predict(uid,mid)
            except Exception as e:
                print(e)
            else:
                # 返回生成器对象
                yield uid,mid,real_rating,pred_rating

最小二乗アルゴリズムの適用

trainset, testset = data_split('ml-latest-small/ratings.csv',random=True)
bcf = BaselineCFByALS(20,25,15,['userId','movieId','rating'])
bcf.fit(trainset)
pred_test = bcf.test(testset)
# 生成器对象用list进行转化,然后转化为dataframe格式
df_pred_als = pd.DataFrame(list(pred_test), columns=[['userId','movieId','rating','pred_rating']])
rmse, mae = accuray(df_pred_als,'all')
print('rmse:',rmse,';mae:',mae)

rmse: 0.8403 ;mae: 0.6462

おすすめ

転載: blog.csdn.net/gjinc/article/details/132201822