首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

JavaScript 中的线性代数与矩阵运算

当我最近开始深入研究机器学习这一主题时,我不得不重新学习我在学校和大学学习过的所有关于线性代数、随机和微积分的知识。并在之前写了一个系列,《茶桁的AI秘籍 - 数学篇》。

我稍微回顾了一下线性代数中的矩阵运算(加法、减法、乘法和除法),并再次了解了不同类型的矩阵(逆矩阵、转置矩阵、单位矩阵)。

这篇文章回顾了这些内容并将它们应用到 JavaScript 中。此外,在文章的最后,将有一个小例子来演示为什么矩阵有利于机器学习中的计算。此外,您还会发现一些关于如何在 JavaScript 中表达类似于 Octave 或 Matlab 的数学方程的技巧。

在此声明,本文并非来自我的原创,只是对于JS中做机器学习很感兴趣。所以这其实是一篇“洗稿”文。

JAVASCRIPT 中的线性代数

使用矩阵进行编程时,矩阵只是数组中的数组。在 JavaScript 中,它们可以简单地表示为:

const matrix = [

[0, 1],

[2, 3],

[4, 5],

];

而m等于矩阵[m][n]的行,n 等于矩阵[m][n] 的列。向量是一种特定类型的矩阵,而矩阵只有一列。

const vector = [

[0],

[2],

[4],

];

线性代数中最简单的数学对象是标量。这只是一个数字。

const scalar = 4;

矩阵和向量在编程中可以用数组来表示。但是二维以上的矩阵呢?他们需要两个以上的轴。一般来说,这些具有可变数量轴的数字数组称为张量。

JAVASCRIPT 中的矩阵运算

您应该能够自己应用矩阵运算,但是当使用带循环的纯 JavaScript 时,它可能会变得丑陋。幸运的是,JavaScript 中有一个名为math.js的数学库。定义矩阵变得如此简单:

const matrix = math.matrix([[0, 1], [2, 3], [4, 5]]);

您可以使用该size()方法获取其维度,并通过该方法将其值作为数组获取valueOf()。此外,您可以应用矩阵运算,例如加法、减法、乘法和除法:

const matrixA = math.matrix([[0, 1], [2, 3], [4, -5]]);

const matrixB = math.matrix([[1, -1], [-2, 4], [-7, 4]]);

// addition

const matrixAdditionAB = math.add(matrixA, matrixB);

// [ [ 1, 0 ], [ 0, 7 ], [ -3, -1 ] ]

// subtraction

const matrixAdditionAB = math.subtract(matrixA, matrixB);

// [ [ -1, 2 ], [ 4, -1 ], [ 11, -9 ] ]

// multiplication

const matrixK = math.matrix([[0, 1], [2, 3], [4, 5]]);

const matrixL = math.matrix([[2, 4], [6, 2]]);

const matrixKL = math.multiply(matrixK, matrixL);

// [ [ 6, 2 ], [ 22, 14 ], [ 38, 26 ] ]

// division

const matrixY = math.matrix([[0, 2], [2, 4], [4, 6]]);

const matrixZ = math.matrix([[2, 1], [2, 2]]);

const matrixYZ = math.divide(matrixY, matrixZ);

// [ [ -2, 2 ], [ -2, 3 ], [ -2, 4 ] ]

请注意,例如,在 math.js 的情况下,矩阵的乘积不仅仅是包含各个矩阵的乘积的新矩阵。这称为逐元素积(或Hardamard 积)。相反,它是矩阵乘积运算。

此外,您还可以执行矩阵标量乘法和除法。它是按元素执行的。

// matrix scalar multiplication

const matrixG = math.matrix([[0, 1], [2, 3], [4, -5]]);

const matrixG3 = math.multiply(3, matrixG);

// [ [ 0, 3 ], [ 6, 9 ], [ 12, -15 ] ]

// matrix scalar division

const matrixH = math.matrix([[2, 4], [6, 2], [4, -4]]);

const matrixH2 = math.divide(matrixH, 2);

