Базовый уровень совместной фильтрации на основе регрессионной модели глубокого обучения

машинное обучение

Обзор

Ранее для вычисления подобия использовались пользовательский вектор и вектор элементов, но если это разреженный массив, в номере группы имеется большое количество значений None, что может привести к некоторым сходствам, которые невозможно вычислить. Обычно это не используется в производственных средах.

Baseline

Это совместная фильтрация, основанная на регрессионной модели. Если мы будем рассматривать рейтинг как непрерывное значение, а не как дискретное значение, то мы можем использовать идею линейной регрессии, чтобы предсказать рейтинг целевого пользователя для элемента. Одна из стратегий реализации называется Baseline.

Базовое дизайн-мышление

  • Некоторые пользователи обычно оцениваются выше, чем другие, а некоторые пользователи обычно оцениваются ниже, чем другие. Например, некоторые пользователи от природы склонны хвалить других, они мягкосердечны и легки в общении, а некоторые более суровы и всегда ставят не более 3 баллов (из 5 баллов).

  • Некоторые элементы обычно оцениваются выше, чем другие, а некоторые, как правило, оцениваются ниже, чем другие. Например, некоторые предметы определяют свой статус сразу после их производства, некоторые пользуются большей популярностью у людей, а другие не нравятся.

    Этот пользователь или элемент, как правило, выше или ниже средней разницы, мы называем смещение (bias)

Базовая цель

  • Найдите значение смещения b, согласно которому каждый пользователь обычно выше или ниже других.u

  • Найдите значение смещения b каждого элемента, как правило, выше или ниже, чем у других элементов.i

  • Наша цель – найти оптимальное buи бi

Квадратная разница,ruiпредставляет пользователяuк пунктуiрейтинг,r^uiпредставляет прогнозируемое значениеПредполагаемая стоимостьr^ui=Среднее значение всех оценок+bu+biкоторыйr^ui=u+bu+biCost=u,iеR(ruir^ui)2=u,iеR(ruiububi)2Квадрат разницы: r_{ui} представляет рейтинг пользователя u для элемента i, \widehat r_{ui} представляет прогнозируемое значение\\прогнозируемое значение\widehat r_{ui} = среднее значение всех оценок + b{u} + b_ {i}\\ т.е. \widehat r_{ui} = u + b{u} + b_{i}\\ Cost= \sum_{u,i∈R}(r_{ui} - \widehat r_{ui}) ^{2} = \sum_{u,i∈R}(r_{ui} - u - b{u} - b_{i})^{2}\\

Чтобы предотвратить переоснащение, регуляризация

Cost=u,iеR(ruiububi)2+λ*(ubu2+ibi2)Стоимость = \sum_{u,i∈R}(r_{ui} - u - b{u} - b_{i})^{2} + \lambda*(\sum_{u}b_{u}^{2 } +\sum_{i}b_{i}^{2} )

градиентный спуск

уже b в приведенной выше формулеuс бiбинарная функция

