闲来再做回搬运工,感觉和机器翻译比起来并没啥优势。。。
deeplearn.js 是一个用于机器智能的开源 WebGL 加速的 JavaScript 库。deeplearn.js 为您的指尖带来高性能机器学习构建模块,允许您在浏览器中训练神经网络或在推理模式下运行预训练模型。
让我们来看看 deeplearn.js 中的一些核心概念。
Tensors
在 deeplearn.js 中数据的核心单元便是 tensor,一个 Tensor 由一组任意维数的数值型数组构成。
Tensors 由 shape
属性定义其形状。该库为低秩的张量(Tensors)提供了 sugar 般的子类:Scalar
, Tensor1D
, Tensor2D
, Tensor3D
和 Tensor4D
,以及一些用于构建它们的辅助函数。
例如使用一个 2x3 维矩阵:
let shape = [2, 3]; // 2 rows, 3 columns
let a = dl.tensor2d([1.0, 2.0, 3.0, 10.0, 20.0, 30.0], shape);
// deeplearn.js can also infer the shape
let b = dl.tensor2d([[0.0, 2.0], [4.0, 6.0]]); // 2 rows, 2 columns
译者注:可见 shape
重新规定了前面数组的形状。
Tensor 既可以用一个 WebGLTexture 将数据存到 GPU,或者以 JavaScript TypedArray
的形式存到 CPU 上。大多时候,用户都无需考虑存储问题,因为它是一个实现细节。
有个地方你得好好想想其差异的,就是当从一个 Tensor 取出数据时,例如在调试时:
let a = dl.tensor2d([[0.0, 2.0], [4.0, 6.0]]);
a = a.square();
// Async call to get the data from the tensor
a.data().then(data => console.log('The data TypedArray', data));
// Alternatively we can also call this synchronously
let data = a.dataSync();
console.log('The data TypedArray', data);
在上例中,我们首先创建了一个 tensor,然后在其上调用了一个数学操作。这会将该 tensor 自动上传到 GPU。当我们想在 JavaScript 上下文使用它时(如打印出来),我们调用 data()
或 dataSync()
将其下载到 CPU 内存中。请注意这是一个相对有代价的操作,所以你可能倾向于异步调用的版本。
译者注:需要搞明白的是——数据从哪导到哪,怎样的过程有什么开销,以及哪些数据该在 GPU 运算更好
Operations (Ops)
Tensors 允许我们存储数据,同时 Ops 允许我们操纵数据。deeplearn.js 带有大量的适合于线性代数和机器学习的数学运算。它们有像square()
的一元运算,像add()
的二元运算以及mul()
这样的对某个 tensors 进行变换并返回一个新的 tensors 结果的运算。
let a = dl.tensor2d([[1.0, 2.0], [3.0, 4.0]]); // tensor
let b = dl.tensor2d([[0.0, 2.0], [4.0, 6.0]]); // tensor
// The library has a chainable API allowing you to call operations
// directly as methods on Tensors.
let average = a.sub(b).square().mean();
// All operations are also exposed as functions in the main namespace
// so we could also do.
// 译者注:这些方法不仅每个 tensor 实例都有,而且在 dl 这个命名空间上能直接访问到
let avg = dl.mean(dl.square(dl.sub(a, b)));
Tidy Operations
因为 deeplearn.js 使用了GPU加速数学运算,因此有必要管理GPU内存。在常规的 JavaScript 中,这部分工作由作用域(Scopes)处理,而在这里,我们提供了一个便捷的函数对 tensors
执行操作时所产生的中间内存进行清理。
我们把这个函数称作dl.tidy
.
let a = dl.tensor2d([1.0, 2.0, 3.0, 4.0]);
// dl.tidy takes a function to tidy up after
let average = dl.tidy(() => {
// dl.tidy will clean up all the GPU memory used by tensors inside
// this function, other than the tensor that is returned.
//
// Even in a short sequence of operations like the one below, a number
// of intermediate tensors get created. So it is a good practice to
// put your math ops in a tidy!
return a.sub(b).square().mean();
});
运用dl.tidy()
有助于防止应用程序的内存泄漏,并且能更谨慎地控制何时内存回收。
手动方式清理 tensor 内存是dispose
方法。
let a = dl.tensor2d([[0.0, 2.0], [4.0, 6.0]]);
a = a.square();
a.dispose(); // Clean up GPU buffer
相比,使用dl.tidy
函数更方便些。
Training
许多机器学习的核心问题是实质上是训练机器完成一些任务的问题。在 deeplearn.js 中,这一过程被封装在Optimizers(优化器)中。优化器是逐步调整模型变量的策略,目的是减少模型预测的错误(或者按机器学习的说法是减少损失)。
我们在教程中涵盖了训练和优化器,这里是 deeplearn.js 如何进行训练的一个大概轮廓:
import * as dl from 'deeplearn';
// 初始化模型变量
const weights = dl.variable(dl.randomNormal([10, 64]));
const biases = dl.variable(dl.zeros([64]));
// 设置学习率和创建优化器.
const LEARNING_RATE = .1;
const optimizer = dl.train.sgd(LEARNING_RATE)
/*
* 进行推断并返回预测结果
*/
function inference(input) { }
/**
* 通过比较预测值和真实值,计算模型损失
*
* 返回一个标量损失值 (如,单数值的张量)
*/
function loss(predictions, labels) { }
/**
* 模型的单步训练
*/
function trainStep(data, labels, returnCost = true) {
// Calling optimizer.minimize will adjust the variables in the
// model based on the loss value returned by your loss function.
// It handles all the backpropogation and weight updates.
const cost = optimizer.minimize(() => {
// Any variables used in this inference function will be optimized
// by the optimizer.
// Make a prediction using the current state of the model
const prediction = inference(data);
// Compute loss of the current model and return it. Calculating this loss
// should involve the variables we are trying to optimize.
//
// Once we return the less the optimizer will adjust the network
// weights for our next iteration.
return loss(prediction, labels);
}, returnCost);
// return the current loss/cost so that we can visualize it
return cost;
}
/**
* 训练模型.
*
* 通过循环调用上述单步训练 trainStep. 用 `await dl.nextFrame()` 避免阻塞浏览器
*
* 可在此加载、批处理、清洗你的数据
*/
function train(data) { }
Backends
该库提供了一些后台来执行本库的核心数学运算,目前我们有一个 CPU 后台和一个 WebGL 后台,deeplearn.js 只要浏览器支持,是默认使用 WebGL 后台。WebGL 后台使用的是计算机的 GPU,旨在执行快速和高度优化的线性代数内核。
若强制使用 CPU 后台,你可以程序开始处调用dl.setBackend('cpu')
。
检查哪种后台正在被使用,可以调用dl.getBackend()
。
WebGL 后台
在使用WebGL后台时,数学运算如dl.add()
会推入着色器程序到GPU上执行。不像在CPU后台中那样,这些操作是不阻塞的(尽管将数据从主内存移动到GPU内存会有一些开销)。
这些着色器程序从 WebGLTextures 读取和写入。在链接数学运算时,textures 可以留在GPU内存中,这对性能非常重要。
你可以通过调用 tensor 的 data()
方法,定期从 gpu 下载数据,这样你就可以在你的主 Javascript 线程中读取这些数据。
获取两个矩阵之间均方差的例子:
const a = dl.tensor2d([[1.0, 2.0], [3.0, 4.0]]);
const b = dl.tensor2d([[0.0, 2.0], [4.0, 6.0]]);
// All these operations will execute on the GPU (if available)
// without blocking the main thread.
const diff = dl.sub(a, b);
// Calling .data returns a promise that resolves to a TypedArray that holds
// the tensor data downloaded from the GPU.
diff.data().then(d => console.log('difference: ' + d));
// We could also use dataSync to do this synchronously.
console.log('difference: ' + diff.dataSync());
TIP: Avoid calling data()/dataSync() between mathematical GPU operations unless you are debugging. This forces a texture download, and subsequent operation calls will have to re-upload the data to a new texture.
CPU 后台
在使用CPU实现时,这些数学运算会阻塞,然后立即在主 JavaScript 线程隐含的 TypedArray 上执行 。
相同的操作在两种后台中都被实现,因此你的代码无需根据客户端上使用的后台进行更改。
想了解更多?请阅读这些教程。
网友评论