Skip to content

Latest commit

 

History

History
638 lines (491 loc) · 34.2 KB

File metadata and controls

638 lines (491 loc) · 34.2 KB

二、NumPy 线性代数

数学的主要分类之一是代数,尤其是线性代数专注于线性方程和映射线性空间(即向量空间)。 当我们在向量空间之间创建线性映射时,实际上是在创建称为矩阵的数据结构。 线性代数的主要用途是求解联立线性方程,但也可用于非线性系统的近似。 想象一下您要理解的复杂模型或系统,将其视为非线性模型。 在这种情况下,您可以将问题的复杂的非线性特征简化为联立的线性方程,并且可以在线性代数的帮助下求解它们。

在计算机科学中, 线性代数在机器学习ML)应用中大量使用。 在 ML 应用中,您处理的是高维数组,可以轻松地将其转换为线性方程式,您可以在其中分析给定空间中要素的相互作用。 想像一下您正在从事图像识别项目并且您的任务是从 MRI 图像中检测出大脑中的肿瘤的情况。 从技术上讲,您的算法应该像医生一样工作,在其中扫描给定的输入并检测大脑中的肿瘤。 医生的优势在于能够发现异常情况。 人类的大脑已经进化了数千年,以解释视觉输入。 无需付出太多努力,人类就可以直观地捕获异常。 但是,对于执行相似任务的算法,您应该尽可能详细地考虑此过程,以了解如何正式表达它,以便机器可以理解。

首先,您应该考虑 MRI 数据如何存储在仅处理 0 和 1 的计算机中。 计算机实际上将像素强度存储在称为矩阵的结构中。 换句话说,您将 MRI 转换为大小为N2的向量,其中每个元素均由像素值组成。 如果此 MRI 大小为512 x 512,则每个像素将是 262,144 像素中的一个点。 因此,您将在此矩阵中进行的任何计算操作都极有可能会使用线性代数原理。 如果该示例不足以证明 ML 中的线性代数的重要性,那么让我们看一下深度学习中的一个流行示例。 简而言之,深度学习是一种算法,该算法使用神经网络结构通过不断更新各层之间神经元连接的权重来学习所需的输出(标签)。 一个简单的深度学习算法的图形表示如下:

神经网络存储各层之间的权重和矩阵中的偏差值。 由于这些参数是您尝试调整到深度学习模型中以最小化损失函数的参数,因此您不断进行计算并更新它们。 通常,机器学习模型需要大量计算,并且需要针对大型数据集进行训练以提供有效的结果。 这就是为什么线性代数是 ML 的基本部分的原因。

在本章中,我们将使用 numpy 库,但请注意,大多数线性代数函数也是通过 scipy 导入的,它们更恰当地属于它。 理想情况下,在大多数情况下,您都可以导入这两个库并执行计算。 scipy 的一个重要特征是拥有功能齐全的线性代数模块。 在本章中,我们强烈建议您阅读 scipy 文档并使用与 scipy 相同的操作进行练习。 这是 scipy 的线性代数模块的链接

在本章中,我们将介绍以下主题:

  • 向量和矩阵数学
  • 什么是特征值?我们如何计算它?
  • 计算范数和行列式
  • 求解线性方程
  • 计算梯度

向量和矩阵运算

在上一章中,您练习了向量和矩阵的入门操作。 在本节中,您将练习在线性代数中大量使用的更高级的向量和矩阵运算。 让我们记住关于矩阵操作的点积透视图,以及当您具有 2D 数组时如何使用不同的方法来完成。 以下代码块显示了执行点积计算的替代方法:

In [1]: import numpy as np 
        a = np.arange(12).reshape(3,2) 
        b = np.arange(15).reshape(2,5) 
        print(a) 
        print(b)
Out[1]: 
[[ 0 1] 
 [ 2 3] 
 [ 4 5]] 