J(θ)=f(bu,bi)J(θ):=J(θ)αJ(θ)J(\theta) = f(b_u,b_i)\\ J(\theta) := J(\theta) - \alpha \nabla{J(\theta)}
buJ(θ)=buf(bu,bi)=2u,iеR(ruiububi)(1)+2λububiJ(θ)=bif(bu,bi)=2u,iеR(ruiububi)(1)+2λibi\frac{\partial}{\partial{b_u}}J(\theta) = \frac{\partial}{\partial{b_u}}f(b_u,b_i) = 2\sum_{u,i∈R}( r_{ui} -u-b_u-b_i) \cdot(-1)+2\lambda\sum_{u}b_u\\ \frac{\partial}{\partial{b_i}}J(\theta) = \frac {\ partial {\ partial {b_i}} f (b_u, b_i) = 2 \ sum_ {u, i∈R} (r_ {ui} -u-b_u-b_i) \ cdot (-1) + 2 \ lambda \sum_{i}b_i\\

Введите формулу градиента

bu:=buαbuJ(θ)=buα(2u,iеR(ruiububi)(1)+2λubu)так какαискусственно контролируется, и формулу можно упростить доbu:=buαbuJ(θ)=bu+α(u,iеR(ruiububi)λubu)bi:=biαbiJ(θ)=bi+α(u,iеR(ruiububi)λibi)b_u:= b_u - \alpha \frac{\partial}{\partial{b_u}}J(\theta)= b_u -\alpha (2\sum_{u,i∈R}(r_{ui} -u-b_u -b_i) \cdot(-1)+2\lambda\sum_{u}b_u)\\ Поскольку \alpha является искусственным, формулу можно упростить до \\ b_u:= b_u - \alpha \frac{\partial}{ \partial{b_u}}J(\theta)= b_u +\alpha (\sum_{u,i∈R}(r_{ui} -u-b_u-b_i)-\lambda\sum_{u}b_u)\\ b_i:= b_i - \alpha \frac{\partial}{\partial{b_i}}J(\theta)= bi +\alpha (\sum_{u,i∈R}(r_{ui} -u-b_u- b_i)-\lambda\sum_{i}b_i)

В приведенной выше формуле необходимо рассчитать сумму рейтинга каждого пользователя по элементу и прогнозируемого рейтинга, который можно использоватьСтохастический градиентный спуск

так какСтохастический градиентный спускПо существу использоватьпотери на образецобновлять параметры, не находя каждый раз полную сумму убытка

error=ruir^ui=ruiububibu:=bu+α[(ruiububi)λbu]bi:=bi+α[(ruiububi)λbi]error = r_{ui} - \widehat r_{ui} = r_{ui} - u - b{u} - b_{i}\\ b_u:= b_u +\alpha [(r_{ui} -u-b_u-b_i)-\lambda b_u]\\ b_i:= bi +\alpha [(r_{ui} -u-b_u-b_i)-\lambda b_i]

Реализация алгоритма

# 数据文件可以到这里下载 https://grouplens.org/datasets/movielens/

import pandas as pd
import numpy as np
from scipy.interpolate import make_interp_spline


class BaselineCFBySGD(object):

    def __init__(self, number_epochs, alpha, reg, columns=["uid", "iid", "rating"]):
        # 梯度下降最高迭代次数
        self.number_epochs = number_epochs
        # 学习率或者跨步
        self.alpha = alpha
        # 正则参数
        self.reg = reg
        # 数据集中user-item-rating字段的名称
        self.columns = columns

    def fit(self, dataset):
        '''
        :param dataset: uid, iid, rating
        :return:
        '''
        self.dataset = dataset
        # 用户评分数据
        print(self.dataset.itertuples(index=False))
        self.users_ratings = dataset.groupby(self.columns[0]).agg([list])[[self.columns[1], self.columns[2]]]
        # 物品评分数据
        self.items_ratings = dataset.groupby(self.columns[1]).agg([list])[[self.columns[0], self.columns[2]]]
        # 计算全局平均分
        self.global_mean = self.dataset[self.columns[2]].mean()
        # 调用sgd方法训练模型参数
        self.bu, self.bi = self.sgd()

        self.showChart()
    def sgd(self):
        '''
        利用随机梯度下降,优化bu,bi的值
        :return: bu, bi
        '''
        # 初始化bu、bi的值,全部设为0
        bu = dict(zip(self.users_ratings.index, np.zeros(len(self.users_ratings))))
        bi = dict(zip(self.items_ratings.index, np.zeros(len(self.items_ratings))))

        for i in range(self.number_epochs):
            print("iter %d" % i)
            for uid, iid, real_rating in self.dataset.itertuples(index=False):
                error = real_rating - (self.global_mean + bu[uid] + bi[iid])

                bu[uid] += self.alpha * (error - self.reg * bu[uid])
                bi[iid] += self.alpha * (error - self.reg * bi[iid])


        return bu, bi

    def predict(self, uid, iid):
        '''预测'''
        predict_rating = self.global_mean + self.bu[uid] + self.bi[iid]
        return predict_rating

    def showChart(self):
        import matplotlib.pyplot as plt

        plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
        plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
        plt.title("匹配结果")
        userId = self.users_ratings.index.values[10]
        # 查看指定用户的预测曲线

        # 找到该用户评分的电影

        movie_ids = []
        real_ratings = []
        for uid, iid, real_rating in self.dataset.itertuples(index=False):
            if uid == userId:
                movie_ids.append(iid)
                real_ratings.append(real_rating)
        model = make_interp_spline(movie_ids, real_ratings)
        xs = np.linspace(min(movie_ids), max(movie_ids), 1000)
        ys = model(xs)

        plt.plot(xs, ys, color='green', label='training accuracy')
        predict_ratings = []
        for iid in movie_ids:
            predict_ratings.append(self.predict(userId, iid))
        model = make_interp_spline(movie_ids, predict_ratings)
        xs = np.linspace(min(movie_ids), max(movie_ids), 1000)
        ys = model(xs)
        plt.plot(xs, ys, color='red', label='training accuracy')
        plt.xticks([])
        plt.yticks([])
        plt.show()


if __name__ == '__main__':
    dtype = [("userId", np.int32), ("movieId", np.int32), ("rating", np.float32)]
    dataset = pd.read_csv("datasets/ml-latest-small/ratings.csv", usecols=range(3), dtype=dict(dtype))
    bcf = BaselineCFBySGD(10, 0.1, 0.1, ["userId", "movieId", "rating"])
    bcf.fit(dataset)

    # while True:
    #     uid = int(input("uid: "))
    #     iid = int(input("iid: "))
    #     print(bcf.predict(uid, iid))