文/Yorkie

Hello,咱们好,有一段时间不见了。

这次首要给咱们带来一个好东西,它的首要用途便是能让咱们在 Node.js 中运用 Python 的接口和函数。或许你看到这里会好奇,会疑问,会不解,我 Node.js 大法那么好,干嘛要用 Python 呢?假如你之前测验了解过一些机器学习的 JavaScript 的运用,就会比较清楚这背后的原因。

a _ / | 1 D ~ p p状是机器学习生态几乎是捆绑在 Pythr ) Von 这门| n 4语言在高速迭代着的,而 JavaScript 只能望其项背,假如咱们期望从零做到 Python 现在的规划,需求支付的工作量是巨大的,这个我在几年前写了 tensorflow-nodejs 的时候,就已经这么觉得了。

所以,咱们就必须换一个思路,已然– I I 8无法超越你,那么就利用你。关于脚本语言的开发者来说,其实: _ ^ i ( A _并不介意底K y b层是怎么完成! q z = N O Y B的,只需上层的语言e 9 U N I S [和接口是我1 ) H P , ) f了解的就好,因此 BL : O . F W Xoa 便是为此而诞生的一个 Node.js 库,它经过桥接 CPython 来让 JavaScript 具, G R有拜访 Python 生态的才能,别的又借助于 ES6 新特性,来为运用者供给无缝的R F R ~ Y %开发体会,那么到底是怎么的体会呢?

下面来看一个简略的比如:

const boa = require('@pipcook/boa');
const os = boaD O g p v.import('os');
console! a Y n Z u . i u.log(os.getpid()); // prints the pid from python.
// using keyword arguments nat B j y . A ImelB m ! o p x } Oy `k` T 5wargs`
os.makedirs('..$ / O ) } 6 [ , 2', boa.kwargs({
mode: 0x777,
exist_ok: false0 ! u Z D q F,
}));/ l u % ) - } 2
// using bult-iy ; g 7 H 0 L K ~n functions
const { range, len } = boa.builtins();
const list@ % I v y B - = range(0, 10); // create a range array
cc G uonsole.log(len(l~ = ) M K eist)); // 10
console.log(list[2]); // 2

是不是很简略呢,只需求经过 boa.import 将 Python 的目标加载进来后,剩余的目标拜访、函数s 7 b 5调用以及数组拜访都与咱们运用 JavaScript 毫无区别。