[[ 0 1 2 3 4] 
 [ 5 6 7 8 9] 
In [2]: np.dot(a,b)
Out[2]: array([[ 5,  6,  7,  8,  9], 
               [15, 20, 25, 30, 35], 
               [25, 34, 43, 52, 61]]) 
In [3]: np.matmul(a,b)
Out[3]: array([[ 5,  6,  7,  8,  9], 
               [15, 20, 25, 30, 35], 
               [25, 34, 43, 52, 61]]) 
In [4]: a@b
Out[4]: array([[ 5,  6,  7,  8,  9], 
               [15, 20, 25, 30, 35], 
               [25, 34, 43, 52, 61]]) 

内积和点积在 ML 算法(如监督学习)中非常重要。 让我们回到有关肿瘤检测的示例。 想象我们有三张图像(MRI):第一张图像有肿瘤(A),第二张图像无肿瘤(B),第三张图像是您要标记为肿瘤无肿瘤的未知 MRI。下图显示了向量ab的点积的几何表示:

作为一个非常简单的例子,点积将向您显示这两个向量之间的相似性,因此,如果您未知的 MRI 方向接近向量 A,则您的算法将被归类为具有肿瘤的 MRI。 否则,它将分类为无肿瘤。 如果要在一个函数中乘以两个或多个数组,linalg.multi_dot()非常方便。 进行此操作的另一种方法是将np.dot()嵌套的数组相乘,但您需要知道哪个计算顺序最快,因为在linalg.multi_dot()中此优化是自动进行的。 如前所述,以下代码块使用不同的方法执行相同的点积运算:

In [5]: from numpy.linalg import multi_dot 
        a = np.arange(12).reshape(4,3) 
        b = np.arange(15).reshape(3,5) 
        c = np.arange(25).reshape(5,5) 
        multi_dot([a, b, c])
Out[5]: array([[ 1700, 1855, 2010, 2165, 2320], 
               [ 5300, 5770, 6240, 6710, 7180], 
               [ 8900, 9685, 10470, 11255, 12040], 
               [12500, 13600, 14700, 15800, 16900]])
In [6]: a.dot(b).dot(c) 
Out[6]: array([[ 1700, 1855, 2010, 2165, 2320], 
               [ 5300, 5770, 6240, 6710, 7180], 
               [ 8900, 9685, 10470, 11255, 12040], 
               [12500, 13600, 14700, 15800, 16900]])

如下面的代码所示,即使使用三个小型矩阵,multi_dot()方法也将运行时间减少了 40%。 如果矩阵数量和大小增加,则该时间间隔会大大增加。 因此,您应该使用multi_dot()方法以确保最快的求值顺序。 以下代码将比较这两种方法的执行时间:

In [7]: import numpy as np 
        from numpy.linalg import multi_dot 
        import time 
        a = np.arange(120000).reshape(400,300) 
        b = np.arange(150000).reshape(300,500) 
        c = np.arange(200000).reshape(500,400) 
        start = time.time() 
        multi_dot([a,b,c]) 
        ft = time.time()-start 
        print ('Multi_dot tooks', time.time()-start,'seconds.') 
        start_ft = time.time()
        a.dot(b).dot(c) 
        print ('Chain dot tooks', time.time()-start_ft,'seconds.')
Out[7]:
Multi_dot tooks 0.14687418937683105 seconds. 
Chain dot tooks 0.1572890281677246 seconds. 

NumPy 的线性代数库中还有另外两种重要的方法,即outer()inner()outer方法计算两个向量的外积。inner方法的行为取决于所采用的参数。 如果您有两个向量作为参数,它将产生一个普通的点积,但是当您具有一个更高维的数组时,它会像tensordot()一样返回最后一个轴的积的和。您将在本章后面看到tensordot()。 现在,让我们首先关注outer()inner()方法。 以下示例将帮助您了解这些函数的作用:

In [8]: a = np.arange(9).reshape(3,3) 
        b = np.arange(3) 
        print(a) 
        print(b)
Out[8]:
[[0 1 2] 
 [3 4 5] 
 [6 7 8]] 
[0 1 2]
In [9]: np.inner(a,b) 
Out[9]: array([ 5, 14, 23])
In [10]: np.outer(a,b) 
Out[10]: array([[ 0, 0, 0], 
                [ 0, 1, 2], 
                [ 0, 2, 4], 
                [ 0, 3, 6], 
                [ 0, 4, 8], 
                [ 0, 5, 10], 
                [ 0, 6, 12], 
                [ 0, 7, 14], 
                [ 0, 8, 16]])

在上述inner()方法的示例中,数组的第i行与向量产生标量积,总和成为输出数组的第i个元素,因此输出数组的构造如下:

[0x0 + 1x1 + 2x2, 0x3 + 1x4 + 2x5, 0x6 + 1x7 + 2x8] = [5, 14, 23]

在下面的代码块中,我们对同一数组但具有一维执行outer()方法。 如您所见,结果与我们对二维数组所做的完全相同:

In [11]: a = np.arange(9) 
         np.ndim(a)
Out[11]: 1 
In [12]: np.outer(a,b) 
Out[12]: array([[ 0, 0, 0], 
                [ 0, 1, 2], 
                [ 0, 2, 4], 
                [ 0, 3, 6], 
                [ 0, 4, 8], 
                [ 0, 5, 10], 
                [ 0, 6, 12], 
                [ 0, 7, 14], 
                [ 0, 8, 16]])

outer()方法计算向量的外积,在我们的示例中为 2D 数组。 这不会改变方法的功能,而是将 2D 数组展平为向量并进行计算。

在进行分解之前,本小节介绍的最后一件事是tensordot()方法。 在数据科学项目中,我们通常处理需要进行发现和应用 ML 算法的 n 维数据。 以前,您了解了向量和矩阵。 张量是向量和矩阵的通用数学对象,可以将向量的关系保持在高维空间中。tensordot()方法用于两个张量的收缩; 换句话说,它通过将指定轴上两个张量的乘积相加来减小维数(张量级)。 以下代码块显示了两个数组的tensordot()操作示例:

In [13]: a = np.arange(12).reshape(2,3,2) 
         b = np.arange(48).reshape(3,2,8) 
         c = np.tensordot(a,b, axes =([1,0],[0,1])) 
         print(a) 
         print(b)
Out[13]: 
[[[ 0 1] 
   [ 2 3] 
   [ 4 5]] 

[[ 6 7] 
 [ 8 9] 
 [10 11]]] 
[[[ 0 1 2 3 4 5 6 7] 
  [ 8 9 10 11 12 13 14 15]]

 [[16 17 18 19 20 21 22 23] 
  [24 25 26 27 28 29 30 31]]

 [[32 33 34 35 36 37 38 39] 
  [40 41 42 43 44 45 46 47]]]
In [14]: c
Out[14]: array([[ 800, 830, 860, 890, 920, 950, 980, 1010], 
                [ 920, 956, 992, 1028, 1064, 1100, 1136, 1172]])

什么是特征值,我们如何计算?

特征值是特征向量的系数。 根据定义,特征向量是一个非零向量,在应用线性变换时仅会按标量因子进行更改。 通常,将线性变换应用于向量时,其跨度(穿过其原点的线)会移动,但是某些特殊向量不受这些线性变换的影响,而是保留在自己的跨度上。 这些就是我们所说的特征向量。 线性变换仅在将向量与标量相乘时才通过拉伸或挤压它们来影响它们。 该标量的值称为特征值。 假设我们有一个 A 矩阵,该矩阵将用于线性变换。 我们可以用以下数学表达式表示特征值和特征向量:

在这里, 是特征向量, 是特征值。 在等式的左侧,向量通过矩阵进行变换,结果只是同一向量的标量形式。 另外,请注意,左侧实际上是矩阵向量乘法,而右侧是标量向量乘法。 为了使边的乘法类型相同,我们可以将 与恒等矩阵相乘,这不会改变右侧。 但是将 从标量更改为矩阵,并且两边都是矩阵向量乘法:

如果我们减去右侧并分解为 ,您将具有以下等式:

因此,我们的新矩阵将如下所示:

如您所知,您正在尝试计算0以外的特征向量的特征值; (如果 = 0,则没有意义),因此特征向量()不能为0。 因此,您正在尝试求解以下等式:

前面的公式正在计算矩阵的行列式,因为只有一个条件,即非零矩阵等于0,即矩阵的行列式等于零。 给定矩阵的行列式为零意味着使用该矩阵进行的转换会将所有内容压缩为较小的维度。 在下一部分中,您将详细了解行列式。 此处的真正目的是找到使行列式为零并将空间压缩为较小维度的 。 找到 后,我们可以根据以下公式计算特征向量():

在 ML 算法中,您需要处理大量维度。 主要问题不是规模很大,而是算法与它们的兼容性和性能。 例如,在 PCA主成分分析)中,您尝试发现大小最有意义的线性组合。 PCA 的主要思想是减少数据集的规模,同时最大程度地减少信息丢失。 此处的信息丢失实际上是您特征的差异。 假设您具有五个特征,并且具有五个示例的类标签,如下所示:

特征 1 特征 2 特征 3 特征 4 特征 5 类别
1.45 42 54 1.001 1.05
2 12 34 1.004 24.1
4 54 10 1.004 13.4
1.2 31 1 1.003 42.1
5 4 41 1.003 41.4

在上表中,在特征 4 中,类别标签是还是实际上并没有明显的区别。 在我的分析中,此特征将是多余的。 这里的主要目标是使各个类之间的特征保持很大的差异,因此特征值在我的决定中起着重要的作用。

在以下示例中,我们将使用一个名为scikit-learn的附加库,以便加载数据集并通过使用该库来执行 PCA。 Scikit-learn 是一个免费的 python 机器学习库,它基于 SciPy 构建。 它具有用于许多机器学习算法(例如分类,回归,聚类)的许多内置函数和模块。 SVM,DBSCAN,K 均值等。

Scikit-learn 与 numpy,scipy 和 pandas 具有很好的兼容性。 您可以将sklearn中的 numpy 数组用作数据结构。 此外,您还可以使用sklearn_pandas将 scikit-learn 管道输出转换为 pandas 数据帧。 Scikit-learn 还通过auto_mlauto-sklearn库支持自动化机器学习算法。sklearn是在 python 中键入 scikit-learn 名称的一种方法。 这就是为什么在导入 skicit-learn 和使用函数时使用sklearn的原因。

