2021腾讯数字生态大会直播预约通道开启!技术内容大爆发,开发者必看! 了解详情
写点什么

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

2019 年 10 月 08 日

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

上次我们初步介绍了使用 SageMaker 快速训练和部署 Factorization Machines 模型,接下来我们利用 Endpoint 进行预测模拟的实际用例。


利用 Endpoint 进行预测模拟的实际用例

考虑几个常见的实际用例在生产环境中的推荐系统


  • 从参与训练的电影库(26004 部)中推荐 10 部给已存在的用户;

  • 从参与训练的电影库(26004 部)中推荐 10 部给新用户;

  • 应用内容筛选并推荐 10 部电影给已存在的用户;


1. 从参与训练的电影库(26004 部)中推荐 10 部给已存在的用户

首先我们随机选择一位用户,本例中该用户 ID 是 100020528。观察该用户在训练集中点评过的电影,共计 4 部。


In [56]:


#select 100020528 as a random userfor k in df[df['UserId']=='100020528'].index:     print(df_movie_all_db[df_movie_all_db['MovieID']==str(df.at[k,'MovieID'])].Title.values[0], "         ", df.at[k,'Rate'])
复制代码


苏醒 6


推动情人的床 6


录影片段 4


雷德怒潮 6


为这一用户生成预测数据的稀疏矩阵。


In [7]:


#revise the loadDataset() to accept the userID as an input.def createInferenceForUser(UserId, lines, columns):    user_number = 53902    X = scipy.sparse.lil_matrix((lines, columns)).astype('float32')    line = 0    for line in range(0, lines):        X[line, u_Dict[UserId]] = 1        X[line, user_number+line] = 1        line=line+1
print(X.shape) return X
复制代码


In [8]:


user_inference = createInferenceForUser('100020528', movie_number, user_number+movie_number)print(user_inference.shape)
复制代码


(26004, 79906)


(26004, 79906)


生成的是 26004✖️79906 的矩阵。


随后调用 Endpoint 对预测数据进行预测,并返回预测结果。由于预测数据较大,我们分段进行,每一个请求传递 10 行预测数据。


In [10]:


#invoke endpointclient = boto3.client('sagemaker-runtime')In [20]:
#Make inferenceimport jsoninf_start = 0step = 10response = []while inf_start <= user_inference.shape[0]+1: raw_response = client.invoke_endpoint( EndpointName = 'factorization-machines-2018-12-18-05-47-26-108', Body = fm_serializer(user_inference[inf_start:inf_start+step].toarray()), ContentType='application/json') result = json.loads(raw_response['Body'].read()) for counter, p in enumerate(result['predictions']): p['MovieID'] = df_m_Dict[df_m_Dict.movie_index == (inf_start + int(counter))].MovieID.values[0] response.append(p) inf_start = inf_start+step print(inf_start)print(len(response))26004
复制代码


对预测结果进行整理后,显示如下:


In [127]:


df_response_like = df_response[df_response.predicted_label == 1.0]final = df_response_like.sort_values('score', ascending=False).head(10)for final_index in final.index:    print(df_movie_all_db[df_movie_all_db['MovieID']==str(final.at[final_index,'MovieID'])].Title.values[0],'\t',df_movie_all_db[df_movie_all_db['MovieID']==str(final.at[final_index,'MovieID'])].Rate.values[0],'\t',final.at[final_index,'score'])
复制代码


逃离循环 6.8 0.8900507092475891


燕尾蝶 8.6 0.8846622705459595


盲证 7.4 0.8812546133995056


头文字 D 新 OVA 启程之绿 7.5 0.8807412981987


狐妖小红娘剧场版:月红篇 9.2 0.8801539540290833


追随你脚步 7.5 0.8792228698730469


黑色闪电 7.4 0.879056453704834


蓝色情人节 7.8 0.8788111805915833


骗中骗 2 7.5 0.878635048866272


放牛班的春天 9.2 0.8783532977104187


2. 从参与训练的电影库(26004 部)中推荐 10 部给新用户

如果是一个全新的用户,并不存在于我们训练数据集中的用户列表里。我们可以将稀疏矩阵中所有表示用户的列置为 0 来生成预测数据。


In [130]:


def createNewUserInference(lines, columns):    user_number = 53902    X = scipy.sparse.lil_matrix((lines, columns)).astype('float32')    line = 0    for line in range(0, lines):        X[line, user_number+line] = 1        line=line+1    print(X.shape)    return X
复制代码


In [123]:


def callEndpointInference(query):    import json    inf_start = 0    step = 10    response = []    while inf_start <= query.shape[0]:        raw_response = client.invoke_endpoint(                    EndpointName = 'factorization-machines-2018-12-18-05-47-26-108',                    Body = fm_serializer(query[inf_start:min(inf_start+step, query.shape[0]+1)].toarray()),                    ContentType='application/json')        result = json.loads(raw_response['Body'].read())        for counter, p in enumerate(result['predictions']):            p['MovieID'] = df_m_Dict[df_m_Dict.movie_index == (inf_start + int(counter))].MovieID.values[0]            response.append(p)        inf_start = inf_start+step        print(inf_start)    return response
复制代码


In [125]:


def showResult(response):    df_response = pd.DataFrame(response)    df_response_like = df_response[df_response.predicted_label == 1.0]    final = df_response_like.sort_values('score', ascending=False).head(10)    for final_index in final.index:        print(df_movie_all_db[df_movie_all_db['MovieID']==str(final.at[final_index,'MovieID'])].Title.values[0],'\t',df_movie_all_db[df_movie_all_db['MovieID']==str(final.at[final_index,'MovieID'])].Rate.values[0],'\t',final.at[final_index,'score'])
复制代码


针对新用户生成的预测数据依然是一个 26004✖️79906 的矩阵。


In [131]:


new_inference = createNewUserInference(movie_number, user_number+movie_number)(26004, 79906)

复制代码


In [132]:


%%timenew_response = callEndpointInference(new_inference) 
CPU times: user 5min 18s, sys: 25.9 s, total: 5min 44s
Wall time: 16min
复制代码


预测结果如下,可以看到,与之前已存在用户的预测结果有很大不同。


In [133]:


showResult(new_response)
复制代码


青涩恋爱 7.9 0.7878496646881104


死亡实验 8.0 0.7846202850341797


时空线索 7.7 0.7836878299713135


侠僧探案传奇之王陵之谜 7.0 0.7817953824996948


宝拉 X 7.1 0.7813129425048828


没有秘密 6.5 0.7806361317634583


雨中曲 9.0 0.7800688743591309


卡里加里博士的小屋 8.7 0.7798536419868469


玩命警车 6.3 0.7796841263771057


玩具总动员 3 8.8 0.7790806293487549


3. 应用内容筛选并推荐 10 部电影给已存在的用户

回到已存在用户的推荐预测部分,之前我们的预测耗时较长,约需要 17 分钟左右才能完成一次查询,这在实际应用中是不现实的。主要原因之一是我们采用对电影库进行全量预测,其实这降低了性能表现和相关性。如果采用内容筛选的办法,提前限定预测影片的范围,缩小预测数据规模,可以提高性能表现和相关性。


让我们看一下 100020528 用户在全量数据中的历史表现。这位用户点评了 135 部电影,对应的类型如下:


In [88]:


df_movie_preference = df_movie_all_db[df_movie_all_db['MovieID'].isin(movie_rated_by_user)].copy()df_movie_preference.groupby('Tags').count()
复制代码


Out[88]:


LinkRateTitleCountryRateCollectedMovieID
Tags
剧情 / 动作 / 恐怖111101
剧情 / 动作 / 犯罪222202
剧情 / 历史 / 奇幻111101
剧情 / 喜剧 / 奇幻111101
剧情 / 喜剧 / 悬疑 / 惊悚 / 恐怖111101
剧情 / 喜剧 / 爱情111101
剧情 / 喜剧 / 爱情 / 惊悚 / 恐怖111101
剧情 / 奇幻111101
剧情 / 恐怖333303
剧情 / 恐怖 / 历史111101
剧情 / 恐怖 / 短片111101
剧情 / 悬疑333303
剧情 / 悬疑 / 恐怖111101
剧情 / 悬疑 / 惊悚222202
剧情 / 悬疑 / 惊悚 / 恐怖111101
剧情 / 悬疑 / 惊悚 / 犯罪111101
剧情 / 情色111101
剧情 / 惊悚999909
剧情 / 惊悚 / 历史111101
剧情 / 惊悚 / 恐怖666606
剧情 / 惊悚 / 犯罪444404
剧情 / 歌舞 / 奇幻111101
剧情 / 爱情333303
剧情 / 爱情 / 冒险111101
剧情 / 爱情 / 同性111101
剧情 / 爱情 / 悬疑 / 恐怖 / 奇幻111101
剧情 / 犯罪444404
剧情 / 科幻 / 恐怖222202
剧情 / 科幻 / 惊悚222202
剧情 / 科幻 / 惊悚 / 犯罪111101
喜剧 / 动作 / 科幻111101
喜剧 / 动作 / 科幻 / 恐怖111101
喜剧 / 恐怖222202
喜剧 / 恐怖 / 奇幻222202
喜剧 / 悬疑 / 恐怖111101
喜剧 / 惊悚222202
喜剧 / 惊悚 / 恐怖111101
喜剧 / 惊悚 / 恐怖 / 犯罪111101
喜剧 / 惊悚 / 犯罪111101
喜剧 / 爱情 / 恐怖111101
喜剧 / 科幻 / 恐怖111101
恐怖15151515015
恐怖 / 奇幻111101
恐怖 / 短片 / 犯罪111101
悬疑 / 恐怖111101
悬疑 / 惊悚222202
悬疑 / 惊悚 / 恐怖666606
悬疑 / 惊悚 / 恐怖 / 犯罪111101
悬疑 / 惊悚 / 犯罪222202
悬疑 / 短片 / 奇幻111101
惊悚777707
惊悚 / 恐怖10101010010
惊悚 / 恐怖 / 犯罪333303
惊悚 / 犯罪111101
爱情111101
爱情 / 恐怖 / 奇幻111101
爱情 / 短片 / 情色111101
科幻111101
科幻 / 奇幻111101
科幻 / 恐怖111101


