Skip to content

TensorFlow推荐系统(二)

Info

作者:Tina,发布于2021-12-27,阅读时间:约3分钟,微信公众号文章链接:

1 前言

读过TensorFlow推荐系统(一)的朋友们应该还有印象,上回我们介绍的模型是信息检索(retrieval),而在推荐系统中还有另一个任务模型,即为信息排序(ranking)。在排序阶段,其主要任务是对检索模型产出的条目进行调整以选择最有可能被用户喜欢和选择的电影条目。

今天,我们将详细介绍一下排序模型的原理和调用实例。

2 源码解析

  • 数据准备,获取数据并拆分数据集。
  • 搭建排序模型。
  • 拟合并评估模型。

2.1 数据准备

import os
import pprint
import tempfile

from typing import Dict, Text

import numpy as np
import tensorflow as tf
## TensorFlow Dataset Resource
import tensorflow_datasets as tfds

## TensorFlow 推荐系统
import tensorflow_recommenders as tfrs

TensorFlow Dataset中引入和信息检索模型相同的movielens电影数据集,并只保留以下三个变量:

ratings = tfds.load("movielens/100k-ratings", split="train")

ratings = ratings.map(lambda x: {
    "movie_title": x["movie_title"],
    "user_id": x["user_id"],
    "user_rating": x["user_rating"]
})

随机打乱数据,并取80%为训练数据集,其余的20%为测试数据集:

tf.random.set_seed(42)
shuffled = ratings.shuffle(100_000, seed=42, reshuffle_each_iteration=False)

train = shuffled.take(80_000)
test = shuffled.skip(80_000).take(20_000)

为了给分类型变量做嵌入向量,这里是先用连续整数来匹配电影标题和用户ID的每个数值:

movie_titles = ratings.batch(1_000_000).map(lambda x: x["movie_title"])
user_ids = ratings.batch(1_000_000).map(lambda x: x["user_id"])

unique_movie_titles = np.unique(np.concatenate(list(movie_titles)))
unique_user_ids = np.unique(np.concatenate(list(user_ids)))

2.2 搭建排序模型

在文本模型中,需要先为用户和电影做词嵌入(Embeddings),简单来说就是将文本型数据转化为可计算数值型向量的过程,这里每一个嵌入向量的维度为32。第二步是搭建顺序模型,其模型调用的是Keras Dense的全连接层,激活函数为relu整流线性单元,它的特点是在用默认值时,返回逐元素的max(x,0)。最后用call()函数输入input,从而返回ranking的结果。

class RankingModel(tf.keras.Model):

  def __init__(self):
    super().__init__()
    ## 嵌入维度为32
    embedding_dimension = 32

    ## Compute embeddings for users.
    self.user_embeddings = tf.keras.Sequential([
      tf.keras.layers.StringLookup(
        vocabulary=unique_user_ids, mask_token=None),
      tf.keras.layers.Embedding(len(unique_user_ids) + 1, embedding_dimension)
    ])

    # Compute embeddings for movies.
    self.movie_embeddings = tf.keras.Sequential([
      tf.keras.layers.StringLookup(
        vocabulary=unique_movie_titles, mask_token=None),
      tf.keras.layers.Embedding(len(unique_movie_titles) + 1, embedding_dimension)
    ])

    # Compute predictions.
    self.ratings = tf.keras.Sequential([
      # Learn multiple dense layers.
      tf.keras.layers.Dense(256, activation="relu"),
      ## 输出维度为 256
      tf.keras.layers.Dense(64, activation="relu"),
      ## 输出层维度为 64
      # Make rating predictions in the final layer.
      tf.keras.layers.Dense(1)
  ])

  def call(self, inputs):
    ##用户ID和电影标题为模型的输入值
    user_id, movie_title = inputs

    user_embedding = self.user_embeddings(user_id)
    movie_embedding = self.movie_embeddings(movie_title)

    return self.ratings(tf.concat([user_embedding, movie_embedding], axis=1))
由下图所示,用未经过训练的模型为用户号为9推荐电影《One Flew Over the Cuckoo's Nest (1975)》,预测的可能性为0.016:

为了可以在训练时有评估值,在原有的模型任务中加入MSE均方误差的损失函数和RMSE均方根误差的评估函数:

task = tfrs.tasks.Ranking(
  loss = tf.keras.losses.MeanSquaredError(),
  metrics=[tf.keras.metrics.RootMeanSquaredError()]
)

将排序模型和上述的函数一起打包为新的电影模型,并准备call()函数和comupte_loss()函数来拟合和评估模型的表现:

class MovielensModel(tfrs.models.Model):

  def __init__(self):
    super().__init__()
    self.ranking_model: tf.keras.Model = RankingModel()
    self.task: tf.keras.layers.Layer = tfrs.tasks.Ranking(
      loss = tf.keras.losses.MeanSquaredError(),
      metrics=[tf.keras.metrics.RootMeanSquaredError()]
    )

  def call(self, features: Dict[str, tf.Tensor]) -> tf.Tensor:
    return self.ranking_model(
        (features["user_id"], features["movie_title"]))

  def compute_loss(self, features: Dict[Text, tf.Tensor], training=False) -> tf.Tensor:
    labels = features.pop("user_rating")

    rating_predictions = self(features)

    # The task computes the loss and the metrics.
    return self.task(labels=labels, predictions=rating_predictions)

2.3 拟合并评估模型

调用模型编译complie()方法并使用Adagrad的优化器,指定学习率为0.1:

model = MovielensModel()
model.compile(optimizer=tf.keras.optimizers.Adagrad(learning_rate=0.1))

数据拟合时,这里将训练集重排,batch处理和数据缓存,共epoch三次:

cached_train = train.shuffle(100_000).batch(8192).cache()
cached_test = test.batch(4096).cache()

model.fit(cached_train, epochs=3)
model.evaluate(cached_test, return_dict=True)

## output:
#{'root_mean_squared_error': 1.1102582216262817,
# 'loss': 1.2078243494033813,
# 'regularization_loss': 0,
# 'total_loss': 1.2078243494033813}

最后,为用户9推荐以下三部电影并附有模型预测的ranking结果:

3 总结

TensorFlow电影推荐系统中,第一步先从数据集中获取用户可能会喜欢的电影条目,第二步则是对这些电影条目进行预测排序,目的就是推荐有限的用户最有可能感兴趣并点击的电影。而在现实生活中,你就是用户。所以,教给大家一个解决剧荒的小技巧,与其随机浏览寻找,不如搜索一些你看过的喜欢的电影,和你喜欢相似的电影就在”为你推荐“一栏了,不妨试试吧!

希望这篇分享可以对你有所帮助,欢迎各位留言讨论。


Viewed times

Comments