(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中。而矩陣乘法可以用復雜度更低的算法來實現,這大大減少了消耗,增加了程序編寫和運行的效率。
其二,解決科學問題,或者與數學有關的問題,其算法往往可以用矩陣來表示。解方程組、數據擬合、線性規划、網絡與圖論、圖像處理、多元統計、矩陣力學等等,很多問題的解決如果借助矩陣來理解則會清晰的多。