可以看到这位用户偏爱含有“恐怖”和“惊悚”标签的电影,加起来几乎是其点评过电影的 50%,所以我们可以对电影库进行一下筛选,看有多少电影是包含这两个标签的。


In [93]:


df_movie_all_db[df_movie_all_db.Tags.isin(['惊悚','恐怖'])].count() 
复制代码


Out[93]:


Link 1625


Rate 1625


Title 1625


Tags 1625


Country 1625


RateCollected 0


MovieID 1625


dtype: int64


In [111]:


#Check whether all 1625 movies is in m_Dict, and clean the movies that are not in m_Dictfor m_h in horror_and_terrified_movie:    if m_h in m_Dict.keys():        continue    else:        print(m_h)        horror_and_terrified_movie.remove(m_h)print(len(horror_and_terrified_movie))1124
复制代码


观测结果,在我们参与训练的电影库(26004 部)中有 1124 部是符合这两个类型的。我们进一步把 100020528 这位用户点评过的电影从这 1124 部中移除出去。


In [137]:


m_rated = []for el_index in df_movie_preference.index:    m_rated.append(df_movie_preference.at[el_index, 'MovieID'])
复制代码


In [141]:


for m_h in horror_and_terrified_movie:    if m_h in m_rated:        print(m_h)        horror_and_terrified_movie.remove(m_h)print(len(horror_and_terrified_movie))1104
复制代码


最后我们获得了共计 1104 部电影的清单。针对这位用户,以及这 1104 部电影的清单重新生成预测数据矩阵。预测数据矩阵为 1104✖️79906 阶。


In [143]:


filtered_user_inference = createInferenceForUserPreference('100020528', horror_and_terrified_movie, user_number+movie_number)print(filtered_user_inference.shape)(1104, 79906)

复制代码


使用这一矩阵进行预测。


In [149]:


%%timefiltered_response = callEndpointInference_noid(filtered_user_inference)CPU times: user 13.2 s, sys: 879 ms, total: 14.1 s
Wall time: 40.9 s
复制代码


预测用时 40.9 秒,预测结果如下:


In [154]:


showResultfromDataFrame(df_filtered_counter)
复制代码


杀人鳄鱼潭 7.4 0.8682911992073059


没有秘密 6.5 0.8636484742164612


热血青年 6.5 0.8625574111938477


驱魔警察 7.4 0.8622710108757019


鬼玩人 7.0 0.8613290786743164


一千灵异夜之灵魂实验 7.0 0.8611863851547241


连体 5.4 0.8586516976356506


僵尸城市 6.3 0.8585190176963806


35 楼的生存游戏 6.7 0.8584233522415161


危险工作 6.5 0.8558875918388367


从预测结果中可以直观感受到,更贴近用户历史行为的喜好。


应用稀疏数据格式优化响应时间

当然 40.9 秒的预测时间在实际中依然过长,这是由于在本例的演示中,预测数据全部采用 json dense 格式传递,造成数据量大,只能分批预测和返回。实际应用中可以采用 FM 接受的 json sparse 数据格式。


我们使用最后的用户示例演示一下 json sparse 格式传递预测数据获得结果的过程以及对这一格式整个响应时间做个测量。


重新定义 Invoke Endpoint 的方法以及内部构成数据的格式如下:


In [298]:


def slicedInferenceUserPreference(userid, step, movielist):    import json    #total_line = 26004 #movie_number        start = 0    response = []    line = 0    response = []        while start <= len(movielist)+1:        js={'instances': []}        for line in range(0, min(int(step), len(movielist)-start)):            js['instances'].append({'data':{'features': {                'keys':[int(u_Dict[userid]), 53902+m_Dict[movielist[line+int(start)]]],                "shape":[79906],                "values":[1,1]}}})                #print(json.dumps(js))        raw_response = client.invoke_endpoint(                EndpointName = 'factorization-machines-2018-12-18-05-47-26-108',                Body = json.dumps(js),                ContentType='application/json')        result = json.loads(raw_response['Body'].read())        for counter, p in enumerate(result['predictions']):            p['MovieID'] = movielist[counter]            response.append(p)        print(start+line)        start = start+step        return(response)In [299]:
%%timetest_user_preference_response = slicedInferenceUserPreference('100020528', 1000, horror_and_terrified_movie)999
1103
CPU times: user 21.8 ms, sys: 519 µs, total: 22.3 ms
Wall time: 5.93 s

