随着深度学习的广泛应用,在搜索引擎/推荐系统/机器视觉等业务系统中,越来越多的深度学习模型部署到线上服务。
机器学习模型在离线训练时,一般要将输入的数据做特征工程预处理,再输入模型在 TensorFlow PyTorch 等框架上做训练。
常见的特征工程逻辑有:
这些特征工程代码,当然一般使用深度学习最主要的语言?python?实现。
离线训练完成,模型上线部署后,同样要?用 C++ 重新实现?这些 python 的特征工程逻辑代码。
我们发现,?“用 C++ 重新实现”?这个步骤,给实际业务带来了大量的问题:
针对这些问题,我调研了这些业界方案:
《推荐系统中模型训练及使用流程的标准化》
https://www.infoq.cn/article/2E6LCqb1GeqFRAjkkjX3
《自主研发、不断总结经验,美团搜索推荐机器学习平台》
/developer/article/1357309
《京东电商推荐系统实践》
https://www.infoq.cn/article/1OkKmb_gEYNR3YqC9RcW
“模型线上线下一致性问题对于模型效果非常重要,我们使用特征日志来实时记录特征,保证特征的一致性。这样离线处理的时候会把实时的用户反馈,和特征日志做一个结合生成训练样本,然后更新到模型训练平台上,平台更新之后在推送到线上,这样整个排序形成了一个闭环。”
总结起来,有几种思路:
(1) .已有方案的缺点
但这些思路都有各种缺点:
(2). 翻译器
回到问题出发点考虑,显而易见,这个问题归根结底就是需要一个 “ python 到 c++ 的翻译器 ” 。
那其实 “翻译器 Transpiler ” ,和编译器解释器类似,也是个古老的热门话题了,比如?WebAssembly?,?CoffeeScript?,?Babel?,
Google Closure Compiler?,?f2c
于是一番搜索,发现 python 到 C++ 的翻译器也不少,其中?Pythran?是新兴比较热门的开源项目。
于是一番尝试后,借助 pythran,我们实现了:
一条命令安装:
pip3?install?pythran?
下面这个 python demo,是 pythran 官方 demo。
import?math?import?numpy?as?np??def?zero(n,?m):?????return?[[0]*n?for?col?in?range(m)]??#pythran?export?matrix_multiply(float?list?list,?float?list?list)?def?matrix_multiply(m0,?m1):?????new_matrix?=?zero(len(m0),len(m1[0]))?????for?i?in?range(len(m0)):?????????for?j?in?range(len(m1[0])):?????????????for?k?in?range(len(m1)):?????????????????new_matrix[i][j]?+=?m0[i][k]*m1[k][j]?????return?new_matrix??#pythran?export?arc_distance(float[],?float[],?float[],?float[])?def?arc_distance(theta_1,?phi_1,?theta_2,?phi_2):?????"""?????Calculates?the?pairwise?arc?distance?????between?all?points?in?vector?a?and?b.?????"""?????temp?=?(np.sin((theta_2-theta_1)/2)**2????????????+?np.cos(theta_1)*np.cos(theta_2)?*?np.sin((phi_2-phi_1)/2)**2)?????distance_matrix?=?2?*?np.arctan2(np.sqrt(temp),?np.sqrt(1-temp))?????return?distance_matrix???#pythran?export?dprod(int?list,?int?list)?def?dprod(l0,l1):?????"""WoW,?generator?expression,?zip?and?sum."""?????return?sum(x?*?y?for?x,?y?in?zip(l0,?l1))???#pythran?export?get_age(int?)?def?get_age(age):?????if?age?<=?20:?????????age_x?=?'0_20'?????elif?age?<=?25:?????????age_x?=?'21_25'?????elif?age?<=?30:?????????age_x?=?'26_30'?????elif?age?<=?35:?????????age_x?=?'31_35'?????elif?age?<=?40:?????????age_x?=?'36_40'?????elif?age?<=?45:?????????age_x?=?'41_45'?????elif?age?<=?50:?????????age_x?=?'46_50'?????else:?????????age_x?=?'50+'?????return?age_x?
一条命令完成翻译:
pythran?-e?demo.py?-o??demo.hpp?
pythran/pythonic/ 目录下是 python 标准库的 C++ 等价实现,翻译出来的 C++ 代码需要 include 这些头文件。
写个 C++ 代码调用:
#include?"demo.hpp"?#include?"pythonic/numpy/random/rand.hpp"?#include?<iostream>??using?std::cout;?using?std::endl;??int?main()?{???pythonic::types::list<pythonic::types::list<double>>?m0?=?{{2.0,?3.0},??????????????????????????????????????????????????????????????{4.0,?5.0}},????????????????????????????????????????????????????????m1?=?{{1.0,?2.0},??????????????????????????????????????????????????????????????{3.0,?4.0}};???cout?<<?m0?<<?"*"?<<?m1?<<?"\n=\n"????????<<?__pythran_demo::matrix_multiply()(m0,?m1)?<<?endl????????<<?endl;????auto?theta_1?=?pythonic::numpy::random::rand(3),????????phi_1?=?pythonic::numpy::random::rand(3),????????theta_2?=?pythonic::numpy::random::rand(3),????????phi_2?=?pythonic::numpy::random::rand(3);???cout?<<?"arc_distance?"?<<?theta_1?<<?","?<<?phi_1?<<?","?<<?theta_2?<<?","????????<<?phi_2?<<?"\n=\n"????????<<?__pythran_demo::arc_distance()(theta_1,?phi_1,?theta_2,?phi_2)?<<?endl????????<<?endl;????pythonic::types::list<int>?l0?=?{2,?3},?l1?=?{4,?5};???cout?<<?"dprod?"?<<?l0?<<?","?<<?l1?<<?"\n=\n"????????<<?__pythran_demo::dprod()(l0,?l1)?<<?endl????????<<?endl;????cout?<<?"get_age?30?=?"?<<?__pythran_demo::get_age()(30)?<<?endl?<<?endl;????return?0;?}?
g++?-g?-std=c++11?main.cpp?-fopenmp?-march=native?-DUSE_XSIMD?-I?/usr/local/lib/python3.6/site-packages/pythran/?-o?pythran_demo??./pythran_demo?
按官方定义,Pythran 是一个 AOT (Ahead-Of-Time - 预先编译) 编译器。给科学计算的 python 加注解后,pythran 可以把 python 代码变成接口相同的原生 python 模块,大幅度提升性能。
并且 pythran 也可以利用 OpenMP 多核和 SIMD 指令集。
支持 python 3 和 Python 2.7 。
pythran 的 manual 挺详细:
https://pythran.readthedocs.io/en/latest/MANUAL.html
pythran 并不支持完整的 python, 只支持 python 语言特性的一个子集:
不支持的功能:
pythran export 可以导出函数和全局变量。
支持导出的数据类型,BNF 定义是:
argument_type?=?basic_type?????????????????|?(argument_type+)????#?this?is?a?tuple?????????????????|?argument_type?list????#?this?is?a?list?????????????????|?argument_type?set????#?this?is?a?set?????????????????|?argument_type?[]+????#?this?is?a?ndarray,?C-style?????????????????|?argument_type?[::]+????#?this?is?a?strided?ndarray?????????????????|?argument_type?[:,...,:]+?#?this?is?a?ndarray,?Cython?style?????????????????|?argument_type?[:,...,3]+?#?this?is?a?ndarray,?some?dimension?fixed?????????????????|?argument_type:argument_type?dict????#?this?is?a?dictionary????basic_type?=?bool?|?byte?|?int?|?float?|?str?|?None?|?slice??????????????|?uint8?|?uint16?|?uint32?|?uint64?|?uintp??????????????|?int8?|?int16?|?int32?|?int64?|?intp??????????????|?float32?|?float64?|?float128??????????????|?complex64?|?complex128?|?complex256?
可以看到基础类型相当全面,支持各种 整数,浮点数,字符串,复数
复合类型支持 tuple, list, set, dict, numpy.ndarray 等,对应 C++ 代码的类型实现在 pythran/pythonic/include/types/ 下面,可以看到比如 dict 实际就是封装了一下 std::unordered_map
https://pythran.readthedocs.io/en/latest/SUPPORT.html
可以看到支持的 python 基础库,其中常用于机器学习的 numpy 支持算比较完善。
和常见的编译器/解释器类似, pythran 的架构是分成 3 层:
目前看起来 ,pythran 还欠缺的:
看文档要自己加也不麻烦,看业务需要可以加。
领取专属 10元无门槛券
私享最新 技术干货