引言 \n
线性代数是数据科学中必备的基础数学工具。理解这些概念有助于加深您对各类算法的认识。它确实是数据科学/机器学习路径上的优秀基石。
\n
本教程的目标是通过 Python/Numpy 编码进入数据科学所需的数学世界。我认为,将线性代数这样的理论主题做成实践性教程非常有用,因为编写和阅读代码是真正理解数学概念的好方式。最重要的是,这也会很有趣!
\n
本教程没有特别的前置要求,但如果您还不清楚什么是矩阵或如何做点积,可以先阅读我关于 Ian Goodfellow 深度学习一书的系列 的前几篇(1 到 4)。
\n
在本教程中,我们将讨论机器学习和深度学习中的一个重要概念:范数。范数被广泛使用,例如用于评估模型的好坏。读完本教程,您有望对这一概念及其在机器学习中的价值有更直观的认识。我们还将看到范数的导数如何用于训练机器学习算法。
\n
基础设置 \n
首先,我们来设置将要使用的库:
\n
import numpy as np\nimport matplotlib. pyplot as plt\nimport seaborn as sns\n技术支持 \n
并为命令 bs(粗体符号)和 norm(范数符号)添加一些 LaTeX 快捷方式:
\n
$$\n\\newcommand\\bs[ 1 ] { \\boldsymbol{ #1 } } \n\\newcommand\\norm[ 1 ] { \\left\\lVert#1 \\right\\rVert} \n$$\n技术支持 \n
$$ \\newcommand\\bs[1]{\\boldsymbol{#1}} \\newcommand\\norm[1]{\\left\\lVert#1\\right\\rVert} $$
\n
最后一项是设置我们将创建的图形外观:
\n
技术支持 \n
Populating the interactive namespace from numpy and matplotlib\n技术支持 \n
一个实际示例 \n
我们从一个简单的例子开始。设想您有一个歌曲数据集,包含不同特征。现在假设您想要构建一个模型,根据音乐流派、配器等其他特征来预测歌曲的时长。您训练了一个模型,并希望评估它对新歌曲时长的预测效果。一种方法是取一些新数据,用您的模型预测歌曲时长。由于对于这些观测,您知道每首歌的真实时长,便可以将真实时长与预测时长逐一比较。对于 7 条观测,您得到以下以秒为单位的结果:
\n
errorModel1 = [ 22 , - 4 , 2 , 7 , 6 , - 3 , 12 ] \n技术支持 \n
这些差值可以看作模型的误差。完美的模型只有 0,而很差的模型会有很大的正或负值。
\n
现在想象您尝试了另一个模型,得到的预测与真实时长之间的差值如下:
\n
errorModel2 = [ 14 , 9 , - 13 , 19 , 8 , - 21 , 4 ] \n技术支持 \n
如果要找出最优模型,可以怎么做?一种自然的方法是对这些误差的绝对值求和。之所以取绝对值,是因为负误差(真实时长小于预测时长)同样是误差。总误差越小的模型越好:
\n
totalErrorModel1 = np. sum ( np. abs ( errorModel1) ) \ntotalErrorModel1\n技术支持 \n
56 \n技术支持 \n
totalErrorModel2 = np. sum ( np. abs ( errorModel2) ) \ntotalErrorModel2\n技术支持 \n
88 \n技术支持 \n
看起来模型 1 明显优于模型 2。
\n
恭喜!您刚刚为每个模型计算了误差向量的范数!
\n
直觉 \n
您可以将范数理解为向量的长度 。为便于图形化理解,我们再次使用前面的例子。误差向量是多维的:每条观测对应一个维度。在上一个例子中,7 条观测对应 7 个维度。表示 7 维依然很难,因此我们再简化,只保留 2 条观测:
\n
errorModel1 = [ 22 , - 4 ] \nerrorModel2 = [ 14 , 9 ] \n技术支持 \n
现在我们可以把数组的第一个元素看作 x 坐标,第二个元素看作 y 坐标来表示这些向量。我们先写一个函数,方便绘制向量并直观看到它们的表示。
\n
如何用 Python 和 Matplotlib 绘制向量 \n
我们需要一个函数来帮助绘制向量。先从使用方式入手。我们希望传入一个数组列表,对应各向量的坐标,并得到这些向量的图。我们也希望可以传入颜色数组,以便在图中区分不同向量。总结一下,我们希望这样调用函数:
\n
plotVectors( [ vector1, vector2] , [ 'red' , 'blue' ] ) \n技术支持 \n
让我们来编写这个函数:
\n
def plotVectors ( vecs, cols, alpha= 1 ) : \n \"\"\"\n Plot set of vectors. \n\n Parameters\n - - - - - - - - - - \n vecs : array- like\n Coordinates of the vectors to plot. Each vector is in an array. For\n instance: [ [ 1 , 3 ] , [ 2 , 2 ] ] can be used to plot 2 vectors. \n cols : array- like\n Colors of the vectors. For instance: [ 'red' , 'blue' ] will display the\n first vector in red and the second in blue. \n alpha : float \n Opacity of vectors\n\n Returns: \n\n fig : instance of matplotlib. figure. Figure\n The figure of the vectors\n \"\"\"\n plt. axvline( x= 0 , color= '#A9A9A9' , zorder= 0 ) \n plt. axhline( y= 0 , color= '#A9A9A9' , zorder= 0 ) \n\n for i in range ( len ( vecs) ) : \n if ( isinstance ( alpha, list ) ) : \n alpha_i = alpha[ i] \n else : \n alpha_i = alpha\n if ( len ( vecs[ i] ) == 2 ) : \n x = np. concatenate( [ [ 0 , 0 ] , vecs[ i] ] ) \n elif ( len ( vecs[ i] ) == 4 ) : \n x = vecs[ i] \n plt. quiver( [ x[ 0 ] ] , \n [ x[ 1 ] ] , \n [ x[ 2 ] ] , \n [ x[ 3 ] ] , \n angles= 'xy' , scale_units= 'xy' , scale= 1 , color= cols[ i] , \n alpha= alpha_i) \n技术支持 \n
该函数接收要绘制的向量数组(vecs)和对应颜色(cols)作为输入。若向量仅给出 2 个维度,则从 (0, 0) 出发。底层通过迭代向量数组并使用 plt.quiver() 进行绘制。
\n
用新函数绘制模型 1 和模型 2 的误差:
\n
plotVectors( [ errorModel1, errorModel2] , [ sns. color_palette( ) [ 0 ] , sns. color_palette( ) [ 1 ] ] ) \n\nplt. xlim( - 1 , 25 ) \nplt. ylim( - 5 , 10 ) \nplt. show( ) \n技术支持 \n
\n
说明:我们没有把 plt.show() 写进函数里,以便像这里这样添加图形配置(比如坐标范围)。
\n
说明 2:这里用 sns.color_palette() 手动取了 seaborn 的颜色。
\n
因此我们对每个模型都有一个误差向量。更好的模型对应更短的向量。计算向量长度的一种方式是用勾股定理:$\\sqrt{x^2+y^2}$。我们来计算两个向量的长度:
\n
技术支持 \n
22.360679774997898 \n技术支持 \n
技术支持 \n
16.643316977093239 \n技术支持 \n
恭喜!您又计算了一种误差向量的范数。第一个模型的误差向量长度为 $22.36$,第二个模型的误差向量长度约为 $16.64$。在这个只用前两个值的情形下,第二个模型更好。
\n
我们一开始计算的是误差之和,但也可以用勾股定理计算向量的范数。这是两种不同的范数,说明计算范数的方法不止一种。
\n
范数函数:定义 \n
向量的范数可以是任何将向量映射到正值的函数。可用的函数有多种,下面我们看几个例子。若满足以下性质 ,这些函数即可称为范数:
\n
\n
范数通常用两条竖线表示:$\\norm{\\bs{x}}$
\n
三角不等式 \n
上面提到,一个函数要成为范数,需要满足三角不等式。这意味着若干向量之和的范数,小于等于这些向量范数之和:
\n
$$ \\norm{\\bs{u}+\\bs{v}} \\leq \\norm{\\bs{u}}+\\norm{\\bs{v}} $$
\n
示例 1. \n
为说明其含义,我们取两个各含两个元素的向量(便于表示为 x 和 y 坐标)。我们的向量是:
\n
$$ \\bs{u}= \\begin{bmatrix} 1 & 6 \\end{bmatrix} $$
\n
u = np. array( [ 1 , 6 ] ) \nu\n技术支持 \n
array ( [ 1 , 6 ] ) \n技术支持 \n
以及
\n
$$ \\bs{v}= \\begin{bmatrix} 4 & 2 \\end{bmatrix} $$
\n
v = np. array( [ 4 , 2 ] ) \nv\n技术支持 \n
array ( [ 4 , 2 ] ) \n技术支持 \n
我们来比较:
\n
$$ \\norm{\\bs{u}+\\bs{v}} $$
\n
以及:
\n
$$ \\norm{\\bs{u}}+\\norm{\\bs{v}} $$
\n
稍后我们会介绍不同类型的范数,这里先用经典的欧几里得范数($L^2$)。$L^2$ 范数可以用 Numpy 函数 np.linalg.norm() 计算(参见文档 )。
\n
$$ \\norm{\\bs{u}+\\bs{v}} = \\sqrt{(1+4)^2+(6+2)^2} = \\sqrt{89} \\approx 9.43 $$
\n
我们稍后会更详细地讨论 $L^1$ 和 $L^2$ 范数。
\n
np. linalg. norm( u+ v) \n技术支持 \n
9.4339811320566032 \n技术支持 \n
以及
\n
$$ \\norm{\\bs{u}}+\\norm{\\bs{v}} = \\sqrt{1^2+6^2}+\\sqrt{4^2+2^2} = \\sqrt{37}+\\sqrt{20} \\approx 10.55 $$
\n
np. linalg. norm( u) + np. linalg. norm( v) \n技术支持 \n
10.554898485297798 \n技术支持 \n
可见三角不等式被满足:
\n
$$ \\norm{\\bs{u}+\\bs{v}} \\leq \\norm{\\bs{u}}+\\norm{\\bs{v}} $$
\n
图形解释 \n
您会发现该定理的图形表示非常直观。我们用 plotVectors 函数绘制向量 $\\bs{u}$、$\\bs{v}$ 和 $\\bs{u}+\\bs{v}$,并添加文字标注:
\n
u = np. array( [ 0 , 0 , 1 , 6 ] ) \nv = np. array( [ 0 , 0 , 4 , 2 ] ) \nw = u+ v\n\nu_bis = [ u[ 2 ] , u[ 3 ] , v[ 2 ] , v[ 3 ] ] \n\nplotVectors( [ u, u_bis, w] , \n [ sns. color_palette( ) [ 0 ] , \n sns. color_palette( ) [ 1 ] , \n sns. color_palette( ) [ 2 ] ] ) \n\nplt. xlim( - 2 , 6 ) \nplt. ylim( - 2 , 9 ) \n\nplt. text( - 1 , 3.5 , r'$||\\vec{u}||$' , color= sns. color_palette( ) [ 0 ] , size= 20 ) \nplt. text( 2.5 , 7.5 , r'$||\\vec{v}||$' , color= sns. color_palette( ) [ 1 ] , size= 20 ) \nplt. text( 2 , 2 , r'$||\\vec{u}+\\vec{v}||$' , color= sns. color_palette( ) [ 2 ] , size= 20 ) \n\nplt. show( ) \nplt. close( ) \n技术支持 \n
\n
$\\bs{u}$ 的长度加上 $\\bs{v}$ 的长度大于向量 $\\bs{u}+\\bs{b}$ 的长度。从几何上,这只是说明两点之间的最短路径是直线!
\n
p-范数:一般规则 \n
我们已经看到,能称为范数的函数需要满足一定条件。这意味着可以有多种函数作为范数。稍后我们会看到不同范数的利弊。以下依赖于 $p$ 的函数类被称为 $p$-范数:
\n
$$ \\norm{\\bs{x}}_p=(\\sum_i|\\bs{x}_i|^p)^{1/p} $$
\n
我们一步步解析这个公式。可以看到这里有对元素的求和,因此可以把它理解为对第 $i$ 个元素的迭代:
\n
\n$\\vert\\bs{x}_i\\vert$ 取第 $i$ 个元素的绝对值 \n$\\vert\\bs{x}_i\\vert^p$ 将其以 $p$ 次幂 \n$\\sum_i\\vert\\bs{x}_i\\vert^p$ 将所有这些幂后的绝对值求和 \n$(\\sum_i\\vert\\bs{x}_i\\vert^p)^{1/p}$ 对结果取 $\\frac{1}{p}$ 次幂 \n \n
配合下面这些常用的 $p$-范数示例会更清楚。
\n
$L^0$ 范数 \n
当 $p=0$ 时,公式变为:
\n
$$ \\norm{\\bs{x}}_0=(\\sum_i|\\bs{x}_i|^0)^{1/0} $$
\n
看看这意味着什么。对绝对值取 0 次幂,对于所有非零值得到 $1$,对 $0$ 得到 $0$。
\n
因此该“范数”对应向量中非零元素的个数。它严格来说并不是范数,因为如果将向量乘以 $\\alpha$,这个数量不变(违背上面的规则 4)。
\n
$L^1$ 范数 \n
当 $p=1$ 时,就是绝对值之和。这正是我们在教程开头凭直觉使用的方法:
\n
\n
$L^2$ 范数(欧几里得范数) \n
欧几里得范数是 $p=2$ 的 $p$-范数。这可能是最常用的范数之一,与平方的 $L^2$ 范数(见下文)并列。
\n
$$ \\norm{\\bs{x}}_2=(\\sum_i \\bs{x}_i^2)^{1/2}=\\sqrt{\\sum_i \\bs{x}_i^2} $$
\n
注意这里不再需要绝对值,因为 $x$ 被平方了。这正是我们上面用勾股定理计算向量长度时用到的。
\n
再看一个该范数的例子:
\n
示例 2. \n
从图形上看,欧几里得范数对应从原点到由线性组合得到的点的向量长度(勾股定理)。我们看一个二维例子:向量 $\\bs{u}$ 有两个值,分别对应 $x$ 坐标和 $y$ 坐标。若绘制出该点并从原点画到该点的向量,$L^2$ 范数就是这个向量的长度。
\n
例如:
\n
$$ \\bs{u}= \\begin{bmatrix} 3 \\ 4 \\end{bmatrix} $$
\n
先用公式计算范数:
\n
\n
因此 $L^2$ 范数为 $5$。
\n
顺便提醒,$L^2$ 范数可以用 Numpy 的 linalg.norm() 计算:
\n
np. linalg. norm( [ 3 , 4 ] ) \n技术支持 \n
5.0 \n技术支持 \n
下面是该向量的图形表示:
\n
u = np. array( [ 3 , 4 ] ) \n\nplt. ylim( - 1 , 5 ) \nplt. xlim( - 1 , 5 ) \nplotVectors( [ u] , [ sns. color_palette( ) [ 0 ] ] ) \n技术支持 \n
\n
可以看到向量从原点 (0, 0) 指向 (3, 4),长度为 5。
\n
此处向量在二维空间,但更高维也同样成立。
\n
$$ \\bs{u}= \\begin{bmatrix} u_1\\ u_2\\ \\cdots \\ u_n \\end{bmatrix} $$
\n
$$ \\norm{\\bs{u}}_2 = \\sqrt{u_1^2+u_2^2+\\cdots+u_n^2} $$
\n
平方欧几里得范数(平方 $L^2$ 范数) \n
$$ \\norm{\\bs{u}}_2^2 = (\\sqrt{\\sum_i \\bs{x}_i^2})^2 = \\sum_i\\bs{x}_i^2 $$
\n
平方的 $L^2$ 范数很方便,因为去掉了平方根,得到的就是向量各分量平方的简单求和。
\n
平方欧几里得范数在机器学习中被广泛使用,部分原因是它可用向量运算 $\\bs{x}^\\text{T}\\bs{x}$ 计算。由于优化带来的性能收益,见这里 和这里 。
\n
示例 3. \n
本例将看到平方欧几里得范数如何用向量化操作计算。先给定一个向量 $\\bs{x}$:
\n
$$ \\bs{x}= \\begin{bmatrix} 2 \\ 5 \\ 3 \\ 3 \\end{bmatrix} $$
\n
照例用代码验证过程。先创建 Numpy 向量 $\\bs{x}$:
\n
x = np. array( [ [ 2 ] , [ 5 ] , [ 3 ] , [ 3 ] ] ) \nx\n技术支持 \n
array ( [ [ 2 ] , \n [ 5 ] , \n [ 3 ] , \n [ 3 ] ] ) \n技术支持 \n
现在对该向量转置。这只是把原本的列向量变为行向量:
\n
$$ \\bs{x}^\\text{T}= \\begin{bmatrix} 2 & 5 & 3 & 3 \\end{bmatrix} $$
\n
我们可以用 Numpy 对象的 T 属性计算 $\\bs{x}$ 的转置:
\n
x. T\n技术支持 \n
array ( [ [ 2 , 5 , 3 , 3 ] ] ) \n技术支持 \n
$\\bs{x}$ 与 $\\bs{x}^\\text{T}$ 的点积(如果需要回顾点积请看这里 )实际上对应每个元素与自身相乘:
\n
\n
这正是平方欧几里得范数的定义!
\n
我们用 Numpy 来验证。记住(也请亲自试试以加深理解)点积中向量的顺序是有影响的:
\n
euclideanNorm = x. T. dot( x) \neuclideanNorm\n技术支持 \n
array ( [ [ 47 ] ] ) \n技术支持 \n
这应该就是我们的平方欧几里得范数!我们从 $L^2$ 范数出发再平方来验证:
\n
np. linalg. norm( x) ** 2 \n技术支持 \n
47.0 \n技术支持 \n
验证通过!可用向量化操作是它相对其他范数的巨大优势。
\n
平方 $L^2$ 范数的导数 \n
我们已经看到,范数可以通过汇总误差向量来评估模型的好坏。
\n
现在我们更进一步,看看如何改变模型参数以降低整体误差。为此可以使用代价函数,它把模型的误差与参数取值关联起来。可用梯度下降算法寻找该函数的最小值。梯度下降通过对每个参数求导(偏导数=梯度)来进行。这就是高效计算导数至关重要的原因。
\n
事实上,平方 $L^2$ 范数的一个巨大优势是其偏导易于计算。设有如下向量:
\n
$$ \\bs{u}= \\begin{bmatrix} u_1\\ u_2\\ \\cdots \\ u_n \\end{bmatrix} $$
\n
我们看到它的平方 $L^2$ 范数为:
\n
$$ \\norm{\\bs{u}}_2^2 = u_1^2+u_2^2+\\cdots+u_n^2 $$
\n
接着,为计算偏导,我们把其他变量视为常数。比如,对 $u_1$ 的偏导就是 $u_1^2+a$ 的导数($a$ 是对应其他变量的常数)。因此有如下偏导:
\n
$$ \\begin{cases} \\dfrac{d\\norm{\\bs{u}}_2^2}{du_1} = 2u_1\\\\ \\dfrac{d\\norm{\\bs{u}}_2^2}{du_2} = 2u_2\\\\ \\cdots\\\\ \\dfrac{d\\norm{\\bs{u}}_2^2}{du_n} = 2u_n \\end{cases} $$
\n
平方 $L^2$ 范数梯度的好处在于,各偏导不依赖其他变量。我们将看到 $L^2$ 范数并非如此。
\n
$L^2$ 范数的导数 \n
对于 $L^2$ 范数,导数更复杂,会受向量中每个元素的影响。以前述向量 $\\bs{u}$ 为例,$L^2$ 范数为:
\n
$$ \\norm{\\bs{u}}_2 = \\sqrt{(u_1^2+u_2^2+\\cdots+u_n^2)} = (u_1^2+u_2^2+\\cdots+u_n^2)^{\\frac{1}{2}} $$
\n
我们对 $u_1$ 求导:
\n
\n
可以看到 $u_1$ 的偏导仍包含 $u_2...u_n$。其他梯度同理:
\n
$$ \\begin{cases} \\dfrac{d\\norm{\\bs{u}}_2}{du_1} = \\dfrac{u_1}{\\sqrt{(u_1^2+u_2^2+\\cdots+u_n^2)}}\\\\ \\dfrac{d\\norm{\\bs{u}}_2}{du_2} = \\dfrac{u_2}{\\sqrt{(u_1^2+u_2^2+\\cdots+u_n^2)}}\\\\ \\cdots\\\\ \\dfrac{d\\norm{\\bs{u}}_2}{du_n} = \\dfrac{u_n}{\\sqrt{(u_1^2+u_2^2+\\cdots+u_n^2)}}\\\\ \\end{cases} $$
\n
其他考量 \n
平方 $L^2$ 范数固然不错,但它在区分 0 与小值方面不够敏感,因为函数增长缓慢。
\n
我们可以通过将平方 $L^2$ 范数与 $L^2$ 范数进行图形比较来看到这一点。$z$ 轴对应范数的值,$x$ 与 $y$ 轴对应两个参数。对于超过 2 个维度的情况也同样成立,只是更难可视化。
\n
$L^2$ 范数:
\n
\n
平方 $L^2$ 范数:
\n
\n
作比较,下面是 $L^1$ 范数:
\n
\n
这些图借助这个网站 完成。如果需要把握其形状,不妨亲自去绘制并交互查看。
\n
最大范数 \n
这是 $L^\\infty$ 范数,对应向量中绝对值最大的元素。
\n
$$ \\norm{\\bs{x}}_\\infty = \\max\\limits_i|x_i| $$
\n
矩阵范数:Frobenius 范数 \n
\n
这相当于将矩阵展平后取 $L^2$ 范数。
\n
同样可以使用 Numpy 函数:
\n
A = np. array( [ [ 1 , 2 ] , [ 6 , 4 ] , [ 3 , 2 ] ] ) \nA\n技术支持 \n
array ( [ [ 1 , 2 ] , \n [ 6 , 4 ] , \n [ 3 , 2 ] ] ) \n技术支持 \n
np. linalg. norm( A) \n技术支持 \n
8.3666002653407556 \n技术支持 \n
用范数表示点积 \n
$$ \\bs{x}^\\text{T}\\bs{y} = \\norm{\\bs{x}}_2\\cdot\\norm{\\bs{y}}_2\\cos\\theta $$
\n
向量 $\\bs{x}$ 与 $\\bs{y}$ 的点积可以由它们的 $L^2$ 范数表示。$\\theta$ 是两向量之间的夹角。
\n
示例 4. \n
我们取两个二维向量:
\n
$$ \\bs{x}= \\begin{bmatrix} 0 \\ 2 \\end{bmatrix} $$
\n
以及
\n
$$ \\bs{y}= \\begin{bmatrix} 2 \\ 2 \\end{bmatrix} $$
\n
下图是它们的图形表示:
\n
x = [ 0 , 0 , 0 , 2 ] \ny = [ 0 , 0 , 2 , 2 ] \n\nplotVectors( [ x, y] , [ sns. color_palette( ) [ 0 ] , sns. color_palette( ) [ 1 ] ] ) \n\nplt. xlim( - 1 , 3 ) \nplt. ylim( - 1 , 3 ) \n\nplt. text( - 0.5 , 1 , r'$\\vec{x}$' , size= 18 , color= sns. color_palette( ) [ 0 ] ) \nplt. text( 1.5 , 0.5 , r'$\\vec{y}$' , size= 18 , color= sns. color_palette( ) [ 1 ] ) \n技术支持 \n
< matplotlib. text. Text at 0x10a33b950 > \n技术支持 \n
\n
我们选择这个例子是因为它简单。正如所见,夹角 $\\theta$ 等于 45°。
\n
首先计算向量的点积:
\n
$$ \\bs{x^\\text{T}y}= \\begin{bmatrix} 0 & 2 \\end{bmatrix} \\cdot \\begin{bmatrix} 2 \\ 2 \\end{bmatrix} = 0\\times2+2\\times2 = 4 $$
\n
x = np. array( [ 0 , 2 ] ) \ny = np. array( [ 2 , 2 ] ) \n\nx. dot( y) \n技术支持 \n
4 \n技术支持 \n
现在计算它们的范数:
\n
$$ \\norm{\\bs{x}}_2=\\sqrt{0^2+2^2}=\\sqrt{4}=2 $$
\n
以及
\n
$$ \\norm{\\bs{y}}_2=\\sqrt{2^2+2^2}=\\sqrt{8} $$
\n
代入上式,有:
\n
$$ 2\\times\\sqrt{8}\\times cos(45)=4 $$
\n
这与点积的结果一致。下面用 numpy 完成这些运算。请注意我们使用了 Numpy 的 deg2rad 函数,因为 np.cos 接收的是弧度,需要做角度到弧度的转换。
\n
技术支持 \n
4.0000000000000009 \n技术支持 \n
结论 \n
$L^2$ 范数(矩阵情形则为 Frobenius 范数)和平方 $L^2$ 范数在机器学习、深度学习及更广泛的数据科学领域被广泛采用。例如,范数可用作代价函数。假设您想为一组数据点拟合一条直线,一种寻找更优直线的方法是以随机参数起步,通过迭代最小化代价函数来进行。代价函数表示模型的误差,您希望该误差尽可能小。范数在这里很有用,因为它提供了衡量误差的方式。范数会把包含所有误差的向量映射为一个标量,而代价函数就是在给定参数取值集下的这个标量。
\n
我们看到,范数本质上就是把一个数组归约为一个标量。我们也注意到,计算所用的函数不同会带来一些差异。选择使用哪种范数很大程度上取决于要解决的问题,因为不同范数有优缺点。例如,$L^1$ 范数比 $L^2$ 范数更稳健。这意味着 $L^2$ 范数对异常值更敏感,因为较大的误差值会带来巨大的平方误差。
\n
本教程基于我关于 Goodfellow 等《深度学习》线性代数章节的这篇文章 。
\n
参考资料 \n
https://www.quora.com/Why-is-L1-regularization-better-than-L2-regularization-provided-that-all-Norms-are-equivalent
\n
http://www.deeplearningbook.org/contents/linear_algebra.html
\n
https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.linalg.norm.html
\n
https://hadrienj.github.io/deep-learning-book-series-home/
\n
http://christopher5106.github.io/deep/learning/2016/09/16/about-loss-functions-multinomial-logistic-logarithm-cross-entropy-square-errors-euclidian-absolute-frobenius-hinge.html
\n
https://datascience.stackexchange.com/questions/10188/why-do-cost-functions-use-the-square-error
引言
\n线性代数是数据科学中必备的基础数学工具。理解这些概念有助于加深您对各类算法的认识。它确实是数据科学/机器学习路径上的优秀基石。
\n本教程的目标是通过 Python/Numpy 编码进入数据科学所需的数学世界。我认为,将线性代数这样的理论主题做成实践性教程非常有用,因为编写和阅读代码是真正理解数学概念的好方式。最重要的是,这也会很有趣!
\n本教程没有特别的前置要求,但如果您还不清楚什么是矩阵或如何做点积,可以先阅读我关于 Ian Goodfellow 深度学习一书的系列的前几篇(1 到 4)。
\n在本教程中,我们将讨论机器学习和深度学习中的一个重要概念:范数。范数被广泛使用,例如用于评估模型的好坏。读完本教程,您有望对这一概念及其在机器学习中的价值有更直观的认识。我们还将看到范数的导数如何用于训练机器学习算法。
\n基础设置
\n首先,我们来设置将要使用的库:
\n\n并为命令
\n\nbs(粗体符号)和norm(范数符号)添加一些 LaTeX 快捷方式:$$ \\newcommand\\bs[1]{\\boldsymbol{#1}} \\newcommand\\norm[1]{\\left\\lVert#1\\right\\rVert} $$
\n最后一项是设置我们将创建的图形外观:
\n\n\n一个实际示例
\n我们从一个简单的例子开始。设想您有一个歌曲数据集,包含不同特征。现在假设您想要构建一个模型,根据音乐流派、配器等其他特征来预测歌曲的时长。您训练了一个模型,并希望评估它对新歌曲时长的预测效果。一种方法是取一些新数据,用您的模型预测歌曲时长。由于对于这些观测,您知道每首歌的真实时长,便可以将真实时长与预测时长逐一比较。对于 7 条观测,您得到以下以秒为单位的结果:
\n\n这些差值可以看作模型的误差。完美的模型只有 0,而很差的模型会有很大的正或负值。
\n现在想象您尝试了另一个模型,得到的预测与真实时长之间的差值如下:
\n\n如果要找出最优模型,可以怎么做?一种自然的方法是对这些误差的绝对值求和。之所以取绝对值,是因为负误差(真实时长小于预测时长)同样是误差。总误差越小的模型越好:
\n\n\n\n\n看起来模型 1 明显优于模型 2。
\n恭喜!您刚刚为每个模型计算了误差向量的范数!
\n直觉
\n您可以将范数理解为向量的长度。为便于图形化理解,我们再次使用前面的例子。误差向量是多维的:每条观测对应一个维度。在上一个例子中,7 条观测对应 7 个维度。表示 7 维依然很难,因此我们再简化,只保留 2 条观测:
\n\n现在我们可以把数组的第一个元素看作 x 坐标,第二个元素看作 y 坐标来表示这些向量。我们先写一个函数,方便绘制向量并直观看到它们的表示。
\n如何用 Python 和 Matplotlib 绘制向量
\n我们需要一个函数来帮助绘制向量。先从使用方式入手。我们希望传入一个数组列表,对应各向量的坐标,并得到这些向量的图。我们也希望可以传入颜色数组,以便在图中区分不同向量。总结一下,我们希望这样调用函数:
\n\n让我们来编写这个函数:
\n\n该函数接收要绘制的向量数组(
\nvecs)和对应颜色(cols)作为输入。若向量仅给出 2 个维度,则从 (0, 0) 出发。底层通过迭代向量数组并使用plt.quiver()进行绘制。用新函数绘制模型 1 和模型 2 的误差:
\n\n说明:我们没有把
\nplt.show()写进函数里,以便像这里这样添加图形配置(比如坐标范围)。说明 2:这里用 sns.color_palette() 手动取了 seaborn 的颜色。
\n因此我们对每个模型都有一个误差向量。更好的模型对应更短的向量。计算向量长度的一种方式是用勾股定理:$\\sqrt{x^2+y^2}$。我们来计算两个向量的长度:
\n\n\n\n\n恭喜!您又计算了一种误差向量的范数。第一个模型的误差向量长度为 $22.36$,第二个模型的误差向量长度约为 $16.64$。在这个只用前两个值的情形下,第二个模型更好。
\n我们一开始计算的是误差之和,但也可以用勾股定理计算向量的范数。这是两种不同的范数,说明计算范数的方法不止一种。
\n范数函数:定义
\n向量的范数可以是任何将向量映射到正值的函数。可用的函数有多种,下面我们看几个例子。若满足以下性质,这些函数即可称为范数:
\n\n- \n
\n- \n
\n- \n
\n- \n
\n
\n范数非负。把范数看作长度,就容易理解它不可能为负。
\n当且仅当向量为零向量时,范数为 $0$。
\n范数满足三角不等式。见下文。
\n$\\norm{k\\cdot \\bs{u}}=\\norm{k}\\cdot\\norm{\\bs{u}}$。其中,$k$ 为标量、$\\bs{u}$ 为向量。向量与标量相乘的范数,等于该标量的绝对值与向量范数之积。
\n范数通常用两条竖线表示:$\\norm{\\bs{x}}$
\n三角不等式
\n上面提到,一个函数要成为范数,需要满足三角不等式。这意味着若干向量之和的范数,小于等于这些向量范数之和:
\n示例 1.
\n为说明其含义,我们取两个各含两个元素的向量(便于表示为 x 和 y 坐标)。我们的向量是:
\n以及
\n我们来比较:
\n以及:
\n稍后我们会介绍不同类型的范数,这里先用经典的欧几里得范数($L^2$)。$L^2$ 范数可以用 Numpy 函数
\nnp.linalg.norm()计算(参见文档)。我们稍后会更详细地讨论 $L^1$ 和 $L^2$ 范数。
\n\n\n以及
\n可见三角不等式被满足:
\n图形解释
\n您会发现该定理的图形表示非常直观。我们用
\n\nplotVectors函数绘制向量 $\\bs{u}$、$\\bs{v}$ 和 $\\bs{u}+\\bs{v}$,并添加文字标注:$\\bs{u}$ 的长度加上 $\\bs{v}$ 的长度大于向量 $\\bs{u}+\\bs{b}$ 的长度。从几何上,这只是说明两点之间的最短路径是直线!
\np-范数:一般规则
\n我们已经看到,能称为范数的函数需要满足一定条件。这意味着可以有多种函数作为范数。稍后我们会看到不同范数的利弊。以下依赖于 $p$ 的函数类被称为 $p$-范数:
\n我们一步步解析这个公式。可以看到这里有对元素的求和,因此可以把它理解为对第 $i$ 个元素的迭代:
\n\n- $\\vert\\bs{x}_i\\vert$ 取第 $i$ 个元素的绝对值
\n- $\\vert\\bs{x}_i\\vert^p$ 将其以 $p$ 次幂
\n- $\\sum_i\\vert\\bs{x}_i\\vert^p$ 将所有这些幂后的绝对值求和
\n- $(\\sum_i\\vert\\bs{x}_i\\vert^p)^{1/p}$ 对结果取 $\\frac{1}{p}$ 次幂
\n
\n配合下面这些常用的 $p$-范数示例会更清楚。
\n$L^0$ 范数
\n当 $p=0$ 时,公式变为:
\n看看这意味着什么。对绝对值取 0 次幂,对于所有非零值得到 $1$,对 $0$ 得到 $0$。
\n因此该“范数”对应向量中非零元素的个数。它严格来说并不是范数,因为如果将向量乘以 $\\alpha$,这个数量不变(违背上面的规则 4)。
\n$L^1$ 范数
\n当 $p=1$ 时,就是绝对值之和。这正是我们在教程开头凭直觉使用的方法:
\n$L^2$ 范数(欧几里得范数)
\n欧几里得范数是 $p=2$ 的 $p$-范数。这可能是最常用的范数之一,与平方的 $L^2$ 范数(见下文)并列。
\n注意这里不再需要绝对值,因为 $x$ 被平方了。这正是我们上面用勾股定理计算向量长度时用到的。
\n再看一个该范数的例子:
\n示例 2.
\n从图形上看,欧几里得范数对应从原点到由线性组合得到的点的向量长度(勾股定理)。我们看一个二维例子:向量 $\\bs{u}$ 有两个值,分别对应 $x$ 坐标和 $y$ 坐标。若绘制出该点并从原点画到该点的向量,$L^2$ 范数就是这个向量的长度。
\n例如:
\n先用公式计算范数:
\n因此 $L^2$ 范数为 $5$。
\n顺便提醒,$L^2$ 范数可以用 Numpy 的
\n\n\nlinalg.norm()计算:下面是该向量的图形表示:
\n\n可以看到向量从原点 (0, 0) 指向 (3, 4),长度为 5。
\n此处向量在二维空间,但更高维也同样成立。
\n平方欧几里得范数(平方 $L^2$ 范数)
\n平方的 $L^2$ 范数很方便,因为去掉了平方根,得到的就是向量各分量平方的简单求和。
\n平方欧几里得范数在机器学习中被广泛使用,部分原因是它可用向量运算 $\\bs{x}^\\text{T}\\bs{x}$ 计算。由于优化带来的性能收益,见这里和这里。
\n示例 3.
\n本例将看到平方欧几里得范数如何用向量化操作计算。先给定一个向量 $\\bs{x}$:
\n照例用代码验证过程。先创建 Numpy 向量 $\\bs{x}$:
\n\n\n现在对该向量转置。这只是把原本的列向量变为行向量:
\n我们可以用 Numpy 对象的
\n\n\nT属性计算 $\\bs{x}$ 的转置:$\\bs{x}$ 与 $\\bs{x}^\\text{T}$ 的点积(如果需要回顾点积请看这里)实际上对应每个元素与自身相乘:
\n这正是平方欧几里得范数的定义!
\n我们用 Numpy 来验证。记住(也请亲自试试以加深理解)点积中向量的顺序是有影响的:
\n\n\n这应该就是我们的平方欧几里得范数!我们从 $L^2$ 范数出发再平方来验证:
\n\n\n验证通过!可用向量化操作是它相对其他范数的巨大优势。
\n平方 $L^2$ 范数的导数
\n我们已经看到,范数可以通过汇总误差向量来评估模型的好坏。
\n现在我们更进一步,看看如何改变模型参数以降低整体误差。为此可以使用代价函数,它把模型的误差与参数取值关联起来。可用梯度下降算法寻找该函数的最小值。梯度下降通过对每个参数求导(偏导数=梯度)来进行。这就是高效计算导数至关重要的原因。
\n事实上,平方 $L^2$ 范数的一个巨大优势是其偏导易于计算。设有如下向量:
\n我们看到它的平方 $L^2$ 范数为:
\n接着,为计算偏导,我们把其他变量视为常数。比如,对 $u_1$ 的偏导就是 $u_1^2+a$ 的导数($a$ 是对应其他变量的常数)。因此有如下偏导:
\n平方 $L^2$ 范数梯度的好处在于,各偏导不依赖其他变量。我们将看到 $L^2$ 范数并非如此。
\n$L^2$ 范数的导数
\n对于 $L^2$ 范数,导数更复杂,会受向量中每个元素的影响。以前述向量 $\\bs{u}$ 为例,$L^2$ 范数为:
\n我们对 $u_1$ 求导:
\n可以看到 $u_1$ 的偏导仍包含 $u_2...u_n$。其他梯度同理:
\n其他考量
\n平方 $L^2$ 范数固然不错,但它在区分 0 与小值方面不够敏感,因为函数增长缓慢。
\n我们可以通过将平方 $L^2$ 范数与 $L^2$ 范数进行图形比较来看到这一点。$z$ 轴对应范数的值,$x$ 与 $y$ 轴对应两个参数。对于超过 2 个维度的情况也同样成立,只是更难可视化。
\n$L^2$ 范数:
\n平方 $L^2$ 范数:
\n作比较,下面是 $L^1$ 范数:
\n这些图借助这个网站完成。如果需要把握其形状,不妨亲自去绘制并交互查看。
\n最大范数
\n这是 $L^\\infty$ 范数,对应向量中绝对值最大的元素。
\n矩阵范数:Frobenius 范数
\n这相当于将矩阵展平后取 $L^2$ 范数。
\n同样可以使用 Numpy 函数:
\n\n\n\n\n用范数表示点积
\n向量 $\\bs{x}$ 与 $\\bs{y}$ 的点积可以由它们的 $L^2$ 范数表示。$\\theta$ 是两向量之间的夹角。
\n示例 4.
\n我们取两个二维向量:
\n以及
\n下图是它们的图形表示:
\n\n\n我们选择这个例子是因为它简单。正如所见,夹角 $\\theta$ 等于 45°。
\n首先计算向量的点积:
\n现在计算它们的范数:
\n以及
\n代入上式,有:
\n这与点积的结果一致。下面用 numpy 完成这些运算。请注意我们使用了 Numpy 的
\n\n\ndeg2rad函数,因为np.cos接收的是弧度,需要做角度到弧度的转换。结论
\n$L^2$ 范数(矩阵情形则为 Frobenius 范数)和平方 $L^2$ 范数在机器学习、深度学习及更广泛的数据科学领域被广泛采用。例如,范数可用作代价函数。假设您想为一组数据点拟合一条直线,一种寻找更优直线的方法是以随机参数起步,通过迭代最小化代价函数来进行。代价函数表示模型的误差,您希望该误差尽可能小。范数在这里很有用,因为它提供了衡量误差的方式。范数会把包含所有误差的向量映射为一个标量,而代价函数就是在给定参数取值集下的这个标量。
\n我们看到,范数本质上就是把一个数组归约为一个标量。我们也注意到,计算所用的函数不同会带来一些差异。选择使用哪种范数很大程度上取决于要解决的问题,因为不同范数有优缺点。例如,$L^1$ 范数比 $L^2$ 范数更稳健。这意味着 $L^2$ 范数对异常值更敏感,因为较大的误差值会带来巨大的平方误差。
\n本教程基于我关于 Goodfellow 等《深度学习》线性代数章节的这篇文章。
\n参考资料
\nhttps://www.quora.com/Why-is-L1-regularization-better-than-L2-regularization-provided-that-all-Norms-are-equivalent
\nhttp://www.deeplearningbook.org/contents/linear_algebra.html
\nhttps://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.linalg.norm.html
\nhttps://hadrienj.github.io/deep-learning-book-series-home/
\nhttp://christopher5106.github.io/deep/learning/2016/09/16/about-loss-functions-multinomial-logistic-logarithm-cross-entropy-square-errors-euclidian-absolute-frobenius-hinge.html
\nhttps://datascience.stackexchange.com/questions/10188/why-do-cost-functions-use-the-square-error