0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

还能这样?把 Python 自动翻译成 C++

电子设计 来源:电子设计 作者:电子设计 2020-12-10 18:29 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

本文主要介绍在机器学习模型部署过程中,怎么样快速得将python转为C++
来源:腾讯技术工程微信号
作者:byronhe,腾讯 WXG 开发工程师

一、问题背景

随着深度学习的广泛应用,在搜索引擎/推荐系统/机器视觉等业务系统中,越来越多的深度学习模型部署到线上服务。

机器学习模型在离线训练时,一般要将输入的数据做特征工程预处理,再输入模型在 TensorFlow PyTorch 等框架上做训练。

1.常见的特征工程逻辑

常见的特征工程逻辑有:

分箱/分桶 离散化

log/exp 对数/幂等 math numpy 常见数学运算

特征缩放/归一化/截断

交叉特征生成

分词匹配程度计算

字符串分隔匹配判断 tong

缺省值填充等

数据平滑

onehot 编码,hash 编码等

这些特征工程代码,当然一般使用深度学习最主要的语言 python 实现。

二、业务痛点

离线训练完成,模型上线部署后,同样要用 C++ 重新实现 这些 python 的特征工程逻辑代码。

我们发现,“用 C++ 重新实现” 这个步骤,给实际业务带来了大量的问题:

繁琐,费时费力,极容易出现 python 和 C++ 代码不一致

不一致会直接影响模型在线上的效果,导致大盘业务指标不如预期,产生各种 bad case

不一致难以发现,无法测试,无法监控,经常要靠用户投诉反馈,甚至大盘数据异常才能发现

1. 业界方案

针对这些问题,我调研了这些业界方案:

《推荐系统中模型训练及使用流程的标准化》

《自主研发、不断总结经验,美团搜索推荐机器学习平台》

《京东电商推荐系统实践》

“模型线上线下一致性问题对于模型效果非常重要,我们使用特征日志来实时记录特征,保证特征的一致性。这样离线处理的时候会把实时的用户反馈,和特征日志做一个结合生成训练样本,然后更新到模型训练平台上,平台更新之后在推送到线上,这样整个排序形成了一个闭环。”

总结起来,有几种思路:

在线特征存储起来给离线用

在线 C++ 代码编译成 so 导出给离线用

根据一份配置生成离线和在线代码

提取公共代码,加强代码复用,等软件工程手段,减少不一致

2. 自动翻译方案

(1) .已有方案的缺点

但这些思路都有各种缺点:

所有在线请求的所有特征,这个存储量数据量很大

算法改代码需要等待后台开发,降低了算法同学的工作效率

特征处理代码的复杂度转移到配置文件中,不一定能充分表达,而且配置格式增加学习成本

就这边真实离线特征处理代码来看,大部分代码都无法抽取出公共代码做复用。

(2). 翻译器

回到问题出发点考虑,显而易见,这个问题归根结底就是需要一个 “ python 到 c++ 的翻译器 ” 。

那其实 “翻译器 Transpiler ” ,和编译器解释器类似,也是个古老的热门话题了,比如 WebAssembly, CoffeeScript ,Babel ,
Google Closure Compiler,f2c

于是一番搜索,发现 python 到 C++ 的翻译器也不少,其中 Pythran 是新兴比较热门的开源项目。

于是一番尝试后,借助 pythran,我们实现了:

一条命令 全自动把 Python 翻译成等价 C++

严格等价保证改写,彻底消除不一致

完全去掉重新实现 这块工作量,后台开发成本降到 0 ,彻底解放生产力

算法同学继续使用纯 python,开发效率无影响, 无学习成本

并能推广到其他需要 python 改写成后台 C++ 代码 的业务场景,解放生产力

三、pythran 的使用流程

(1). 安装

一条命令安装:

pip3 install pythran

(2). 写 Python 代码

下面这个 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

(3). Python 转成 C++

一条命令完成翻译

pythran -e demo.py -o  demo.hpp

(4). 写 C++ 代码调用

pythran/pythonic/ 目录下是 python 标准库的 C++ 等价实现,翻译出来的 C++ 代码需要 include 这些头文件

写个 C++ 代码调用

#include "demo.hpp"
#include "pythonic/numpy/random/rand.hpp"
#include 