现在是时候进行实际练习,以了解特征值和特征向量在 PCA 中的用法和重要性。 在下面的代码中,您将 PCA 与 NumPy 一起导入并应用,然后将您的结果与sklearn是用于验证的内置方法进行比较。 您将使用 sklearn 数据集库中的乳腺癌数据集。 让我们先导入所需的库和数据集,然后再对数据进行标准化。 标准化非常重要,有时甚至对于 scikit-learn 之类的某些 ML 库中的估计器来说也是必需的。 在我们的示例中,使用StandardScaler()方法来标准化特征; 主要目的是具有标准正态分布数据中的特征(均值= 0和单位方差)。fit_transform()方法用于转换原始数据,其形式为分布的平均值为 0,标准差为 1。它将计算所需的参数并应用转换。 当我们使用StandardScaler()时,此参数将为 。 请记住,标准化不会从我们的原始数据集中产生正态分布的数据。 只是重新缩放了数据,我们的平均值为零,标准差为 1:

In [15]: import numpy as np 
         from sklearn import decomposition, datasets
         from sklearn.preprocessing import StandardScaler 
         data = datasets.load_breast_cancer() 
         cancer = data.data 
         cancer = StandardScaler().fit_transform(cancer) 
         cancer.shape
Out[15]: (569, 30) 

在前面的代码中检查数据形状时,它显示数据由569行和30列组成。 您也可以在标准化开始和标准化之后比较数据,以便更清楚地了解原始数据是如何转换的。 以下代码块为您展示了癌症数据中列转换的示例:

In [16]: before_transformation = data.data 
         before_transformation[:10,:1]
Out[16]: array([[ 17.99], 
                [ 20.57], 
                [ 19.69], 
                [ 11.42], 
                [ 20.29], 
                [ 12.45], 
                [ 18.25], 
                [ 13.71], 
                [ 13\. ], 
                [ 12.46]])
In [17]: cancer[:10,:1]
Out[17]: array([[ 1.09706398], 
                [ 1.82982061], 
                [ 1.57988811], 
                [-0.76890929], 
                [ 1.75029663], 
                [-0.47637467], 
                [ 1.17090767], 
                [-0.11851678], 
                [-0.32016686], 
                [-0.47353452]])

从结果中可以看到,原始数据已转换为标准化版本的形式。 通过以下公式计算得出:

  • :正在标准化的值(原始数据中的值)
  • :分布的平均值
  • :分布的标准差
  • :标准化值

转换数据后,您将如下计算协方差矩阵,以便使用np.linalg.eig()方法计算特征值和特征向量,然后将其用于分解:

In [18]: covariance_matrix = np.cov(cancer,rowvar=False)
         covariance_matrix.shape
