TensorFlow 中的建议:创建模型

本文是由多篇文章组成的系列教程中的第 1 篇,该系列教程介绍了如何在 Google Cloud Platform (GCP) 中使用 TensorFlow 1.xAI Platform 实现机器学习推荐系统。此部分展示如何在开发系统上安装 TensorFlow 模型代码并在 MovieLens 数据集上运行模型。

本教程中的推荐系统使用加权交替最小二乘 (WALS) 算法。WALS 随附在 TensorFlow 1.x 代码库的 contrib.factorization 软件包中,用于对大型用户和项目评分矩阵进行因式分解。要详细了解 WALS,请查看概览

本文详细介绍了模型的代码,包括数据预处理和在 TensorFlow 中执行 WALS 算法。

该系列包含以下部分:

目标

  • 了解将 WALS 应用于矩阵因式分解所使用的 TensorFlow 代码的结构。
  • 在本地运行示例 TensorFlow 代码,基于 MovieLens 数据集执行推荐。

费用

本教程使用 Cloud StorageAI Platform 这两项收费服务。您可以使用价格计算器来估算预计使用量的费用。本教程的预计费用为 0.15 美元。如果您是 GCP 新用户,可能有资格申请免费试用

准备工作

  1. 在 Cloud Console 的项目选择器页面上,选择或创建 Cloud 项目。

    转到项目选择器页面

  2. 确保您的 Google Cloud 项目已启用结算功能。 了解如何确认您的项目已启用结算功能

  3. 启用 Compute Engine and AI Platform API。

    启用 API

运行 TensorFlow 模型

您可以在具有至少 7G 内存的 Compute Engine 实例上运行本部分中的步骤,如以下过程中所述。 或者,您可以在本地 macOS 或 Linux 系统上运行本部分中的步骤;这样的话,则无需创建 Compute Engine 实例。

创建 Compute Engine 实例

  1. 在 Google Cloud Console 中,转到虚拟机实例页面。

    转到“虚拟机实例”页面

  2. 点击创建实例

  3. 根据您的喜好命名实例,并选择一个地区。如果您还没有首选地区,请选择一个地理位置靠近您的地区。

  4. 机器类型下拉列表中,选择 2 vCPUn1-standard-2

  5. 访问权限范围部分中,选择允许对所有 API 的全面访问权限 (Allow Full Access To All APIs)。

  6. 点击创建

安装代码

  1. 转到虚拟机实例页面。

    转到“虚拟机实例”页面

  2. 在实例对应的行中,点击 SSH 以打开基于浏览器的终端,该终端以安全的方式连接到该实例。

  3. 在新终端窗口中,更新实例的软件代码库。

    sudo apt-get update
    
  4. 安装 gitbzip2unzip

    sudo apt-get install -y git bzip2 unzip
    
  5. 克隆示例代码库:

    git clone https://github.com/GoogleCloudPlatform/tensorflow-recommendation-wals
  6. 安装 miniconda。示例代码需要 Python 2.7。

    wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh
    bash Miniconda2-latest-Linux-x86_64.sh
    export PATH="/home/$USER/miniconda2/bin:$PATH"
  7. 安装 Python 软件包和 TensorFlow。在本教程中,将在 TensorFlow 的任意 1.x 版本上运行。

    cd tensorflow-recommendation-wals
    conda create -y -n tfrec
    conda install -y -n tfrec --file conda.txt
    source activate tfrec
    pip install -r requirements.txt
    pip install tensorflow==1.15
  8. 下载 MovieLens 数据集。数据集具有多个版本。

    • 若要开发,建议使用 100k 版本,它包含 943 位用户对 1682 个项的 100000 个评分。请使用以下命令下载该版本:

      curl -O 'http://files.grouplens.org/datasets/movielens/ml-100k.zip'
      unzip ml-100k.zip
      mkdir -p data
      cp ml-100k/u.data data/
    • 若要训练,则推荐 1m 数据集,它包含一百万个评分。1m 集的格式与 100k 集略有不同。例如,评分文件以 :: 字符分隔。通过示例代码,您可以使用 --delimiter 参数指定数据集使用此分隔符。

      curl -O 'http://files.grouplens.org/datasets/movielens/ml-1m.zip'
      unzip ml-1m.zip
      mkdir -p data
      cp ml-1m/ratings.dat data/
    • 您还可对此项目使用 20m 数据集。该数据集以 CSV 文件提供。如果使用此数据集,则必须传递 --headers 标志,因为该文件包含标头行。

      curl -O 'http://files.grouplens.org/datasets/movielens/ml-20m.zip'
      unzip ml-20m.zip
      mkdir -p data
      cp ml-20m/ratings.csv data/