using std::cout;
using std::endl;

int main() {
  pythonic::types::list> 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 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;
}

(5). 编译运行

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 的功能与特性

(1). 介绍

按官方定义,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

(2). 功能

pythran 并不支持完整的 python, 只支持 python 语言特性的一个子集:

polymorphic functions 多态函数(翻译成 C++ 的泛型模板函数)

lambda

list comprehension 列表推导式

map, reduce 等函数

dictionary, set, list 等数据结构

exceptions 异常

file handling 文件处理

部分 numpy

不支持的功能:

classes 类

polymorphic variables 可变类型变量

(3). 支持的数据类型和函数

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.i...
可以看到支持的 python 基础库,其中常用于机器学习的 numpy 支持算比较完善。

五、pythran 的基本原理

和常见的编译器/解释器类似, pythran 的架构是分成 3 层:

python 代码解析成抽象语法树 AST 。用 python 标准库自带的的 ast 模块实现

代码优化。

在 AST 上做优化,有多种 transformation pass,比如 deadcode_elimination 死代码消除,loop_full_unrolling 循环展开 等。还有 Function/Module/Node 级别的 Analysis,用来遍历 AST 供 transformation 利用。

后端,实现代码生成。目前有 2 个后端,Cxx / Python, Cxx 后端可以把 AST 转成 C++ 代码( Python 后端用来调试)。

目前看起来 ,pythran 还欠缺的:

字符串处理能力欠缺,缺少 str.encode()/str.decode() 对 utf8 的支持

缺少正则表达式 regex 支持

看文档要自己加也不麻烦,看业务需要可以加。

