写点什么

Boa: 在 Node.js 中使用 Python

  • 2020-04-21
  • 本文字数:3072 字

    阅读完需:约 10 分钟

Boa: 在 Node.js 中使用 Python

Hello,大家好,有一段时间不见了。


这次主要给大家带来一个好东西,它的主要用途就是能让大家在 Node.js 中使用 Python 的接口和函数。可能你看到这里会好奇,会疑惑,会不解,我 Node.js 大法那么好,干嘛要用 Python 呢?如果你之前尝试了解过一些机器学习的 JavaScript 的应用,就会比较清楚这背后的原因。


现状是机器学习生态几乎是捆绑在 Python 这门语言在高速迭代着的,而 JavaScript 只能望其项背,如果我们期望从零做到 Python 如今的规模,需要付出的工作量是巨大的,这个我在几年前写了 tensorflow-nodejs 的时候,就已经这么觉得了。


所以,我们就必须换一个思路,既然无法超越你,那么就利用你。对于脚本语言的开发者来说,其实并不在意底层是如何实现的,只要上层的语言和接口是我熟悉的就好,因此 Boa 就是为此而诞生的一个 Node.js 库,它通过桥接 CPython 来让 JavaScript 具备访问 Python 生态的能力,另外又借助于 ES6 新特性,来为使用者提供无缝的开发体验,那么到底是如何的体验呢?


下面来看一个简单的例子:


const boa = require('@pipcook/boa');const os = boa.import('os');console.log(os.getpid()); // prints the pid from python.
// using keyword arguments namely `kwargs`os.makedirs('..', boa.kwargs({ mode: 0x777, exist_ok: false,}));
// using bult-in functionsconst { range, len } = boa.builtins();const list = range(0, 10); // create a range arrayconsole.log(len(list)); // 10console.log(list[2]); // 2
复制代码


是不是很简单呢,只需要通过 boa.import 将 Python 的对象加载进来后,剩下的对象访问、函数调用以及数组访问都与我们使用 JavaScript 毫无区别。


const boa = require('@pipcook/boa');const { len, tuple, enumerate } = boa.builtins();const torch = boa.import('torch');const torchtext = boa.import('torchtext');const { nn, optim } = torch;
class TextSentiment extends nn.Module { constructor(sizeOfVocab, dimOfEmbed, numOfClass) { super(); this.embedding = nn.EmbeddingBag(sizeOfVocab, dimOfEmbed, boa.kwargs({ sparse: true, })); this.fc = nn.Linear(dimOfEmbed, numOfClass); this.init_weights(); } init_weights() { const initrange = 0.5 this.embedding.weight.data.uniform_(-initrange, initrange); this.fc.weight.data.uniform_(-initrange, initrange); this.fc.bias.data.zero_(); } forward(text, offsets) { const embedded = this.embedding(text, offsets); return this.fc(embedded); }}
复制代码


上面的例子除了示例了如何从 JavaScript 中继承自一个 Python 的类之外,还展示了我们如何使用 PyTorch 来创建一个模型,这是不是很 JavaScript 呢?


值得一提的是,在 Boa 的代码中,没有对 PyTorch 做过任何的封装,只要你在本地通过 Python 安装了对应的包就可以像上面的代码一样使用了,所以理论上你可以对任何 Python 包做上面所做的事情。


接下来,我们分别介绍一些主要的方法。

builtins()

Python 会内置一些常用的方法在 builtin 中,具体的 API 列表在:


https://docs.python.org/3.7/library/functions.html


那么 Boa 也提供了对应的方法:


const { len, list, range } = boa.builtins();
复制代码

import(name)

除了内置的方法外,最重要的功能便是加载 Python 包,那么 import 就是做这个事儿的。


const np = boa.import('numpy');
复制代码

kwargs(map)

接下来是 Python 中的关键字参数(Keyword Arguments),在 Python 中,提供了一种使用 Map 的方式来表示参数,如:


foobar(100, x=10, y=20)
复制代码


它能更好地帮助调用者了解每个参数的含义,为此,在 Boa 中增加了 kwargs 方法来支持这种用法:


foobar(100, boa.kwargs({ x: 10, y: 20 }));
复制代码

with(ctx, fn)

With 可能对于一些熟悉 JavaScript 历史的人会比较眼熟,但 Python 中的 with,用法和目的并不与 JavaScript 相同,Python 中的 with 语句有点类似于 JavaScript 中的 Block Scoping:


with(localcontext()) {  # balabala}
复制代码


上面的 Python 代码是将 localcontext() 的状态保存下来,然后开始执行 with 语句中的块代码,最后,将 localcontext() 的状态释放。


内部的实现机制就是每个传到 with 语句中的变量需要实现两个方法:enter 和 exit,然后分别在块代码执行前后调用,因此对于 Boa 中的用法,如下:


boa.with(torch.no_grad(), () => {  const output = model(text, offsets);  const loss = criterion(output, cls);  validLoss += loss.item();  validAcc += boa.eval`(${output.argmax(1)} == ${cls}).sum().item()`;});
复制代码


上面的例子是 PyTorch 中一个普通的计算模型效果的逻辑,首先通过 torch.no_grad() 设置了一个上下文,然后开始执行计算的代码,在块代码执行结束后,会自动将状态恢复。

eval(str)

最后一个要说的,就是动态的执行一些 Python 表达式(单行),为什么要提供这么一个方法呢?这还是要说回 Python 的优势,在一些很复杂的数据处理的场景,往往 Python 表达式还是能非常简单易懂地表达,这样就大大地减少了代码的复杂度,我们先来看一个例子:


const line = (boa.eval`'\t'.join([str(x) for x in ${vec}])`);
复制代码


上面的代码如果要换成 JavaScript 的话:


vec.map(x => x.toString()).join('\t');
复制代码


看着似乎差不多了多少是吧?那么再来看看下面的例子:


boa.eval`{u:i for i, u in enumerate(${vocab})}`;boa.eval`[${char2idx}[c] for c in ${text}]`boa.eval`${chunk}[:-1]`boa.eval`${chunk}[0:-1:2]`
复制代码


怎么样,是否是感觉上面的例子已经没法使用 JavaScript 简单的一行就能搞定了呢?


不过值得一提的是,JavaScript 在这方面也在渐渐地弥补,这里 是整理的一些 TC39 正在做的一些相关的标准,其中就包括上面的 Slice Notation。


说回到 eval 的定位,它像是对 JavaScript 的补充,它在一些标准还未落地和稳定之前,可以让我们使用 Python 表达式来更简单地表达,而所需要的仅仅是一些低成本的学习即可。


接下来就说说 eval 到底如何使用,它接受一个“字符串”,但我们一般在使用时都会通过 Template String,下来先看两个例子:


boa.eval('print("foobar")');boa.eval(`print("${txt}")`);
复制代码


看完上面两行代码,它们是比较少见的用法。真正常用,也是最能发挥出 eval 效果的是使用 Tagged Template String,这种用法就像我们一开始看到的一样,在 eval 后面直接跟模版字符串的内容,这样做的好处是 eval 函数会接收到所有的模版参数,这样我们便可以将 JavaScript 的对象和 Python 表达式打通,实现更平滑的使用体验,如下:


const chunk = range(0, 10);boa.eval`${chunk}[0:-1:2]`
复制代码


上面就是把 chunk 传到了表达式中,再通过 Python 的 Slice Notation 语法去取到对应的值,最后返回到 JavaScript 的世界中。

尾声

好了,简单的 API 介绍就先到这里,如果想了解更多 API 和 Boa 的能力,可以到 Boa 的文档了解:


https://github.com/alibaba/pipcook/blob/master/docs/tutorials/want-to-use-python.md


另外,Boa 作为 Pipcook 的一个子项目,也非常欢迎大家来加入进来,对于想加入的同学可以通过这些 Issue 作为不错的开始:


https://github.com/alibaba/pipcook/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22


最后再说一下 Boa 的初衷,就是希望能让 Node.js 开发者更无缝地使用 Python 中丰富的机器学习生态。可以说,从今天开始,你就可以开始看着 Python 的文档,使用 JavaScript 来“学习和使用”机器学习和深度学习了!


2020-04-21 15:533168

评论 1 条评论

发布
用户头像
「望其项背」一般以否定句式使用
2020-04-22 10:44
回复
没有更多了
发现更多内容

模块二

Winston

在线HTML转MarkDown工具

入门小站

工具

模块三

江南巴飞特

我的职场规划

escray

学习 极客时间 朱赟的技术管理课 7月日更

音视频时代你还不会NDK开发?详细的Android学习指南

欢喜学安卓

android 程序员 面试 移动开发

设计消息队列存储消息数据的MySQL表格

chenmin

GitHub 如何创建 Access Token

HoneyMoose

Vue进阶(九十七):对象动态添加属性和值

No Silver Bullet

Vue 7月日更 $set 动态赋值

小师妹学IO系列文章集合-附PDF下载

程序那些事

Java io nio 程序那些事

CWE发布2021年最危险的25种软件缺陷

华为云开发者联盟

安全 隐私保护 安全漏洞 cwe 软件缺陷

模块三作业

俊杰

风险投资

stars

今天,2021可信云大会即将开幕!

云计算 云原生 可信云

Vue进阶(九十八):Vue.set() 和 this.$set()

No Silver Bullet

Vue 7月日更 $set 动态赋值

手写计数排序算法

实力程序员

程序员 C语言 排序算法 造轮子

国际视频质量评估算法竞赛:火山引擎成功夺冠

音视频时代你还不会NDK开发?看完必懂

欢喜学安卓

android 程序员 面试 移动开发

AI开发者十问:10分钟了解AI开发的基本过程

华为云开发者联盟

AI 框架 modelarts hilens

手写桶排序算法

实力程序员

程序员 C语言 排序算法

开源 Rainbond 5.3.2 版本发布,易用的云原生应用管理平台

Barnett

云计算 开源 基础软件 小版本升级

短视频获客软件系统开发公司

抖音获客系统开发

获客I3O6O643Z97

抖音 头条抖音 抖音霸屏

英特尔加速制程工艺和封装技术创新

E科讯

Linux之ps命令

入门小站

Linux

架构实战营 - 模块 3 - 外包学生管理系统架构文档

雪中亮

架构实战营 #架构实战营

趣说开源|开源路线图入门攻略

SphereEx

论文解读丨Zero-Shot场景下的信息结构化提取

华为云开发者联盟

测试 图网络 信息结构化 图卷积网络 zero-shot

经典译文 | 项目经理必读的基本工作法则

LigaAI

项目管理 项目经理

整合重复的初始化逻辑

喵叔

7月日更

短视频智能询盘获客系统开发

一个Android程序员的腾讯面试心得,进阶学习资料!

欢喜学安卓

android 面试 移动开发 程序猿

Boa: 在 Node.js 中使用 Python_大前端_徐明强_InfoQ精选文章