了解模型代码

模型代码包含在 wals_ml_engine 目录中。代码的高级功能由以下文件实现:

mltrain.sh
+ 启动各种类型的 AI Platform 作业。此 shell 脚本可接受数据集文件位置的参数、用于分隔文件中的值的分隔符,以及数据文件是否具有标头行。最佳做法是创建一个自动配置和执行 AI Platform 作业的脚本。
task.py
+ 解析 AI Platform 作业的参数并进行训练。
model.py
+ 加载数据集。
  • 根据数据创建两个稀疏矩阵:一个用于训练,一个用于测试。在训练用的稀疏评分矩阵上执行 WALS。
wals.py
+ 创建 WALS 模型。
  • 执行 WALS 算法。
  • 计算一组行/列因子和评分矩阵的均方根误差 (RMSE)。

模型预处理数据的方式

模型代码执行数据预处理,以创建稀疏评分矩阵并为矩阵因式分解做准备。这涉及到下列步骤:

  1. 模型代码从带分隔符的文本文件加载数据。每一行都包含一个评分。

    ratings_df = pd.read_csv(input_file,
                             sep=args['delimiter'],
                             names=headers,
                             header=header_row,
                             dtype={
                               'user_id': np.int32,
                               'item_id': np.int32,
                               'rating': np.float32,
                               'timestamp': np.int32,
                             })
  2. 该代码为用户和项建立一组唯一 ID(从 0 开始编入索引)。这可保证唯一 ID 与稀疏评分矩阵的特定行和列索引相对应。

    • MovieLens 100k 数据使用基于 1 的 ID,其中唯一集的最小索引是 1。为了标准化,代码会从每个索引中减去 1。以下内容来自 model.py

      ratings = ratings_df.as_matrix(['user_id', 'item_id', 'rating'])
      # deal with 1-based user indices
      ratings[:,0] -= 1
      ratings[:,1] -= 1
    • 1m20m MovieLens 数据集会跳过一些用户 ID 和项 ID。这会产生一个问题:您必须将唯一用户 ID 集映射到等于 [0 ... num_users-1] 的索引集,并对项 ID 执行相同操作。项映射是使用以下 [numpy](http://www.numpy.org/) 代码完成的。该代码会创建一个大小为 [0..max_item_id] 的数组来执行映射,因此如果最大项 ID 非常大,则此方法可能会占用过多内存。

      np_items = ratings_df.item_id.as_matrix()
      unique_items = np.unique(np_items)
      n_items = unique_items.shape[0]
      max_item = unique_items[-1]
      
      # map unique items down to an array 0..n_items-1
      z = np.zeros(max_item+1, dtype=int)
      z[unique_items] = np.arange(n_items)
      i_r = z[np_items]
    • 映射用户的代码与映射项的代码基本相同。

  3. 模型代码随机选择一个评分测试集。默认选择 10% 的评分用于测试集。这些评分会从训练集中移除,并将用于评估用户和项目因子的预测准确率。

    test_set_size = len(ratings) / TEST_SET_RATIO
    test_set_idx = np.random.choice(xrange(len(ratings)),
                                    size=test_set_size, replace=False)
    test_set_idx = sorted(test_set_idx)
    
    ts_ratings = ratings[test_set_idx]
    tr_ratings = np.delete(ratings, test_set_idx, axis=0)
  4. 最后,该代码以坐标形式 (coo_matrix) 创建一个 scipy 稀疏矩阵,其中包括用户和推荐项的索引及评分。coo_matrix 对象充当稀疏矩阵的封装容器。它还可验证用户和评分索引,并检查预处理过程中是否出现错误。

    u_tr, i_tr, r_tr = zip(*tr_ratings)
    tr_sparse = coo_matrix((r_tr, (u_tr, i_tr)), shape=(n_users, n_items))

如何在 TensorFlow 中实现 WALS 算法

对数据进行预处理后,代码会将稀疏训练矩阵传递到 TensorFlow WALS 模型,以将其分解为行因子 X 和列因子 Y

执行模型的 TensorFlow 代码实际上很简单,因为它依赖于 TensorFlow contrib.factorization_ops 模块中包含的 WALSModel 类。

  1. SparseTensor 对象已初始化,其中用户 ID 和项 ID 用作索引,评分用作值。以下内容来自 wals.py

    input_tensor = tf.SparseTensor(indices=zip(data.row, data.col),
                                    values=(data.data).astype(np.float32),
                                    dense_shape=data.shape)

    数据变量是在预处理步骤中创建的训练评分的 coo_matrix 对象。

  2. 该模型已实例化:

    model = factorization_ops.WALSModel(num_rows, num_cols, dim,
                                        unobserved_weight=unobs,
                                        regularization=reg,
                                        row_weights=row_wts,
                                        col_weights=col_wts)
  3. 行因子和列因子张量由 WALSModel 类自动创建并进行检索,以便在矩阵因式分解后对其求值:

    # retrieve the row and column factors
    row_factor = model.row_factors[0]
    col_factor = model.col_factors[0]
  4. 训练过程使用 wals.py 中的 simple_train 方法在 TensorFlow 会话中执行以下循环:

    row_update_op = model.update_row_factors(sp_input=input_tensor)[1]
    col_update_op = model.update_col_factors(sp_input=input_tensor)[1]
    
    sess.run(model.initialize_op)
    sess.run(model.worker_init)
    for _ in xrange(num_iterations):
        sess.run(model.row_update_prep_gramian_op)
        sess.run(model.initialize_row_update_op)
        sess.run(row_update_op)
        sess.run(model.col_update_prep_gramian_op)
        sess.run(model.initialize_col_update_op)
        sess.run(col_update_op)
  5. 执行 num_iterations 迭代之后,在会话中对行和列因子张量求值,从而为每个因子生成 numpy 数组:

    # evaluate output factor matrices
    output_row = row_factor.eval(session=session)
    output_col = col_factor.eval(session=session)

这些因子数组用于计算评分测试集上的 RMSE。 这两个数组还会以 numpy 格式保存在输出目录中。

训练模型

在该场景中,训练模型涉及到将稀疏评分矩阵分解成用户因子矩阵 X 和推荐项因子矩阵 Y。所保存的用户和推荐项因子可用作推荐系统的基本模型。

该系统会将用户作为输入,从 X 中检索该用户的用户因子矢量,将该矢量与所有推荐项因子 Y 相乘,并根据预测评分返回前 N 个推荐项。

本教程集的第 4 部分详细介绍了使用经过训练的模型执行预测的系统,同时讲解了如何在 GCP 上部署此类系统。

在本地训练模型

在本地训练模型对于开发目的很有用。它能让您快速测试代码更改并包含断点以轻松进行调试。如要在 Cloud Shell 或本地系统上运行模型,请使用 local 选项从 wals_ml_engine 目录运行 mltrain.sh 脚本。

cd wals_ml_engine
  • 对于 MovieLens 100k 数据集,请指定 100k 数据文件的路径:

    ./mltrain.sh local ../data u.data
  • 对于 MovieLens 1m 数据集,请添加 --delimiter 选项并指定 1m 数据文件的路径:

    ./mltrain.sh local ../data ratings.dat --delimiter ::
  • 对于 MovieLens 20m 数据集,请使用 --delimiter--headers 选项:

    ./mltrain.sh local ../data ratings.csv --headers --delimiter ,

训练作业输出会显示测试集上计算得出的 RMSE。对于 1m 数据集,如果使用源代码中指定的默认超参数,输出应如下所示:

INFO:tensorflow:Train Start: <timestamp>
...
INFO:tensorflow:Train Finish: <timestamp>
INFO:tensorflow:train RMSE = 1.29
INFO:tensorflow:test RMSE = 1.34

如需了解详情,请参阅第 2 部分

RMSE 对应于测试集内的预测评分的平均误差。平均而言,该算法生成的每个评分均在 1m 数据集的测试集中实际用户评分的 ±1.29 范围内。如本系列的第 2 部分所示,WALS 算法使用经过调整的超参数时性能更好。

清理

如果您创建了 Compute Engine 实例用于运行 TensorFlow,则必须停止该实例,以避免对您的 GCP 帐号产生费用。本系列的第 2 部分不使用此实例。但是,第 3 部分第 4 部分需要使用 Compute Engine 实例。

关停 Compute Engine 实例

  1. 在 Cloud Console 中,打开 Compute Engine 虚拟机实例列表页面。
  2. 选择实例名称。
  3. 点击停止并确认操作。

删除项目

为了避免产生费用,最简单的方法是删除您为本教程创建的项目。

如需删除项目,请执行以下操作:

  1. 在 Cloud Console 中,转到管理资源页面。

    转到“管理资源”页面

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

后续步骤