美文网首页程序员
deeplearnjs一个最简单的应用

deeplearnjs一个最简单的应用

作者: Jeremy_young | 来源:发表于2018-03-04 23:55 被阅读438次

结合 deeplearnjs 和之前的一篇神经网络的文章《小白成长记之神经网络》,来看看二者各自在最简单情况下的一次应用,以此继续寻找一些学习线索。

通过一些样本 X,去预测一个一元二次方程:y = ax^2 + bx + c,实际上就是通过样本数据的拟合真实曲线的过程,通过不断的调整方程系数,并最终确定一组最优系数 a, b, c。

怎么动手看起来似乎有点麻烦。如果将上述方程改写成一个二元一次方程:y = ax1 + bx2 + c。这个问题就变成了一个简单的线性规划问题。

在神经网络模型下,上述问题就是一个单层网络,且该层只有一个神经节点(节点偏移量为 c),且该层的激活函数是一个线性函数。在两个变量 x1, x2 及其权重系数 a, b 下,预测出 y 值。

一个最简单的线性规划的网络.png

现在看看 deeplearnjs 框架下,是如何编程实现的——实际上来自于它的教程

import * as dl from 'deeplearn';

/**
 * We want to learn the coefficients that give correct solutions to the
 * following quadratic equation:
 *      y = a * x^2 + b * x + c
 * In other words we want to learn values for:
 *      a
 *      b
 *      c
 * Such that this function produces 'desired outputs' for y when provided
 * with x. We will provide some examples of xs and ys to allow this model
 * to learn what we mean by desired outputs and then use it to produce new
 * values of y that fit the curve implied by our example.
 */


// Step 1. Set up variables, these are the things we want the model
// to learn in order to do prediction accurately. We will initialize
// them with random values.
const a = dl.variable(dl.scalar(Math.random()));
const b = dl.variable(dl.scalar(Math.random()));
const c = dl.variable(dl.scalar(Math.random()));

// 注:dl.scalar, dl.mul, dl.square 等等方法生成(或返回)的都是一个 Tensor
// dl.variable 创建了一个变量,实质上 Variable 类继承自 Tensor 类

// Step 2. Create an optimizer, we will use this later
const learningRate = 0.01;
const optimizer = dl.train.sgd(learningRate);

// Step 3. Write our training process functions.

/*
 * This function represents our 'model'. Given an input 'x' it will try and predict
 * the appropriate output 'y'.
 *
 * This could be as complicated a 'neural net' as we would like, but we can just
 * directly model the quadratic equation we are trying to model.
 *
 * It is also sometimes referred to as the 'forward' step of our training process.
 * Though we will use the same function for predictions later.
 *
 *
 * @return number predicted y value
 */
function predict(input) { // y = a * x ^ 2 + b * x + c
  return dl.tidy(() => {
    const x = dl.scalar(input);
    const ax2 = a.mul(x.square());
    const bx = b.mul(x);
    const y = ax2.add(bx).add(c);
    return y;
  });
}

/*
 * This will tell us how good the 'prediction' is given what we actually expected.
 *
 * prediction is a tensor with our predicted y value.
 * actual number is a number with the y value the model should have predicted.
 */
function loss(prediction, actual) {
  // Having a good error metric is key for training a machine learning model
  const error = dl.scalar(actual).sub(prediction).square();
  return error;
}

/*
 * This will iteratively train our model. We test how well it is doing
 * after numIterations by calculating the mean error over all the given
 * samples after our training.
 *
 * xs - training data x values
 * ys — training data y values
 */
async function train(xs, ys, numIterations, done) {
  let currentIteration = 0;

  for (let iter = 0; iter < numIterations; iter++) {
    for (let i = 0; i < xs.length; i++) {
      // Minimize is where the magic happens, we must return a
      // numerical estimate (i.e. loss) of how well we are doing using the
      // current state of the variables we created at the start.

      // This optimizer does the 'backward' step of our training data
      // updating variables defined previously in order to minimize the
      // loss.
      optimizer.minimize(() => {
        // Feed the examples into the model
        const pred = predict(xs[i]);
        const predLoss = loss(pred, ys[i]);

        return predLoss;
      });
    }

    // Use dl.nextFrame to not block the browser.
    await dl.nextFrame();
  }

  done();
}
/*
 * This function compare expected results with the predicted results from
 * our model.
 */
