NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

使用 AWS Sagemaker 训练因子分解机模型并应用于推荐系统

  • 2019-10-08
  • 本文字数:6554 字

    阅读完需:约 22 分钟

使用AWS Sagemaker训练因子分解机模型并应用于推荐系统

使用 AWS Sagemaker 系列文章:

第一篇:使用 AWS Sagemaker 训练因子分解机模型并应用于推荐系统(本博文)


第二篇:使用 AWS Sagemaker 部署的终端节点进行推荐预测的常用场景


————


在线服务和应用中,经常遇到需要对用户历史行为进行分析并预测,典型的案例如页面点击预测及推荐系统。这些案例的特点是历史数据集非常庞大,而且大多数情况,数据集是稀疏的。让我们以电影点评网站为例来理解稀疏数据集。在电影点评网站中,拥有大量的用户以及大量的电影,然而几乎不可能实现每个用户对每个电影都进行点评或打分。因此如果我们以用户为行,以电影为列,构建一个表格,对该用户点评过的电影单元格置 1,未点评过的置 0,我们可以发现,该表格中绝大部分数据都将是 0。这就是稀疏数据集的典型例子。


针对稀疏数据集,因子分解机(Factorization Machines,FM)是比较有效的算法模型。



直观来说因子分解机可以考虑为由用户(User)为行、电影(Movie)为列构成的矩阵 R 可以表示为一个用户(User)为行、K 列特征构成的矩阵 P 与电影(Movie)为行、K 列特征构成的矩阵 Q 的转置的乘积。其中的 K 即潜藏特征值,可以将他理解为用户与电影之间的关系。在因子分解机中,这一值是可以自行设定的。算法的主要目的就是计算出 P 和 Q 矩阵,并使 P 与 QT 的乘积尽可能与 R 一致。


本次实验采用国内用户对大量国内外电影的评论作为训练数据集,利用 AWS SageMaker 自带的因子分解机算法构建模型,通过 SageMaker 的超参调优服务观察参数调整对模型表现的影响。最后,以实际应用中经常会遇到的用法演示模型的预测结果。本次实验全部使用 Python3.6 完成,在 SageMaker 中选用 conda_python3 的 Kernel。

数据准备阶段

本次数据总量大约为 396 万条,用户数 49 万,电影数 3 万多。考虑到演示的目的和运算的效率,训练模型时使用其中 10 万条数据的采样。


In [3]:


df_all = pd.read_csv('all_movie_rates_noblank.csv')print(df_all.shape)print(df_all['UserId'].nunique())print(df_all['MovieID'].nunique())
复制代码


(3963891, 3)


490790


33345


首先我们对数据进行基本的观察。可以看到这 10 万条数据包含 53902 个用户和 26004 部电影。随后我们对数据集进行训练集和测试集的拆分,这里采用训练数据:测试数据 = 4:1 的比例进行拆分。


In [4]:


df=pd.read_csv('all_movie_rates_100k.csv')print(df.shape)print(df['UserId'].nunique())print(df['MovieID'].nunique())
df_train, df_test = train_test_split(df, test_size=0.2, random_state=42)print(df_train.shape, df_test.shape)
print(df_train.head(10))print(df_test.head(10))(107029, 3)
复制代码


53902


26004


(85623, 3) (21406, 3)


MovieID Rate UserId


63287 1296827 10 BloodzBoi


73804 3564327 6 LadyHoney


76527 6874441 8 49886917


71411 3152563 8 feathercat


64499 2004250 6 jingtianwst83


42130 3230459 6 funni


88502 3313801 6 chrisocean


79349 3072140 0 lala1123


21431 2053746 6 145992805


39240 1464338 8 HeroineDaode


MovieID Rate UserId


44496 26304167 8 152833029


11855 1299900 8 leonah


13955 26085750 2 49298107


27112 4202982 8 Kylin-2015


28103 4739952 10 likong


53598 3443393 4 3832465


65338 2052363 6 1926472


1116 6129707 8 vero_nicat


38854 26841337 2 158039357


78721 1307026 8 TowaErio

为用户和电影分别建立字典(Python Dictionary)

数据集中的用户 ID 和电影 ID 均为随机字符串,为了方便我们后续建立有序矩阵以及模型训练后预测结果的数据对应,我们首先为用户和电影分别建立由 0 开始的 index 序列,并使其与 ID 字符串对应。


In [5]:


filename = 'all_movie_rates_100k.csv'user_number = 53902movie_number = 26004
def createIDtoIndexDict(filename, user_number, movie_number): u_Dict = {} m_Dict = {} i = 0 m = 0 with open(filename, 'r') as f: sample = csv.reader(f, delimiter=',') for MovieID, Rate, UserId in sample: if UserId == 'UserId': continue else: if UserId not in u_Dict.keys(): u_Dict[UserId] = i i = i+1 if MovieID not in m_Dict.keys(): m_Dict[MovieID] = m m = m+1 return u_Dict, m_Dict u_Dict, m_Dict = createIDtoIndexDict(filename, user_number, movie_number)
复制代码