复制代码


运行结果共计耗时 5.93 秒,且其中大部分是由于网络延时,已经可以考虑在实际中应用了。预测结果也与之前保持一致。


In [301]:


showResultfromDataFrame(preference_response)
复制代码


杀人鳄鱼潭 7.4 0.8682911992073059


没有秘密 6.5 0.8636484742164612


热血青年 6.5 0.8625574111938477


驱魔警察 7.4 0.8622710108757019


鬼玩人 7.0 0.8613290786743164


一千灵异夜之灵魂实验 7.0 0.8611863851547241


连体 5.4 0.8586516976356506


僵尸城市 6.3 0.8585190176963806


35 楼的生存游戏 6.7 0.8584233522415161


危险工作 6.5 0.8558875918388367


从本次的实验示例中可以感受到,SageMaker 作为 AWS 平台上机器学习及人工智能的核心服务之一,极大地简化了我们准备数据、分析数据、构建模型、验证模型、参数调优和部署模型的整个工作流程。使用 SageMaker 提供的算法,更可以使拥有大量数据资产的用户以极快的速度发挥数据的价值,在应用中引入机器学习或人工智能。


使用 AWS Sagemaker 系列文章:


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


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


作者介绍:


崔辰


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


本文转载自 AWS 技术博客。


原文链接:


https://amazonaws-china.com/cn/blogs/china/aws-sagemaker-recommendation-scene/


2019 年 10 月 08 日 10:21422
用户头像

发布了 1299 篇内容, 共 41.9 次阅读, 收获喜欢 43 次。

关注

欲了解 AWS 的更多信息,请访问【AWS 技术专区】

评论

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

极光开发者周刊【No.0813】

极光开发者

介绍一个好用的网络工具traceroute命令

liuzhen007

8月日更

ElasticSearch 亿级数据检索深度优化

王知无

架构实战营 1 期 - 模块五作业

李东旭

#架构实战营

四万字硬刚Kudu | Kudu基础原理实践小总结

王知无

前端基础一之HTML篇

ベ布小禅

8月日更

ElastricSearch第三弹之存储原理(详细+易懂)

阿Q说代码

ES 8月日更 flush Refresh translog

《社会心理学》-怎么说服他人(整理稿)

箭上有毒

8月日更

如何写好一篇自媒体文案:把握节奏引起共鸣

石头IT视角

从小白程序员到大厂高级技术专家我看过哪些书籍?

冰河

学习 程序员 面试 程序人生

智能时代的信任口诀:让计算远离算计

脑极体

QDS07 Mysql 安装指定版本

耳东@Erdong

MySQL 8月日更 qds

你的登录接口真的安全吗?快看看你有没有中招!

xcbeyond

安全性 8月日更

既然人注定孤独,那为什么要结婚呢?

m小幼

前端之算法(六)分而治之

Augus

算法 8月日更

JAVA对于文件IO操作的支持

卢卡多多

Java 文件 io 8月日更

漏洞挖掘的快乐你想象不到

网络安全学海

黑客 网络安全 信息安全 渗透测试 漏洞挖掘

云原生-工作流引擎Zeebe

QiyihaoLabs

云原生 k8s cncf BPM zeebe

【Spring MVC 新手指北】1、Spring MVC 简介及入门实

村雨遥

Java Spring MVC 8月日更

你知道关闭页面时怎么向后台发送消息吗?

编程三昧

JavaScript 前端 8月日更

网络攻防学习笔记 Day107

穿过生命散发芬芳

网络安全 8月日更

【设计模式】观察者模式

Andy阿辉

C# 编程 后端 设计模式 8月日更

分享 6 个实用的 Vue 技巧

devpoint

Vue Vue3 8月日更

占楼

IT蜗壳-Tango

8月日更

Linux之wget命令

入门小站

Linux

MySQL 字段NOT NULL

一个大红包

8月日更

Java 操作 Office:POI word之网络图片处理

程序员架构进阶

Java Apache POI 实战问题 8月日更

模块5作业

Geek_35a345

Elasticsearch 日志监控方案

Se7en

OLAP 简介

LeifChen

OLAP 多维分析 8月日更

python--构造方法笔记

加哥

英特尔On技术创新峰会

英特尔On技术创新峰会

使用AWS Sagemaker部署的终端节点进行推荐预测的常用场景-InfoQ