deeplearnjs 介绍 <一>

闲来再做回搬运工,感觉和机器翻译比起来并没啥优势。。。

deeplearn.js 是一个用于机器智能的开源 WebGL 加速的 JavaScript 库。deeplearn.js 为您的指尖带来高性能机器学习构建模块,允许您在浏览器中训练神经网络或在推理模式下运行预训练模型。

让我们来看看 deeplearn.js 中的一些核心概念。

Tensors

在 deeplearn.js 中数据的核心单元便是 tensor,一个 Tensor 由一组任意维数的数值型数组构成。
Tensors 由 shape 属性定义其形状。该库为低秩的张量(Tensors)提供了 sugar 般的子类:Scalar, Tensor1D, Tensor2D, Tensor3DTensor4D,以及一些用于构建它们的辅助函数。

例如使用一个 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 上执行 。

相同的操作在两种后台中都被实现,因此你的代码无需根据客户端上使用的后台进行更改。

想了解更多?请阅读这些教程。

你可能感兴趣的:(deeplearnjs 介绍 <一>)