第1章
必备数学知识
在这一章我会介绍一些必不可少的数学知识,其中一些可能贯穿了整本书,不学透其中一些你也许会寸步难行。
在这一章开始我们都会按照“教学内容”、“例题精讲”、“编程开发”、“习题”这几部分来组织文章,使读者可以循序渐进学习。可能会有人对这种“小学式”的教学模式嗤之以鼻,但是机器学习是一门严谨的学科,它一点也不轻松,也不会那么容易学习,要想让它为你所用,没有捷径可走,一个定理一个定理地推导,一行代码一行代码地敲,才能牢牢掌握。
教学内容
1、张量
张量(Tensor),从它的英文名Tensor来说你也许听过Google大名鼎鼎的深度学习库“TensorFlow”。
要理解张量就必须理解基向量,对于基向量我有个自己的理解。比如我们熟知的
XYZ
坐标系中基向量的大小是1方向分别是
XYZ
,那么,
(1,1,0)
就表示先沿
X
方向移动1个单位,再向
这对应到现实里是什么?比如你要从木樨地去中关村,先搭1号线3站,再搭10号线5站,如果每站长度是单位大小,不同线的地铁是基向量方向,那么从木樨地去中关村就是 (3,5) 。
那么张量,可以简单地看作高维矩阵,比如我们平时接触的是 m×n 的矩阵,现在有一个3阶张量,那么它对应的就是三维矩阵,顺便一提,张量阶和我们熟知的数学量对应如下:
张量 | 数学量 |
---|---|
0阶张量 | 标量 |
1阶张量 | 向量 |
2阶张量 | 矩阵 |
为什么需要引入张量呢?就是为了获取“更丰富的表达能力”。举个例子,你需要记录你某个状态下的体重,如果只能用标量,那么你只有一个值,体重;如果可以用向量,你可以记录你在不同时候的体重,比如你用向量 V(24) 来记录你一天的体重,那么 V(12)=54 代表你中午12点的体重是54kg,更丰富的,当你使用张量 T(24,3,35) 来表达体重,那么 T(12,2,20)=54 代表在中午12点时、你吃了2顿饭、且当日气温为20度时你的体重为54kg,这样是不是就获得了更丰富的表达能力?
总之,张量可以理解为一个带有很多维度信息的数据结构,它本身对应我们在编程中熟知的多维数组。在机器学习中,它可以组织较高维的数据。
2、生成子空间
生成子空间(span)是一个线性代数的概念,线性代数里是这样定义它的:一组不线性相关的向量的线性组合集合称之为它们的生成子空间。
最常见的例子,就像二维空间的笛卡尔坐标系
XY
,有向量
x=(1,0)
和
y=(0,1)
,很明显整个坐标系
XY
就是这两个向量的生成子空间。
数学表达上,是指有一系列向量
V=(v1,v2...vn)
构成了任意的
v
:
其中 ci 为任意实数。
如果想形象描述生成子空间,可以这么想,这一组向量相当于我们汉语里一个个汉字,线性累加运算相当于我们组合这些汉字来造句,那么生成子空间这个集合(其本质是一个集合)的大小相当于我们能用这些字表达多少意思。
3、范数
范数(norm)是数学中的一种基本概念。在泛函分析中,它定义在赋范线性空间中,并满足一定的条件,即①非负性;②齐次性;③三角不等式。它常常被用来度量某个向量空间(或矩阵)中的每个向量的长度或大小。
这段话或许有些费解,其实我们可以从需求来考虑范数这个工具(本质是一个数学工具,设计来解决某个痛处)的需求是什么。当你只做标量运算时,它们的大小很好比较,因为比如5的大小就是5(它本身),那向量的大小呢?矩阵的大小呢?怎么度量?为了可计算可比较,就引入了范数这个工具。
其实早在初中时大家都学过绝对值,这是为了比较所有实数(包括了正负数)的大小而设计的一种工具,可以认为绝对值也是一种范数。
下面就介绍几种范数,对于向量
v
来说,范数
无穷范数
||x||=max|xi|
1-范数
||x||1=∑ni=0|xi|
2-范数
||x||2=∑ni=0x2i
对于矩阵来说也需要大小的度量,列举一些矩阵的范数:
行范数
||A||=maxi∑nj=0|aij|
列范数
||A||=maxj∑ni=0|aij|
Frobenius范数
F(A)=(∑aij2)1/2
如果想形象理解范数可以这么想,现在需要对一组人进行度量,人这个事物不是一个数,怎么度量?就可以构造一种范数,比如取这个人的身高,或者这个人的高考分数,亦或者其工资数目都是将人映射到正实数的变换,那就是一种范数(严格来说也需要满足范数的三大条件)。总之范数是一种数学变换工具,要用活它。
4、特征值与奇异值分解
特征值和特征向量是一一对应的概念,求取矩阵的特征值和特征向量的运算过程叫做特征分解。现在来看,对于一个矩阵
A∈Rn×n
,满足:
每个矩阵有一组满足上式的 λ ,每一个 λ 都对应了一个特征向量 v 。
上式的右侧的含义,是一个经大小
奇异值分解(SVD分解)应用场景是
其定义:对于单位向量
v∈Rn
、
u∈Rm
,分别称他们为矩阵
A
对应于
实数矩阵一定有奇异值分解,却不一定有特征分解。
5、似然估计
极大似然估计方法(Maximum Likelihood Estimate)也称为最大概似估计或最大似然估计,是求估计的一种方法,最大概似是1821年首先由德国数学家高斯(C. F. Gauss)提出。
首先需要讲明,似然估计不是在估计概率,而是在估计概率计算式里的某个或多个参数。
它是建立在极大似然原理的基础上的一个统计方法,极大似然原理的直观想法是,一个随机试验如有若干个可能的结果A,B,C,… ,若在一次试验中,结果A出现了,那么可以认为实验条件对A的出现有利,也即出现的概率 P(A) 较大。
反过来说,当只有一个实验结果,比如R时,有几套参数a,b,c,…那么他们分别用同一个概率算式计算概率结果是
P(R,a)
、
P(R,b)
、
P(R,c)
…如果
P(R,a)
最大,我可以认为,“这个参数是真实参数的可能性最大,所以正确参数就是它”。这就是极大似然估计。
极大似然估计法的具体算法:
极大似然估计算法
输入:样本 x1...xn 及算式 L(x1,x2...xn;s1,s2...sn)
输出:参数 s^=(s1^,s2^...sn^)
根据总体样本分布数据,建立似然函数 L(x1,x2...xn;s1,s2...sn)
因为 L 与lnL 有相同的极大值点,所以求导:
∂lnL∂si^=0
得到 s^=(s1^,s2^...sn^)
6、期望值、方差和协方差
期望值是随机试验在同样的机会下重复多次的结果计算出的等同“期望”的平均值。
离散随机变量期望:
Ep(x)(f(x))=∫f(x)p(x)dx
连续随机变量期望:
Ep(x)(X)=∑xp(x)
均值是随机变量,具有概率特性;期望是常数,不具有概率特性,它是经随机变量映射而来的。
方差(variance)是在概率论和统计方差衡量随机变量或一组数据时离散程度的度量。概率论中方差用来度量随机变量和其数学期望(即均值)之间的偏离程度。统计中的方差(样本方差)是每个样本值与全体样本值的平均数之差的平方值的平均数。
计算随机变量
X
的方差:
具体推导留作练习。
从定义上看,方差是随机变量到期望值距离的期望。意即随机变量整体偏离期望的程度。
协方差(Covariance)在概率论和统计学中用于衡量两个或者多个变量的总体误差。而方差是协方差的一种特殊情况,即只有一个变量的情况。
换句话说,协方差是方差由单个变量扩充到多个随机变量的时候出现的概念,所以协方差一般以协方差矩阵的形式出现。
首先介绍随机变量向量的概念,简单来说,就是把一个联合分布(多个随机概率分布)
X1,X2...Xn
写成一个向量的形式:
那么协方差矩阵如下:
所以 cov(x⃗ ) 本质是一个 n×n 维的矩阵。
cov(x⃗ )ij 是矩阵内 ij 号元素,代表第 i 个随机变量元素对第
j 个随机变量元素的变化依赖程度。
例题精讲
例题1
证明:假设
Z1,Z2...Zm
是一列独立同分布的随机变量,令
Z¯¯¯=1m∑Zi
。假设
E(Z¯¯¯)=u
并且
P[a≤Zi≤b]
对所有
i
成立,那么对于任意
我们已知Hoeffding引理:
取值于 [a,b] 且满足 E(X)=0 的随机变量 X ,对任意
和马尔科夫不等式:
知识点
本题考察的是Hoeffding不等式的证明,在后期机器学习的问题中,要具备构造模型并寻找各种上下界的能力。所以在这里首先熟悉一下不等式证明的技巧。
注意,这道题要利用引理,需要先进行“换元法”替换一下。
解析
注意题目中的条件:独立同分布,做概率统计的题,要对“独立”这个条件敏感,如果 Z1,Z2...Zm 满足独立性假设,意味着我们可以得到:
E[∏Xi]=∏E[Xi]
在证明过程中,还要注意 “凑”,也就是朝着结果去构造变量来换元。
令
Xi=Zi−E[Zi]
并且
Xi=1m∑Xi
。由指数函数的单调性及马尔科夫不等式,对任意的
λ>0
和
ϵ>0
有:
根据独立性假设:
再由Hoeffding引理:
那么:
那么令 λ=4mϵ/(b−a)2 :
也易证对于 −X¯¯¯ 有:
综上,得证。
例题2
Hessian矩阵意即包含了函数的所有2阶偏导数的矩阵。比如对于函数方程
f(x,w=(w1,w2...wn))
(通常这是一个最大似然估计表达式,需要你求参数
w
)其Hessian矩阵为:
易知 w=(w1,w2...wn) 是 n 维空间的一个点,假设有拐点
负定矩阵:如果一个实数矩阵 A 满足对于任意实向量
则称之为负定矩阵。
问题:今有某似然函数 L 的Hessian矩阵为
求证满足该式的 w 为极大值点。
知识点
本题考察的倒不如说是矩阵的运算,事实上这道题改编自《机器学习基础教程》的讲义,将其内容稍作推广变成了样本
x 的维度不仅是2维的情形。注意,这道题要注意运算中维度的统一。
解析
注意题目中的条件:负定矩阵的判定,可以从这个条件入手来证明。其实这道题目的在于介绍Hessian矩阵的概念和它的意义,题目非常容易。
设 z 为和随机变量
x 同维的任意实向量,那么根据条件,相当于证明:
−1δ2zTXTXz<0
也就是:
zTXTXz>0
注意 X=⎡⎣⎢⎢⎢⎢xT1xT2...xTm⎤⎦⎥⎥⎥⎥ 于是:
zTXTXz=[zTx1zTx2...zTxm]⎡⎣⎢⎢⎢⎢xT1zxT2z...xTmz⎤⎦⎥⎥⎥⎥=∑(xTiz)2>0
得证。例题3
证明:如果 ||B||<1 ,那么:
1、 I±B 为非奇异矩阵
2、 ||(I±B)−1||≤11−||B||知识点
本题考察矩阵范数及其不等式的证明,实质上也是考察范数满足三大的三大条件。证明亦要从三大原则入手。
注意,在数学的证明中,假如正面入手困难,可以考虑反证法。
解析
这道题的证明中有的技巧对在之后机器学习的矩阵范数上下界的寻找中大有裨益,这是设计为例题的意义。
先考虑反证 I±B 为非奇异矩阵,首先若为奇异那么就有 det(I±B)=0 也就是 (I±B)x=0 有非零解 x ,也就是有±Bx=x ,那么就有 ||Bx||/||x||=1 ,于是就有 ||B||≥0 ,矛盾!现在直接证明本题小题2;首先
(I−B)(I−B)−1=I
因此:
(I−B)−1=I+B(I−B)−1
从而:
||(I±B)−1||≤||I||+||B||||(I±B)−1||
||(I−B)−1||≤11−||B||
得证。变式题
请证明:
||(I±B)−1||≥11+||B||编程开发
在本章中我们将首先学习使用c++来进行矩阵计算。使用c++进行科学计算,有如下几个原因:
- c++作为编译语言速度没的说,科学计算对速度性能要求比较高。
- c++有许多优秀的矩阵计算库:Armadillo、Eigen3和经典的科学计算库GSL。
- 帮助学习者掌握c++,因为要成为比较全能的机器学习开发者,许多工程接口使用或开发都须用c++。
本章我们选取强大的矩阵运算库Eigen来学习矩阵计算编程。因为Eigen有一个优点是它只需要头文件就能嵌入工程来进行计算开发,这对于初学者要简单一些。Eigen无需提前安装依赖库,它内部的头文件就完成全部功能,所以只需要把用到的头文件include进代码就可以了,但是需要注意路径正确。
本章我们选取Linux系统来编程,如果你没有Linux系统,建议查一下虚拟机安装教程来安装一个Linux系统。如果想成为一个全能的机器学习开发者,不上手Linux是不可能的。我们会用到代码编辑器Vi,还有c++的编译器g++。
首先我们要安装Eigen库以备使用,在Linux shell下执行:
[root@master ]# wget https://bitbucket.org/eigen/eigen/get/3.3.4.tar.gz [root@master ]# tar -zxvf 3.3.4.tar.gz -C Eigen [root@master ]# cd Eigen
现在可以在Eigen库根目录写代码,也可以在其他目录编写,但是要注意Eigen根目录里的头文件夹Eigen,编译时需要设定为头文件夹。
SVD分解
我们在前面说过,SVD分解本质上是一个提取矩阵特征的方法,不是所有实矩阵都有特征分解,当 A∈Rm,n 且 m≠n 时,特征值分解就不再适用。而奇异值分解是一个能适用于任意的矩阵的一种分解的方法。
首先,我们新建一个c++文件,我是在Eigen根目录下面进行创建的。Vi的命令和其他有界面的编辑器不一样,在按键“i”时才会进入“插入模式”即可以改写文件。输入“:”会等待用户输入命令,我们输入“wq”并回车代表“写入并退出”。(更多Vi/Vim命令可以查看文档学习:http://vimcdoc.sourceforge.net/)
[root@master Eigen]# vi svd.cpp [root@master Eigen]# ls bench COPYING.MPL2 failtest blas COPYING.README INSTALL build_dir CTestConfig.cmake lapack cmake CTestCustom.cmake.in README.md CMakeLists.txt debug scripts COPYING.BSD demos svd.cpp signature_of_eigen3_matrix_library COPYING.GPL doc test COPYING.LGPL Eigen unsupported COPYING.MINPACK eigen3.pc.in
编写SVD分解程序:
#include <Eigen/Core> #include <Eigen/SVD> #include <iostream> using namespace Eigen; using namespace std; int main() { MatrixXd C; C.setRandom(27,18); //设定一个27*18的随机数实矩阵; JacobiSVD<MatrixXd> svd( C, ComputeThinU | ComputeThinV); //用Jacabi迭代法求其SVD分解; cout << "奇异值矩阵D:" << svd.singularValues() << endl; //打印对角为奇异值的矩阵D; cout << "左向量矩阵V:" << svd.matrixU() << endl; //打印包含左奇异向量的矩阵U; cout << "右奇异矩阵U:" << svd.matrixV() << endl; //打印包含右奇异向量的矩阵V; return 0; }
下面我们要用g++编译器来编译链接程序,并且运行。
“-I”参数选项后面跟的就是前文提到的头文件路径,由于我们在Eigen根目录新建文件,所以它的库文件地址就在本目录,所以跟的“./”表示本目录。
[root@master Eigen]# g++ svd.cpp -o svd -I ./ [root@master Eigen]# ./svd 奇异值矩阵D: 5.27426 4.76929 4.15556 3.90333 3.70927 ... ... 左向量矩阵V: -0.270841 0.352983 0.0879032 0.0917501 0.0148428 0.225016 0.0682433 0.221633 0.164197 0.0547747 -0.256958 0.0117632 -0.0509078 ... ... 右奇异矩阵U: 0.0248013 0.339778 0.265057 0.30822 -0.0431028 -0.177124 0.288433 0.295475 0.0735039 -0.276087 -0.0596259 -0.333997 -0.096404 ... ...
关于g++/gcc详细教程可以参考:https://gcc.gnu.org/。
QR分解
QR 分解是把矩阵分解成一个正交矩阵与一个上三角矩阵的积。QR 分解的意义之一是解决线性最小二乘法问题,这是基于正交矩阵乘上另外一个任意矩阵不会改变矩阵的欧几里得范数这个重要特性,而最小二乘法就是最小化一个范数:
min(||Ax−b||)
现在来编写求解程序,我们同样新建一个QR分解的c++文件。
[root@master Eigen]# vi qr.cpp
接下来导入相关头文件,编写求解程序:
#include <Eigen/Core> #include <Eigen/QR> #include <iostream> using namespace Eigen; using namespace std; int main() { MatrixXf A(MatrixXf::Random(20,10)), Q; //设定一个20*10的随机矩阵A; A.setRandom(); HouseholderQR<MatrixXf> qr(A); //使用豪斯霍尔德方法分解; Q = qr.householderQ(); //获取正交矩阵Q; cout << "正规正交矩阵Q:\n" << Q << "\n"; return 0; }
下面我们同样用g++编译器来编译链接程序,并且运行。
[root@master Eigen]# g++ qr.cpp -o qr -I ./ [root@master Eigen]# ./qr 正规正交矩阵Q: -0.355764 0.172644 -0.296729 0.133281 0.275621 0.299565 0.346883 -0.189299 0.308725 0.067709 0.111453 -0.0286177 -0.100385 -0.203137 -0.323175 ... ...
这就是使用c++库进行计算开发的大致流程,我们使用的示例和解决的问题比较简单,但是流程和方法都大同小异。
使用c++计算库的技巧
其实我们要说的是所有外部库的使用技巧,无论你使用什么语言,解决什么问题,你都不希望重复造轮子去解决同一个问题。本章的Eigen用来解决矩阵计算,但是我并不是倡导一定要去把这个库用的多么熟练,为什么?用的再熟练,也只是一堆函数接口,它们本身没有意义,也不代表你有什么能力,作为机器学习的开发者你需要具备的能力是真正理解SVD分解和QR分解的算法原理和用途。但是使用库也有技巧:
- 养成看接口文档和官方教程的习惯。
- 要用好官方的demo代码,即示例代码,一般某个库的网站和包目录下都有示例代码。
- 实在找不到相关文档,要用grep工具去定位相关源代码,必有收获。
关于grep的用法可以自行查看程:
http://www.runoob.com/linux/linux-comm-grep.html课后习题
练习题1
设 ||A||<1 ,求证:
||I−(I−A)−1||≤||A||1−||A||提示:尝试使用例题3中的技巧。
练习题2
现在我们在设计一个线性模型: y=wx ,我们已知
Ep(y|X,w)(w^)=∫w^p(y|X,w)dy
求证: Ep(y|X,w)(w^)=w提示:利用 w^=(XTX)−1XTy 和 Ep(y|X,w)(y)=∫yp(y|X,w)dy
关于 p(y|X,w) :其中 X 是已知的自变量序列组成的矩阵(也就是训练集的特征部分)。
w 是我们想要求取的线性模型参数。 p(y|X,w) 代表在给定某个已知 (X,w) 组合时,预测值为 y <script type="math/tex" id="MathJax-Element-1090">y</script>的条件概率。