斯坦福大学机器学习笔记及代码(一)
(Notes and Codes of Machine Learning by Andrew Ng from Stanford University)
说明:为了保证连贯性,文章按照专题而不是原本的课程进度来组织。
零、什么是机器学习?
机器学习就是:根据已有的训练集D,采用学习算法A,得到特定的假设h,h能最恰当的拟合D。h被称为最终假设,它实际上是从假设集H中筛选的,筛选的基本要求是代价函数(cost function)最小。学习算法和假设集的不同组合就构成了不同的学习模型。
简单的说,训练集D的产生规则符合某个函数(目标函数),但它难以显式(explicitly)给出,所以难以用非学习型算法实现。学习型算法就是为了找到一种最恰当或最合理的方式来拟合这个函数,它就是最终假设h。有了h就可以预测新的情况,进行分类或回归,进而做出决策。
一、线性回归
线性回归在统计中是一种最基础的模型,也是最重要的模型,它根据给定的数据拟合出一条曲线来预测需要了解的情况,所用的算法就是最小二乘法。机器学习以线性回归开始是合理的,与常见的知识点结合紧密,不突兀,同时破除了机器学习的一些神秘性,也为后续课程的展开奠定了基础,因为很多“非线性”的算法还是以线性算法为基础的。
1.1 假设(Hypothesis)
线性回归的假设很简单,一元线性回归是二维平面的直线方程,多元线性回归则是多维空间中的平面(或者超平面):$$\theta_0+\theta_1x_1+\theta_2x_2+\cdots+\theta_nx_n = h_\theta(x^{(i)})$$
其中,$\theta_0$的系数为1,即$x_0$为1,写成向量形式如下:$$\theta^TX = h_\theta(x^{(i)})$$
其中:$$X=\begin{bmatrix}x_0\\x_1\\x_2\\\cdots\\x_n\end{bmatrix}\theta=\begin{bmatrix}\theta_0\\ \theta_1\\ \theta_2\\ \cdots\\ \theta_n\end{bmatrix}$$
把m个训练实例(Example)的变量X列成一个维度为m*(n+1)的矩阵:
$$
X=\begin{bmatrix}
1 & x^{(1)}_1 & x^{(1)}_2 & \cdots & x^{(1)}_n \\
1 & x^{(2)}_1 & x^{(2)}_2 & \cdots & x^{(2)}_n \\
\vdots & \vdots & \vdots & x_j^{(i)} & \vdots \\
1 & x^{(m)}_1 & x^{(m)}_2 & \cdots & x^{(m)}_n \\
\end{bmatrix}_{m\times (n+1)}
=\begin{bmatrix}
x^{(1)}\\x^{(2)}\\\vdots\\x^{(m)}
\end{bmatrix}_{m\times1}
=\begin{bmatrix}
x_0&x_1&x_2&\cdots&x_n
\end{bmatrix}_{1\times (n+1)}$$
其中每行$x^{(i)}$对应一个训练实例,每列$x_j$是一个特征(Feature),X的元素即第i个训练实例的第j个特征,可以表示为:$$x^{(i)}_j$$
而假设可以进一步简写为:$$X\theta =\begin{bmatrix}h_\theta(x^{(1)})\\ h_\theta(x^{(2)})\\\cdots\\h_\theta(x^{(m)})\\\end{bmatrix}= h_\theta(X)$$
1.2 代价函数(Cost Function)
机器学习之所以能够逐渐收敛到最终假设,就是因为每一次试用训练实例进行迭代时,都减小了代价函数的值,也就是:$$min\ J(\theta)$$
线性回归的代价函数定义为预测结果与真实值误差的平方和:$$J(\theta)=\frac{1}{2m}\sum_{i=1}^m{(h_\theta(x^{(i)})-y^{(i)})^2}$$
但是,要注意的就是代价函数不总是平方和,在后面的学习中还会接触到其他形式的代价函数。
在线性回归中,将m个误差组成一个列向量为:$$E=\begin{bmatrix}h_\theta(x^{(1)})-y^{(1)}\\ h_\theta(x^{(2)})-y^{(2)} \\\cdots\\h_\theta(x^{(m)})-y^{(m)} \\\end{bmatrix}$$
E为可以理解为误差向量,就是每个预测结果$h_\theta(x^{(i)})$和对应的$y^{(i)}$之间的误差组成的列向量。注意到假设的矩阵表示形式(1.1最后一个公式),则有:$$E=X\theta-y$$
上述代价函数用矩阵表示,就是:$$J(\theta)=\frac{1}{2m}E^T E=\frac{1}{2m}(X\theta-y)^T(X\theta-y)$$
1.3 梯度下降(Gradient Descent)
数学上,函数f(x,y,z)(讨论3元函数不失一般性)的梯度是一个向量,它指向函数值变化最快的方向,它的模就是方向导数的最大值。首先引入所谓梯度算子(operator):$$\nabla = \langle\frac{\partial }{\partial x},\frac{\partial }{\partial y},\frac{\partial }{\partial z}\rangle$$
形式上来看它是一个向量,但本身不是向量,也不是任何数量。它本质是一个运算符,和加减乘除(+-*/)是同类东西,只不过它是“一元”运算符,作用于某个函数f就能到一个向量,因为相当于两边乘了f这个“数”,这个向量就是梯度:$$\nabla f= \langle\frac{\partial f}{\partial x},\frac{\partial f}{\partial y},\frac{\partial f}{\partial z}\rangle$$
梯度下降是一种优化方法,其思想是函数梯度最小时一定是该函数的一个局部极小值。梯度下降的公式为:$$\theta_j\ := \theta_j - \alpha\frac{\partial}{\partial\theta_j}J(\theta)$$
上式右侧的偏导数其实就是代价函数梯度的第j个分量,也就是:$$\frac{\partial }{\partial\theta_j}J(\theta)=\frac{1}{m}\sum_{i=1}^m{(h_\theta^{x(i)}-y^{(i)})x_j^{(i)}}$$
需要注意的是,这里的$\alpha$是学习速率(Learning Rate),它控制了算法收敛的速度,以及是否能够收敛。$\alpha$越大则$\theta$减小越快,越容易收敛,但太大的话会超出(overshoot)目标最小值,反而会发散。
代价函数的梯度写成矩阵形式为:$$\frac{\partial }{\partial\theta}J(\theta)=\frac{1}{m}X^T E$$
因此,求回归参数$\theta$的公式为:$$\theta\ := \theta - \alpha\frac{1}{m}X^T E$$
二、正规方程(Normal Equations)
这是线性代数中很投影的一个应用,用正规方程求线性回归参数的公式为:
$$X^T X\theta=X^T y$$
$$\theta=(X^T X)^{-1} X^T y$$
正规方程一般是可逆的,不可逆时用伪逆(Pseudo Inverse)实现。
三、作业代码(主要部分)
以上就是线性回归的主要内容,按照课后习题,主要的Matlab(Octave中测试通过)代码如下:
3.1 代价函数
1 function J = computeCostMulti(X, y, theta) 2 m = length(y); 3 J = 0; 4 h = X * theta; 5 E = h - y; 6 J = 1/(2*m) * E' * E; 7 end
3.2 梯度下降
1 function [theta, J_history] = gradientDescentMulti(X, y, theta, alpha, num_iters) 2 m = length(y); 3 J_history = zeros(num_iters, 1); 4 for iter = 1:num_iters 5 h = X * theta; 6 E = h - y; 7 theta = theta - alpha/m * X' * E; 8 J_history(iter) = computeCostMulti(X, y, theta); 9 end 10 end
四、矩阵与科学计算
Andrew Ng在课上推荐使用矢量化(Vectorization)方法编程,作业中也要求用矢量化方法,我认为这有着深层的原因。
其一,单从梯度下降来说,如果不使用矢量化方法,则需要用到循环(loop)来依次求所有$\theta_j$,由于循环的算法复杂度较高(可以简单的认为是$O(n^k)$,k是循环次数,也就是指数增长),对于系统资源的消耗很大,而且程序编写效率不高。幸运的是有Matlab,它长于处理矩阵(否则也不叫Matrix Laboratory了),是一门真正的科学计算语言,在Matlab中解决科学计算问题完全可以使用矢量化编程,所需要的只是在草纸上把所有过程用矩阵来表示,然后“cpoy”到Matlab中。而矩阵乘法可以用复杂度更低的算法来实现,这大大减少了消耗,增加了程序编写和运行的效率。
其二,解决科学问题,或者与数学有关的问题,其算法往往可以用矩阵来表示。解方程组、数据拟合、线性规划、网络与图论、图像处理、多元统计、矩阵力学等等,很多问题的解决如果借助矩阵来理解则会清晰的多。