审核编辑 黄昊宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 人工智能
    +关注

    关注

    1820

    文章

    50313

    浏览量

    266868
  • C++
    C++
    +关注

    关注

    22

    文章

    2129

    浏览量

    77360
  • 机器学习
    +关注

    关注

    67

    文章

    8562

    浏览量

    137209
  • python
    +关注

    关注

    58

    文章

    4884

    浏览量

    90299
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    手搓C++离散小波变换DWT程序

    关于小波变换,我们当然可以选择速度更快地方式来使用它。和python相比,C++编译后的代码在对同一组参数配置并且处理同一组数据,在没有优化的情况下,处理速度可以快达10倍上,均不包括结果写入csv文件步骤的时间。下图中的19
    的头像 发表于 04-22 14:15 46次阅读
    手搓<b class='flag-5'>C++</b>离散小波变换DWT程序

    C++与lua联合编程

    之间进行低成本、高安全“贸易”的绝对定价权。 一、 消除“翻译摩擦”,将跨边界通信成本降至极限 在跨国贸易中,海关的通关效率直接决定了商品的流通成本。在混合编程中,C++ 与 Lua 拥有各自独立
    发表于 04-19 16:27

    C++:const 的空间,常量也能占内存?

    ] != BLACK)): return True return False c++语言5g.FsW.dgdfgsg.cnJIWWQc++语言 c++语言5g.I5y.dgdfgsg.cnJIWWQc++
    发表于 04-16 19:19

    C语言与C++的区别及联系

    是面向过程语言,而C++是面向对象语言。说CC++的区别,也就是在比较面向过程和面向对象的区别。 1、面向过程和面向对象的区别 面向过程:面向过程编程就是分析出解决问题的步骤,然后
    发表于 12-24 07:23

    纺织厂工业灵魂沟通指南:如何ETHERNET/IP的英语翻译成CC-LINK的日语

    纺织厂工业灵魂沟通指南:如何ETHERNET/IP的英语翻译成CC-LINK的日语 一、产品特性与项目背景 在纺织服装生产车间,设备往往来自不同供应商,导致自动化系统协议杂乱。某大型纺织企业原有罗
    的头像 发表于 12-12 15:01 474次阅读
    纺织厂工业灵魂沟通指南:如何<b class='flag-5'>把</b>ETHERNET/IP的英语<b class='flag-5'>翻译成</b>CC-LINK的日语

    CC++之间的联系

    且不需要高级特性的场景,而C++则在提供高效性能的同时,还能借助面向对象和泛型编程等特性提高软件的可维护性和可扩展性
    发表于 12-11 06:51

    C语言和C++之间的区别是什么

    区别 1、面向对象编程 (OOP): C语言是一种面向过程的语言,它强调的是通过函数将任务分解为一系列步骤进行执行。 C++C语言的基础上扩展了面向对象的特性,支持类(class)、封装、继承
    发表于 12-11 06:23

    嵌入式C/C++回归测试四大最佳实践(附自动化测试工具TESSY使用教程)

    嵌入式开发中,一次微小的代码改动都可能引发“蝴蝶效应”,如何守护系统的稳健?推荐专业的自动化测试工具#TESSY,源自戴姆勒-奔驰,是嵌入式C/C++单元/集成测试的标杆。
    的头像 发表于 10-31 14:21 560次阅读
    嵌入式<b class='flag-5'>C</b>/<b class='flag-5'>C++</b>回归测试四大最佳实践(附<b class='flag-5'>自动</b>化测试工具TESSY使用教程)

    技能+1!如何在树莓派上使用C++控制GPIO?

    在使用树莓派时,你会发现Python和Scratch是许多任务(包括GPIO编程)中最常用的编程语言。但你知道吗,你也可以使用C++进行GPIO编程,而且这样做还有不少好处。借助WiringPi
    的头像 发表于 08-06 15:33 4488次阅读
    技能+1!如何在树莓派上使用<b class='flag-5'>C++</b>控制GPIO?

    Gerber变成电磁波:推荐一款基于Gerber的openEMS仿真工具 - gerber2ems

    “Gerber + 钻孔+ 叠层”一键翻译成 openEMS 能吃的 3D 模型,跑完还能跟 VNA 实测对波。
    的头像 发表于 08-03 11:21 4393次阅读
    <b class='flag-5'>把</b>Gerber变成电磁波:推荐一款基于Gerber的openEMS仿真工具 - gerber2ems

    C++Python:树莓派上哪种语言更优?

    Python是树莓派上的首选编程语言,我们的大部分教程都使用它。然而,C++在物联网项目中同样广受欢迎且功能强大。那么,在树莓派项目中选择哪种语言更合适呢?Python因其简洁性、丰富的库和资源而被
    的头像 发表于 07-24 15:32 1104次阅读
    <b class='flag-5'>C++</b> 与 <b class='flag-5'>Python</b>:树莓派上哪种语言更优?

    AI耳机变身翻译官+会议总结大师?涂鸦AI音频开发方案,让耳机升级到下一个level

    在接入AI能力后,耳机这种日常化的产品,能有多大的想象空间?它不仅能帮你轻松听懂全球外语和地方方言,还能将语音转化为文字、翻译成不同语言,甚至自动总结会议要点、生成思维导图,适配办公、学习、跨语言
    的头像 发表于 07-10 18:47 2240次阅读
    AI耳机变身<b class='flag-5'>翻译</b>官+会议总结大师?涂鸦AI音频开发方案,让耳机升级到下一个level

    水表界的“翻译官”:让CCLinkIE和Modbus TCP“无障碍聊天”!

    ”,接收控制指令;转身又在Modbus TCP网络中当“指挥主站”,指令翻译成水表能懂的格式。核心秘诀是地址映射:通过配置工具,将PLC的软元件地址(如D1000)与水表的Modbus寄存器地址(如
    发表于 07-10 15:43

    CCLINKIE转PROFINET:电机的“网络冲浪神器”!

    咋整? 别急别急,这时候耐达讯CCLINKIE转PROFINET网关就闪亮登场啦!它就像一个超级“翻译官”,能把CCLINKIE的“方言”翻译成PROFINET能听懂的“普通话”,让电机轻松融入
    发表于 05-28 15:21

    主流的 MCU 开发语言为什么是 C 而不是 C++

    在单片机的地界儿里,C语言稳坐中军帐,C++想分杯羹?难喽。咱电子工程师天天跟那针尖大的内存空间较劲,C++那些花里胡哨的玩意儿,在这儿真玩不转。先说内存这道坎儿。您当stm32f4的256kRAM
    的头像 发表于 05-21 10:33 1203次阅读
    主流的 MCU 开发语言为什么是 <b class='flag-5'>C</b> 而不是 <b class='flag-5'>C++</b>?