全文共3796字,预计学习时长7分钟
要点:本文使用了自上而下的方式来解释用于深度学习的线性代数。首先介绍线性代数的应用和使用范围,然后自上而下分析其概念。
线性代数在维基百科中的定义:线性代数是数学的一个分支,它的研究对象是线性方程组、线性函数、以及它们在矩阵和矢量空间中的表现形式。
导言
如果你开始学习深度学习,首先要接触到的是前馈神经网络,它是在深度学习中最简单也是非常有用的网络。在引擎盖下,前馈神经网络只是一个复合函数,此函数将一些矩阵和向量相乘。这并不表示向量和矩阵是执行这些操作的唯一方法,但却是非常有效的方法。
一个向前传播信息的简单前馈神经网络。
此图是一个典型的神经网络图,但计算机将怎样识别它呢?在计算机中,神经网络层用向量表示。假设输入层为X, 隐蔽层为H, 输出层目前不予考虑(因为这里不涉及前馈神经网络的计算过程)。因此,它的向量和矩阵方程式为:
上图显示了计算上述神经网络图的第一层和唯一隐藏层的输出所需的操作(输出层的计算未显示)。接下来让我们一起将其分解吧。
网络结构的每一列都可以表示为向量。向量是动态数组,是数据(或特征)的集合。在当前的神经网络中,向量“x”保存输入。我们不强制将输入表示为向量,但这样表示会更方便,且利于同时执行操作。深度学习,尤其是神经网络的学习,显而易见代价是昂贵的,所以需要利用这个技巧来增加其计算速度。此过程被称为向量化,向量化使计算速度变得非常快。这是GPU用于深度学习的主要原因之一,因为它们专门用于像矩阵乘法这样的向量化操作(我们将在文章最后深入分析这一点)。
隐藏层H的输出通过执行H=f(W.x+b)计算出。
这里W称为权重矩阵,b称为偏差,f是激活函数。
下面分解此方程:
第一个分量是W.x;这是一个矩阵向量积,因为W是一个矩阵,x是一个向量。在将它们相乘之前,让我们先了解一下这些符号的使用:向量通常由小粗体斜体字母(如x)表示,而矩阵则由大写字母(如X)表示,如果一个字母是大写字母和粗体但不是斜体,它就是张量(如X)。
从计算机科学的角度看:
标量:一个数字。
向量:值的列表。(阶层1张量)
矩阵:二维数值表。(阶层2张量)
张量:等级为n的多维矩阵。
自上而下分解:
数学视角下的向量和矩阵
向量:
向量是既具有大小又具有方向的量。它是存在于空间中的实体,如果它是存在于真实空间中的二维向量,则它的存在用x∈ ℝ²表示。(每个元素表示沿不同轴的坐标。)
二维空间中的所有向量都可以通过称为基向量的两个向量的线性组合来获得。(用i和j表示)(一般来说,N维向量可以用N个基向量来表示。)它们是单位向量,因为它们的长度为一,并且互相垂直。这两个向量中的任何一个都不能用另一个表示,所以它们被称为线性独立向量(如果任何向量都不能通过一组向量的线性组合来获得,则称该向量与该集合是线性独立的)。这两个向量通过线性组合得到的2D空间中的所有点集都称为这些向量的跨度。如果一个向量由其他向量集的线性组合(加法、乘法)表示,那么它线性依赖于该向量集(将新向量添加到现有集合中是没有用的。)任何两个向量都可以相加、相乘,且它们的乘法有两种类型,分别是点积和交叉积。
矩阵:
矩阵是一个二维数组,表示转换。在变换应用后的2D空间中,2*2矩阵的每一列都表示每个2基向量,它们的空间表示是W ∈ ℝ³*²,具有3行2列。
矩阵向量乘积称为该向量的变换,而矩阵矩阵乘积称为变换的组合。
只有一个矩阵对向量不作任何变换,那就是恒等矩阵(I)。I的列表示基向量。
矩阵A的行列式用det(A)表示,指由矩阵描述的线性变换的缩放因子。
为什么数学观点对深度学习研究者很重要?因为它们帮助我们理解基本对象的基本设计概念。它们也有助于为深度学习问题设计创造性的解决方案。但我们大可不必担心,因为有许多语言和软件包会帮助我们,但我们最好还是了解一下它们的执行方式。
对于python编程语言来说,这样的库是微不足道的。
有许多用于Numpy学习的资源(如果你使用python深度学习,这是至关重要的。)
这里,np数组生成了一个numpy数组。
np.random 是指一个软件包,包含用于生成随机数的方法。
点方法用于计算矩阵之间的乘积。
我们可以改变numpy数组的形状并检查它。
这里可以看到,W.x的乘积是向量,它与b相加,b是标量。之后自动将b扩展为其变位([1,1])。把b隐式复制到几个位置的行为,称为广播。
注意到变位这个词了吗:矩阵的变位指的是矩阵在对角线上的镜像(从矩阵的左上角到右下角)。
## numpy code for transpos
import numpy as np
A = np.array([[1,2],
[3,4],
[5,6]]
B = np.transpose(A)
##or
B = A.T
矩阵的类型
对角矩阵:除了主要的对角元素外,所有的元素都是零。
单位矩阵:对角值为1的对角矩阵。
## numpy code to create identity matrix
import numpy as np
a = np.eye(4)
对称矩阵:等于其变位矩阵。A=变位(A)
单数矩阵:行列式为零且列线性相关的矩阵。它们的阶层小于矩阵的行或列数。
矩阵的分解
矩阵分解是指将一个矩阵分解为若干矩阵。矩阵分解有不同的类型,每种矩阵分解都用在特定的一类问题中。最广泛使用的一种矩阵分解称为特征分解,其中我们把矩阵分解成一组特征向量和特征值。
一个正方形矩阵A的特征向量是非零向量v,因此特征向量乘以A仅改变v的比例。
A .v = λ .v
这里v是本征向量,λ是本征值。
## numpy program to find eigen vectors.
from numpy import array
from numpy.linalg import eig
# define matrix
A = array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(A)
# calculate eigendecomposition
values, vectors = eig(A)
print(values)
print(vectors)
特征分解在机器学习中非常有用,特别是对于像降维这样的概念。
过拟合与欠拟合:
听关于深度学习讲座时,经常听到过拟合和欠拟合这类术语。这些术语用于描述深度学习模型的准确度。
这张图像是对过拟合和欠拟合的最佳解释。
过拟合是一种对训练数据掌握十分严格的模型。从字面意思理解,过拟合抢占了训练数据,因而在过拟合模型中,训练精度很高,但验证精度却很低。
欠拟合模型不能很好地掌握训练数据,在欠拟合模型中,训练精度和验证精度都很低。
过拟合和欠拟合都会导致模型性能变差。但是到目前为止,应用机器学习中最常见的问题是过拟合。
为了减少过拟合,必须使用一种叫做正则化的技术。它防止了训练数据被抢占,从而避免了过拟合的风险。
深度学习工程师最重要的职责是创建适合于输入的模型。正则化方法有多种,最常见的是L1正则化(Lasso)和L2正则化(Ridge)。
范数
针对上面这些问题,我们没有提供详细说明,想要理解这些,必须知道什么是范数。
范数指的是向量的大小或长度。
一个向量x的范数的一般公式可表示为:
在L²范数中,当p=2,则此范数被称为欧几里得范数,因为它表示的是原点与x之间的欧几里德距离。
L¹范数是向量中所有元素的总和。当系统要求更高精度时,它被运用于机器学习,用于清晰地区分零元素和非零元素。L¹范数也称为曼哈顿范数。
此外还有最大范数,它是具有最大值的元素的绝对值。
当L²范数值等于一个矩阵的大小时,此范数被称为矩阵范数。
矩阵范数
范数不仅用于正则化过程,也用于优化过程。
矢量化
在了解这些概念和理论之后,接下来讨论深度学习最重要的部分。它们是矢量化和广播。
矢量化指的是通过将数据作为向量,以减少循环执行,并使执行过程并行的一种技巧。
许多中央处理器(CPU)都有“向量”或“SIMD”(单指令多数据)指令集,这些指令集同时对两段、四段或多段数据执行相同的操作。上世纪90年代初,SIMD在通用CPU上流行起来。
矢量化指的是重写循环的一个过程。通过矢量化,重写循环过程不用对数组的单个元素进行N次处理,而能够同时对(例如)数组的4个元素进行N/4次处理。
Numpy已经在算法中大量实现了向量化。
向量化描述了在代码中没有任何显式的循环、索引等过程,当然,这些过程只是在优化的、预先编译的C代码中的“幕后”进行着。
矢量化码有许多优点,其中包括:
向量化代码更简洁,更易于阅读
更少的代码行通常意味着会产生更少的问题
代码更接近于标准数学符号(通常使正确编码数学构造更简易)
向量化能生成更多的“Pythonic”代码。如果没有向量化,我们的代码将乱七八糟,处理的效率低下,并且难以读取循环过程。
代码示例:
## to add two arrays together.
## consider two basic python lists.
a = [1,2,3,4,5]
b = [2,3,4,5,6]
c = []
## without vectorization.
for i in range(len(a)):
c.append(a[i]+b[i])
## using vectorization.
a = np.array([1,2,3,4,5])
b = np.array([2,3,4,5,6])
c = a+b
上面的代码示例是过度简化的向量化示例。当输入数据变大时,向量化过程才能真正显现出来。
广播
下一个重要的概念是广播。杰里米·霍华德曾在他的一场机器学习讲座中说道,广播可能是机器学习程序员最重要的工具和技能。
下面内容来自Numpy 提供的文件:
术语广播描述了numpy如何处理在算术运算中具有不同的形状的数组。由于受到某种约束,较小的数组跨越较大的数组进行“广播”,以便形成兼容的形状。广播提供了一种对数组操作进行向量化的方法,使得循环在C语言中而不是在Python语言中发生。在这过程中不需要复制不必要的数据,并且通常能实现高效的算法过程。
代码示例:
## to add two arrays together.
## consider two basic python lists.
a = np.array([1.0, 2.0, 3.0])
b = 2.0
a * b
array([ 2., 4., 6.])
this is similar to
a = np.array([1.0, 2.0, 3.0])
b = np.array([2.0, 2.0, 2.0])
a * b
array([ 2., 4., 6.])
数组b被扩展,以便执行算术运算过程。
广播并不是一个新概念,而是一种相对古老的工具,可以追溯到50年代。肯尼斯·艾弗森在他的论文《作为思维工具的符号》中描述了几种数学工具,这些工具赋予我们新的思考视角。最初他提到广播,不是指一种计算机算法,而是一个数学过程。他在一个叫做APL的软件中将这些工具进行应用。
他的儿子后来扩展了他的想法,并创建了另一种软件,叫做J软件。这意味着,J软件是在50多年的深入研究后才得到的成果,利用这一软件,我们可以在一小段代码中实现非常复杂的数学函数的编写。
这些研究还为我们今天使用的语言(如python和numpy)找到了一条方便的途径。
所以请记住,这些并不是一夜之间突然出现的不成熟的想法。这些都是基于数学以及它在软件中的实现的基本方法的思考而得出的结果。
留言 点赞 发个朋友圈
我们一起分享AI学习与发展的干货
编译组:王玲、赵璇
领取 专属20元代金券
Get大咖技术交流圈