Out[18]: (30, 30)
In [19]: eig_val_cov, eig_vec_cov = np.linalg.eig(covariance_matrix)
         eig_pairs = [(np.abs(eig_val_cov[i]), eig_vec_cov[:,i]) for i in
         range(len(eig_val_cov))]

正如您在前面的代码中已经注意到的那样,我们计算为所有要素构造的协方差矩阵。 由于我们在数据集中具有 30 个特征,因此协方差矩阵是形状为(30, 30)的 2D 数组。 以下代码块按降序对特征值进行排序:

In [20]: sorted_pairs = eig_pairs.sort(key=lambda x: x[0], reverse=True)
         for i in eig_pairs:
             print(i[0])
Out[20]:
13.3049907944 
5.70137460373 
2.82291015501 
1.98412751773 
1.65163324233 
1.2094822398 
0.676408881701 
0.47745625469 
0.417628782108 
0.351310874882 
0.294433153491 
0.261621161366 
0.241782421328 
0.157286149218 
0.0943006956011 
0.0800034044774 
0.0595036135304 
0.0527114222101 
0.049564700213 
0.0312142605531 
0.0300256630904 
0.0274877113389 
0.0243836913546 
0.0180867939843 
0.0155085271344 
0.00819203711761 
0.00691261257918 
0.0015921360012 
0.000750121412719 
0.000133279056663

您需要以降序对特征值进行排序,以确定要降低维度工作空间要删除的特征向量。 正如您在前面的排序列表中所看到的那样,具有较高特征值的前两个特征向量具有有关数据分布的最多信息。 因此,其余的将针对低维子空间删除:

In [21]: matrix_w = np.hstack((eig_pairs[0][1].reshape(30,1), eig_pairs[1]             [1].reshape(30,1)))
         matrix_w.shape 
         transformed = matrix_w.T.dot(cancer.T) 
         transformed = transformed.T 
         transformed[0]
Out[21]: array([ 9.19283683, 1.94858307])
In [22]: transformed.shape
Out[22]: (569, 2)        

在前面的代码块中,前两个特征向量将水平堆叠,因为它们将用于矩阵乘法,以将新维度的数据转换到新的子空间上。 最终数据从(569,30)转换为(569,2)矩阵,这意味着28特征在 PCA 过程中下降了:

In [23]: import numpy as np 
         from sklearn import decomposition 
         from sklearn import datasets 
         from sklearn.preprocessing import StandardScaler 
         pca = decomposition.PCA(n_components=2) 
         x_std = StandardScaler().fit_transform(cancer)
         pca.fit_transform(x_std)[0]
Out[23]: array([ 9.19283683, 1.94858307]) 

另一方面,其他库中有内置函数可以执行与您相同的操作。 在 scikit-learn 中,有许多内置方法可用于 ML 算法。 如您在前面的代码块中看到的,同一 PCA 是通过三行代码和两种方法执行的。 不过,本示例的目的是向您展示特征值和特征向量的重要性,因此,本书展示了使用 NumPy 进行 PCA 的普通方法。

计算范数和行列式

本小节将介绍线性代数中的两个重要值,即范数和行列式。 简而言之,范数给出向量的长度。 最常用的范数是 L2 范数,也称为欧几里得范数。 正式地,Lp 的范数计算如下:

L0 范数实际上是向量的基数。 您可以通过仅计算非零元素的总数来计算它。 例如,向量A = [2,5,9,0]包含三个非零元素,因此||A||[0] = 3。 以下代码块显示了与 numpy 相同的范数计算:

In [24]: import numpy as np 
         x = np.array([2,5,9,0]) 
         np.linalg.norm(x,ord=0)
Out[24]: 3.0 

在 NumPy 中,您可以使用linalg.norm()方法来计算向量的范数。 第一个参数是输入数组,而ord参数用于范数的顺序。 L1 范数也称为出租车范数曼哈顿范数。 它通过计算直线距离来计算向量的长度,因此||A||[1] = (2 + 5 + 9),如此||A||[1] = 16。以下代码块显示了相同的 numpy 范数计算:

In [25]: np.linalg.norm(x,ord=1) 
Out[25]: 16.0 

L1 范数的用途之一是用于计算平均绝对误差MAE),如下式所示:

L2 范数是最受欢迎的范数。 它通过应用勾股定理来计算向量的长度,因此||A||[2] = ,所以||A||[2] = 10.48

