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

给非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提供强大的硬件加速线性代数,可以用于从图像识别到文本生成的一切。 查看其他教程了解更多!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容