JavaScript 中的机器学习基础知识

2025-06-10

JavaScript 中的机器学习基础知识

maxresdefault

我最近一直在探索机器学习领域;说实话,我不得不重新学习几乎所有的数学基础知识。大学毕业已经有一段时间了,机器学习很大程度上依赖于线性代数。在这篇博客中,我计划整理一下这些基础知识,并附上我用 JavaScript 实现它们的方法。我知道 Python 的NumPy库是行业标准,但我一直很好奇这些基本操作在 JavaScript 中是如何实现的。坦白说,我目前还在自学,所以如果过程中有任何错误,请指正!

主题包括:

  • 线性代数基础
  • 矩阵运算
  • 矩阵的类型
  • 复杂的数学表达式

我还编译了一个 NodeJS 应用程序,其中包含本博客中所有示例的工作版本:ML Fundamentals 1

开始吧〜!

线性代数基础

线性代数是代数的一个子集,它处理标量、向量和矩阵。简单来说,它们的含义如下:

矩阵

在编程中,我喜欢把矩阵想象成一个数组或一个数组的数组。其中m,是行数,n是列数matrix[m][n]。如果我们进行编程,它看起来应该是这样的:

const matrix = [
  [0, 1],
  [2, 3],
  [3, 4]
];
Enter fullscreen mode Exit fullscreen mode

向量

向量只是矩阵的一种,并且只有一列。因此,它看起来像这样:

const vector = [
  [0],
  [1],
  [2],
];
Enter fullscreen mode Exit fullscreen mode

标量

线性代数中最简单的数学对象可能就是标量。它只是一个经常用作乘数的数字。

const scalar = 2;
Enter fullscreen mode Exit fullscreen mode

在 JavaScript 或任何语言中,大多数矩阵和向量都可以用数组表示。但是,3D、4D 或 XD 矩阵呢?通常,大多数线性代数课程都指出,矩阵的x维度可以x是大于 0 的数。这时,我们开始在编程中使用张量的概念,向量本质上是按维度排序的。事实上,JavaScript 有一个名为Tensorflow.js的框架,它使用张量定义和运行计算。我将在以后的博客中深入探讨这一点。现在,让我们回到基础知识。

矩阵运算

说到矩阵运算,我通常会想到循环。但用循环来迭代矩阵很快就会变得非常难看。这时,我发现了Math.js库的功能,它为 JS 和 Node.js 项目提供了强大且优化的计算能力。这意味着,你不必将矩阵定义为数组的数组,而是可以直接定义一个数组matrix,在 Math.js 中它看起来就像这样:

const matrix = math.matrix([[0,1], [2,3], [4,5]])
Enter fullscreen mode Exit fullscreen mode

一些有用的方法包括size()valueOf()

math.size(matrix) //returns dimensions of matrix
//[3,2]
matrix.valueOf() //returns string representation of the matrix
//[[0,1], [2,3], [4,5]]
Enter fullscreen mode Exit fullscreen mode

让我们探索四种主要矩阵运算的示例,例如加法、减法、乘法和除法:

矩阵加法

matA = math.matrix([[0, 1], [2, 3], [4, -5]]);
matB = math.matrix([[1, -1], [-2, 4], [-7, 4]]);

const matAdd = math.add(matA, matB);

console.log(matAdd.valueOf());
//[ [ 1, 0 ], [ 0, 7 ], [ -3, -1 ] ]
Enter fullscreen mode Exit fullscreen mode

矩阵减法

matA = math.matrix([[0, 1], [2, 3], [4, -5]]);
matB = math.matrix([[1, -1], [-2, 4], [-7, 4]]);

const matSub = math.subtract(matA, matB);

console.log(matSub.valueOf());
//[ [ -1, 2 ], [ 4, -1 ], [ 11, -9 ] ]
Enter fullscreen mode Exit fullscreen mode

矩阵乘法

事情开始变得有趣了。在开始写代码示例之前,理解矩阵乘法的两个性质很重要:交换律和结合律。

交换律

矩阵乘法不满足交换律,这意味着 A x B != B x A。让我们测试一下,并使用 MathJS 的内置equal比较器进行检查:

matA = math.matrix([[0, 1], [2, 3]]);
matB = math.matrix([[2, 4], [6, 2]]);

const matAxB = math.multiply(matA, matB);
const matBxA = math.multiply(matB, matA);

console.log(math.equal(matAxB.valueOf(), matBxA.valueOf()));
// false
Enter fullscreen mode Exit fullscreen mode

联想

矩阵乘法是结合的,简单地转换为 A x (B x C) == (A x B) x C。让我们也测试一下:


const matA = math.matrix([[0, 1], [2, 3], [4, 5]]);
const matB = math.matrix([[2, 4], [6, 2]]);
const matC = math.matrix([[5, 2], [2, -2]]);

const matAxB_C = math.multiply(math.multiply(matA, matB), matC);
const matA_BxC = math.multiply(matA, math.multiply(matB, matC));

console.log(math.equal(matAxB_C.valueOf(), matA_BxC.valueOf()));
// true
Enter fullscreen mode Exit fullscreen mode

需要特别注意的是,在 math.js 中,矩阵的乘积不仅仅是一个包含各个矩阵乘积的新矩阵——逐元素乘积(或哈达玛乘积)。实际上,我们看到的乘积是一个矩阵乘积运算。