In [26]: np.linalg.norm(x,ord=2) 
Out[26]: 10.488088481701515

L2 范数的众所周知的应用之一是均方误差MSE)计算。 类似地,由于矩阵由向量组成,因此范数给出了长度或大小,但解释和计算略有不同。 在前面的章节中,您学习了向量矩阵乘法。 在结果中将矩阵与向量相乘时,将拉伸向量。 矩阵的范数揭示了该矩阵可以拉伸向量的程度。 让我们看看如何为矩阵A计算 L1 和 L∞ 范数的模数:

假设您有一个m x n矩阵。||A||[1]的计算如下:

||A||[1] =

因此结果将如下所示:

||A||[1] = max(3 + |-2| + 1; 7 + |-5| + 3; 6 + 4 + |-14|) = max(6, 15, 24) = 24

让我们使用linalg.norm()方法,用 NumPy 计算同一数组的范数:

In [27]: a = np.array([3,7,6,-2,-5,4,1,3,-14]).reshape(3,3)
In [28]: a
Out[28]: array([[ 3, 7, 6], 
                [ -2, -5, 4], 
                [ 1, 3, -14]])
In [29]: np.linalg.norm(a, ord=1)
Out[29]: 24.0
In [30]: np.linalg.norm(a, np.inf)
Out[30]: 18.0 

在前面的计算中||A||[1]首先按列计算最大元素,并在所有列中给出最大结果,这将是一阶矩阵的范数。 对于 L∞ 元素计算按行执行,并在所有获得无穷范数范数矩阵的行中给出最大值。 计算结果如下:

||A||[∞] = max(3 + 7 + 6; |-2| + |-5| + 4; 1 + 3 + |-14|) = max(16, 11, 18) = 18 

为了验证和使用 NumPy 函数,与我们在向量范数计算中所做的一样,使用linalg.norm()方法进行了相同的计算。 一阶和无限阶的计算比 Lp 范数(其中p > 2)的常规计算相对更直接,例如欧几里德/弗罗宾纽斯范数(其中p = 2)。 下面的公式显示了p范数的形式公式,只需将p替换为 2,就可以为给定数组制定欧几里得/弗罗比纽斯范数:

 

对于p = 2的特殊情况,它变为:

以下代码块显示了数组a的 L2 范数计算:

In [31]: np.linalg.norm(a, ord=2)
Out[31]: 15.832501006406099  

在 NumPy 中,可以通过在linalg.norm()方法中将order参数设置为 2 来计算欧几里德/弗罗比纽斯范数,如您在前面的代码中所见。 在 ML 算法中,特征在特征空间的距离计算中大量使用。 例如,在模式识别中, 范数计算用于 K 最近邻算法(KNN),以便为连续变量或离散变量创建距离度量。 同样,范数对于 K 均值聚类中的距离度量也非常重要。 最受欢迎的顺序是曼哈顿范数和欧几里得范数。

线性代数中的另一个重要概念是计算矩阵的行列式。 根据定义,行列式是线性变换中给定矩阵的比例因子。 在上一节中,当您计算特征值和特征向量时,将给定矩阵乘以特征向量,并假定它们的行列式将为零。 换句话说,您假设将特征向量乘以矩阵后,会将矩阵展平为较低的维度。2 x 23 x 3矩阵的行列式如下:

在 NumPy 中,您可以使用linalg.det()方法来计算矩阵的行列式。 让我们用上式计算一个2 x 23 x 3矩阵行列式的示例,然后用 NumPy 交叉验证我们的结果:

因此计算如下:

对于我们有3 x 3矩阵的情况,它变为:

让我们使用linalg.det()方法,用 NumPy 计算相同数组的行列式:

In [32]: A= np.array([2,3,1,4]).reshape(2,2) 
In [33]: A
Out[33]: array([[2, 3], 
                [1, 4]])
In [34]: np.linalg.det(A)
Out[34]: 5.0
In [35]: B= np.array([2,3,5,1,4,8,5,6,2]).reshape(3,3)
In [36]: B
Out[36]: array([[2, 3, 5], 
                [1, 4, 8], 
                [5, 6, 2]]) 
In [37]: np.linalg.det(B) 
Out[37]: -36.0

