我一直致力于推荐系统,到目前为止,我们只是构建了一个简单的算法,即邻域方法,用于电子商务商店。
系统是用Python构建的,可以广泛使用它的数学库,它以一种非常简单的方式工作:它接收客户导航的产品列表,输出是前10个推荐项目的列表
为此,我们在python中实现了2个脚本:一个是我们使用Tornado接收URL的Web服务,第二个是邻居本身,我们在其中使用我们的相似性矩阵来计算推荐。它们通过Inter Process Communication(IPC)进行通信,我们使用内置的库多处理来实现这一目的。
我们创建了这样的网络服务:
import sys
sys.path.append("..")
import tornado.httpserver
import tornado.ioloop
import tornado.web
from tornado.escape import json_encode
from shared.bootstrap import *
import argparse
from clients import ClientFactory, ClientNotFoundException
class WService(tornado.web.RequestHandler):
_clients = {}
def get(self, algorithm = None):
algorithm = 'neighborhood' if not algorithm else algorithm
rec_list = []
if algorithm == 'favicon.ico':
algorithm = 'neighborhood'
print "value of algorithm %s" %(algorithm)
try:
if not algorithm in self._clients:
self._clients[algorithm] = ClientFactory.get_instance(algorithm)
arguments = self.get_arguments_by_client(self._clients[algorithm].get_expected_arguments())
rec_list = self._clients[algorithm].call(arguments)
except ClientNotFoundException as err:
error("Erro " + str(err))
except Exception as err:
error("Erro: " + str(err))
self._clients[algorithm] = ClientFactory.get_instance(algorithm)
rec_dict = {"skus" : [str(sku) for sku in rec_list]}
self.write(json_encode(rec_dict))
def get_arguments_by_client(self, expected_arguments):
arguments = {}
for key in expected_arguments:
arguments[key] = self.get_argument(key, expected_arguments[key])
return arguments
application = tornado.web.Application([
(r"/(.*)", WService),
])
def parse_command_line_params():
parser = argparse.ArgumentParser()
parser.add_argument('--port', help="Service running port number", required=True)
return parser.parse_args()
if __name__ == "__main__":
http_server = tornado.httpserver.HTTPServer(application)
cmd_args = parse_command_line_params()
http_server.listen(cmd_args.port)
tornado.ioloop.IOLoop.instance().start()
基本上当Tornado收到GET
请求时,我们的ClientFactory会启动我们的邻居应用程序,如果它尚未启动并启动IPC(为此我们会执行this thread on SO,第二个回答)。
因此,当Tornado收到一个URL时,它基本上解析它,通过IPC将项目列表发送到我们的邻居应用程序,然后处理信息并通过相同的IPC将结果发送回Tornado,最终以JSON格式输出选择的产品。
这里的例子是发送到Tornado的URL:
http://localhost:8000/?skus_navigated=PR840ACG60NNV,BO185SHF79ZRG,BO185SHF99OBK&skus_carted=&skus_purchased=AN658APF41AIC&category=49
发送的最大项目是每个参数中的10个产品。如果它们最终都是null或无法识别的东西,则默认响应为空JSON。
我们的邻域算法有一个稀疏的Scipy矩阵,目前它的形状围绕(300k,300k)的值。如果我们发送参数:
skus_navigated = PR840ACG60NNV,BO185SHF79ZRG,BO185SHF99OBK
skus PR840ACG60NNV ,BO185SHF79ZRG ,BO185SHF99OBK
在内部映射(如果在数据中观察到它们)并且它们会收到给定的分数(假设为1.0),所以如果它们的映射类似于:
PR840ACG60NNV = 7
BO185SHF79ZRG = 5000
BO185SHF99OBK = 300
然后我们创建一个向量v
,例如:
v = zeros(300k)
v[[7, 300, 5000]] = 1.0
然后我们使用v
乘以我们的scipy矩阵来获得最佳推荐。
之后我们尝试使用系统并且它工作了几分钟,但随后响应时间开始大幅增加到我们不得不关闭系统的程度。
即使系统平均只需要10毫秒来解析URL以输出JSON结果,但系统开始需要更长时间来响应和分解。
我们的基础架构似乎没有问题,我们正在使用一些可以处理我们请求数量的虚拟机。
所以我想问你是否有办法使用Tornado制作这个IPC并连接到我们的邻居,可能是以某种异步方式,以避免请求排队并最终崩溃。
在Tornado和邻居之间有没有更好的方法来建立这个系统?
我们认为在龙卷风中加载我们的邻域矩阵会使它有点不稳定,因为如果一个发生故障,那么另一个发生故障。但到目前为止我们创建的解决方案似乎已经使系统更不稳定,即使处理请求的速度相当快。
感谢您的帮助,如果您需要更多信息,请告诉我们。
提前致谢,