建立稀疏矩阵(Sparse Matrix)和标签向量(Label Vector)

因子分解机的训练是针对稀疏矩阵的,因此我们要将数据集中电影、评分、用户的序列转为稀疏矩阵,并根据用户评分的结果生成标签向量。我们使用 Python Scipy 模块中的 lil_matrix 来构建。


生成的矩阵应当是每一个用户 ID 作为单独一列、每一部电影在所有用户列之后也作为单独一列,针对原数据集中每行的数据,在对应的用户列和电影列置 1。标签向量以用户评分为基准,我们设定用户评分大于等于 6 的为“喜爱”,并在对应的标签向量位置置 1,反之为“不喜爱”,标签向量相应位置置 0。


训练集和测试集均进行同样的操作。


df_train.to_csv('100k_train.csv', index=False, encoding="utf_8_sig")df_test.to_csv('100k_test.csv', index=False, encoding="utf_8_sig")
columns = user_number+movie_numberdef loadDataset(filename, lines, columns): X = scipy.sparse.lil_matrix((lines, columns)).astype('float32') Y = [] line = 0 with open (filename, 'r') as f: sample = csv.reader(f, delimiter=',') for MovieID, Rate, UserId in sample: if UserId == 'UserId': continue else: X[line, u_Dict[UserId]] = 1 X[line, user_number+m_Dict[MovieID]] = 1 if Rate == 'Rate' or int(Rate) < 6: Y.append(0) else: Y.append(1) line=line+1
Y=np.array(Y).astype('float32') print(X.shape) print(Y.shape) return X, Y
复制代码


In [222]:


X_train, Y_train = loadDataset('100k_train.csv', df_train.shape[0], columns)X_test, Y_test = loadDataset('100k_test.csv', df_test.shape[0], columns)print(X_train.shape, X_test.shape)print(Y_train.shape, Y_test.shape)
复制代码


(85623, 79906)


(85623,)


(21406, 79906)


(21406,)


(85623, 79906) (21406, 79906)


(85623,) (21406,)


我们获得了训练集为 85623✖️79906 的矩阵,训练标签向量为 85263 元素;测试集为 21406✖️79906 矩阵,其标签向量为 21406 元素。

转换稀疏矩阵为 protobuf 格式,并保存到 S3

稀疏矩阵中绝大多数元素均为 0,如果直接保存稀疏矩阵,会占用大量的存储空间,因此我们将其转为 protobuf 格式的数据,并保存到 S3。


In [224]:


bucket = 'movie-recommendation-demo-raw-data'prefix = 'sagemaker/movie-recommendation'train_key      = 'train.protobuf'train_prefix   = '{}/{}'.format(prefix, 'train')test_key       = 'test.protobuf'test_prefix    = '{}/{}'.format(prefix, 'test')output_prefix  = 's3://{}/{}/output'.format(bucket, prefix)
def writeDatasetToProtobuf(X, Y, bucket, prefix, key): import io,boto3 import sagemaker.amazon.common as smac buf = io.BytesIO() smac.write_spmatrix_to_sparse_tensor(buf, X, Y) buf.seek(0) print(buf) obj = '{}/{}'.format(prefix, key) boto3.resource('s3').Bucket(bucket).Object(obj).upload_fileobj(buf) print('Wrote dataset: {}/{}'.format(bucket,obj)) return 's3://{}/{}'.format(bucket,obj) train_data = writeDatasetToProtobuf(X_train, Y_train, bucket, train_prefix, train_key) test_data = writeDatasetToProtobuf(X_test, Y_test, bucket, test_prefix, test_key)
print(train_data)print(test_data)print('Output: {}'.format(output_prefix))<_io.BytesIO object at 0x7f99a8b8e1a8>
Wrote dataset: movie-recommendation-demo-raw-data/sagemaker/movie-recommendation/train/train.protobuf
<_io.BytesIO object at 0x7f99a8b8e1a8>
Wrote dataset: movie-recommendation-demo-raw-data/sagemaker/movie-recommendation/test/test.protobuf
s3://movie-recommendation-demo-raw-data/sagemaker/movie-recommendation/train/train.protobuf
s3://movie-recommendation-demo-raw-data/sagemaker/movie-recommendation/test/test.protobuf
Output: s3://movie-recommendation-demo-raw-data/sagemaker/movie-recommendation/output
复制代码


程序输出中 Output 的内容是模型训练完成后,保存模型代码的位置。

FM 模型训练