// [ [ 1, 2 ], [ 3, 1 ], [ 2, -2 ] ]

由于向量只是矩阵的一种特定形式,因此您也可以执行矩阵向量乘法。

const matrixI = math.matrix([[0, 1], [2, 3], [4, 5]]);

const vectorJ = math.matrix([[2], [1]]);

const vectorIJ = math.multiply(matrixI, vectorJ);

// [ [ 1 ], [ 7 ], [ 13 ] ]

如果您想在 JavaScript 中进行逐元素乘法或除法,您可以使用math.dotMultiply(matrixI, vectorJ);or math.dotDivide(matrixY, matrixZ)。否则,当在 math.js 中使用矩阵的默认运算符时,您将应用默认的矩阵运算。

毕竟,在 math.js 中处理矩阵不再那么困难了。但是您必须知道操作中每个矩阵的维度,因为并非每个矩阵都对另一个矩阵进行操作。另一件值得了解的好事情是关联和交换矩阵运算。

矩阵乘法是否具有结合律和交换律?

矩阵乘法有两个重要的性质。首先,矩阵乘法不可交换:A x B != B x A。

const matrixN = math.matrix([[0, 1], [2, 3]]);

const matrixO = math.matrix([[2, 4], [6, 2]]);

const matrixNO = math.multiply(matrixN, matrixO);

const matrixON = math.multiply(matrixO, matrixN);

console.log('Is matrix multiplication commutative?');

console.log(math.equal(matrixNO.valueOf(), matrixON.valueOf()));

// false

其次,矩阵乘法是结合律:A x (B x C) == (A x B) x C。

const matrixP = math.matrix([[0, 1], [2, 3], [4, 5]]);

const matrixQ = math.matrix([[2, 4], [6, 2]]);

const matrixR = math.matrix([[5, 2], [2, -2]]);

const matrixPQ_R = math.multiply(math.multiply(matrixP, matrixQ), matrixR);

const matrixP_QR = math.multiply(matrixP, math.multiply(matrixQ, matrixR));

console.log('Is matrix multiplication associative?');

console.log(math.equal(matrixPQ_R.valueOf(), matrixP_QR.valueOf()));

// true

在对矩阵进行任何进一步更复杂的运算之前,应该先内化这些矩阵乘法属性。

JAVASCRIPT 中的转置、逆矩阵和单位矩阵

线性代数中还有一些其他矩阵运算和矩阵类型。首先,将维度为 i * j 的单位 (I) 矩阵定义为 i 维矩阵,而 i == j。以下矩阵是单位矩阵。

const matrix = [

[1, 0, 0],

[0, 1, 0],

[0, 0, 1],

];

在 math.js 中,您可以使用该eye(i)方法生成维度为 i 的数据。

const matrixI3 = math.eye(3);

// [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]

单位矩阵稍后用于更复杂的矩阵运算。在矩阵运算中与另一个矩阵一起使用时,单位矩阵是一种特殊情况,因为它们是可交换的:A x I == I x A。

另一种类型的矩阵是转置矩阵。它是一个维度翻转的矩阵。基本上,行变成列,列变成行。在下面的示例中,向量变为所谓的行向量。

const matrixV = math.matrix([[0], [1], [2]]);

const matrixV_T = math.transpose(matrixV);

// [ [ 0, 1, 2 ] ]

最后但并非最不重要的一点是,矩阵可以有逆 A',但并非所有矩阵(称为奇异矩阵或简并矩阵)都有逆 A'。您可以使用单位矩阵求矩阵的逆:A(A') = (A')A = I。

const matrixT = math.matrix([[0, 1], [2, 3]]);

const matrixU = math.eye(2);

const matrixT_I = math.divide(matrixU, matrixT);

// [ [ -1.5, 0.5 ], [ 1, -0 ] ]

Math.js 免费为您提供逆运算。您可以使用上一个示例中的相同矩阵并对其调用该inv()方法。

const matrixS = math.matrix([[0, 1], [2, 3]]);

const matrixS_I = math.inv(matrixS);

// [ [ -1.5, 0.5 ], [ 1, -0 ] ]

