美文网首页
deeplearn.js教程 - 给非ML专家的指南

deeplearn.js教程 - 给非ML专家的指南

作者: 黑衣魔笛手 | 来源:发表于2017-09-30 23:09 被阅读0次

    给非ML专家的指南

    您可以在这里找到补充本教程的代码。

    运行:

    ./scripts/watch-demo demos/ml_beginners/ml_beginners.ts
    

    然后访问http://localhost:8080/demos/ml_beginners/

    或者直接点击这里观看我们的演示。

    NDArrays,Tensors,以及numbers

    数学张量

    在数学上,“张量”是线性代数的最基本的对象,数字,向量和矩阵的泛化。 向量可以被认为是数字的一维列表,矩阵则是二维数字列表。 张量简单地将概念概括为n维数字列表。这是布置在任意多维矩形阵列中的任意的数字(或甚至字符串或其他数据类型)排列。

    张量具有几个属性:

    • 它有一个类型,它描述了每个元素的类型,例如整型,浮点等。deeplearn.js现在只支持32位浮点数。
    • 它有一个形状,一个整数列表,它描述了矩形矩阵阵列的形状。例如当你说一个矩阵是“4乘4”时,你就在描述矩阵的形状。
    • 它有一个等级,这是它的形状的长度,元素数组的维数。 矢量为1级,矩阵为2级。
    示例张量 类型 形状 等级
    Scalar: 3.0 float [] 0
    Vector: (1, 5, -2) int [3] 1
    2x2 Matrix int [2, 2] 2

    number[],NDArray,Tensor:数学张量的三种数据类型

    数学家称之为“张量”的相同对象在deeplearn.js中以三种不同的方式表示。上述讨论(等级,形状和类型)适用于所有这些,但它们不同,保持直线是很重要的:

    • number []是与数组相对应的底层JavaScript类型。实际上number是0级张量,number[]是1级张量,number[][]是二级张量,以此类推。你不会在dee​​plearn.js中使用太多,但这是你获得deeplearn.js之内或以外的内容的方法。
    • NDArraydeeplearn.js更强大的实现。可以在客户端的GPU上执行涉及NDArray的计算,这是deeplearn.js的根本优点。这是计算最终发生时最实际的张量数据的格式。例如,当您调用Session.eval时,这是您得到的返回值(以及FeedEntry的输入)。您可以使用NDArray.new(number [])NDArray.get([indices])NDArraynumber []之间进行转换。
    • Tensor是一个空袋子,它里面没有实际的数据。当构建Graph时,它是一个占位符,它记录最终将适合其中的数据的形状和类型。它的组件中不包含实际值。但是,只要知道形状和类型,在Graph构建时就可以做到重要的错误捕获。如果您要将2x3矩阵乘以10x10矩阵,则在创建节点之前,该Graph可以在您给出输入数据之前向提出警告。在TensorNDArraynumber[]之间直接转换是没有意义的。如果您发现您正在尝试这样做,以下其中之一可能是真的:
      • 您有一个静态的NDArray,并且想在一个Graph中使用。您应该使用graph.constant()创建一个常量Tensor节点。
      • 您有一个NDArray要作为Graph输入。在Graph中创建一个带有graph.placeholder的占位符,然后将您的输入发送到FeedEntry中的Graph
      • 您有一个输出张量,您希望会话评估并返回其值。调用Session.eval(tensor)

    一般来说,您只需要在自动区分(训练)时使用Graph。如果您只想使用库进行正向模式推理,或仅使用通用数值计算,则使用NDArrayMathNDArrays就足够了。

    如果您对训练感兴趣,您必须使用Graph。构建Graph时,您将使用Tensors,当您使用Session.eval执行时,结果将是NDArray

    正向模式推理/数值计算

    如果您只想对NDArray执行数学运算,则可以简单地使用数据构建NDArray,并使用NDArrayMath对象对它们执行操作。

    例如,如果要在GPU上计算矩阵乘以矢量:

    const math = new NDArrayMathGPU();
    
    math.scope((keep, track) => {
      const matrixShape = [2, 3];  // 2行,3列。
      const matrix = track(Array2D.new(matrixShape, [10, 20, 30, 40, 50, 60]));
      const vector = track(Array1D.new([0, 1, 2]));
      const result = math.matrixTimesVector(matrix, vector);
    
      console.log("result shape:", result.shape);
      console.log("result", result.getValues());
    });
    

    有关NDArrayMath,保持和跟踪的更多信息,请参阅简介和核心概念

    NDArray /NDArrayMath层可以被认为类似于NumPy

    训练:延迟执行,图形(Graphs)和会话(Sessions)

    deeplearn.js中了解训练(自动区分)的最重要的事情是它使用延迟执行模型。您的代码将包含两个独立的阶段:首先,您将构建一个Graph,表示您要执行的计算的对象,然后执行Graph并获取结果。

    大多数情况下,您的Graph将会将某些输入转换为某些输出。一般来说,Graph的架构将保持固定,但它将包含将自动更新的参数。

    执行Graph时,有两种模式:训练和推断。

    推断是提供Graph输入以产生输出的动作。

    训练Graph涉及提供Graph许多标记输入/输出对的示例,并自动更新Graph的参数,以便在评估(推断)输入时Graph的输出更接近标记的输出。给出表示对生成的输出的标签输出接近的Scalar的函数称为“成本函数”(也称为“损失函数”)。当模型运行良好时,损失函数应该输出接近零。训练时必须提供成本函数。

    deeplearn.js的结构非常类似于Google的基于python的机器学习语言Tensorflow。如果你知道TensorFlow,TensorGraphSession的概念几乎是一样的,但是我们假设在这里你没有TensorFlow的知识。

    图形(graphs)作为函数

    可以通过类比于常规JavaScript代码来理解差异。 对于本教程的其余部分,我们将使用这个二次方程:

    // y = a * x^2 + b * x + c
    const x = 4;
    const a = Math.random();
    const b = Math.random();
    const c = Math.random();
    
    const order2 = a * Math.pow(x, 2);
    const order1 = b * x;
    const y = order2 + order1 + c;
    

    在这个原始代码中,数学计算在每一行被马上处理。

    与以下代码相反,类似于deeplearn.jsGraph推断的工作原理。

    function graph(x, a, b, c) {
      const order2 = a * Math.pow(x, 2);
      const order1 = b * x;
      return order2 + order1 + c;
    }
    
    const a = Math.random();
    const b = Math.random();
    const c = Math.random();
    const y = graph(4, a, b, c);
    

    这个代码有两个步骤:首先设置图形函数,然后调用它。 在最后一行调用函数之前,在前几行中设置图形函数的代码不会执行任何实际的数学运算。 在安装过程中,即使尚未执行计算,也可以捕获基本的编译器类型安全错误。

    这完全类似于Graphdeeplearn.js中的工作原理。 您的代码的第一部分将设置Graph,描述如下:

    • 输入,我们的例子为“x”。 输入表示为占位符(例如graph.placeholder())。
    • 输出,在我们的例子中是“order1”,“order2”,最终输出“y”。
    • 产生输出的操作,在我们的例子中是二次(x ^ 2,乘法,加法)的分解函数。
    • 可更新的参数,在我们的例子中是“a”,“b”,“c”。 可更新的参数表示为变量(例如graph.variable()

    然后,在代码的后续部分,您将在某些输入上“调用”(Session.eval)图形的功能,您将学习“a”,“b”和“c”的值,即用Session.train处理某些数据。

    上述函数类比和deeplearn.jsGraph之间的一个细微差别在于Graph没有指定其输出。 相反,Graph函数的调用者指定要返回哪些张量。 这允许对同一个Graph的不同调用来执行它的不同部分。 只有获得呼叫者要求的结果所需的部分才能被评估。

    Graph的推理和训练由一个Session对象驱动。 该对象包含运行时状态,权重,激活和渐变(派生),而Graph对象只保存连接信息。

    所以上面的功能将在deeplearn.js中实现,如下所示:

    const graph = new Graph();
    // 在graph中创建一个新的输入,称为“x”,其形状为[](标量)。
    const x: Tensor = graph.placeholder('x', []);
    // 在图形中创建新的变量'a','b','c',形状为[],并且随机初始值。
    const a: Tensor = graph.variable('a', Scalar.new(Math.random()));
    const b: Tensor = graph.variable('b', Scalar.new(Math.random()));
    const c: Tensor = graph.variable('c', Scalar.new(Math.random()));
    // 使新张量表示二次运算的输出。
    const order2: Tensor = graph.multiply(a, graph.square(x));
    const order1: Tensor = graph.multiply(b, x);
    const y: Tensor = graph.add(graph.add(order2, order1), c);
    
    // 训练时需要提供标签和成本函数。
    const yLabel: Tensor = graph.placeholder('y label', []);
    // 提供训练的平均成本函数。 cost =(y - yLabel)^ 2
    const cost: Tensor = graph.meanSquaredCost(y, yLabel);
    
    // 此时,图形(Graph)已设置,但尚未被评估。
    // **deeplearn.js** 需要一个Session对象来评估一个图形。
    const math = new NDArrayMathGPU();
    const session = new Session(graph, math);
    
    math.scope((keep, track) => {
      /**
       * 推断
       */
      // 现在,我们要求图形来评估(推断),并在为“x”提供值4时给出结果。
      // 注意:“a”,“b”和“c”被随机初始化,所以这将给我们一些随机的东西。
      let result: NDArray =
          session.eval(y, [{tensor: x, data: track(Scalar.new(4))}]);
      console.log(result.shape);
      console.log(result.getValues());
    
      /**
       * 训练
       */
      // 现在让我们学习给出一些数据的这个二次方的系数。
      // 为此,我们需要提供x和y的例子。
      // 这里给出的值是值a = 3,b = 2,c = 1,随机噪声加到输出上,因此不是一个完美契合。
      const xs: Scalar[] = [
        track(Scalar.new(0)),
        track(Scalar.new(1)),
        track(Scalar.new(2)),
        track(Scalar.new(3))
      ];
      const ys: Scalar[] = [
        track(Scalar.new(1.1)),
        track(Scalar.new(5.9)),
        track(Scalar.new(16.8)),
        track(Scalar.new(33.9))
      ];
      // 训练时,重要的是打乱你的数据!
      const shuffledInputProviderBuilder =
          new InCPUMemoryShuffledInputProviderBuilder([xs, ys]);
      const [xProvider, yProvider] =
          shuffledInputProviderBuilder.getInputProviders();
    
      // 将训练分成各个批次。
      const NUM_BATCHES = 20;
      const BATCH_SIZE = xs.length;
      // 在开始训练之前,我们需要提供一个优化器。
      // 这是负责更新权重的对象。
      // 学习率参数是一个值,表示更新权重时需要做的步骤。
      // 如果这太大,你可能会超越和振荡。
      // 如果太小,模型可能需要很长时间才能训练。
      const LEARNING_RATE = .01;
      const optimizer = new SGDOptimizer(LEARNING_RATE);
      for (let i = 0; i < NUM_BATCHES; i++) {
        // 训练需要一个成本张量来最小化; 
        // 此次调用训练一批数据,并将此批次的平均成本返回为标量(Scalar)。
        const costValue = session.train(
            cost,
            // 将图表(Graph)上的输入提供者映射到Tensors。
            [{tensor: x, data: xProvider}, {tensor: yLabel, data: yProvider}],
            BATCH_SIZE, optimizer, CostReduction.MEAN);
    
        console.log('average cost: ' + costValue.get());
      }
    
      // 现在打印x = 4的训练模型的值,应该是大约57.0。
      result = session.eval(y, [{tensor: x, data: track(Scalar.new(4))}]);
      console.log('result should be ~57.0:');
      console.log(result.shape);
      console.log(result.getValues());
    });
    

    在训练模型之后,您可以再次通过Graph来推断给出“x”的“y”值。

    当然,在实践中,你不会只想使用Scalar值。deeplearn.js提供强大的硬件加速线性代数,可以用于从图像识别到文本生成的一切。 查看其他教程了解更多!

    相关文章

      网友评论

          本文标题:deeplearn.js教程 - 给非ML专家的指南

          本文链接:https://www.haomeiwen.com/subject/hetoextx.html