FM 是 SageMaker 自带的算法之一,因此通过 SageMaker 训练模型非常容易。首先我们需要引入 SageMaker 的 SDK,并建立 SageMaker 的 session、定义位于该 Region 的因子分解机算法 Container 以及获取 SageMaker 的运行角色。


In [225]:


from sagemaker import get_execution_rolefrom sagemaker.amazon.amazon_estimator import get_image_uriimport sagemakersess = sagemaker.Session()
role = get_execution_role()container = get_image_uri(boto3.Session().region_name, 'factorization-machines')
复制代码


随后我们定义 FM 训练需要的一些参数。首先是环境参数,包括之前定义好的 Container、角色、输出位置和 session、还包括训练使用的 EC2 实例,本例中采用“ml.c4.xlarge”来训练。


之后,我们需要定义 FM 算法的超参(Hyperparameters)。在本例中特征列为用户数与电影数的总和 79906、预测方式为二分类(即结果为判断“喜爱”或是“不喜爱”)、最小批量为 1000、epoch 时期为 50 次。其中 num_factors 即为在算法介绍中提到的潜藏特征 K 的数量,根据 SageMaker 官方文档的说明,建议在 2-1000 之间,通常 64 为最优值,因此,我们也设为 64。


最后为模型提供训练集和测试集在 S3 中的位置,训练就开始了。


In [226]:


fm = sagemaker.estimator.Estimator(container,                                   role,                                    train_instance_count=1,                                    train_instance_type='ml.c4.xlarge',                                   output_path=output_prefix,                                   sagemaker_session=sess)fm.set_hyperparameters(feature_dim=79906,                      predictor_type='binary_classifier',                      mini_batch_size=1000,                      num_factors=64,                      epochs=50)
fm.fit({'train': train_data, 'test':test_data})
复制代码


INFO:sagemaker:Creating training-job with name: factorization-machines-2018-12-18-03-18-50-388


2018-12-18 03:18:50 Starting – Starting the training job…


2018-12-18 03:18:56 Starting – Launching requested ML instances……


2018-12-18 03:19:58 Starting – Preparing the instances for training…


2018-12-18 03:20:45 Downloading – Downloading input data…


2018-12-18 03:20:55 Training – Downloading the training image…


Docker entrypoint called with argument(s): train


[12/18/2018 03:22:09 INFO 139830159329088] #quality_metric: host=algo-1, test binary_classification_accuracy=0.736288890965


[12/18/2018 03:22:09 INFO 139830159329088] #quality_metric: host=algo-1, test binary_classification_cross_entropy=0.548373071499


[12/18/2018 03:22:09 INFO 139830159329088] #quality_metric: host=algo-1, test binary_f_1.000=0.84806072188


[2018-12-18 03:22:09.767] [tensorio] [info] data_pipeline_stats={“name”: “/opt/ml/input/data/test”, “epoch”: 1, “duration”: 366, “num_examples”: 22}


[2018-12-18 03:22:09.767] [tensorio] [info] data_pipeline_stats={“name”: “/opt/ml/input/data/test”, “duration”: 40228, “num_epochs”: 2, “num_examples”: 23}


#metrics {“Metrics”: {“totaltime”: {“count”: 1, “max”: 40280.484199523926, “sum”: 40280.484199523926, “min”: 40280.484199523926}, “setuptime”: {“count”: 1, “max”: 39.59202766418457, “sum”: 39.59202766418457, “min”: 39.59202766418457}}, “EndTime”: 1545103329.767301, “Dimensions”: {“Host”: “algo-1”, “Operation”: “training”, “Algorithm”: “factorization-machines”}, “StartTime”: 1545103329.299489}
复制代码


[2018-12-18 03:22:09.784] [tensorio] [info] data_pipeline_stats={“name”: “/opt/ml/input/data/train”, “epoch”: 50, “duration”: 1287, “num_examples”: 86}


[2018-12-18 03:22:09.784] [tensorio] [info] data_pipeline_stats={“name”: “/opt/ml/input/data/train”, “duration”: 39923, “num_epochs”: 51, “num_examples”: 4301}


Billable seconds: 92


模型训练完成了。在模型训练结束后的总结中,我们可以看到几个重要的指标:


  • 模型训练计费时间 92 秒,所以并不会花很多钱;

  • 模型的二分准确度为 73.62%

  • 接下来,我们考虑一下应用 SageMaker 的超参调优(Hyperparameters Tuning)来尝试其他的超参设置是否可以获得更好的二分准确度。SageMaker 的超参调优可以通过 SageMaker 的 Console 直接配置完成。简单来讲,就是首先设定目标,本例中我们希望最大化(Maximize)模型二分准确度。之后给予可调参数的变动范围,本例中我们希望测试 mini batch size 和 epochs 的设置是否可以提升结果表现。最后定义训练集、测试集、算法的相应位置,以及优化任务运行的次数(最大为 100),即可开始。


