示例:SLi_Rec——结合长短期偏好的自适应用户建模个性化推荐
与不考虑用户活动顺序的通用推荐系统(如矩阵分解或xDeepFM)不同,序列推荐系统将用户行为的顺序作为上下文,目标是预测用户在短时间内可能会互动的项目(极端情况下,是用户下一个互动的项目)。
本文旨在通过一个公共的Amazon数据集,为您提供快速训练序列模型的示例。目前,我们支持NextItNet、GRU、Caser、A2SVD、SLi_Rec和SUM模型。在不失一般性的前提下,本示例将以SLi_Rec模型为例。
SLi_Rec是一种基于深度学习的模型,旨在捕捉用户的长期和短期偏好,以实现精确的推荐系统。总结一下,SLi_Rec具有以下关键特性:
- 采用注意力机制的“非对称SVD”范式进行长期建模;
- 在LSTM的门控逻辑中考虑时间和语义的不规则性;
- 使用注意力机制动态融合长期和短期组件。
在本文中,我们将测试SLi_Rec在一个公共数据集的子集上: Amazon_reviews 和 Amazon_metadata。
本文在TensorFlow 2.6环境下测试。
0. 全局设置和导入
import os
import sys
import tensorflow.compat.v1 as tf
tf.get_logger().setLevel('ERROR') # 仅显示错误信息
from recommenders.utils.timer import Timer
from recommenders.utils.constants import SEED
from recommenders.models.deeprec.deeprec_utils import prepare_hparams
from recommenders.datasets.amazon_reviews import download_and_extract, data_preprocessing
from recommenders.models.deeprec.models.sequential.sli_rec import SLI_RECModel as SeqModel
from recommenders.models.deeprec.io.sequential_iterator import SequentialIterator
from recommenders.utils.notebook_utils import store_metadata
print(f"System version: {sys.version}")
print(f"Tensorflow version: {tf.__version__}")
参数设置
EPOCHS = 10
BATCH_SIZE = 400
RANDOM_SEED = SEED # 设置为None则结果不确定
data_path = os.path.join("..", "..", "tests", "resources", "deeprec", "slirec")
yaml_file = '../../recommenders/models/deeprec/config/sli_rec.yaml'
1. 输入数据格式
输入数据包含8列,即:<label> <user_id> <item_id> <category_id> <timestamp> <history_item_ids> <history_cateory_ids> <hitory_timestamp>
,各列之间用\t
分隔。item_id
和category_id
表示目标项目和类别,这意味着对于这个实例,我们想猜测用户user_id
是否会在timestamp
与item_id
互动。<history_*>
列记录了用户在<timestamp>
之前的行为列表,元素之间用逗号分隔。<label>
是一个二进制值,1表示正实例,0表示负实例。实例示例如下:
1 A1QQ86H5M2LVW2 B0059XTU1S Movies 1377561600 B002ZG97WE,B004IK30PA,B000BNX3AU,B0017ANB08,B005LAIHW2 Movies,Movies,Movies,Movies,Movies 1304294400,1304812800,1315785600,1316304000,1356998400
在数据预处理中,我们会生成一些ID映射字典,因此user_id
、item_id
和category_id
会被映射成从1开始的整数索引。您需要告诉输入迭代器ID映射文件的位置(例如在下节中,我们有一些映射文件如user_vocab
、item_vocab
和cate_vocab
)。
train_file = os.path.join(data_path, r'train_data')
valid_file = os.path.join(data_path, r'valid_data')
test_file = os.path.join(data_path, r'test_data')
user_vocab = os.path.join(data_path, r'user_vocab.pkl')
item_vocab = os.path.join(data_path, r'item_vocab.pkl')
cate_vocab = os.path.join(data_path, r'category_vocab.pkl')
output_file = os.path.join(data_path, r'output.txt')
reviews_name = 'reviews_Movies_and_TV_5.json'
meta_name = 'meta_Movies_and_TV.json'
reviews_file = os.path.join(data_path, reviews_name)
meta_file = os.path.join(data_path, meta_name)
train_num_ngs = 4 # 训练时每个正实例的负实例数量
valid_num_ngs = 4 # 验证时每个正实例的负实例数量
test_num_ngs = 9 # 测试时每个正实例的负实例数量
sample_rate = 0.01 # 采样率
input_files = [reviews_file, meta_file, train_file, valid_file, test_file, user_vocab, item_vocab, cate_vocab]
if not os.path.exists(train_file):
download_and_extract(reviews_name, reviews_file)
download_and_extract(meta_name, meta_file)
data_preprocessing(*input_files, sample_rate=sample_rate, valid_num_ngs=valid_num_ngs, test_num_ngs=test_num_ngs)
1.1 准备超参数
prepare_hparams()
函数将创建一组完整的超参数用于模型训练,如学习率、特征数量和丢弃率。我们可以将这些参数放在yaml文件中(完整参数列表见config文件夹),也可以作为函数参数传递(会覆盖yaml设置)。
hparams = prepare_hparams(yaml_file,
embed_l2=0.,
layer_l2=0.,
learning_rate=0.001, # 如果禁用批量归一化,设为0.01
epochs=EPOCHS,
batch_size=BATCH_SIZE,
show_step=20,
MODEL_DIR=os.path.join(data_path, "model/"),
SUMMARIES_DIR=os.path.join(data_path, "summary/"),
user_vocab=user_vocab,
item_vocab=item_vocab,
cate_vocab=cate_vocab,
need_sample=True,
train_num_ngs=train_num_ngs)
1.2 创建数据加载器
为模型指定数据迭代器。我们所有的序列模型都使用SequentialIterator
。数据格式如上所述。
input_creator = SequentialIterator
2. 创建模型
当超参数和数据迭代器准备好后,我们可以创建一个模型:
model = SeqModel(hparams, input_creator, seed=RANDOM_SEED)
2.1 训练模型
接下来我们要在训练集上训练模型,并检查其在验证集上的表现。训练模型非常简单,只需调用一个函数:
with Timer() as train_time:
model = model.fit(train_file, valid_file, valid_num_ngs=valid_num_ngs)
print('Time cost for training is {0:.2f} mins'.format(train_time.interval/60.0))
2.2 评估模型
再次查看模型的性能(训练后):
res_syn = model.run_eval(test_file, num_ngs=test_num_ngs)
print(res_syn)
如果我们想获取完整的预测分数而不是评估指标,可以这样做:
model = model.predict(test_file, output_file)
2.3 使用大数据集运行模型
以下是使用整个Amazon数据集在流行的序列模型上的性能,包含1,697,533个正实例。
设置如下:
learning_rate=0.001, dropout=0.3, item_embedding_dim=32, cate_embedding_dim=8, l2_norm=0, batch_size=400, train_num_ngs=4, valid_num_ngs=4, test_num_ngs=49
我们比较了仅使用CPU和使用GPU在较大数据集上的运行时间。结果显示GPU可以显著加速训练。硬件规格如下:
- GPU: Tesla P100-PCIE-16GB
- CPU: 6 cores Intel® Xeon® CPU E5-2690 v4 @ 2.60GHz
模型 | AUC | g-AUC | NDCG@2 | NDCG@10 | 每个epoch的GPU时间(秒) | 每个epoch的CPU时间(秒) | 配置 |
---|---|---|---|---|---|---|---|
A2SVD | 0.8251 | 0.8178 | 0.2922 | 0.4264 | 249.5 | 440.0 | N/A |
GRU | 0.8411 | 0.8332 | 0.3213 | 0.4547 | 439.0 | 4285.0 | max_seq_length=50, hidden_size=40 |
Caser | 0.8244 | 0.8171 | 0.283 | 0.4194 | 314.3 | 5369.9 | T=1, n_v=128, n_h=128, L=3, min_seq_length=5 |
SLi_Rec | 0.8631 | 0.8519 | 0.3491 | 0.4842 | 549.6 | 5014.0 | attention_size=40, max_seq_length=50, hidden_size=40 |
NextItNet* | 0.6793 | 0.6769 | 0.0602 | 0.1733 | 112.0 | 214.5 | min_seq_length=3, dilations=[1,2,4,1,2,4], kernel_size=3 |
SUM | 0.8481 | 0.8406 | 0.3394 | 0.4774 | 1005.0 | 9427.0 | hidden_size=40, slots=4, dropout=0 |
注意:
- 这些模型的结果是通过粗粒度网格搜索得到的,仅供参考。
- NextItNet模型需要一个具有强序列特性的dataset,但本文使用的Amazon数据集不满足这一要求,因此NextItNet模型的性能可能不佳。如果使用其他具有强序列特性的数据集,推荐使用NextItNet。
- NextItNet模型的时间成本显著低于其他模型,因为它不需要扩展训练数据的历史记录。
3. 加载训练好的模型
此小节提供一个简单示例,说明如何使用训练好的模型以满足生产需求。
假设我们在一个新会话中。首先加载之前训练的模型:
model_best_trained = SeqModel(hparams, input_creator, seed=RANDOM_SEED)
path_best_trained = os.path.join(hparams.MODEL_DIR, "best_model")
print('loading saved model in {0}'.format(path_best_trained))
model_best_trained.load_model(path_best_trained)
检查是否正确加载了模型。测试指标应接近训练阶段的数值。
model_best_trained.run_eval(test_file, num_ngs=test_num_ngs)
使用加载的模型进行预测:
model_best_trained.predict(test_file, output_file)
参考文献
[1] Zeping Yu, Jianxun Lian, Ahmad Mahmoody, Gongshen Liu, Xing Xie. Adaptive User Modeling with Long and Short-Term Preferences for Personailzed Recommendation. In Proceedings of the 28th International Joint Conferences on Artificial Intelligence, IJCAI’19, Pages 4213-4219. AAAI Press, 2019.
[2] Kyunghyun Cho, Bart van Merrienboer, Caglar Gulcehre, Dzmitry Bahdanau, Fethi Bougares, Holger Schwenk, and Yoshua Bengio. Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation. arXiv preprint arXiv:1406.1078. 2014.
[3] Tang, Jiaxi, and Ke Wang. Personalized top-n sequential recommendation via convolutional sequence embedding. Proceedings of the Eleventh ACM International Conference on Web Search and Data Mining. ACM, 2018.
[4] Yuan, F., Karatzoglou, A., Arapakis, I., Jose, J. M., & He, X. A Simple Convolutional Generative Network for Next Item Recommendation. WSDM, 2019.
[5] Lian, J., Batal, I., Liu, Z., Soni, A., Kang, E. Y., Wang, Y., & Xie, X. Multi-Interest-Aware User Modeling for Large-Scale Sequential Recommendations. arXiv preprint arXiv:2102.09211. 2021.