最后,无论您使用哪种编程语言,您都会找到一个强大的数学库(例如 math.js)来应用所有这些运算。

如何将这些知识应用到机器学习中?

之前的学习对 JavaScript 中使用的矩阵线性代数有了基本的了解。它对我们的机器学习有什么帮助?您可以以线性回归为例。矩阵运算可用于使线性回归更易于执行且计算效率更高。未来还有其他机器学习算法。例如,当线性回归具有三个(经过训练的)竞争假设函数时,使用矩阵计算其结果变得很简单。

// Predicting Housing Prices with 3 competing Hypotheses

// const HOUSE_SIZES = [2104, 1416, 1534, 852];

// const h1 = x => -40 + 0.25 * x;

// const h2 = x => 200 + 0.1 * x;

// const h3 = x => -150 + 0.4 * x;

const houseSizeMatrix = math.matrix([

[1, 2104],

[1, 1416],

[1, 1534],

[1, 852],

]);

const hypothesesMatrix = math.matrix([

[-40, 200, -150],

[0.25, 0.1, 0.4],

]);

const competingResults = math.multiply(houseSizeMatrix, hypothesesMatrix);

// Column: Result for each Hypothesis

// Row: Result for each House Size

// [

// [ 486, 410.4, 691.6 ],

// [ 314, 341.6, 416.4 ],

// [ 343.5, 353.4, 463.6 ],

// [ 173, 285.2, 190.8 ],

// ]

您现在可以将这些计算放入矩阵中,而不是单独执行每个函数。循环变成一个矩阵运算。在更高的层面上,您可以说非矢量化实现变成了矢量化实现。因此,在执行机器学习算法时,它的计算效率变得很高,而且也更简单。此外,这些矩阵运算默认用于正规方程,用作梯度下降的替代方法。

JAVASCRIPT 中的 OCTAVE / MATLAB 类似操作

在某些时候,使用 math.js 所提出的方法不再具有扩展性。您将在复杂的数学表达式中执行不止一种矩阵运算。那么下面的表达式呢?

是的,它取自梯度下降的多元线性回归。它可以很容易地用Matlab或Octave等数学编程语言来表达。在 math.js 中,使用标准方法时它不会扩展。

// Octave:

// theta = theta - ALPHA / m * ((X * theta - y)' * X)';

// Math.js in JavaScript

theta = math.subtract(

theta,

math.multiply(

(ALPHA / m),

math.transpose(

math.multiply(

math.transpose(

math.subtract(

math.multiply(

X,

theta

),

y

)

),

X

)

)

)

);

那真是一团糟。然而,幸运的是,您可以通过使用 eval 功能来简化它,该功能采用数学表达式和范围值来应用表达式中的值。

// Octave:

// theta = theta - ALPHA / m * ((X * theta - y)' * X)';

// Math.js in JavaScript

theta = math.eval(`theta - ALPHA / m * ((X * theta - y)' * X)'`, {

theta,

ALPHA,

m,

X,

y,

});

仍然不像使用 Octave 或 Matlab 那样简洁,但您现在可以评估复杂的数学表达式。它在其他情况下也能帮助您。例如,它可用于通过范围索引提取矩阵的子集:

// Octave:

// matrixAsub = matrixA(:, 1:2);

// Math.js in JavaScript

let matrixAsub = math.eval('matrixA[:, 1:2]', {

matrixA,

});

它返回第一列和第二列(索引从 1 开始),其所有行作为新矩阵中的两个向量。它甚至更进一步,为矩阵中的列分配一个新向量。

// Octave:

// matrixA(:, 1) = vectorB;

// Math.js in JavaScript

math.eval(`matrixA[:, 1] = vectorB`, {

matrixA,

vectorB,

});

总之,希望有关 JavaScript 中应用矩阵的演练有助于开始 JavaScript 中的线性代数或作为 JavaScript 中机器学习的基础。您可以在命令行上使用可执行矩阵运算查看GitHub 存储库。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OVOrVw0nHw5Fd89NP9WIxHXQ0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券