元素级乘积的一个例子是通过矩阵标量乘法 A x 3,其执行方式如下:

matA = math.matrix([[0, 1], [2, 3], [4, -5]]);
const scalar = 3;
const matAx3 = math.multiply(matA, scalar);

console.log(matAx3.valueOf());
//[ [ 0, 3 ], [ 6, 9 ], [ 12, -15 ] ]
Enter fullscreen mode Exit fullscreen mode

当然,由于向量只是一个矩阵,因此也可以执行矩阵向量乘法:

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

const matAxvB = math.multiply(matA, matB);

console.log(matAxvB.valueOf());
//[ [ 1 ], [ 7 ], [ 13 ] ]
Enter fullscreen mode Exit fullscreen mode

矩阵除法

矩阵除法也可以用 JavaScript 实现。需要注意的是,数学上并不存在“矩阵除法”,其定义如下:
A/B = AB -1。
不过,为了省去除法和逆运算的麻烦,我们可以在 JavaScript 中实现 matdix.divide():

matA = math.matrix([[0, 2], [2, 4], [4, 6]]);
matB = math.matrix([[2, 1], [2, 2]]);

const matAB = math.divide(matA, matB);

console.log(matAB.valueOf());
// [ [ -2, 2 ], [ -2, 3 ], [ -2, 4 ] ]
Enter fullscreen mode Exit fullscreen mode

毕竟,在 math.js 中处理矩阵已经不再那么难了!但是你必须知道操作中每个矩阵的维度,因为并非每个矩阵都会“作用”于另一个矩阵!

矩阵的类型

线性代数中还有几种矩阵类型也很重要。

单位矩阵

维度为 i * j 的单位矩阵 (I) 定义为 i 维矩阵,其中 i == j。它们很特殊,并且由于满足交换律而被广泛使用;这意味着 A x I === I x A。以下是单位矩阵的示例:

const matrix = [
  [1, 0, 0],
  [0, 1, 0],
  [0, 0, 1],
];
Enter fullscreen mode Exit fullscreen mode

在 math.js 中可以使用该eye(i)方法快速生成维度为 i 的单位矩阵:

const matI_3 = math.eye(3);
console.log(matA.valueOf());
// [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]
Enter fullscreen mode Exit fullscreen mode

转置矩阵

在转置矩阵中,维度被翻转。简单地说,行变成了列,列变成了行。以下是一个对向量进行转置的示例。转置后的矩阵称为行向量:

const matV = math.matrix([[0], [1], [2]]);
const matV_T = math.transpose(matV);

console.log(matV_T.valueOf());
// [ [ 0, 1, 2 ] ]
Enter fullscreen mode Exit fullscreen mode

逆矩阵

当然,讨论逆矩阵非常重要。有趣的是,矩阵可以有逆 A -1,但并非所有矩阵(特别是奇异矩阵或退化矩阵)都有。求矩阵逆的公式为:A(A -1 ) = (A -1 )A = I。
不过 Math.js 为我们提供了逆运算,您可以自行math.inv()查看:

matA = math.matrix([[0, 1], [2, 3]]);
const matA_1 = math.inv(matA);

console.log(matA_1.valueOf());
// [ [ -1.5, 0.5 ], [ 1, -0 ] ]
Enter fullscreen mode Exit fullscreen mode

复杂的数学表达式

到了一定时候,使用内置的 math.js 提出的方案就不再具有可扩展性了。说实话,机器学习很快就会变得复杂起来。尤其是当你尝试执行包含多个特征的运算,并使用带有梯度下降的多元线性回归(也就是包含多个输入的函数)时。以下面的 theta 函数为例:

theta = theta - ALPHA / m * ((X * theta - y)^T * X)^T
Enter fullscreen mode Exit fullscreen mode

如果你尝试在 Javascript 中将其表示出来,你将会得到如下的混乱:

theta = math.subtract(
  theta,
  math.multiply(
    (ALPHA / m),
    math.transpose(
      math.multiply(
        math.transpose(
          math.subtract(
            math.multiply(
              X,
              theta
            ),
            y
          )
        ),
        X
      )
    )
  )
);
Enter fullscreen mode Exit fullscreen mode

真是一团糟,对吧?幸运的是,有一个简洁易读的方法可以使用以下eval函数来求值:

theta = math.eval(`theta - ALPHA / m * ((X * theta - y)' * X)'`, {
  theta,
  ALPHA,
  m,
  X,
  y,
});
Enter fullscreen mode Exit fullscreen mode

惊讶于这一切竟然可以用 JavaScript 实现?你并不孤单!无论你现在使用哪种编程语言,你一定能找到一个强大的数学库,例如 MathJS,它能够支持矩阵运算和其他复杂的运算,帮助你掌握机器学习的基础知识!希望这篇文章对你有所帮助。

如果您想自己尝试 Math.js 库,请查看 Github 存储库,其中包含在 NodeJS 应用程序中编译的所有这些示例:
https://github.com/mrinasugosh/ml-fundamentals-1

==== 在社交媒体上关注我( @mrinasugosh) ====
Dev.to: @mrinasugosh
Github: @mrinasugosh
Twitter: @mrinasugosh
LinkedIn: @mrinasugosh

鏂囩珷鏉ユ簮锛�https://dev.to/mrinasugosh/ml-fundamentals-in-javascript-5fm5
PREV
如何为代码设计创建单元测试?
NEXT
ES6 是 Node 的必由之路