function test(xs, ys) {
  dl.tidy(() => {
    const predictedYs = xs.map(predict);
    console.log('Expected', ys);
    console.log('Got', predictedYs.map((p) => p.dataSync()[0]));
  })
}

// 样本数据
const data = {
  xs: [0, 1, 2, 3],
  ys: [1.1, 5.9, 16.8, 33.9]
};

// Lets see how it does before training.
console.log('Before training: using random coefficients')
test(data.xs, data.ys);
train(data.xs, data.ys, 50, () => {
  console.log(
      `After training: a=${a.dataSync()}, b=${b.dataSync()}, c=${c.dataSync()}`)
  test(data.xs, data.ys);
  console.log('Start to predict the output of 4 through the trained model:', predict(4).dataSync()[0])
});
// Huzzah we have trained a simple machine learning model!

我们来看看代码中几个重要的过程。

变量初始化

我们并不关心变量的初始值,因而用了一个随机的标量,作为变量的初始值。变量初始化后,整个模型就完成来初始化——这一初始化,是迭代的起点,必不可少。因为随机的初始化,故此时的模型与“真实”的模型有着非常大的差异。

可以通过打印初始系数,和训练后最终的系数做对比:

a.print(); b.print(); c.print();

预测 predict

输入经过模型产生输出,这个输出就是一个预测。所有的数据无论是训练集、测试集还是验证集,走的都是同样一个模型。

代码中predict方法实际上就调用了这个隐藏着的模型,而模型在每一个样本接受训练时都会做细微调整,模型所有的变动都存放在 CPU 或 GPU 的内存中。这也是为什么系数 a、b、c 被声明成 const 量,而实际上它的值是一直在变化的。

训练好了模型,就可以拿来输入新数据,预测新输出了。

console.log('Start to predict the output of 4 through the trained model:', predict(4).dataSync()[0])

训练 train

训练过程其实很简单,因为模型已经初始化好了,训练只不过将样本数据交给机器,让它循环迭代而已。在每一轮迭代中,都会产生一个损失值。代码中的损失仍然是一个张量:

const error = dl.scalar(actual).sub(prediction).square();

通过损失反向回溯,从而不断对权重系数进行调整。这个过程就是交给优化器optimizer做的。此处暂时不必追究优化器是如何办到的。

梯度下降 Gradient Descent

4 个样本,依次经过 50 次迭代训练,最终能训练出一个比较满意(损失较小)的结果,这是为什么?梯度下降 起到了什么作用?

首先我们要知道,梯度下降一般有这些变体,它们彼此容易相互推导。上例中用的是随机梯度下降,后边本文介绍最基本款“批量梯度下降”。

  • 批量梯度下降
  • 随机梯度下降 sdl.train.sgd
  • 小批量梯度下降

批量梯度下降算法

取神经网络模型中一个节点,其拓扑结构就是简单的多输入对单输出。

单层神经网络-单个节点.png

取任意一个样本 x ,输入到网络后,得到一个输出值的 hw 。变量 x 包含有多个特征属性值:x1, x2, ..., xj,每个特征属性都有一个权重系数与之对应——于是进一步将该计算准确描述为一个加权运算:

image.png

假设该节点有一个已知真实值y,那么在该节点的损失就是:

image.png

实际中,对一个问题我们往往有非常多个样本,比如m个样本,任意一个样本x(i)都应该经过这样的运算,得到损失。最后所有m个样本的总损失就是:

image.png

因为这是整个训练集的总损失,它从整体上描述了模型的拟合度。如果找到了它的一个最小值,那么也就找到了最小损失。最小值问题,我们这里可以求导数。

将权重系数w作为参数,同样它包含若干个分量 w1, w2, ..., wj, 对w求导意味着求每个分量的偏导:

image.png

要想取到极小值,导数应该趋向于0。但是趋向于0并不是一步就能做到的,需要在反复迭代中调整权重系数。那么每次权重系数w怎样的变化,才有助于做到这一点呢?事实上,只需这样更新:

image.png

后记

通过一个简单的线性规划的例子,其实可以发现很多可以优化、发散、深化的线索。这本身就是深度学习算法的乐趣所在~

相关文章

网友评论

    本文标题:deeplearnjs一个最简单的应用

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