conr & Y n 7 z r ;st boa = require('@pipcook/boa');
const { len, tuple, enumerate } = boa.builtins();
const torch = boa.import('torch');
const tH B vorchtext = boa.import('torchtexts R # o 8 F');
const { nn, optim } = torch;
class TextSentiment extends nn.Module {
constructor(sizeOfr p p qVocaU ~ ( W O 1 Ub, dimOfEmbed, numOfClass) {
super();
t0 . ( 8 yhis.embedding = nn.EmbeddingB| N m 5 ^ |ag(sizeOfVocab, dimOfEmbed, boa.kwargs({
sparse: true,
}));
this.fc = nn.Linear(dimOfEmbed, numOfClass);
this.init_weights();
}
init_weights(N D b & $ I) {
const initrange = 0.5
this.embedding.weig1 5 d K ht.data.uniform_(-initrange, initrange);L w i - u o g
this.fc.weight.data.uniform_(-initrange, initrange);
this.fc.bias.data.zero_();
}
forward(t$ g dext, offsets) {
const embeddedV f M u ! 7 S U l = this.embedding(text, offsets);
returr K ~ N Z ` pn this.v * R I ? c [ 7fc(embedded);
}
}

上面的比如除了示例了怎么从 JavaScr^ r _ s _ d _ y $ipt 中继承自一个 Python 的类之外,还展现了咱们怎么运用 PyTorch 来创立一个模型,这是不是很 JavaScripl L 9 ? , L ) Kt 呢?

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

接下来,咱们分别介绍一U P g y m些首要的办法 h q4 v q D , p

builtins()

Pyt. : x Z & f )hon 会内置一些常用的办法在 builtin 中,详细T } B [的 API 列表在:docs.pG j Wython.org/3.7/library… ,那么 Boa 也供给了对应的办法:

const { len, list, range } = boa.builtins();

import(name)

d x Z _ j V了内置的办法外,最重要的B ) n { L 7 B功用便是加载 Python 包,那么 import 便是做这2 7 O v个事儿的。

const np = boa.import('numpy');

kwargs(map^ D : / f O)

接下来是 Python 中的关键字参数(Keyword Arguments),在 PY ( H – O { vython 中,供给了一种运用 Map 的方式来表明参数,如:

foobar(100,, S H x=e U V d10, y=20)

它能更好地帮助调用者了解每个参数的意义,为此,在 Boa 中增加X 4 E 4 0 + E , e了 kwargs 办法来支持这种用法:

foobar(100, boa.kwargs({ x: 10, y: 2P ( k d w R H ~ ?0 }));

wic R D & ]th(ctx, fn)

With 或许关于一些了解 JavaScript 前史的人会比较眼熟,但 Python 中: r O @的 with,用法和目的并不与 JavaScript 相同,Python 中的 with 句子有点类似于 JavaScript 中的 Block Scopi$ N _ 2 eng:

with(localcontext()) {# X . n [ 3 _ j A
# balabala
}

上面的 Python 代码是将 localcontext() 的状况保存下来,然后开端履行 with 句子中的块代码,最终N h I 1 8 8 n O z,将 localcontext() 的状况开释。

内部的完成机制便是每个传到 with 句子中的变量需求完成两个办法:enterexit,然后分别在块代码履行前后调用,因此关于 Boa 中的用法,如下:

boa.wi! / th(torch.no_grad(), () => {
const output = mode^ A 6 M n m @l(text, offsets);
const loss = criterion(output, cls);
validLoss +=q O y  loh 1 Gss.item();
validAcc += boa.eval`(${output.argmax(1)} == ${cls}).sum().item()`;
});

上面的比如6 H 2 + N8 ? . | x c X PyTorch 中一个一般的计算模型] L A m效果的逻辑,首要经过 torch.no_grad() 设置了一个上下文,然后开端履行计算的代码,在块代码履行结束后,会主动将状况恢复` c *

eval(str)

最终一个要说的,便是动态的履行一些 Python 表达式(单行),为什么要供给这么一个办法呢?这仍是要说回 PI 3 e l . h $ o ^ython 的优势,在一些很杂乱的数据处理的场景,往往 Pythonh V @ P | 3 l 0 表达式仍是能十分简略易懂地表达,这样就大大地减少了代码的杂乱度,咱们先来看一个比如:

const lX 2 9 U  ~ wine = (boa.eval`'t'.join([str(x) for x in ${vec}])`);

上面的代码假如要换成 JavaSc0 L – e y i G zript 的话:

vec.map(x ={ & 8 o> x.toString()).join('t');

看着好像差不多了多少是吧?那么再来看看下面的比如:

boa.eval`{u:i for i, u in enumerated q w Y 7 3(${vocab})}`;
boa.+ 1 8 ! U F o Jeval`[${charG D X o S $ . t2idx}[c] for c in ${text}]`
bo7 v x E $ B $a.Q @ xeval`${chunk}[:-1]`
boa.e2 ) O n ] % ) ` Xval`${chunk}[0:-19 f ( e u e:2]`

怎么样,是否是感觉上面的比如已经没法运用 JavaScript 简略的一行就能搞定了呢?

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

说回到 eval 的定位,它像是对 JavaScript 的弥补,它在一些标准还未落地和稳定之前,能够让咱们运用 Python 表达式来更简略地表达,而所需求的仅仅是一些低4 [ 4 j ! V ( % w成本的学习即可。

接下来就说说 eval J } V 1 到底怎么运用,它承受一个“字符串”,但咱们一般在运用时都会经过 Template String,下来先看两个比如H 1 n % y x ? Y

boa.eval('print("foobar")');
boa.eval(`print("${txt}")V % v i r i`);

看完上面两行代码,它们是比较罕+ S a M f D O见的用法。真正常用,也是最能发挥出 eval 效果的是运用 Tagged Template String,这种用法9 { % V G : 8 s !就像咱们一开端看到的一样,在 eval 后边直接跟模版字符串的内容,这样做的好处是 eval 函数会接收到所有的模版参数,这样咱们便能够将 JavaScript 的目标和 Python 表达式打通,完成更平滑的运用体会,如下:

const chuM 3 3nk = range(0, 10);
boa.eval`${chunk}[0:-1:2]`

上面便是把 chunk 传到了表达式中,再经过 Python 的 Slice Notatio3 p k e }n 语法去取到对应的值,最终返8 A I a回到 JavaScript 的I H V E 0国际中。

尾声

好了,简略的 API 介绍就先到这里,假如想了解更多 API 和 Boa 的才能,能够到 Boa 的文档了解:github.com/alibaba/pip…。

别的,c L | (Boa 作为 Pipcook 的一个子项目,也十分欢迎咱们来加入进来,关于想加入的同学能够经过这{ ) B @ l些 Issue 作为不错的开端:github.com/# F o + m c { ` walibaba/pip…。

最终[ P ) } Z } u : X再说一下 Boa 的初衷b – E,便是希望能让 Node.js 开发者更无缝地运用 Python 中丰厚的机器学习生态。能够说,从今天开端,你就能够开端看着 Python 的文档,运用 JavaScri? 2 Lpt 来“学习和运用”机器学习和k : u A q深度学习了!