当优化任务全部运行完成后,我们可以获得表现最好的模型的数据,如图



在这一参数配置下,模型的二分准确度提升为 76.2%。应用这一超参配置训练模型,并部署为 Endpoint。Endpoint 可以理解为模型基于 http 访问的 API 接口,有了 Endpoint 就可以进行预测服务了。


In [227]:


fm.set_hyperparameters(feature_dim=79906,                      predictor_type='binary_classifier',                      mini_batch_size=200,                      num_factors=64,                      epochs=134)In [228]:
fm_predictor = fm.deploy(initial_instance_count=1, instance_type='ml.t2.medium')INFO:sagemaker:Creating model with name: factorization-machines-2018-12-18-05-56-31-040
INFO:sagemaker:Creating endpoint with name factorization-machines-2018-12-18-05-47-26-108
—————————————————————–!
复制代码


我们的模型部署完成,Endpoint 名称为“factorization-machines-2018-12-18-05-47-26-108”。之后我们可以通过这个名称来调用 Endpoint 完成预测任务。


后续我们会继续利用已部署的终端节点 Endpoint 对常见的应用场景进行预测。


使用 AWS Sagemaker 系列文章:


第一篇:使用AWS Sagemaker训练因子分解机模型并应用于推荐系统(本博文)


第二篇:使用AWS Sagemaker部署的终端节点进行推荐预测的常用场景


————


作者介绍:


崔辰


AWS 大中华区创新中心技术业务拓展经理。加入 AWS 之前,崔辰在中国惠普、IBM、微软以及海航科技等公司担任过售前技术顾问、市场经理和战略合作经理等职务。在 10 多年的科技领域工作经历中,崔辰服务过众多企业级客户。


本文转载自 AWS 技术博客。


原文链接:


https://amazonaws-china.com/cn/blogs/china/aws-sagemaker-system-recommend-use/


2019-10-08 10:03637
用户头像

发布了 1836 篇内容, 共 92.4 次阅读, 收获喜欢 73 次。

关注

评论

发布
暂无评论
发现更多内容

Gtags解决UnicodeEncodeError问题

alps2006

SpringMVC 基础

Andy

Java | 继承

陌上

Java 编程 10月月更

强烈推荐!商业洞察就用这个工具

产品海豚湾

产品经理 产品设计 产品运营 商业化 10月月更

数字经济浪潮下,企业如何通过数字体验平台(DXP)更好的与用户建立联系?

Baklib

客户体验

对话熊飞2022:好的经营需要时间,TO B 从好产品好故事进化为好商业

B Impact

SpringBoot简介

Andy

开源直播课丨大数据集成框架ChunJun类加载器隔离方案探索及实践

袋鼠云数栈

标签评分:海量标签如何进行系统治理?

袋鼠云数栈

Java | 面向对象(二)

陌上

编程 Java、 10月月更

10-15-有效兼顾员工工作幸福感和工作进展,华为云会议稳定便捷又高效!

路过的憨憨

华为

SpringBoot之开发深入

Andy

Java | 数组工具类Arrays、数学工具类Math

陌上

编程 Java、 10月月更

“程”风破浪的开发者|satoken实现优雅鉴权

codingyt

学习方法 安全 鉴权 10月月更 “程”风破浪的开发者

八大技术亮点,华为云数据灾备解决方案为企业数据铸就安全防护强

科技之光

SpringBoot之基础知识

Andy

赴一场开源盛会丨10月29日 COSCon'22 开源年会杭州分会场,这里只差一个「你」!

袋鼠云数栈

袋鼠云平台代码规范化编译部署的提效性改进实践

袋鼠云数栈

安全架构师的运营一二事

I

运营 安全架构 企业安全 安全运营

SpringBoot之快速启动

Andy

SpringBoot之错误处理

Andy

激活数据价值,探究DataOps下的数据架构及其实践丨DTVision开发治理篇

袋鼠云数栈

一文读懂:开源大数据调度系统Taier1.2版本新增的「工作流」到底是什么?

袋鼠云数栈

华为云桌面,如何用心保护企业安全?

科技之光

Spring事务

Andy

开源大数据集成框架ChunJun在数据还原上的探索和实践

袋鼠云数栈

EMQ助力构建工业生产数字孪生基础架构|智慧工厂系列专题03

EMQ映云科技

物联网 IoT 工业互联网 数字孪生 10月月更

Spring之AOP

Andy

选择华为云数据库,三大亮眼优势来助力

科技之光

灾变来袭不用怕,华为云数据灾备解决方案为你排忧解难

科技之光

华为云连接CC,解决企业跨地区发展的网络难题

路过的憨憨

华为

使用AWS Sagemaker训练因子分解机模型并应用于推荐系统_语言 & 开发_亚马逊云科技 (Amazon Web Services)_InfoQ精选文章