TensorFlow 中的建议:创建模型

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

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

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

该系列包含以下部分:

目标

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

费用

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

准备工作

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

    转到项目选择器页面

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

  3. 启用Compute Engine 和 AI Platform API。

    启用 API

运行 TensorFlow 模型

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

创建 Compute Engine 实例

  1. 在 Google Cloud Platform 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
  7. 安装 Python 软件包和 TensorFlow。在本教程中,将在 TensorFlow 的任意 1.x 版本上运行。

    cd tensorflow-recommendation-wals
    conda create -n tfrec
    conda install -n tfrec --file conda.txt
    source activate tfrec
    pip install -r requirements.txt
    pip install tensorflow
  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 部分不使用此实例。但是,Compute Engine 实例会在第 3 部分第 4 部分中用到。

关停 Compute Engine 实例

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

删除项目

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

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

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

    转到“管理资源”页面

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

后续步骤