转换的决定因素实际上显示了将扩展或压缩多少体积的因素。 如果矩阵的行列式等于2,则意味着此矩阵的变换将使体积增加2。 如果要进行链乘法,则还可以计算变换后音量的变化量。 假设您将两个矩阵相乘,这意味着将有两个转换。 如果det(A)= 2并且det(B)= 3,则总转换因子将乘以 6 作为det(AB) = det(A)det(B)

最后,本章将为您的 ML 模型介绍一个非常有用的值,该值称为迹。 根据定义,迹线是矩阵对角线元素的总和。 在 ML 模型中,大多数情况下,您将使用几种回归模型来解释数据。 这些模型中的某些很有可能在某种程度上解释了您的数据质量,因此在这种情况下,您总是倾向于使用更简单的模型。 每当您必须进行权衡时,跟踪值对于量化复杂性就变得非常有价值。 以下代码块显示了使用 numpy 进行 2D 和 3D 矩阵的跟踪计算:

In [38]: a = np.arange(9).reshape(3,3)
In [39]: a
Out[39]: array([[0, 1, 2], 
                [3, 4, 5], 
                [6, 7, 8]])
In [40]: np.trace(a) 
Out[40]: 12
In [41]: b = np.arange(27).reshape(3,3,3) 
In [42]: b
Out[42]: array([[[ 0, 1, 2], 
                 [ 3, 4, 5], 
                 [ 6, 7, 8]], 

                [[ 9, 10, 11], 
                 [12, 13, 14], 
                 [15, 16, 17]], 

                [[18, 19, 20], 
                 [21, 22, 23], 
                 [24, 25, 26]]])

In [43]: np.trace(b)
Out[43]: array([36, 39, 42])

在 NumPy 中,可以使用trace()方法来计算矩阵的跟踪值。 如果您的矩阵是二维的,那么轨迹就是对角线的总和。 如果矩阵的维数大于2,则迹线是沿对角线的和数组。 在前面的示例中,矩阵B具有三个维度,因此跟踪数组的构造如下:

在本小节中,您学习了如何在 ML 算法中计算范数,行列式,跟踪和用法。 主要目的是学习这些概念并熟悉 NumPy 线性代数库。

求解线性方程

在本节中,您将学习如何使用linalg.solve()方法求解线性方程。 当您有线性方程要求解时,例如形式 ,在简单情况下,您只需计算A的反函数即可。然后将其乘以B即可得到解,但是当A具有高维数时,这使得计算起来很难进行计算A的倒数。 让我们从具有三个未知数的三个线性方程式的示例开始,如下所示:

因此,这些方程式可以用矩阵形式化如下:

然后,我们的问题是解决 。 我们可以不使用linalg.solve(),而使用普通 NumPy 计算解决方案。颠倒A矩阵后,您将与B相乘以获得x的结果。在下面的代码块中,我们计算AB的逆矩阵的点积,以便计算

In [44]: A = np.array([[2, 1, 2], [3, 2, 1], [0, 1, 1]])
         A
Out[44]: array([[2, 1, 2], 
                [3, 2, 1], 
                [0, 1, 1]])
In [45]: B = np.array([8,3,4])
         B 
Out[45]: array([8, 3, 4])
In [46]: A_inv = np.linalg.inv(A)
         A_inv
Out[46]: array([[ 0.2, 0.2, -0.6], 
                [-0.6, 0.4, 0.8], 
                [ 0.6, -0.4, 0.2]])
In [47]: np.dot(A_inv,B)
Out[47]: array([-0.2, -0.4, 4.4])

最后,得到a = -0.2b = -0.4c = 4.4的结果。 现在,让我们使用linalg.solve()执行相同的计算,如下所示:

In [48]: A = np.array([[2, 1, 2], [3, 2, 1], [0, 1, 1]]) 
         B = np.array([8,3,4]) 
         x = np.linalg.solve(A, B) 
         x
Out[48]: array([-0.2, -0.4, 4.4]) 

为了检查我们的结果,我们可以使用allclose()函数,该函数用于按元素比较两个数组:

In [49]: np.allclose(np.dot(A, x), B)
Out[49]: True

linalg.lstsq()方法是求解线性方程组的另一个重要函数,它返回最小二乘解。 此函数将返回回归线的参数。 回归线的主要思想是最小化每个数据点到回归线的距离平方和。 距离的平方和实际上量化了回归线的总误差。 距离越大,误差越大。 因此,我们正在寻找可以最大程度减少此误差的参数。 为了可视化我们的线性回归模型,我们将使用一个非常流行的 Python 二维绘图库matplotlib。 以下代码块在矩阵中运行最小二乘解,并返回权重和偏差:

In [50]: from numpy import arange,array,ones,linalg
         from pylab import plot,show
         x = np.arange(1,11) 
         A = np.vstack([x, np.ones(len(x))]).T
         A
Out[50]: array([[ 1., 1.], 
                [ 2., 1.], 
                [ 3., 1.], 
                [ 4., 1.], 
                [ 5., 1.], 
                [ 6., 1.], 
                [ 7., 1.], 
                [ 8., 1.], 
                [ 9., 1.], 
                [ 10., 1.]]) 
In [51]: y = [5, 6, 6.5, 7, 8,9.5, 10, 10.4,13.1,15.5] 
         w = linalg.lstsq(A,y)[0] 
         w
Out[51]: array([ 1.05575758, 3.29333333])
In [52]: line = w[0]*x+w[1] 
         plot(x,line,'r-',x,y,'o') 
         show()

前面代码的输出如下:

上图给出了适合我们数据的回归线拟合结果。 该模型生成可用于预测或预测的线性线。 尽管线性回归有很多假设(恒定方差,误差的独立性,线性等),但它仍然是建模数据点之间线性关系的最常用方法。

计算梯度

当您有一条直线时,您可以使用导数,以便导数显示该线的斜率。 当函数中有多个变量时,梯度是导数的泛化,因此,梯度的结果实际上是向量函数,而不是导数中的标量值。 ML 的主要目标实际上是找到适合您数据的最佳模型。 您可以通过将损失函数或目标函数最小化来求值最佳均值。 梯度用于查找系数或函数,以最大程度地减少损失或成本函数。 查找最佳点的一种众所周知的方法是采用目标函数的导数,然后将其设置为零以找到模型系数。 如果系数不止一个,则它将成为梯度而不是导数,并且将成为向量方程式而不是标量值。 您可以在每个点上将梯度解释为向量,该梯度将指向函数的下一个局部最小值。 有一种非常流行的优化技术,可以通过计算每个点的梯度并沿该方向移动系数来寻找函数的最小值,以寻求最小值。 该方法称为梯度下降。 在 NumPy 中,gradient()函数用于计算数组的梯度:

In [53]: a = np.array([1,3, 6, 7, 11, 14]) 
         gr = np.gradient(a) 
         gr
Out[53]: array([ 2\. , 2.5, 2\. , 2.5, 3.5, 3\. ])

让我们看看如何计算此梯度:

gr[0] = (a[1]-a[0])/1 = (3-1)/1 = 2
gr[1] = (a[2]-a[0])/2 = (6-1)/2 = 2.5
gr[2] = (a[3]-a[1])/2 = (7-3)/2 = 2
gr[3] = (a[4]-a[2])/2 = (11-6)/2 = 2.5
gr[4] = (a[5]-a[3])/2 = (14-7)/2 = 3.5
gr[5] = (a[5]-a[4])/1 = (14-11)/1 = 3

在前面的代码块中,我们计算了一维数组的梯度。 让我们添加另一个维度,并查看计算如何更改,如下所示:

[54]: a = np.array([1,3, 6, 7, 11, 14]). reshape(2,3) 
         gr = np.gradient(a) 
         gr
Out[54]: [array([[ 6., 8., 8.], 
                 [ 6., 8., 8.]]), array([[ 2\. , 2.5, 3\. ], 
                 [ 4\. , 3.5, 3\. ]])]

对于二维数组,与前面的代码一样,按列和按行计算梯度。 因此,结果将是两个数组的返回。 第一个数组代表行方向,第二个数组代表列方向。

总结

在本章中,我们介绍了线性代数的向量和矩阵运算。 我们研究了高级矩阵运算,尤其是特征点运算。 您还了解了的特征值和特征向量,然后在主成分分析PCA)中实践了它们的用法。 此外,我们介绍了范数和行列式计算,并提到了它们在 ML 中的重要性和用法。 在最后两个小节中,您学习了如何将线性方程式转换为矩阵并求解它们,并研究了梯度的计算和重要性。

在下一章中,我们将使用 NumPy 统计数据进行解释性数据分析,以探索 2015 年美国住房数据。