(一)背景--算法平台架构制约了优化的天花板
12年在腾讯微博工程转算法开始,至今也有7、8个年头了。
中间经历过微软、阿里、腾讯等内部算法架构,同时对百度、头条、一点资讯的架构也有些了解。
作为一个工程出身,本身算法优化策略并不具备太大的通用性。但是算法平台架构却是非常实际通用。
而架构虽然只起支撑作用,但无丰富优化经验的纯工程团队,设计出来的算法平台架构,虽不至于害死人不偿命,但无疑也会成为限制算法优化的天花板。
笔者尝试以自己的理解角度去分析并设计算法平台架构框架的核心思路,从算法优化需求出发,迭代思维为主,进而提出目前阶段我认为较为理想的算法架构。主要以推荐、广告为主,也兼顾部分Bot、NLP、图像/视频等。
PS:鉴于时间精力考虑,只说干货不说废话,本文将尽量不存在任何虚假夸大的地方,朋友圈同行请多指教,如有任何问题欢迎点出,仅做抛砖引玉。
(二)实际项目及复盘总结:
(1)神马搜索(年),小说推荐架构变为机器学习平台架构。(2)腾讯微博广告训练平台,LDA召回等。
(3)腾讯周边服务项目重构,内搜检索架构变为机器学习架构。
(4)微软Twitter实时社交算法架构,BingNews推荐架构,Bing检索图像架构。一些零散搜索质量提升及AI相关的小项目。
(5)OPPO推荐架构平台。
(6)芒果TV推荐架构平台。
(7)纯工程向:百度知道重构,百度网盘API,百度云存储,康盛创想Cache架构,PHPChina网站架构
2.1)神马搜索小说推荐排序服务。--年V1推荐算法的单机性能极致
选此作为切入,一是够小,二是全程我自己单独完成,三是从无到有,四是有部分突破性的优化,项目涉及到关键的3个方面:1训练2排序线上服务Serving3优化性能。
背景及问题:神马小说收购合并书旗,用16台线上机器替换了原数百台机器。单次请求时延控制在50ms内。原排序层是简单规则策略。
核心难题:单台每秒加特征运算,需要做到百万次运算/秒,以排序模型来算需要3万次/秒计算。
hashtable,以unordered_map为例,虽说都是O(1),但是与key的长度成正相关,即O(K),K为key的长度。而双数组trie,为trie树的高度。
常见方案:小说名直接做哈希,当冲突时,内容对应特征无效。×
解决方案:全方位缓存,并把计算复杂度变为O(1),进一步把存取长字符串key的读取计算开销变为微秒级。如下图:分为用户、内容item和其他缓存。
(1)千万每秒-小说内容(item)库特征加载
计算量:部小说*原始特征数*特征处理(原始特征-离散特征-归一化为模型输入)
cache方案:把以上计算分为:线上次取计算结果+离线特征复杂处理+2小时全量增量实时消息队列更新。则线上只剩次取计算结果的过程。
存储成本和计算成本大幅降低。离线处理完通过队列push到线上。
开始先尝试用谷歌sparsehashmap的densehashmap进行万内容的存取操作。但小说标题平均长度在20~30字节(UTF8,一个汉字3-5字节)左右,而为避免重复,作者也会加入做key。整体存储的空间成本较高,而读取性能较低。
但实测性能抖动很厉害,字符串长度成关键瓶颈。理论上6万每秒,字节数变多后实测只有次~次/每秒左右。双数组Trie则每秒68万。
后用双数组TRIE优化实现O(1)复杂度及较小的内存消耗读取。
由于双数组TRIE的写性能很低,必须分桶更新时,当时计算在2小时内更新完一轮全量最佳的数量是棵树,参看下图,构建完后个base和check对应的文件。双数组trie分桶棵树,需据实际场景来定全量更新部分的时延。
所以需要hash算法,当时hash算法很少能达到千万次/秒的级别。而且hash性能与字符串的长度成线性相关。
在性能测试时,同样60字节的查询,MurmurHash2.0是万/秒,而改造优化完的time33Hash可到万次/每秒。
突破hash计算上限,通过改写time33hash方案:
1)(////)魔法常量放寄存器,乘法改为位运算。
2)遍历一遍所有的中文字(一般都是常用字,6个左右),映射到一个数组取出偏移编号,再hash。
3)同时通过缓存预热方式把万文章对应的id哈希计算结果在线上机器加热,加大CPU一二级缓存的命中率。
当时,我记得阿里云好多基础库,由于内存的copy操作较多,或者本身实现没有考虑到极致性能的情况,所以必须把这些基础库的方法重载,改写实现才能符合性能要求。当时改写了很大一部分基础库实现才满足了上线需求。
(2)用户及context特征加载及计算
单次请求一次的缓存用户和context较为简单,redis一次请求即可。更新也相对简单,但因为是网络交互,控制好包的大小,只存score或vector;而且最好是开始时先发网络的异步请求,同时内容等本地计算,最后等usercache包返回再算。同时把其他单次请求也对应的特征都分离到外围。
结论:
(1)通过缓存分离解决内容item的大量计算转为离线计算,并通过push方式更新线上。用户等单次请求cache部分采用分布式网络KV即可如redis,因为每次请求只有1次计算,用户数据相关取操作尽量合并在一次网络交互中。
内容item因为单次请求几百甚至上千、近万个量级,尽量采用本地单机全量的方式做本地读取,以便达到超高性能。这个思路,在后续腾讯周边项目,微软推荐项目,搜索项目等都会涉及到,也是我这几年总结出来通过C++实现推荐、搜索、广告算法高性能线上serving的主要基础方案之一。
(2)线上的计算存取偏轻偏简单后,可以做大幅度的性能优化,达到循环为单位的千万每秒运算,而排序计算达到每秒万级别的运算。
(3)该项目取得点击率+40%的效果,具体算法上优化,可参考我后续会整理的推荐算法优化系列,之后做了一些推荐理由相关的项目。
PS:因为涉及到us级别的优化,加写日志本身会对性能产生较大影响,一把日志是延时buff积累然后再落地,如果不是最好改写,或先计算好日志本身的平均耗时,或在外面增加循环大倍数来跟踪实际性能。
这是排序层,下篇文章,会以自底至顶的方式,围绕腾讯周边项目来具体展开,通过怎么把搜索架构改造为推荐算法架构,来了解推荐算法涉及到的数据流、训练平台、线上serving的特征、召回、排序、重排等环节的整体架构。
预览时标签不可点收录于话题#个上一篇下一篇